@korajs/react 0.4.0 → 0.6.0

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.cjs CHANGED
@@ -1,9 +1,7 @@
1
1
  "use strict";
2
- var __create = Object.create;
3
2
  var __defProp = Object.defineProperty;
4
3
  var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
4
  var __getOwnPropNames = Object.getOwnPropertyNames;
6
- var __getProtoOf = Object.getPrototypeOf;
7
5
  var __hasOwnProp = Object.prototype.hasOwnProperty;
8
6
  var __export = (target, all) => {
9
7
  for (var name in all)
@@ -17,14 +15,6 @@ var __copyProps = (to, from, except, desc) => {
17
15
  }
18
16
  return to;
19
17
  };
20
- var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
21
- // If the importer is in node compatibility mode or this is not an ESM
22
- // file that has been converted to a CommonJS file using a Babel-
23
- // compatible transform (i.e. "__esModule" has not been set), then set
24
- // "default" to the CommonJS "module.exports" for node compatibility.
25
- isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
26
- mod
27
- ));
28
18
  var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
29
19
 
30
20
  // src/index.ts
@@ -32,8 +22,10 @@ var index_exports = {};
32
22
  __export(index_exports, {
33
23
  KoraProvider: () => KoraProvider,
34
24
  useApp: () => useApp,
25
+ useCollaborators: () => useCollaborators,
35
26
  useCollection: () => useCollection,
36
27
  useMutation: () => useMutation,
28
+ usePresence: () => usePresence,
37
29
  useQuery: () => useQuery,
38
30
  useRichText: () => useRichText,
39
31
  useSyncStatus: () => useSyncStatus
@@ -41,6 +33,7 @@ __export(index_exports, {
41
33
  module.exports = __toCommonJS(index_exports);
42
34
 
43
35
  // src/context/kora-context.ts
36
+ var import_store = require("@korajs/store");
44
37
  var import_react = require("react");
45
38
  var KoraContext = (0, import_react.createContext)(null);
46
39
  function KoraProvider({
@@ -54,6 +47,10 @@ function KoraProvider({
54
47
  const [resolvedSync, setResolvedSync] = (0, import_react.useState)(syncEngine ?? null);
55
48
  const [ready, setReady] = (0, import_react.useState)(!app);
56
49
  const [initError, setInitError] = (0, import_react.useState)(null);
50
+ const fallbackQueryStoreCache = (0, import_react.useRef)(null);
51
+ if (!fallbackQueryStoreCache.current) {
52
+ fallbackQueryStoreCache.current = new import_store.QueryStoreCache();
53
+ }
57
54
  (0, import_react.useEffect)(() => {
58
55
  if (!app) return;
59
56
  let cancelled = false;
@@ -72,6 +69,26 @@ function KoraProvider({
72
69
  cancelled = true;
73
70
  };
74
71
  }, [app]);
72
+ (0, import_react.useEffect)(() => {
73
+ return () => {
74
+ if (!app) {
75
+ fallbackQueryStoreCache.current?.clear();
76
+ }
77
+ };
78
+ }, [app]);
79
+ const contextValue = (0, import_react.useMemo)(() => {
80
+ if (!resolvedStore) {
81
+ return null;
82
+ }
83
+ return {
84
+ store: resolvedStore,
85
+ syncEngine: resolvedSync,
86
+ app: app ?? null,
87
+ events: app?.events ?? null,
88
+ subscribeSyncStatus: app?.sync?.subscribeStatus ?? null,
89
+ queryStoreCache: app && typeof app.getQueryStoreCache === "function" ? app.getQueryStoreCache() : fallbackQueryStoreCache.current
90
+ };
91
+ }, [resolvedStore, resolvedSync, app]);
75
92
  if (initError) {
76
93
  return (0, import_react.createElement)(
77
94
  "div",
@@ -83,17 +100,12 @@ function KoraProvider({
83
100
  if (!ready) {
84
101
  return fallback ?? null;
85
102
  }
86
- if (!resolvedStore) {
103
+ if (!contextValue) {
87
104
  throw new Error(
88
105
  'KoraProvider requires either an "app" or "store" prop. Pass a KoraApp from createApp() or a Store instance.'
89
106
  );
90
107
  }
91
- const value = {
92
- store: resolvedStore,
93
- syncEngine: resolvedSync,
94
- app: app ?? null
95
- };
96
- return (0, import_react.createElement)(KoraContext.Provider, { value }, children);
108
+ return (0, import_react.createElement)(KoraContext.Provider, { value: contextValue }, children);
97
109
  }
98
110
  function useKoraContext() {
99
111
  const context = (0, import_react.useContext)(KoraContext);
@@ -117,111 +129,35 @@ function useApp() {
117
129
  }
118
130
 
119
131
  // src/hooks/use-query.ts
132
+ var import_store2 = require("@korajs/store");
120
133
  var import_react2 = require("react");
121
-
122
- // src/query-store/query-store.ts
123
134
  var EMPTY_ARRAY = Object.freeze([]);
124
- var QueryStore = class {
125
- snapshot = EMPTY_ARRAY;
126
- listeners = /* @__PURE__ */ new Set();
127
- unsubscribeQuery = null;
128
- active = false;
129
- queryBuilder;
130
- constructor(queryBuilder) {
131
- this.queryBuilder = queryBuilder;
132
- }
133
- /**
134
- * Subscribe to snapshot changes. Compatible with useSyncExternalStore.
135
- *
136
- * Lazily starts the underlying query subscription when the first listener
137
- * attaches, and stops it when the last listener detaches. This allows
138
- * React StrictMode to unmount/remount without permanently killing the subscription.
139
- *
140
- * @returns Unsubscribe function
141
- */
142
- subscribe = (onStoreChange) => {
143
- this.listeners.add(onStoreChange);
144
- if (!this.active) {
145
- this.startSubscription();
146
- }
147
- return () => {
148
- this.listeners.delete(onStoreChange);
149
- if (this.listeners.size === 0) {
150
- this.stopSubscription();
151
- }
152
- };
153
- };
154
- /**
155
- * Get the current snapshot synchronously. Compatible with useSyncExternalStore.
156
- * Returns EMPTY_ARRAY before the first async fetch completes.
157
- */
158
- getSnapshot = () => {
159
- return this.snapshot;
160
- };
161
- /**
162
- * Clean up the underlying subscription and release resources.
163
- * Called by useMemo when the query descriptor changes.
164
- */
165
- destroy() {
166
- this.stopSubscription();
167
- this.listeners.clear();
168
- this.snapshot = EMPTY_ARRAY;
169
- }
170
- startSubscription() {
171
- this.active = true;
172
- this.unsubscribeQuery = this.queryBuilder.subscribe((results) => {
173
- if (!this.active) return;
174
- const newSnapshot = Object.freeze([...results]);
175
- this.snapshot = newSnapshot;
176
- this.notifyListeners();
177
- });
178
- }
179
- stopSubscription() {
180
- this.active = false;
181
- if (this.unsubscribeQuery) {
182
- this.unsubscribeQuery();
183
- this.unsubscribeQuery = null;
184
- }
185
- }
186
- notifyListeners() {
187
- for (const listener of this.listeners) {
188
- listener();
189
- }
190
- }
191
- };
192
-
193
- // src/hooks/use-query.ts
194
- var EMPTY_ARRAY2 = Object.freeze([]);
195
135
  var noopSubscribe = (_onStoreChange) => {
196
136
  return () => {
197
137
  };
198
138
  };
199
139
  function useQuery(query, options) {
140
+ const { queryStoreCache } = useKoraContext();
200
141
  const enabled = options?.enabled !== false;
201
142
  const descriptorKey = JSON.stringify(query.getDescriptor());
202
- const queryStoreRef = (0, import_react2.useRef)(null);
203
- const prevKeyRef = (0, import_react2.useRef)(null);
204
- const queryStore = (0, import_react2.useMemo)(() => {
143
+ const queryRef = (0, import_react2.useRef)(query);
144
+ queryRef.current = query;
145
+ const [queryStore, setQueryStore] = (0, import_react2.useState)(null);
146
+ (0, import_react2.useEffect)(() => {
205
147
  if (!enabled) {
206
- if (queryStoreRef.current) {
207
- queryStoreRef.current.destroy();
208
- queryStoreRef.current = null;
209
- }
210
- prevKeyRef.current = null;
211
- return null;
148
+ setQueryStore(null);
149
+ return;
212
150
  }
213
- if (prevKeyRef.current !== descriptorKey) {
214
- if (queryStoreRef.current) {
215
- queryStoreRef.current.destroy();
216
- }
217
- const newStore = new QueryStore(query);
218
- queryStoreRef.current = newStore;
219
- prevKeyRef.current = descriptorKey;
220
- return newStore;
221
- }
222
- return queryStoreRef.current;
223
- }, [descriptorKey, enabled, query]);
224
- const disabledGetSnapshot = () => EMPTY_ARRAY2;
151
+ const currentQuery = queryRef.current;
152
+ (0, import_store2.assertQueryReady)(currentQuery);
153
+ const store = queryStoreCache.getOrCreate(currentQuery);
154
+ setQueryStore(store);
155
+ return () => {
156
+ queryStoreCache.release(currentQuery);
157
+ setQueryStore(null);
158
+ };
159
+ }, [descriptorKey, enabled, queryStoreCache]);
160
+ const disabledGetSnapshot = () => EMPTY_ARRAY;
225
161
  return (0, import_react2.useSyncExternalStore)(
226
162
  queryStore ? queryStore.subscribe : noopSubscribe,
227
163
  queryStore ? queryStore.getSnapshot : disabledGetSnapshot
@@ -229,111 +165,54 @@ function useQuery(query, options) {
229
165
  }
230
166
 
231
167
  // src/hooks/use-mutation.ts
168
+ var import_bindings = require("@korajs/core/bindings");
232
169
  var import_react3 = require("react");
233
- function useMutation(mutationFn) {
234
- const [state, setState] = (0, import_react3.useState)({
235
- isLoading: false,
236
- error: null
237
- });
238
- const mountedRef = (0, import_react3.useRef)(true);
239
- (0, import_react3.useEffect)(() => {
240
- mountedRef.current = true;
241
- return () => {
242
- mountedRef.current = false;
243
- };
244
- }, []);
170
+ function useMutation(mutationFn, options) {
245
171
  const fnRef = (0, import_react3.useRef)(mutationFn);
246
172
  fnRef.current = mutationFn;
247
- const mutateAsync = (0, import_react3.useCallback)(async (...args) => {
248
- if (mountedRef.current) {
249
- setState({ isLoading: true, error: null });
250
- }
251
- try {
252
- const result = await fnRef.current(...args);
253
- if (mountedRef.current) {
254
- setState({ isLoading: false, error: null });
255
- }
256
- return result;
257
- } catch (err) {
258
- const error = err instanceof Error ? err : new Error(String(err));
259
- if (mountedRef.current) {
260
- setState({ isLoading: false, error });
261
- }
262
- throw error;
263
- }
264
- }, []);
265
- const mutate = (0, import_react3.useCallback)(
266
- (...args) => {
267
- mutateAsync(...args).catch(() => {
268
- });
269
- },
270
- [mutateAsync]
173
+ const optionsRef = (0, import_react3.useRef)(options);
174
+ optionsRef.current = options;
175
+ const controller = (0, import_react3.useMemo)(
176
+ () => (0, import_bindings.createMutationController)({
177
+ mutationFn: (...args) => fnRef.current(...args),
178
+ resolveOptions: () => optionsRef.current
179
+ }),
180
+ []
181
+ );
182
+ (0, import_react3.useEffect)(() => () => controller.destroy(), [controller]);
183
+ const state = (0, import_react3.useSyncExternalStore)(
184
+ (onStoreChange) => controller.subscribe(onStoreChange),
185
+ () => controller.getSnapshot(),
186
+ () => controller.getSnapshot()
271
187
  );
272
- const reset = (0, import_react3.useCallback)(() => {
273
- if (mountedRef.current) {
274
- setState({ isLoading: false, error: null });
275
- }
276
- }, []);
277
188
  return {
278
- mutate,
279
- mutateAsync,
189
+ mutate: (...args) => controller.mutate(...args),
190
+ mutateAsync: (...args) => controller.mutateAsync(...args),
280
191
  isLoading: state.isLoading,
281
192
  error: state.error,
282
- reset
193
+ reset: () => controller.reset()
283
194
  };
284
195
  }
285
196
 
286
197
  // src/hooks/use-sync-status.ts
198
+ var import_sync = require("@korajs/sync");
287
199
  var import_react4 = require("react");
288
- var POLL_INTERVAL_MS = 500;
289
- var OFFLINE_STATUS = Object.freeze({
290
- status: "offline",
291
- pendingOperations: 0,
292
- lastSyncedAt: null,
293
- lastSuccessfulPush: null,
294
- lastSuccessfulPull: null,
295
- conflicts: 0
296
- });
297
200
  function useSyncStatus() {
298
- const { syncEngine } = useKoraContext();
299
- const snapshotRef = (0, import_react4.useRef)(OFFLINE_STATUS);
300
- const serializedRef = (0, import_react4.useRef)(JSON.stringify(OFFLINE_STATUS));
301
- const subscribe = (0, import_react4.useCallback)(
302
- (onStoreChange) => {
303
- if (!syncEngine) return () => {
304
- };
305
- const intervalId = setInterval(() => {
306
- const newStatus = syncEngine.getStatus();
307
- const newSerialized = JSON.stringify(newStatus);
308
- if (newSerialized !== serializedRef.current) {
309
- snapshotRef.current = newStatus;
310
- serializedRef.current = newSerialized;
311
- onStoreChange();
312
- }
313
- }, POLL_INTERVAL_MS);
314
- const initialStatus = syncEngine.getStatus();
315
- const initialSerialized = JSON.stringify(initialStatus);
316
- if (initialSerialized !== serializedRef.current) {
317
- snapshotRef.current = initialStatus;
318
- serializedRef.current = initialSerialized;
319
- onStoreChange();
320
- }
321
- return () => {
322
- clearInterval(intervalId);
323
- };
324
- },
325
- [syncEngine]
201
+ const { syncEngine, subscribeSyncStatus, events } = useKoraContext();
202
+ const controller = (0, import_react4.useMemo)(
203
+ () => (0, import_sync.createSyncStatusController)({
204
+ syncEngine,
205
+ subscribeSyncStatus,
206
+ events: subscribeSyncStatus ? null : events
207
+ }),
208
+ [syncEngine, subscribeSyncStatus, events]
209
+ );
210
+ (0, import_react4.useEffect)(() => () => controller.destroy(), [controller]);
211
+ return (0, import_react4.useSyncExternalStore)(
212
+ (onStoreChange) => controller.subscribe(onStoreChange),
213
+ () => controller.getSnapshot(),
214
+ () => controller.getSnapshot()
326
215
  );
327
- const getSnapshot = (0, import_react4.useCallback)(() => {
328
- return snapshotRef.current;
329
- }, []);
330
- (0, import_react4.useEffect)(() => {
331
- if (!syncEngine) {
332
- snapshotRef.current = OFFLINE_STATUS;
333
- serializedRef.current = JSON.stringify(OFFLINE_STATUS);
334
- }
335
- }, [syncEngine]);
336
- return (0, import_react4.useSyncExternalStore)(subscribe, getSnapshot);
337
216
  }
338
217
 
339
218
  // src/hooks/use-collection.ts
@@ -346,165 +225,118 @@ function useCollection(name) {
346
225
  }
347
226
 
348
227
  // src/hooks/use-rich-text.ts
228
+ var import_store3 = require("@korajs/store");
349
229
  var import_react6 = require("react");
350
- var Y = __toESM(require("yjs"), 1);
351
- var LOAD_ORIGIN = "kora-load";
352
- var TEXT_KEY = "content";
353
- var COMPACT_AFTER_DELTAS = 20;
354
- function useRichText(collectionName, recordId, fieldName) {
230
+ function useRichText(collectionName, recordId, fieldName, options) {
355
231
  const { store, syncEngine } = useKoraContext();
356
232
  const collection = (0, import_react6.useMemo)(
357
233
  () => store.collection(collectionName),
358
234
  [store, collectionName]
359
235
  );
360
- const [doc] = (0, import_react6.useState)(() => new Y.Doc());
361
- const [ready, setReady] = (0, import_react6.useState)(false);
362
- const [error, setError] = (0, import_react6.useState)(null);
363
- const [canUndo, setCanUndo] = (0, import_react6.useState)(false);
364
- const [canRedo, setCanRedo] = (0, import_react6.useState)(false);
365
- const [cursors, setCursors] = (0, import_react6.useState)([]);
366
- const baseUpdateRef = (0, import_react6.useRef)(null);
367
- const pendingDeltasRef = (0, import_react6.useRef)([]);
368
- const text = (0, import_react6.useMemo)(() => doc.getText(TEXT_KEY), [doc]);
369
- const undoManager = (0, import_react6.useMemo)(() => new Y.UndoManager(text), [text]);
370
- const syncHistoryState = (0, import_react6.useCallback)(() => {
371
- setCanUndo(undoManager.undoStack.length > 0);
372
- setCanRedo(undoManager.redoStack.length > 0);
373
- }, [undoManager]);
374
- const undo = (0, import_react6.useCallback)(() => {
375
- undoManager.undo();
376
- syncHistoryState();
377
- }, [syncHistoryState, undoManager]);
378
- const redo = (0, import_react6.useCallback)(() => {
379
- undoManager.redo();
380
- syncHistoryState();
381
- }, [syncHistoryState, undoManager]);
382
- (0, import_react6.useEffect)(() => {
383
- let disposed = false;
384
- const initialize = async () => {
385
- setReady(false);
386
- setError(null);
387
- try {
388
- const record = await collection.findById(recordId);
389
- if (disposed) return;
390
- doc.transact(() => {
391
- const target = doc.getText(TEXT_KEY);
392
- target.delete(0, target.length);
393
- }, LOAD_ORIGIN);
394
- const encoded = encodeRichtextInput(record?.[fieldName]);
395
- baseUpdateRef.current = encoded;
396
- pendingDeltasRef.current = [];
397
- if (encoded) {
398
- Y.applyUpdate(doc, encoded, LOAD_ORIGIN);
399
- }
400
- setReady(true);
401
- } catch (cause) {
402
- if (disposed) return;
403
- setError(cause instanceof Error ? cause : new Error(String(cause)));
404
- }
405
- };
406
- const persist = async (_update, origin) => {
407
- syncHistoryState();
408
- if (origin === LOAD_ORIGIN) {
409
- return;
410
- }
411
- pendingDeltasRef.current.push(_update);
412
- const snapshot = composeRichtextSnapshot(baseUpdateRef.current, pendingDeltasRef.current);
413
- if (pendingDeltasRef.current.length >= COMPACT_AFTER_DELTAS) {
414
- baseUpdateRef.current = snapshot;
415
- pendingDeltasRef.current = [];
416
- }
417
- try {
418
- await collection.update(recordId, {
419
- [fieldName]: snapshot
420
- });
421
- } catch (cause) {
422
- if (!disposed) {
423
- setError(cause instanceof Error ? cause : new Error(String(cause)));
424
- }
425
- }
426
- };
427
- doc.on("update", persist);
428
- void initialize();
429
- syncHistoryState();
430
- return () => {
431
- disposed = true;
432
- doc.off("update", persist);
433
- undoManager.destroy();
434
- baseUpdateRef.current = null;
435
- pendingDeltasRef.current = [];
436
- };
437
- }, [collection, doc, fieldName, recordId, syncHistoryState, undoManager]);
236
+ const controller = (0, import_react6.useMemo)(
237
+ () => (0, import_store3.createRichTextController)({
238
+ collection,
239
+ collectionName,
240
+ recordId,
241
+ fieldName,
242
+ store,
243
+ syncEngine: (0, import_store3.asRichTextSyncEngine)(syncEngine),
244
+ useDocChannel: options?.useDocChannel,
245
+ user: options?.user
246
+ }),
247
+ [collection, collectionName, fieldName, options?.useDocChannel, recordId, store, syncEngine]
248
+ );
438
249
  (0, import_react6.useEffect)(() => {
439
- if (!syncEngine) return;
250
+ controller.setUser(options?.user);
251
+ }, [controller, options?.user]);
252
+ (0, import_react6.useEffect)(() => () => controller.destroy(), [controller]);
253
+ const snapshot = (0, import_react6.useSyncExternalStore)(
254
+ (onStoreChange) => controller.subscribe(onStoreChange),
255
+ () => controller.getSnapshot(),
256
+ () => controller.getSnapshot()
257
+ );
258
+ const undo = (0, import_react6.useCallback)(() => controller.undo(), [controller]);
259
+ const redo = (0, import_react6.useCallback)(() => controller.redo(), [controller]);
260
+ const setCursor = (0, import_react6.useCallback)(
261
+ (anchor, head) => controller.setCursor(anchor, head),
262
+ [controller]
263
+ );
264
+ const clearCursor = (0, import_react6.useCallback)(() => controller.clearCursor(), [controller]);
265
+ return buildResult(controller, snapshot, undo, redo, setCursor, clearCursor);
266
+ }
267
+ function buildResult(controller, snapshot, undo, redo, setCursor, clearCursor) {
268
+ return {
269
+ doc: controller.doc,
270
+ text: controller.text,
271
+ undo,
272
+ redo,
273
+ canUndo: snapshot.canUndo,
274
+ canRedo: snapshot.canRedo,
275
+ ready: snapshot.ready,
276
+ error: snapshot.error,
277
+ cursors: [...snapshot.cursors],
278
+ setCursor,
279
+ clearCursor
280
+ };
281
+ }
282
+
283
+ // src/hooks/use-presence.ts
284
+ var import_react7 = require("react");
285
+ function usePresence(user) {
286
+ const { syncEngine } = useKoraContext();
287
+ const name = user?.name ?? null;
288
+ const color = user?.color ?? null;
289
+ const avatar = user?.avatar ?? null;
290
+ (0, import_react7.useEffect)(() => {
291
+ if (!syncEngine || !name || !color) return;
440
292
  const awareness = syncEngine.getAwarenessManager();
441
- const localClientId = awareness.clientId;
442
- const updateCursors = () => {
443
- const states = awareness.getStates();
444
- const fieldCursors = [];
445
- for (const [clientId, state] of states) {
446
- if (clientId === localClientId) continue;
447
- if (!state.cursor) continue;
448
- if (state.cursor.collection !== collectionName || state.cursor.recordId !== recordId || state.cursor.field !== fieldName) {
449
- continue;
450
- }
451
- fieldCursors.push({
452
- clientId,
453
- userName: state.user.name,
454
- color: state.user.color,
455
- anchor: state.cursor.anchor,
456
- head: state.cursor.head
457
- });
458
- }
459
- setCursors(fieldCursors);
460
- };
461
- const unsubscribe = awareness.on("change", () => {
462
- updateCursors();
293
+ awareness.setLocalState({
294
+ user: { name, color, avatar: avatar ?? void 0 }
463
295
  });
464
- updateCursors();
465
- return unsubscribe;
466
- }, [syncEngine, collectionName, recordId, fieldName]);
467
- return { doc, text, undo, redo, canUndo, canRedo, ready, error, cursors };
296
+ return () => {
297
+ awareness.setLocalState(null);
298
+ };
299
+ }, [syncEngine, name, color, avatar]);
468
300
  }
469
- function encodeRichtextInput(value) {
470
- if (value === null || value === void 0) {
471
- return null;
472
- }
473
- if (typeof value === "string") {
474
- const doc = new Y.Doc();
475
- doc.getText(TEXT_KEY).insert(0, value);
476
- return Y.encodeStateAsUpdate(doc);
477
- }
478
- if (value instanceof Uint8Array) {
479
- return value;
480
- }
481
- if (value instanceof ArrayBuffer) {
482
- return new Uint8Array(value);
483
- }
484
- if (typeof globalThis !== "undefined" && "Buffer" in globalThis) {
485
- const BufferCtor = globalThis.Buffer;
486
- if (BufferCtor.isBuffer(value)) {
487
- return new Uint8Array(value.buffer, value.byteOffset, value.byteLength);
301
+
302
+ // src/hooks/use-collaborators.ts
303
+ var import_sync2 = require("@korajs/sync");
304
+ var import_react8 = require("react");
305
+ var EMPTY_ARRAY2 = [];
306
+ function useCollaborators() {
307
+ const { syncEngine } = useKoraContext();
308
+ const snapshotRef = (0, import_react8.useRef)(EMPTY_ARRAY2);
309
+ const subscribe = (0, import_react8.useCallback)(
310
+ (onStoreChange) => {
311
+ if (!syncEngine) {
312
+ snapshotRef.current = EMPTY_ARRAY2;
313
+ return () => {
314
+ };
315
+ }
316
+ const awareness = syncEngine.getAwarenessManager();
317
+ return (0, import_sync2.subscribeRemoteAwarenessStates)(awareness, (states) => {
318
+ snapshotRef.current = states;
319
+ onStoreChange();
320
+ });
321
+ },
322
+ [syncEngine]
323
+ );
324
+ const getSnapshot = (0, import_react8.useCallback)(() => snapshotRef.current, []);
325
+ (0, import_react8.useEffect)(() => {
326
+ if (!syncEngine) {
327
+ snapshotRef.current = EMPTY_ARRAY2;
488
328
  }
489
- }
490
- throw new Error("Richtext record value must be a string, Uint8Array, ArrayBuffer, or null.");
491
- }
492
- function composeRichtextSnapshot(base, deltas) {
493
- const doc = new Y.Doc();
494
- if (base) {
495
- Y.applyUpdate(doc, base);
496
- }
497
- for (const delta of deltas) {
498
- Y.applyUpdate(doc, delta);
499
- }
500
- return Y.encodeStateAsUpdate(doc);
329
+ }, [syncEngine]);
330
+ return (0, import_react8.useSyncExternalStore)(subscribe, getSnapshot);
501
331
  }
502
332
  // Annotate the CommonJS export names for ESM import in node:
503
333
  0 && (module.exports = {
504
334
  KoraProvider,
505
335
  useApp,
336
+ useCollaborators,
506
337
  useCollection,
507
338
  useMutation,
339
+ usePresence,
508
340
  useQuery,
509
341
  useRichText,
510
342
  useSyncStatus