@korajs/react 0.4.0 → 0.5.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 +243 -54
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +61 -13
- package/dist/index.d.ts +61 -13
- package/dist/index.js +247 -58
- package/dist/index.js.map +1 -1
- package/package.json +4 -4
package/dist/index.cjs
CHANGED
|
@@ -91,7 +91,9 @@ function KoraProvider({
|
|
|
91
91
|
const value = {
|
|
92
92
|
store: resolvedStore,
|
|
93
93
|
syncEngine: resolvedSync,
|
|
94
|
-
app: app ?? null
|
|
94
|
+
app: app ?? null,
|
|
95
|
+
events: app?.events ?? null,
|
|
96
|
+
subscribeSyncStatus: app?.sync?.subscribeStatus ?? null
|
|
95
97
|
};
|
|
96
98
|
return (0, import_react.createElement)(KoraContext.Provider, { value }, children);
|
|
97
99
|
}
|
|
@@ -191,6 +193,18 @@ var QueryStore = class {
|
|
|
191
193
|
};
|
|
192
194
|
|
|
193
195
|
// src/hooks/use-query.ts
|
|
196
|
+
var APP_NOT_READY_CODE = "APP_NOT_READY";
|
|
197
|
+
function assertQueryReady(query) {
|
|
198
|
+
const descriptor = query.getDescriptor();
|
|
199
|
+
if (descriptor.collection === "__pending__") {
|
|
200
|
+
const err = new Error(
|
|
201
|
+
"Cannot use useQuery() before app.ready. Await app.ready or wrap your UI in <KoraProvider app={app}>."
|
|
202
|
+
);
|
|
203
|
+
err.name = "AppNotReadyError";
|
|
204
|
+
Object.assign(err, { code: APP_NOT_READY_CODE });
|
|
205
|
+
throw err;
|
|
206
|
+
}
|
|
207
|
+
}
|
|
194
208
|
var EMPTY_ARRAY2 = Object.freeze([]);
|
|
195
209
|
var noopSubscribe = (_onStoreChange) => {
|
|
196
210
|
return () => {
|
|
@@ -198,6 +212,9 @@ var noopSubscribe = (_onStoreChange) => {
|
|
|
198
212
|
};
|
|
199
213
|
function useQuery(query, options) {
|
|
200
214
|
const enabled = options?.enabled !== false;
|
|
215
|
+
if (enabled) {
|
|
216
|
+
assertQueryReady(query);
|
|
217
|
+
}
|
|
201
218
|
const descriptorKey = JSON.stringify(query.getDescriptor());
|
|
202
219
|
const queryStoreRef = (0, import_react2.useRef)(null);
|
|
203
220
|
const prevKeyRef = (0, import_react2.useRef)(null);
|
|
@@ -230,7 +247,7 @@ function useQuery(query, options) {
|
|
|
230
247
|
|
|
231
248
|
// src/hooks/use-mutation.ts
|
|
232
249
|
var import_react3 = require("react");
|
|
233
|
-
function useMutation(mutationFn) {
|
|
250
|
+
function useMutation(mutationFn, options) {
|
|
234
251
|
const [state, setState] = (0, import_react3.useState)({
|
|
235
252
|
isLoading: false,
|
|
236
253
|
error: null
|
|
@@ -244,21 +261,35 @@ function useMutation(mutationFn) {
|
|
|
244
261
|
}, []);
|
|
245
262
|
const fnRef = (0, import_react3.useRef)(mutationFn);
|
|
246
263
|
fnRef.current = mutationFn;
|
|
264
|
+
const optionsRef = (0, import_react3.useRef)(options);
|
|
265
|
+
optionsRef.current = options;
|
|
247
266
|
const mutateAsync = (0, import_react3.useCallback)(async (...args) => {
|
|
267
|
+
const opts = optionsRef.current;
|
|
268
|
+
let context;
|
|
248
269
|
if (mountedRef.current) {
|
|
249
270
|
setState({ isLoading: true, error: null });
|
|
250
271
|
}
|
|
251
272
|
try {
|
|
273
|
+
if (opts?.onMutate) {
|
|
274
|
+
context = await opts.onMutate(...args);
|
|
275
|
+
}
|
|
252
276
|
const result = await fnRef.current(...args);
|
|
253
277
|
if (mountedRef.current) {
|
|
254
278
|
setState({ isLoading: false, error: null });
|
|
255
279
|
}
|
|
280
|
+
opts?.onSuccess?.(result, ...args);
|
|
281
|
+
opts?.onSettled?.(result, null, ...args);
|
|
256
282
|
return result;
|
|
257
283
|
} catch (err) {
|
|
258
284
|
const error = err instanceof Error ? err : new Error(String(err));
|
|
285
|
+
if (context !== void 0 && opts?.onRollback) {
|
|
286
|
+
await opts.onRollback(context, ...args);
|
|
287
|
+
}
|
|
259
288
|
if (mountedRef.current) {
|
|
260
289
|
setState({ isLoading: false, error });
|
|
261
290
|
}
|
|
291
|
+
opts?.onError?.(error, ...args);
|
|
292
|
+
opts?.onSettled?.(void 0, error, ...args);
|
|
262
293
|
throw error;
|
|
263
294
|
}
|
|
264
295
|
}, []);
|
|
@@ -285,7 +316,6 @@ function useMutation(mutationFn) {
|
|
|
285
316
|
|
|
286
317
|
// src/hooks/use-sync-status.ts
|
|
287
318
|
var import_react4 = require("react");
|
|
288
|
-
var POLL_INTERVAL_MS = 500;
|
|
289
319
|
var OFFLINE_STATUS = Object.freeze({
|
|
290
320
|
status: "offline",
|
|
291
321
|
pendingOperations: 0,
|
|
@@ -294,15 +324,36 @@ var OFFLINE_STATUS = Object.freeze({
|
|
|
294
324
|
lastSuccessfulPull: null,
|
|
295
325
|
conflicts: 0
|
|
296
326
|
});
|
|
327
|
+
var SYNC_STATUS_EVENT_TYPES = [
|
|
328
|
+
"sync:connected",
|
|
329
|
+
"sync:disconnected",
|
|
330
|
+
"sync:schema-mismatch",
|
|
331
|
+
"sync:auth-failed",
|
|
332
|
+
"sync:sent",
|
|
333
|
+
"sync:received",
|
|
334
|
+
"sync:acknowledged",
|
|
335
|
+
"sync:apply-failed",
|
|
336
|
+
"sync:diagnostics",
|
|
337
|
+
"sync:initial-sync-progress"
|
|
338
|
+
];
|
|
297
339
|
function useSyncStatus() {
|
|
298
|
-
const { syncEngine } = useKoraContext();
|
|
340
|
+
const { syncEngine, subscribeSyncStatus, events } = useKoraContext();
|
|
299
341
|
const snapshotRef = (0, import_react4.useRef)(OFFLINE_STATUS);
|
|
300
342
|
const serializedRef = (0, import_react4.useRef)(JSON.stringify(OFFLINE_STATUS));
|
|
301
343
|
const subscribe = (0, import_react4.useCallback)(
|
|
302
344
|
(onStoreChange) => {
|
|
303
|
-
if (
|
|
304
|
-
|
|
305
|
-
|
|
345
|
+
if (subscribeSyncStatus) {
|
|
346
|
+
return subscribeSyncStatus((status) => {
|
|
347
|
+
snapshotRef.current = status;
|
|
348
|
+
serializedRef.current = JSON.stringify(status);
|
|
349
|
+
onStoreChange();
|
|
350
|
+
});
|
|
351
|
+
}
|
|
352
|
+
if (!syncEngine) {
|
|
353
|
+
return () => {
|
|
354
|
+
};
|
|
355
|
+
}
|
|
356
|
+
const refresh = () => {
|
|
306
357
|
const newStatus = syncEngine.getStatus();
|
|
307
358
|
const newSerialized = JSON.stringify(newStatus);
|
|
308
359
|
if (newSerialized !== serializedRef.current) {
|
|
@@ -310,29 +361,28 @@ function useSyncStatus() {
|
|
|
310
361
|
serializedRef.current = newSerialized;
|
|
311
362
|
onStoreChange();
|
|
312
363
|
}
|
|
313
|
-
}
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
364
|
+
};
|
|
365
|
+
if (events) {
|
|
366
|
+
const unsubs = SYNC_STATUS_EVENT_TYPES.map((type) => events.on(type, refresh));
|
|
367
|
+
refresh();
|
|
368
|
+
return () => {
|
|
369
|
+
for (const unsub of unsubs) {
|
|
370
|
+
unsub();
|
|
371
|
+
}
|
|
372
|
+
};
|
|
320
373
|
}
|
|
374
|
+
refresh();
|
|
321
375
|
return () => {
|
|
322
|
-
clearInterval(intervalId);
|
|
323
376
|
};
|
|
324
377
|
},
|
|
325
|
-
[syncEngine]
|
|
378
|
+
[syncEngine, subscribeSyncStatus, events]
|
|
326
379
|
);
|
|
327
380
|
const getSnapshot = (0, import_react4.useCallback)(() => {
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
(0, import_react4.useEffect)(() => {
|
|
331
|
-
if (!syncEngine) {
|
|
332
|
-
snapshotRef.current = OFFLINE_STATUS;
|
|
333
|
-
serializedRef.current = JSON.stringify(OFFLINE_STATUS);
|
|
381
|
+
if (subscribeSyncStatus) {
|
|
382
|
+
return snapshotRef.current;
|
|
334
383
|
}
|
|
335
|
-
|
|
384
|
+
return syncEngine ? syncEngine.getStatus() : OFFLINE_STATUS;
|
|
385
|
+
}, [syncEngine, subscribeSyncStatus]);
|
|
336
386
|
return (0, import_react4.useSyncExternalStore)(subscribe, getSnapshot);
|
|
337
387
|
}
|
|
338
388
|
|
|
@@ -346,12 +396,16 @@ function useCollection(name) {
|
|
|
346
396
|
}
|
|
347
397
|
|
|
348
398
|
// src/hooks/use-rich-text.ts
|
|
399
|
+
var import_store = require("@korajs/store");
|
|
349
400
|
var import_react6 = require("react");
|
|
350
401
|
var Y = __toESM(require("yjs"), 1);
|
|
351
402
|
var LOAD_ORIGIN = "kora-load";
|
|
403
|
+
var REMOTE_ORIGIN = "kora-remote";
|
|
404
|
+
var DOC_CHANNEL_ORIGIN = "kora-doc-channel";
|
|
352
405
|
var TEXT_KEY = "content";
|
|
353
406
|
var COMPACT_AFTER_DELTAS = 20;
|
|
354
|
-
|
|
407
|
+
var PERSIST_DEBOUNCE_MS = 400;
|
|
408
|
+
function useRichText(collectionName, recordId, fieldName, options) {
|
|
355
409
|
const { store, syncEngine } = useKoraContext();
|
|
356
410
|
const collection = (0, import_react6.useMemo)(
|
|
357
411
|
() => store.collection(collectionName),
|
|
@@ -365,6 +419,12 @@ function useRichText(collectionName, recordId, fieldName) {
|
|
|
365
419
|
const [cursors, setCursors] = (0, import_react6.useState)([]);
|
|
366
420
|
const baseUpdateRef = (0, import_react6.useRef)(null);
|
|
367
421
|
const pendingDeltasRef = (0, import_react6.useRef)([]);
|
|
422
|
+
const docChannelActiveRef = (0, import_react6.useRef)(false);
|
|
423
|
+
const persistTimerRef = (0, import_react6.useRef)(null);
|
|
424
|
+
const userRef = (0, import_react6.useRef)(options?.user);
|
|
425
|
+
(0, import_react6.useEffect)(() => {
|
|
426
|
+
userRef.current = options?.user;
|
|
427
|
+
}, [options?.user]);
|
|
368
428
|
const text = (0, import_react6.useMemo)(() => doc.getText(TEXT_KEY), [doc]);
|
|
369
429
|
const undoManager = (0, import_react6.useMemo)(() => new Y.UndoManager(text), [text]);
|
|
370
430
|
const syncHistoryState = (0, import_react6.useCallback)(() => {
|
|
@@ -379,6 +439,72 @@ function useRichText(collectionName, recordId, fieldName) {
|
|
|
379
439
|
undoManager.redo();
|
|
380
440
|
syncHistoryState();
|
|
381
441
|
}, [syncHistoryState, undoManager]);
|
|
442
|
+
const resolveAwarenessUser = (0, import_react6.useCallback)(() => {
|
|
443
|
+
if (userRef.current) {
|
|
444
|
+
return userRef.current;
|
|
445
|
+
}
|
|
446
|
+
if (syncEngine) {
|
|
447
|
+
const existing = syncEngine.getAwarenessManager().getLocalState()?.user;
|
|
448
|
+
if (existing) {
|
|
449
|
+
return existing;
|
|
450
|
+
}
|
|
451
|
+
}
|
|
452
|
+
return {
|
|
453
|
+
name: "Anonymous",
|
|
454
|
+
color: "#6366f1"
|
|
455
|
+
};
|
|
456
|
+
}, [syncEngine]);
|
|
457
|
+
const setCursor = (0, import_react6.useCallback)(
|
|
458
|
+
(anchor, head) => {
|
|
459
|
+
if (!syncEngine) {
|
|
460
|
+
return;
|
|
461
|
+
}
|
|
462
|
+
const awareness = syncEngine.getAwarenessManager();
|
|
463
|
+
const state = {
|
|
464
|
+
user: resolveAwarenessUser(),
|
|
465
|
+
cursor: {
|
|
466
|
+
collection: collectionName,
|
|
467
|
+
recordId,
|
|
468
|
+
field: fieldName,
|
|
469
|
+
anchor,
|
|
470
|
+
head
|
|
471
|
+
}
|
|
472
|
+
};
|
|
473
|
+
awareness.setLocalState(state);
|
|
474
|
+
},
|
|
475
|
+
[collectionName, fieldName, recordId, resolveAwarenessUser, syncEngine]
|
|
476
|
+
);
|
|
477
|
+
const clearCursor = (0, import_react6.useCallback)(() => {
|
|
478
|
+
if (!syncEngine) {
|
|
479
|
+
return;
|
|
480
|
+
}
|
|
481
|
+
const awareness = syncEngine.getAwarenessManager();
|
|
482
|
+
const current = awareness.getLocalState();
|
|
483
|
+
if (!current?.cursor) {
|
|
484
|
+
return;
|
|
485
|
+
}
|
|
486
|
+
if (current.cursor.collection !== collectionName || current.cursor.recordId !== recordId || current.cursor.field !== fieldName) {
|
|
487
|
+
return;
|
|
488
|
+
}
|
|
489
|
+
awareness.setLocalState({ user: current.user });
|
|
490
|
+
}, [collectionName, fieldName, recordId, syncEngine]);
|
|
491
|
+
const applyRemoteSnapshot = (0, import_react6.useCallback)(
|
|
492
|
+
(value) => {
|
|
493
|
+
const encoded = encodeRichtextInput(value);
|
|
494
|
+
if (!encoded) {
|
|
495
|
+
return;
|
|
496
|
+
}
|
|
497
|
+
const currentSnapshot = Y.encodeStateAsUpdate(doc);
|
|
498
|
+
if (updatesEqual(currentSnapshot, encoded)) {
|
|
499
|
+
return;
|
|
500
|
+
}
|
|
501
|
+
Y.applyUpdate(doc, encoded, REMOTE_ORIGIN);
|
|
502
|
+
baseUpdateRef.current = encoded;
|
|
503
|
+
pendingDeltasRef.current = [];
|
|
504
|
+
syncHistoryState();
|
|
505
|
+
},
|
|
506
|
+
[doc, syncHistoryState]
|
|
507
|
+
);
|
|
382
508
|
(0, import_react6.useEffect)(() => {
|
|
383
509
|
let disposed = false;
|
|
384
510
|
const initialize = async () => {
|
|
@@ -397,18 +523,15 @@ function useRichText(collectionName, recordId, fieldName) {
|
|
|
397
523
|
if (encoded) {
|
|
398
524
|
Y.applyUpdate(doc, encoded, LOAD_ORIGIN);
|
|
399
525
|
}
|
|
526
|
+
const channel = syncEngine?.getRichtextDocChannel?.();
|
|
527
|
+
docChannelActiveRef.current = channel?.shouldUseChannel(encoded?.length ?? 0, options?.useDocChannel) ?? false;
|
|
400
528
|
setReady(true);
|
|
401
529
|
} catch (cause) {
|
|
402
530
|
if (disposed) return;
|
|
403
531
|
setError(cause instanceof Error ? cause : new Error(String(cause)));
|
|
404
532
|
}
|
|
405
533
|
};
|
|
406
|
-
const
|
|
407
|
-
syncHistoryState();
|
|
408
|
-
if (origin === LOAD_ORIGIN) {
|
|
409
|
-
return;
|
|
410
|
-
}
|
|
411
|
-
pendingDeltasRef.current.push(_update);
|
|
534
|
+
const flushPersist = async () => {
|
|
412
535
|
const snapshot = composeRichtextSnapshot(baseUpdateRef.current, pendingDeltasRef.current);
|
|
413
536
|
if (pendingDeltasRef.current.length >= COMPACT_AFTER_DELTAS) {
|
|
414
537
|
baseUpdateRef.current = snapshot;
|
|
@@ -424,17 +547,77 @@ function useRichText(collectionName, recordId, fieldName) {
|
|
|
424
547
|
}
|
|
425
548
|
}
|
|
426
549
|
};
|
|
550
|
+
const schedulePersist = () => {
|
|
551
|
+
if (persistTimerRef.current) {
|
|
552
|
+
clearTimeout(persistTimerRef.current);
|
|
553
|
+
}
|
|
554
|
+
persistTimerRef.current = setTimeout(() => {
|
|
555
|
+
persistTimerRef.current = null;
|
|
556
|
+
void flushPersist();
|
|
557
|
+
}, PERSIST_DEBOUNCE_MS);
|
|
558
|
+
};
|
|
559
|
+
const persist = (_update, origin) => {
|
|
560
|
+
syncHistoryState();
|
|
561
|
+
if (origin === LOAD_ORIGIN || origin === REMOTE_ORIGIN || origin === DOC_CHANNEL_ORIGIN) {
|
|
562
|
+
return;
|
|
563
|
+
}
|
|
564
|
+
pendingDeltasRef.current.push(_update);
|
|
565
|
+
if (docChannelActiveRef.current && syncEngine) {
|
|
566
|
+
syncEngine.getRichtextDocChannel().send(collectionName, recordId, fieldName, _update);
|
|
567
|
+
schedulePersist();
|
|
568
|
+
return;
|
|
569
|
+
}
|
|
570
|
+
void flushPersist();
|
|
571
|
+
};
|
|
427
572
|
doc.on("update", persist);
|
|
428
573
|
void initialize();
|
|
429
574
|
syncHistoryState();
|
|
430
575
|
return () => {
|
|
431
576
|
disposed = true;
|
|
577
|
+
if (persistTimerRef.current) {
|
|
578
|
+
clearTimeout(persistTimerRef.current);
|
|
579
|
+
persistTimerRef.current = null;
|
|
580
|
+
}
|
|
432
581
|
doc.off("update", persist);
|
|
433
582
|
undoManager.destroy();
|
|
434
583
|
baseUpdateRef.current = null;
|
|
435
584
|
pendingDeltasRef.current = [];
|
|
585
|
+
docChannelActiveRef.current = false;
|
|
436
586
|
};
|
|
437
|
-
}, [
|
|
587
|
+
}, [
|
|
588
|
+
collection,
|
|
589
|
+
collectionName,
|
|
590
|
+
doc,
|
|
591
|
+
fieldName,
|
|
592
|
+
options?.useDocChannel,
|
|
593
|
+
recordId,
|
|
594
|
+
syncEngine,
|
|
595
|
+
syncHistoryState,
|
|
596
|
+
undoManager
|
|
597
|
+
]);
|
|
598
|
+
(0, import_react6.useEffect)(() => {
|
|
599
|
+
if (!ready || !syncEngine || !docChannelActiveRef.current) {
|
|
600
|
+
return;
|
|
601
|
+
}
|
|
602
|
+
const channel = syncEngine.getRichtextDocChannel();
|
|
603
|
+
return channel.subscribe(collectionName, recordId, fieldName, (update) => {
|
|
604
|
+
Y.applyUpdate(doc, update, DOC_CHANNEL_ORIGIN);
|
|
605
|
+
syncHistoryState();
|
|
606
|
+
});
|
|
607
|
+
}, [collectionName, doc, fieldName, ready, recordId, syncEngine, syncHistoryState]);
|
|
608
|
+
(0, import_react6.useEffect)(() => {
|
|
609
|
+
if (!ready) {
|
|
610
|
+
return;
|
|
611
|
+
}
|
|
612
|
+
const unsubscribe = store.collection(collectionName).where({ id: recordId }).subscribe((results) => {
|
|
613
|
+
const record = results[0];
|
|
614
|
+
if (!record) {
|
|
615
|
+
return;
|
|
616
|
+
}
|
|
617
|
+
applyRemoteSnapshot(record[fieldName]);
|
|
618
|
+
});
|
|
619
|
+
return unsubscribe;
|
|
620
|
+
}, [applyRemoteSnapshot, collectionName, fieldName, ready, recordId, store]);
|
|
438
621
|
(0, import_react6.useEffect)(() => {
|
|
439
622
|
if (!syncEngine) return;
|
|
440
623
|
const awareness = syncEngine.getAwarenessManager();
|
|
@@ -462,42 +645,48 @@ function useRichText(collectionName, recordId, fieldName) {
|
|
|
462
645
|
updateCursors();
|
|
463
646
|
});
|
|
464
647
|
updateCursors();
|
|
465
|
-
return
|
|
466
|
-
|
|
467
|
-
|
|
648
|
+
return () => {
|
|
649
|
+
unsubscribe();
|
|
650
|
+
clearCursor();
|
|
651
|
+
};
|
|
652
|
+
}, [clearCursor, collectionName, fieldName, recordId, syncEngine]);
|
|
653
|
+
return { doc, text, undo, redo, canUndo, canRedo, ready, error, cursors, setCursor, clearCursor };
|
|
468
654
|
}
|
|
469
655
|
function encodeRichtextInput(value) {
|
|
470
656
|
if (value === null || value === void 0) {
|
|
471
657
|
return null;
|
|
472
658
|
}
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
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);
|
|
659
|
+
try {
|
|
660
|
+
return (0, import_store.encodeRichtext)(value);
|
|
661
|
+
} catch {
|
|
662
|
+
if (typeof value === "string") {
|
|
663
|
+
const fallbackDoc = new Y.Doc();
|
|
664
|
+
fallbackDoc.getText(TEXT_KEY).insert(0, value);
|
|
665
|
+
return Y.encodeStateAsUpdate(fallbackDoc);
|
|
488
666
|
}
|
|
667
|
+
throw new Error("Richtext record value must be a string, Uint8Array, ArrayBuffer, or null.");
|
|
489
668
|
}
|
|
490
|
-
throw new Error("Richtext record value must be a string, Uint8Array, ArrayBuffer, or null.");
|
|
491
669
|
}
|
|
492
670
|
function composeRichtextSnapshot(base, deltas) {
|
|
493
|
-
const
|
|
671
|
+
const mergedDoc = new Y.Doc();
|
|
494
672
|
if (base) {
|
|
495
|
-
Y.applyUpdate(
|
|
673
|
+
Y.applyUpdate(mergedDoc, base);
|
|
496
674
|
}
|
|
497
675
|
for (const delta of deltas) {
|
|
498
|
-
Y.applyUpdate(
|
|
676
|
+
Y.applyUpdate(mergedDoc, delta);
|
|
677
|
+
}
|
|
678
|
+
return Y.encodeStateAsUpdate(mergedDoc);
|
|
679
|
+
}
|
|
680
|
+
function updatesEqual(left, right) {
|
|
681
|
+
if (left.length !== right.length) {
|
|
682
|
+
return false;
|
|
683
|
+
}
|
|
684
|
+
for (let index = 0; index < left.length; index++) {
|
|
685
|
+
if (left[index] !== right[index]) {
|
|
686
|
+
return false;
|
|
687
|
+
}
|
|
499
688
|
}
|
|
500
|
-
return
|
|
689
|
+
return true;
|
|
501
690
|
}
|
|
502
691
|
// Annotate the CommonJS export names for ESM import in node:
|
|
503
692
|
0 && (module.exports = {
|
package/dist/index.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts","../src/context/kora-context.ts","../src/hooks/use-app.ts","../src/hooks/use-query.ts","../src/query-store/query-store.ts","../src/hooks/use-mutation.ts","../src/hooks/use-sync-status.ts","../src/hooks/use-collection.ts","../src/hooks/use-rich-text.ts"],"sourcesContent":["// @korajs/react — public API\n// Every export here is a public API commitment. Be explicit.\n\n// === Types ===\nexport type {\n\tKoraAppLike,\n\tKoraContextValue,\n\tKoraProviderProps,\n\tUseQueryOptions,\n\tUseMutationResult,\n\tUseRichTextResult,\n} from './types'\n\n// === Context ===\nexport { KoraProvider } from './context/kora-context'\n\n// === Hooks ===\nexport { useApp } from './hooks/use-app'\nexport { useQuery } from './hooks/use-query'\nexport { useMutation } from './hooks/use-mutation'\nexport { useSyncStatus } from './hooks/use-sync-status'\nexport { useCollection } from './hooks/use-collection'\nexport { useRichText } from './hooks/use-rich-text'\n","import type { Store } from '@korajs/store'\nimport type { SyncEngine } from '@korajs/sync'\nimport { createContext, createElement, useContext, useEffect, useState } from 'react'\nimport type { ReactNode } from 'react'\nimport type { KoraContextValue, KoraProviderProps } from '../types'\n\nconst KoraContext = createContext<KoraContextValue | null>(null)\n\n/**\n * Provides Kora store and optional sync engine to all child components.\n * Must wrap any component that uses Kora hooks (useQuery, useMutation, etc.).\n *\n * Accepts either an `app` prop (recommended) or explicit `store` + `syncEngine` props.\n *\n * When using the `app` prop, KoraProvider waits for `app.ready` before rendering\n * children. A `fallback` prop can be provided to show content while initializing.\n *\n * @example\n * ```typescript\n * // Recommended: pass the app object directly\n * const app = createApp({ schema })\n * <KoraProvider app={app}><App /></KoraProvider>\n *\n * // Advanced: pass store and syncEngine explicitly\n * <KoraProvider store={store} syncEngine={syncEngine}><App /></KoraProvider>\n * ```\n */\nfunction KoraProvider({\n\tapp,\n\tstore,\n\tsyncEngine,\n\tfallback,\n\tchildren,\n}: KoraProviderProps): ReactNode {\n\tconst [resolvedStore, setResolvedStore] = useState<Store | null>(store ?? null)\n\tconst [resolvedSync, setResolvedSync] = useState<SyncEngine | null>(syncEngine ?? null)\n\t// If no app prop, we're using the store prop and are ready immediately\n\tconst [ready, setReady] = useState(!app)\n\tconst [initError, setInitError] = useState<Error | null>(null)\n\n\tuseEffect(() => {\n\t\tif (!app) return\n\t\tlet cancelled = false\n\t\tapp.ready\n\t\t\t.then(() => {\n\t\t\t\tif (cancelled) return\n\t\t\t\tsetResolvedStore(app.getStore())\n\t\t\t\tsetResolvedSync(app.getSyncEngine())\n\t\t\t\tsetReady(true)\n\t\t\t})\n\t\t\t.catch((error: unknown) => {\n\t\t\t\tif (cancelled) return\n\t\t\t\tconst err = error instanceof Error ? error : new Error(String(error))\n\t\t\t\tconsole.error('[Kora] Initialization failed:', err)\n\t\t\t\tsetInitError(err)\n\t\t\t})\n\t\treturn () => {\n\t\t\tcancelled = true\n\t\t}\n\t}, [app])\n\n\tif (initError) {\n\t\treturn createElement(\n\t\t\t'div',\n\t\t\t{ style: { color: 'red', padding: '1rem', fontFamily: 'monospace' } },\n\t\t\tcreateElement('strong', null, 'Kora initialization error: '),\n\t\t\tinitError.message,\n\t\t)\n\t}\n\n\tif (!ready) {\n\t\treturn (fallback ?? null) as ReactNode\n\t}\n\n\tif (!resolvedStore) {\n\t\tthrow new Error(\n\t\t\t'KoraProvider requires either an \"app\" or \"store\" prop. ' +\n\t\t\t\t'Pass a KoraApp from createApp() or a Store instance.',\n\t\t)\n\t}\n\n\tconst value: KoraContextValue = {\n\t\tstore: resolvedStore,\n\t\tsyncEngine: resolvedSync,\n\t\tapp: app ?? null,\n\t}\n\treturn createElement(KoraContext.Provider, { value }, children)\n}\n\n/**\n * Internal hook to access the Kora context.\n * Throws if used outside of a KoraProvider.\n */\nfunction useKoraContext(): KoraContextValue {\n\tconst context = useContext(KoraContext)\n\tif (context === null) {\n\t\tthrow new Error(\n\t\t\t'useKoraContext must be used within a <KoraProvider>. ' +\n\t\t\t\t'Wrap your component tree with <KoraProvider store={store}>.',\n\t\t)\n\t}\n\treturn context\n}\n\nexport { KoraProvider, useKoraContext }\n","import { useKoraContext } from '../context/kora-context'\nimport type { KoraAppLike } from '../types'\n\n/**\n * React hook that returns the Kora app instance from context.\n *\n * Use the generic parameter to cast to your typed app for full type inference:\n *\n * ```typescript\n * // In your app setup:\n * export const app = createApp({ schema: mySchema })\n * export type App = typeof app\n *\n * // In components:\n * const app = useApp<App>()\n * app.todos.insert({ title: 'Hello' }) // fully typed\n * const todos = useQuery(app.todos.where({ completed: false }))\n * ```\n *\n * Requires `KoraProvider` to be initialized with the `app` prop.\n * Throws if used outside of `KoraProvider` or without an `app` prop.\n *\n * @returns The KoraApp instance, typed as `T`\n */\nexport function useApp<T extends KoraAppLike = KoraAppLike>(): T {\n\tconst { app } = useKoraContext()\n\tif (!app) {\n\t\tthrow new Error(\n\t\t\t'useApp() requires KoraProvider to be initialized with an \"app\" prop. ' +\n\t\t\t\t'Pass your createApp() result to <KoraProvider app={app}>.',\n\t\t)\n\t}\n\treturn app as T\n}\n","import type { CollectionRecord, QueryBuilder } from '@korajs/store'\nimport { useMemo, useRef, useSyncExternalStore } from 'react'\nimport { QueryStore } from '../query-store/query-store'\nimport type { UseQueryOptions } from '../types'\n\n/**\n * Frozen empty array returned when the query is disabled or before data loads.\n * Same reference prevents unnecessary re-renders.\n */\nconst EMPTY_ARRAY: readonly unknown[] = Object.freeze([])\n\nconst noopSubscribe = (_onStoreChange: () => void): (() => void) => {\n\treturn () => {}\n}\n\n/**\n * React hook for reactive queries against the local Kora store.\n *\n * Returns data synchronously from the local store — no loading spinners needed.\n * Re-renders automatically when the query results change due to mutations.\n * Uses `useSyncExternalStore` for React 18+ concurrent mode safety.\n *\n * The generic parameter `T` is inferred from the QueryBuilder, providing\n * full type safety when used with typed collection accessors.\n *\n * @param query - A QueryBuilder instance (e.g., `app.todos.where({ done: false })`)\n * @param options - Optional configuration (e.g., `{ enabled: false }` to skip the query)\n * @returns Readonly array of matching records\n *\n * @example\n * ```typescript\n * const todos = useQuery(app.todos.where({ completed: false }).orderBy('createdAt'))\n * // todos is typed as readonly InferRecord<typeof todoFields>[]\n * ```\n */\nexport function useQuery<T = CollectionRecord>(\n\tquery: QueryBuilder<T>,\n\toptions?: UseQueryOptions,\n): readonly T[] {\n\tconst enabled = options?.enabled !== false\n\n\t// Compute a stable key from the query descriptor\n\tconst descriptorKey = JSON.stringify(query.getDescriptor())\n\n\t// Track the current QueryStore instance\n\tconst queryStoreRef = useRef<QueryStore<T> | null>(null)\n\tconst prevKeyRef = useRef<string | null>(null)\n\n\t// Create or reuse a QueryStore when the descriptor changes\n\tconst queryStore = useMemo(() => {\n\t\tif (!enabled) {\n\t\t\t// Destroy previous if it exists\n\t\t\tif (queryStoreRef.current) {\n\t\t\t\tqueryStoreRef.current.destroy()\n\t\t\t\tqueryStoreRef.current = null\n\t\t\t}\n\t\t\tprevKeyRef.current = null\n\t\t\treturn null\n\t\t}\n\n\t\t// If descriptor changed, destroy previous and create new\n\t\tif (prevKeyRef.current !== descriptorKey) {\n\t\t\tif (queryStoreRef.current) {\n\t\t\t\tqueryStoreRef.current.destroy()\n\t\t\t}\n\t\t\tconst newStore = new QueryStore<T>(query)\n\t\t\tqueryStoreRef.current = newStore\n\t\t\tprevKeyRef.current = descriptorKey\n\t\t\treturn newStore\n\t\t}\n\n\t\treturn queryStoreRef.current\n\t}, [descriptorKey, enabled, query])\n\n\t// No useEffect cleanup needed: QueryStore's subscribe/unsubscribe cycle\n\t// manages the underlying subscription lifetime. When the last listener\n\t// detaches (on unmount), the subscription auto-stops. This also makes\n\t// the hook resilient to React StrictMode's double-mount cycle.\n\n\tconst disabledGetSnapshot = (): readonly T[] => EMPTY_ARRAY as readonly T[]\n\n\treturn useSyncExternalStore(\n\t\tqueryStore ? queryStore.subscribe : noopSubscribe,\n\t\tqueryStore ? queryStore.getSnapshot : disabledGetSnapshot,\n\t)\n}\n","import type { CollectionRecord, QueryBuilder } from '@korajs/store'\n\n/**\n * Frozen empty array returned as the initial snapshot before any data loads.\n * Same reference every time prevents infinite re-render loops with useSyncExternalStore.\n */\nconst EMPTY_ARRAY: readonly unknown[] = Object.freeze([])\n\n/**\n * Bridges the async QueryBuilder.subscribe() API with the synchronous\n * getSnapshot() required by React's useSyncExternalStore.\n *\n * Uses a lazy subscription model: the underlying query subscription starts\n * when the first listener attaches (via useSyncExternalStore's subscribe)\n * and stops when the last listener detaches. This makes QueryStore resilient\n * to React StrictMode's double-mount cycle, where useEffect cleanup fires\n * between mount and remount.\n *\n * Generic parameter `T` defaults to `CollectionRecord` for backward compatibility.\n */\nexport class QueryStore<T = CollectionRecord> {\n\tprivate snapshot: readonly T[] = EMPTY_ARRAY as readonly T[]\n\tprivate listeners = new Set<() => void>()\n\tprivate unsubscribeQuery: (() => void) | null = null\n\tprivate active = false\n\tprivate readonly queryBuilder: QueryBuilder<T>\n\n\tconstructor(queryBuilder: QueryBuilder<T>) {\n\t\tthis.queryBuilder = queryBuilder\n\t}\n\n\t/**\n\t * Subscribe to snapshot changes. Compatible with useSyncExternalStore.\n\t *\n\t * Lazily starts the underlying query subscription when the first listener\n\t * attaches, and stops it when the last listener detaches. This allows\n\t * React StrictMode to unmount/remount without permanently killing the subscription.\n\t *\n\t * @returns Unsubscribe function\n\t */\n\tsubscribe = (onStoreChange: () => void): (() => void) => {\n\t\tthis.listeners.add(onStoreChange)\n\n\t\t// Start the underlying query subscription when the first listener attaches\n\t\tif (!this.active) {\n\t\t\tthis.startSubscription()\n\t\t}\n\n\t\treturn () => {\n\t\t\tthis.listeners.delete(onStoreChange)\n\t\t\t// Stop the underlying subscription when the last listener detaches.\n\t\t\t// This ensures cleanup on unmount without needing a useEffect.\n\t\t\tif (this.listeners.size === 0) {\n\t\t\t\tthis.stopSubscription()\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * Get the current snapshot synchronously. Compatible with useSyncExternalStore.\n\t * Returns EMPTY_ARRAY before the first async fetch completes.\n\t */\n\tgetSnapshot = (): readonly T[] => {\n\t\treturn this.snapshot\n\t}\n\n\t/**\n\t * Clean up the underlying subscription and release resources.\n\t * Called by useMemo when the query descriptor changes.\n\t */\n\tdestroy(): void {\n\t\tthis.stopSubscription()\n\t\tthis.listeners.clear()\n\t\tthis.snapshot = EMPTY_ARRAY as readonly T[]\n\t}\n\n\tprivate startSubscription(): void {\n\t\tthis.active = true\n\t\tthis.unsubscribeQuery = this.queryBuilder.subscribe((results) => {\n\t\t\tif (!this.active) return\n\t\t\tconst newSnapshot = Object.freeze([...results])\n\t\t\tthis.snapshot = newSnapshot\n\t\t\tthis.notifyListeners()\n\t\t})\n\t}\n\n\tprivate stopSubscription(): void {\n\t\tthis.active = false\n\t\tif (this.unsubscribeQuery) {\n\t\t\tthis.unsubscribeQuery()\n\t\t\tthis.unsubscribeQuery = null\n\t\t}\n\t}\n\n\tprivate notifyListeners(): void {\n\t\tfor (const listener of this.listeners) {\n\t\t\tlistener()\n\t\t}\n\t}\n}\n","import { useCallback, useEffect, useRef, useState } from 'react'\nimport type { UseMutationResult } from '../types'\n\n/**\n * React hook for performing mutations against the local Kora store.\n *\n * Returns `mutate` for fire-and-forget usage (optimistic) and `mutateAsync`\n * for when you need to await the result. Tracks loading and error state.\n *\n * @param mutationFn - An async function to execute (e.g., `app.todos.insert`)\n * @returns Object with mutate, mutateAsync, isLoading, error, and reset\n *\n * @example\n * ```typescript\n * const { mutate } = useMutation(app.todos.insert)\n * mutate({ title: 'New todo' }) // fire-and-forget\n * ```\n */\nexport function useMutation<TData, TArgs extends unknown[]>(\n\tmutationFn: (...args: TArgs) => Promise<TData>,\n): UseMutationResult<TData, TArgs> {\n\tconst [state, setState] = useState<{ isLoading: boolean; error: Error | null }>({\n\t\tisLoading: false,\n\t\terror: null,\n\t})\n\n\t// Track mounted state to avoid state updates after unmount\n\tconst mountedRef = useRef(true)\n\tuseEffect(() => {\n\t\tmountedRef.current = true\n\t\treturn () => {\n\t\t\tmountedRef.current = false\n\t\t}\n\t}, [])\n\n\t// Keep latest mutation function in a ref to avoid stale closures\n\tconst fnRef = useRef(mutationFn)\n\tfnRef.current = mutationFn\n\n\tconst mutateAsync = useCallback(async (...args: TArgs): Promise<TData> => {\n\t\tif (mountedRef.current) {\n\t\t\tsetState({ isLoading: true, error: null })\n\t\t}\n\t\ttry {\n\t\t\tconst result = await fnRef.current(...args)\n\t\t\tif (mountedRef.current) {\n\t\t\t\tsetState({ isLoading: false, error: null })\n\t\t\t}\n\t\t\treturn result\n\t\t} catch (err) {\n\t\t\tconst error = err instanceof Error ? err : new Error(String(err))\n\t\t\tif (mountedRef.current) {\n\t\t\t\tsetState({ isLoading: false, error })\n\t\t\t}\n\t\t\tthrow error\n\t\t}\n\t}, [])\n\n\tconst mutate = useCallback(\n\t\t(...args: TArgs): void => {\n\t\t\tmutateAsync(...args).catch(() => {\n\t\t\t\t// Fire-and-forget: error is captured in state, no unhandled rejection\n\t\t\t})\n\t\t},\n\t\t[mutateAsync],\n\t)\n\n\tconst reset = useCallback((): void => {\n\t\tif (mountedRef.current) {\n\t\t\tsetState({ isLoading: false, error: null })\n\t\t}\n\t}, [])\n\n\treturn {\n\t\tmutate,\n\t\tmutateAsync,\n\t\tisLoading: state.isLoading,\n\t\terror: state.error,\n\t\treset,\n\t}\n}\n","import type { SyncStatusInfo } from '@korajs/sync'\nimport { useCallback, useEffect, useRef, useSyncExternalStore } from 'react'\nimport { useKoraContext } from '../context/kora-context'\n\nconst POLL_INTERVAL_MS = 500\n\n/** Default status returned when no sync engine is configured */\nconst OFFLINE_STATUS: SyncStatusInfo = Object.freeze({\n\tstatus: 'offline',\n\tpendingOperations: 0,\n\tlastSyncedAt: null,\n\tlastSuccessfulPush: null,\n\tlastSuccessfulPull: null,\n\tconflicts: 0,\n})\n\n/**\n * React hook for monitoring the sync engine's connection status.\n *\n * Polls the SyncEngine at ~500ms intervals and re-renders only when\n * the status actually changes. Returns a default offline status when\n * no sync engine is configured.\n *\n * @returns Current sync status information\n *\n * @example\n * ```typescript\n * const status = useSyncStatus()\n * // status.status: 'connected' | 'syncing' | 'synced' | 'offline' | 'error'\n * // status.pendingOperations: number\n * // status.lastSyncedAt: number | null\n * ```\n */\nexport function useSyncStatus(): SyncStatusInfo {\n\tconst { syncEngine } = useKoraContext()\n\n\t// Cache the latest status snapshot for stable references\n\tconst snapshotRef = useRef<SyncStatusInfo>(OFFLINE_STATUS)\n\tconst serializedRef = useRef<string>(JSON.stringify(OFFLINE_STATUS))\n\n\tconst subscribe = useCallback(\n\t\t(onStoreChange: () => void): (() => void) => {\n\t\t\tif (!syncEngine) return () => {}\n\n\t\t\t// Poll the sync engine status at regular intervals\n\t\t\tconst intervalId = setInterval(() => {\n\t\t\t\tconst newStatus = syncEngine.getStatus()\n\t\t\t\tconst newSerialized = JSON.stringify(newStatus)\n\n\t\t\t\t// Only notify React if status actually changed\n\t\t\t\tif (newSerialized !== serializedRef.current) {\n\t\t\t\t\tsnapshotRef.current = newStatus\n\t\t\t\t\tserializedRef.current = newSerialized\n\t\t\t\t\tonStoreChange()\n\t\t\t\t}\n\t\t\t}, POLL_INTERVAL_MS)\n\n\t\t\t// Do an immediate check\n\t\t\tconst initialStatus = syncEngine.getStatus()\n\t\t\tconst initialSerialized = JSON.stringify(initialStatus)\n\t\t\tif (initialSerialized !== serializedRef.current) {\n\t\t\t\tsnapshotRef.current = initialStatus\n\t\t\t\tserializedRef.current = initialSerialized\n\t\t\t\tonStoreChange()\n\t\t\t}\n\n\t\t\treturn () => {\n\t\t\t\tclearInterval(intervalId)\n\t\t\t}\n\t\t},\n\t\t[syncEngine],\n\t)\n\n\tconst getSnapshot = useCallback((): SyncStatusInfo => {\n\t\treturn snapshotRef.current\n\t}, [])\n\n\t// Reset snapshot when syncEngine changes\n\tuseEffect(() => {\n\t\tif (!syncEngine) {\n\t\t\tsnapshotRef.current = OFFLINE_STATUS\n\t\t\tserializedRef.current = JSON.stringify(OFFLINE_STATUS)\n\t\t}\n\t}, [syncEngine])\n\n\treturn useSyncExternalStore(subscribe, getSnapshot)\n}\n","import type { CollectionAccessor } from '@korajs/store'\nimport { useMemo } from 'react'\nimport { useKoraContext } from '../context/kora-context'\n\n/**\n * React hook that returns a CollectionAccessor for the given collection name.\n * Convenience hook for accessing a collection without going through the store directly.\n *\n * @param name - The collection name (must match a collection in the schema)\n * @returns CollectionAccessor with insert, findById, update, delete, and where methods\n *\n * @example\n * ```typescript\n * const todos = useCollection('todos')\n * await todos.insert({ title: 'New todo' })\n * ```\n */\nexport function useCollection(name: string): CollectionAccessor {\n\tconst { store } = useKoraContext()\n\n\treturn useMemo(() => {\n\t\treturn store.collection(name)\n\t}, [store, name])\n}\n","import type { CollectionAccessor } from '@korajs/store'\nimport type { AwarenessState, CursorInfo } from '@korajs/sync'\nimport { useCallback, useEffect, useMemo, useRef, useState } from 'react'\nimport * as Y from 'yjs'\nimport { useKoraContext } from '../context/kora-context'\nimport type { UseRichTextResult } from '../types'\n\nconst LOAD_ORIGIN = 'kora-load'\nconst TEXT_KEY = 'content'\nconst COMPACT_AFTER_DELTAS = 20\n\n/**\n * Binds a richtext field to a shared Yjs document for editor integration.\n */\nexport function useRichText(\n\tcollectionName: string,\n\trecordId: string,\n\tfieldName: string,\n): UseRichTextResult {\n\tconst { store, syncEngine } = useKoraContext()\n\tconst collection = useMemo<CollectionAccessor>(\n\t\t() => store.collection(collectionName),\n\t\t[store, collectionName],\n\t)\n\tconst [doc] = useState(() => new Y.Doc())\n\tconst [ready, setReady] = useState(false)\n\tconst [error, setError] = useState<Error | null>(null)\n\tconst [canUndo, setCanUndo] = useState(false)\n\tconst [canRedo, setCanRedo] = useState(false)\n\tconst [cursors, setCursors] = useState<CursorInfo[]>([])\n\tconst baseUpdateRef = useRef<Uint8Array | null>(null)\n\tconst pendingDeltasRef = useRef<Uint8Array[]>([])\n\n\tconst text = useMemo(() => doc.getText(TEXT_KEY), [doc])\n\tconst undoManager = useMemo(() => new Y.UndoManager(text), [text])\n\n\tconst syncHistoryState = useCallback(() => {\n\t\tsetCanUndo(undoManager.undoStack.length > 0)\n\t\tsetCanRedo(undoManager.redoStack.length > 0)\n\t}, [undoManager])\n\n\tconst undo = useCallback(() => {\n\t\tundoManager.undo()\n\t\tsyncHistoryState()\n\t}, [syncHistoryState, undoManager])\n\n\tconst redo = useCallback(() => {\n\t\tundoManager.redo()\n\t\tsyncHistoryState()\n\t}, [syncHistoryState, undoManager])\n\n\tuseEffect(() => {\n\t\tlet disposed = false\n\n\t\tconst initialize = async (): Promise<void> => {\n\t\t\tsetReady(false)\n\t\t\tsetError(null)\n\n\t\t\ttry {\n\t\t\t\tconst record = await collection.findById(recordId)\n\t\t\t\tif (disposed) return\n\n\t\t\t\tdoc.transact(() => {\n\t\t\t\t\tconst target = doc.getText(TEXT_KEY)\n\t\t\t\t\ttarget.delete(0, target.length)\n\t\t\t\t}, LOAD_ORIGIN)\n\n\t\t\t\tconst encoded = encodeRichtextInput(record?.[fieldName])\n\t\t\t\tbaseUpdateRef.current = encoded\n\t\t\t\tpendingDeltasRef.current = []\n\t\t\t\tif (encoded) {\n\t\t\t\t\tY.applyUpdate(doc, encoded, LOAD_ORIGIN)\n\t\t\t\t}\n\n\t\t\t\tsetReady(true)\n\t\t\t} catch (cause) {\n\t\t\t\tif (disposed) return\n\t\t\t\tsetError(cause instanceof Error ? cause : new Error(String(cause)))\n\t\t\t}\n\t\t}\n\n\t\tconst persist = async (_update: Uint8Array, origin: unknown): Promise<void> => {\n\t\t\tsyncHistoryState()\n\t\t\tif (origin === LOAD_ORIGIN) {\n\t\t\t\treturn\n\t\t\t}\n\n\t\t\tpendingDeltasRef.current.push(_update)\n\t\t\tconst snapshot = composeRichtextSnapshot(baseUpdateRef.current, pendingDeltasRef.current)\n\n\t\t\tif (pendingDeltasRef.current.length >= COMPACT_AFTER_DELTAS) {\n\t\t\t\tbaseUpdateRef.current = snapshot\n\t\t\t\tpendingDeltasRef.current = []\n\t\t\t}\n\n\t\t\ttry {\n\t\t\t\tawait collection.update(recordId, {\n\t\t\t\t\t[fieldName]: snapshot,\n\t\t\t\t})\n\t\t\t} catch (cause) {\n\t\t\t\tif (!disposed) {\n\t\t\t\t\tsetError(cause instanceof Error ? cause : new Error(String(cause)))\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tdoc.on('update', persist)\n\t\tvoid initialize()\n\n\t\tsyncHistoryState()\n\n\t\treturn () => {\n\t\t\tdisposed = true\n\t\t\tdoc.off('update', persist)\n\t\t\tundoManager.destroy()\n\t\t\tbaseUpdateRef.current = null\n\t\t\tpendingDeltasRef.current = []\n\t\t}\n\t}, [collection, doc, fieldName, recordId, syncHistoryState, undoManager])\n\n\t// Track remote collaborators' cursors for this specific field\n\tuseEffect(() => {\n\t\tif (!syncEngine) return\n\n\t\tconst awareness = syncEngine.getAwarenessManager()\n\t\tconst localClientId = awareness.clientId\n\n\t\tconst updateCursors = (): void => {\n\t\t\tconst states = awareness.getStates()\n\t\t\tconst fieldCursors: CursorInfo[] = []\n\n\t\t\tfor (const [clientId, state] of states) {\n\t\t\t\tif (clientId === localClientId) continue\n\t\t\t\tif (!state.cursor) continue\n\t\t\t\t// Only include cursors for this specific field\n\t\t\t\tif (\n\t\t\t\t\tstate.cursor.collection !== collectionName ||\n\t\t\t\t\tstate.cursor.recordId !== recordId ||\n\t\t\t\t\tstate.cursor.field !== fieldName\n\t\t\t\t) {\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\t\t\t\tfieldCursors.push({\n\t\t\t\t\tclientId,\n\t\t\t\t\tuserName: state.user.name,\n\t\t\t\t\tcolor: state.user.color,\n\t\t\t\t\tanchor: state.cursor.anchor,\n\t\t\t\t\thead: state.cursor.head,\n\t\t\t\t})\n\t\t\t}\n\n\t\t\tsetCursors(fieldCursors)\n\t\t}\n\n\t\tconst unsubscribe = awareness.on('change', () => {\n\t\t\tupdateCursors()\n\t\t})\n\n\t\t// Initial check\n\t\tupdateCursors()\n\n\t\treturn unsubscribe\n\t}, [syncEngine, collectionName, recordId, fieldName])\n\n\treturn { doc, text, undo, redo, canUndo, canRedo, ready, error, cursors }\n}\n\nfunction encodeRichtextInput(value: unknown): Uint8Array | null {\n\tif (value === null || value === undefined) {\n\t\treturn null\n\t}\n\n\tif (typeof value === 'string') {\n\t\tconst doc = new Y.Doc()\n\t\tdoc.getText(TEXT_KEY).insert(0, value)\n\t\treturn Y.encodeStateAsUpdate(doc)\n\t}\n\n\tif (value instanceof Uint8Array) {\n\t\treturn value\n\t}\n\n\tif (value instanceof ArrayBuffer) {\n\t\treturn new Uint8Array(value)\n\t}\n\n\t// Handle Node.js Buffer without requiring @types/node\n\tif (typeof globalThis !== 'undefined' && 'Buffer' in globalThis) {\n\t\tconst BufferCtor = (globalThis as Record<string, unknown>).Buffer as {\n\t\t\tisBuffer(v: unknown): v is { buffer: ArrayBuffer; byteOffset: number; byteLength: number }\n\t\t}\n\t\tif (BufferCtor.isBuffer(value)) {\n\t\t\treturn new Uint8Array(value.buffer, value.byteOffset, value.byteLength)\n\t\t}\n\t}\n\n\tthrow new Error('Richtext record value must be a string, Uint8Array, ArrayBuffer, or null.')\n}\n\nfunction composeRichtextSnapshot(base: Uint8Array | null, deltas: Uint8Array[]): Uint8Array {\n\tconst doc = new Y.Doc()\n\tif (base) {\n\t\tY.applyUpdate(doc, base)\n\t}\n\n\tfor (const delta of deltas) {\n\t\tY.applyUpdate(doc, delta)\n\t}\n\n\treturn Y.encodeStateAsUpdate(doc)\n}\n"],"mappings":"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","names":["import_react","EMPTY_ARRAY","import_react","import_react","import_react","import_react"]}
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/context/kora-context.ts","../src/hooks/use-app.ts","../src/hooks/use-query.ts","../src/query-store/query-store.ts","../src/hooks/use-mutation.ts","../src/hooks/use-sync-status.ts","../src/hooks/use-collection.ts","../src/hooks/use-rich-text.ts"],"sourcesContent":["// @korajs/react — public API\n// Every export here is a public API commitment. Be explicit.\n\n// === Types ===\nexport type {\n\tKoraAppLike,\n\tKoraContextValue,\n\tKoraProviderProps,\n\tUseQueryOptions,\n\tUseMutationOptions,\n\tUseMutationResult,\n\tUseRichTextResult,\n} from './types'\n\n// === Context ===\nexport { KoraProvider } from './context/kora-context'\n\n// === Hooks ===\nexport { useApp } from './hooks/use-app'\nexport { useQuery } from './hooks/use-query'\nexport { useMutation } from './hooks/use-mutation'\nexport { useSyncStatus } from './hooks/use-sync-status'\nexport { useCollection } from './hooks/use-collection'\nexport { useRichText } from './hooks/use-rich-text'\nexport type { UseRichTextOptions } from './hooks/use-rich-text'\n","import type { Store } from '@korajs/store'\nimport type { SyncEngine } from '@korajs/sync'\nimport { createContext, createElement, useContext, useEffect, useState } from 'react'\nimport type { ReactNode } from 'react'\nimport type { KoraContextValue, KoraProviderProps } from '../types'\n\nconst KoraContext = createContext<KoraContextValue | null>(null)\n\n/**\n * Provides Kora store and optional sync engine to all child components.\n * Must wrap any component that uses Kora hooks (useQuery, useMutation, etc.).\n *\n * Accepts either an `app` prop (recommended) or explicit `store` + `syncEngine` props.\n *\n * When using the `app` prop, KoraProvider waits for `app.ready` before rendering\n * children. A `fallback` prop can be provided to show content while initializing.\n *\n * @example\n * ```typescript\n * // Recommended: pass the app object directly\n * const app = createApp({ schema })\n * <KoraProvider app={app}><App /></KoraProvider>\n *\n * // Advanced: pass store and syncEngine explicitly\n * <KoraProvider store={store} syncEngine={syncEngine}><App /></KoraProvider>\n * ```\n */\nfunction KoraProvider({\n\tapp,\n\tstore,\n\tsyncEngine,\n\tfallback,\n\tchildren,\n}: KoraProviderProps): ReactNode {\n\tconst [resolvedStore, setResolvedStore] = useState<Store | null>(store ?? null)\n\tconst [resolvedSync, setResolvedSync] = useState<SyncEngine | null>(syncEngine ?? null)\n\t// If no app prop, we're using the store prop and are ready immediately\n\tconst [ready, setReady] = useState(!app)\n\tconst [initError, setInitError] = useState<Error | null>(null)\n\n\tuseEffect(() => {\n\t\tif (!app) return\n\t\tlet cancelled = false\n\t\tapp.ready\n\t\t\t.then(() => {\n\t\t\t\tif (cancelled) return\n\t\t\t\tsetResolvedStore(app.getStore())\n\t\t\t\tsetResolvedSync(app.getSyncEngine())\n\t\t\t\tsetReady(true)\n\t\t\t})\n\t\t\t.catch((error: unknown) => {\n\t\t\t\tif (cancelled) return\n\t\t\t\tconst err = error instanceof Error ? error : new Error(String(error))\n\t\t\t\tconsole.error('[Kora] Initialization failed:', err)\n\t\t\t\tsetInitError(err)\n\t\t\t})\n\t\treturn () => {\n\t\t\tcancelled = true\n\t\t}\n\t}, [app])\n\n\tif (initError) {\n\t\treturn createElement(\n\t\t\t'div',\n\t\t\t{ style: { color: 'red', padding: '1rem', fontFamily: 'monospace' } },\n\t\t\tcreateElement('strong', null, 'Kora initialization error: '),\n\t\t\tinitError.message,\n\t\t)\n\t}\n\n\tif (!ready) {\n\t\treturn (fallback ?? null) as ReactNode\n\t}\n\n\tif (!resolvedStore) {\n\t\tthrow new Error(\n\t\t\t'KoraProvider requires either an \"app\" or \"store\" prop. ' +\n\t\t\t\t'Pass a KoraApp from createApp() or a Store instance.',\n\t\t)\n\t}\n\n\tconst value: KoraContextValue = {\n\t\tstore: resolvedStore,\n\t\tsyncEngine: resolvedSync,\n\t\tapp: app ?? null,\n\t\tevents: app?.events ?? null,\n\t\tsubscribeSyncStatus: app?.sync?.subscribeStatus ?? null,\n\t}\n\treturn createElement(KoraContext.Provider, { value }, children)\n}\n\n/**\n * Internal hook to access the Kora context.\n * Throws if used outside of a KoraProvider.\n */\nfunction useKoraContext(): KoraContextValue {\n\tconst context = useContext(KoraContext)\n\tif (context === null) {\n\t\tthrow new Error(\n\t\t\t'useKoraContext must be used within a <KoraProvider>. ' +\n\t\t\t\t'Wrap your component tree with <KoraProvider store={store}>.',\n\t\t)\n\t}\n\treturn context\n}\n\nexport { KoraProvider, useKoraContext }\n","import { useKoraContext } from '../context/kora-context'\nimport type { KoraAppLike } from '../types'\n\n/**\n * React hook that returns the Kora app instance from context.\n *\n * Use the generic parameter to cast to your typed app for full type inference:\n *\n * ```typescript\n * // In your app setup:\n * export const app = createApp({ schema: mySchema })\n * export type App = typeof app\n *\n * // In components:\n * const app = useApp<App>()\n * app.todos.insert({ title: 'Hello' }) // fully typed\n * const todos = useQuery(app.todos.where({ completed: false }))\n * ```\n *\n * Requires `KoraProvider` to be initialized with the `app` prop.\n * Throws if used outside of `KoraProvider` or without an `app` prop.\n *\n * @returns The KoraApp instance, typed as `T`\n */\nexport function useApp<T extends KoraAppLike = KoraAppLike>(): T {\n\tconst { app } = useKoraContext()\n\tif (!app) {\n\t\tthrow new Error(\n\t\t\t'useApp() requires KoraProvider to be initialized with an \"app\" prop. ' +\n\t\t\t\t'Pass your createApp() result to <KoraProvider app={app}>.',\n\t\t)\n\t}\n\treturn app as T\n}\n","import type { CollectionRecord, QueryBuilder } from '@korajs/store'\nimport { useMemo, useRef, useSyncExternalStore } from 'react'\nimport { QueryStore } from '../query-store/query-store'\nimport type { UseQueryOptions } from '../types'\n\nconst APP_NOT_READY_CODE = 'APP_NOT_READY'\n\nfunction assertQueryReady(query: QueryBuilder<unknown>): void {\n\tconst descriptor = query.getDescriptor()\n\tif (descriptor.collection === '__pending__') {\n\t\tconst err = new Error(\n\t\t\t'Cannot use useQuery() before app.ready. Await app.ready or wrap your UI in <KoraProvider app={app}>.',\n\t\t)\n\t\terr.name = 'AppNotReadyError'\n\t\tObject.assign(err, { code: APP_NOT_READY_CODE })\n\t\tthrow err\n\t}\n}\n\n/**\n * Frozen empty array returned when the query is disabled or before data loads.\n * Same reference prevents unnecessary re-renders.\n */\nconst EMPTY_ARRAY: readonly unknown[] = Object.freeze([])\n\nconst noopSubscribe = (_onStoreChange: () => void): (() => void) => {\n\treturn () => {}\n}\n\n/**\n * React hook for reactive queries against the local Kora store.\n *\n * Returns data synchronously from the local store — no loading spinners needed.\n * Re-renders automatically when the query results change due to mutations.\n * Uses `useSyncExternalStore` for React 18+ concurrent mode safety.\n *\n * The generic parameter `T` is inferred from the QueryBuilder, providing\n * full type safety when used with typed collection accessors.\n *\n * @param query - A QueryBuilder instance (e.g., `app.todos.where({ done: false })`)\n * @param options - Optional configuration (e.g., `{ enabled: false }` to skip the query)\n * @returns Readonly array of matching records\n *\n * @example\n * ```typescript\n * const todos = useQuery(app.todos.where({ completed: false }).orderBy('createdAt'))\n * // todos is typed as readonly InferRecord<typeof todoFields>[]\n * ```\n */\nexport function useQuery<T = CollectionRecord>(\n\tquery: QueryBuilder<T>,\n\toptions?: UseQueryOptions,\n): readonly T[] {\n\tconst enabled = options?.enabled !== false\n\n\tif (enabled) {\n\t\tassertQueryReady(query)\n\t}\n\n\t// Compute a stable key from the query descriptor\n\tconst descriptorKey = JSON.stringify(query.getDescriptor())\n\n\t// Track the current QueryStore instance\n\tconst queryStoreRef = useRef<QueryStore<T> | null>(null)\n\tconst prevKeyRef = useRef<string | null>(null)\n\n\t// Create or reuse a QueryStore when the descriptor changes\n\tconst queryStore = useMemo(() => {\n\t\tif (!enabled) {\n\t\t\t// Destroy previous if it exists\n\t\t\tif (queryStoreRef.current) {\n\t\t\t\tqueryStoreRef.current.destroy()\n\t\t\t\tqueryStoreRef.current = null\n\t\t\t}\n\t\t\tprevKeyRef.current = null\n\t\t\treturn null\n\t\t}\n\n\t\t// If descriptor changed, destroy previous and create new\n\t\tif (prevKeyRef.current !== descriptorKey) {\n\t\t\tif (queryStoreRef.current) {\n\t\t\t\tqueryStoreRef.current.destroy()\n\t\t\t}\n\t\t\tconst newStore = new QueryStore<T>(query)\n\t\t\tqueryStoreRef.current = newStore\n\t\t\tprevKeyRef.current = descriptorKey\n\t\t\treturn newStore\n\t\t}\n\n\t\treturn queryStoreRef.current\n\t}, [descriptorKey, enabled, query])\n\n\t// No useEffect cleanup needed: QueryStore's subscribe/unsubscribe cycle\n\t// manages the underlying subscription lifetime. When the last listener\n\t// detaches (on unmount), the subscription auto-stops. This also makes\n\t// the hook resilient to React StrictMode's double-mount cycle.\n\n\tconst disabledGetSnapshot = (): readonly T[] => EMPTY_ARRAY as readonly T[]\n\n\t// The query object is deliberately excluded from the dependency array.\n\t// QueryBuilder instances are ephemeral (created on every render when\n\t// developers use inline `app.todos.where({...})`). Using it as a dep\n\t// would destroy and recreate the QueryStore on every render, causing\n\t// subscription storms. Instead, we only react to the stable descriptor\n\t// JSON key, which captures the actual query parameters.\n\treturn useSyncExternalStore(\n\t\tqueryStore ? queryStore.subscribe : noopSubscribe,\n\t\tqueryStore ? queryStore.getSnapshot : disabledGetSnapshot,\n\t)\n}\n","import type { CollectionRecord, QueryBuilder } from '@korajs/store'\n\n/**\n * Frozen empty array returned as the initial snapshot before any data loads.\n * Same reference every time prevents infinite re-render loops with useSyncExternalStore.\n */\nconst EMPTY_ARRAY: readonly unknown[] = Object.freeze([])\n\n/**\n * Bridges the async QueryBuilder.subscribe() API with the synchronous\n * getSnapshot() required by React's useSyncExternalStore.\n *\n * Uses a lazy subscription model: the underlying query subscription starts\n * when the first listener attaches (via useSyncExternalStore's subscribe)\n * and stops when the last listener detaches. This makes QueryStore resilient\n * to React StrictMode's double-mount cycle, where useEffect cleanup fires\n * between mount and remount.\n *\n * Generic parameter `T` defaults to `CollectionRecord` for backward compatibility.\n */\nexport class QueryStore<T = CollectionRecord> {\n\tprivate snapshot: readonly T[] = EMPTY_ARRAY as readonly T[]\n\tprivate listeners = new Set<() => void>()\n\tprivate unsubscribeQuery: (() => void) | null = null\n\tprivate active = false\n\tprivate readonly queryBuilder: QueryBuilder<T>\n\n\tconstructor(queryBuilder: QueryBuilder<T>) {\n\t\tthis.queryBuilder = queryBuilder\n\t}\n\n\t/**\n\t * Subscribe to snapshot changes. Compatible with useSyncExternalStore.\n\t *\n\t * Lazily starts the underlying query subscription when the first listener\n\t * attaches, and stops it when the last listener detaches. This allows\n\t * React StrictMode to unmount/remount without permanently killing the subscription.\n\t *\n\t * @returns Unsubscribe function\n\t */\n\tsubscribe = (onStoreChange: () => void): (() => void) => {\n\t\tthis.listeners.add(onStoreChange)\n\n\t\t// Start the underlying query subscription when the first listener attaches\n\t\tif (!this.active) {\n\t\t\tthis.startSubscription()\n\t\t}\n\n\t\treturn () => {\n\t\t\tthis.listeners.delete(onStoreChange)\n\t\t\t// Stop the underlying subscription when the last listener detaches.\n\t\t\t// This ensures cleanup on unmount without needing a useEffect.\n\t\t\tif (this.listeners.size === 0) {\n\t\t\t\tthis.stopSubscription()\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * Get the current snapshot synchronously. Compatible with useSyncExternalStore.\n\t * Returns EMPTY_ARRAY before the first async fetch completes.\n\t */\n\tgetSnapshot = (): readonly T[] => {\n\t\treturn this.snapshot\n\t}\n\n\t/**\n\t * Clean up the underlying subscription and release resources.\n\t * Called by useMemo when the query descriptor changes.\n\t */\n\tdestroy(): void {\n\t\tthis.stopSubscription()\n\t\tthis.listeners.clear()\n\t\tthis.snapshot = EMPTY_ARRAY as readonly T[]\n\t}\n\n\tprivate startSubscription(): void {\n\t\tthis.active = true\n\t\tthis.unsubscribeQuery = this.queryBuilder.subscribe((results) => {\n\t\t\tif (!this.active) return\n\t\t\tconst newSnapshot = Object.freeze([...results])\n\t\t\tthis.snapshot = newSnapshot\n\t\t\tthis.notifyListeners()\n\t\t})\n\t}\n\n\tprivate stopSubscription(): void {\n\t\tthis.active = false\n\t\tif (this.unsubscribeQuery) {\n\t\t\tthis.unsubscribeQuery()\n\t\t\tthis.unsubscribeQuery = null\n\t\t}\n\t}\n\n\tprivate notifyListeners(): void {\n\t\tfor (const listener of this.listeners) {\n\t\t\tlistener()\n\t\t}\n\t}\n}\n","import { useCallback, useEffect, useRef, useState } from 'react'\nimport type { UseMutationOptions, UseMutationResult } from '../types'\n\n/**\n * React hook for performing mutations against the local Kora store.\n *\n * Returns `mutate` for fire-and-forget usage and `mutateAsync` when you need\n * to await the result. Optional `onMutate` / `onRollback` support optimistic\n * UI updates that revert if the mutation throws.\n *\n * @param mutationFn - An async function to execute (e.g., `app.todos.insert`)\n * @param options - Optional optimistic/rollback and lifecycle callbacks\n * @returns Object with mutate, mutateAsync, isLoading, error, and reset\n *\n * @example\n * ```typescript\n * const { mutate } = useMutation(app.todos.insert, {\n * onMutate: (data) => {\n * const previous = todos\n * setTodos((list) => [...list, { id: 'temp', ...data }])\n * return previous\n * },\n * onRollback: (previous) => setTodos(previous),\n * })\n * ```\n */\nexport function useMutation<TData, TArgs extends unknown[], TContext = void>(\n\tmutationFn: (...args: TArgs) => Promise<TData>,\n\toptions?: UseMutationOptions<TData, TArgs, TContext>,\n): UseMutationResult<TData, TArgs> {\n\tconst [state, setState] = useState<{ isLoading: boolean; error: Error | null }>({\n\t\tisLoading: false,\n\t\terror: null,\n\t})\n\n\tconst mountedRef = useRef(true)\n\tuseEffect(() => {\n\t\tmountedRef.current = true\n\t\treturn () => {\n\t\t\tmountedRef.current = false\n\t\t}\n\t}, [])\n\n\tconst fnRef = useRef(mutationFn)\n\tfnRef.current = mutationFn\n\n\tconst optionsRef = useRef(options)\n\toptionsRef.current = options\n\n\tconst mutateAsync = useCallback(async (...args: TArgs): Promise<TData> => {\n\t\tconst opts = optionsRef.current\n\t\tlet context: TContext | undefined\n\n\t\tif (mountedRef.current) {\n\t\t\tsetState({ isLoading: true, error: null })\n\t\t}\n\n\t\ttry {\n\t\t\tif (opts?.onMutate) {\n\t\t\t\tcontext = await opts.onMutate(...args)\n\t\t\t}\n\n\t\t\tconst result = await fnRef.current(...args)\n\n\t\t\tif (mountedRef.current) {\n\t\t\t\tsetState({ isLoading: false, error: null })\n\t\t\t}\n\t\t\topts?.onSuccess?.(result, ...args)\n\t\t\topts?.onSettled?.(result, null, ...args)\n\t\t\treturn result\n\t\t} catch (err) {\n\t\t\tconst error = err instanceof Error ? err : new Error(String(err))\n\n\t\t\tif (context !== undefined && opts?.onRollback) {\n\t\t\t\tawait opts.onRollback(context, ...args)\n\t\t\t}\n\n\t\t\tif (mountedRef.current) {\n\t\t\t\tsetState({ isLoading: false, error })\n\t\t\t}\n\t\t\topts?.onError?.(error, ...args)\n\t\t\topts?.onSettled?.(undefined, error, ...args)\n\t\t\tthrow error\n\t\t}\n\t}, [])\n\n\tconst mutate = useCallback(\n\t\t(...args: TArgs): void => {\n\t\t\tmutateAsync(...args).catch(() => {\n\t\t\t\t// Fire-and-forget: error is captured in state, no unhandled rejection\n\t\t\t})\n\t\t},\n\t\t[mutateAsync],\n\t)\n\n\tconst reset = useCallback((): void => {\n\t\tif (mountedRef.current) {\n\t\t\tsetState({ isLoading: false, error: null })\n\t\t}\n\t}, [])\n\n\treturn {\n\t\tmutate,\n\t\tmutateAsync,\n\t\tisLoading: state.isLoading,\n\t\terror: state.error,\n\t\treset,\n\t}\n}\n","import type { KoraEventType } from '@korajs/core'\nimport type { SyncStatusInfo } from '@korajs/sync'\nimport { useCallback, useRef, useSyncExternalStore } from 'react'\nimport { useKoraContext } from '../context/kora-context'\n\nconst OFFLINE_STATUS: SyncStatusInfo = Object.freeze({\n\tstatus: 'offline',\n\tpendingOperations: 0,\n\tlastSyncedAt: null,\n\tlastSuccessfulPush: null,\n\tlastSuccessfulPull: null,\n\tconflicts: 0,\n})\n\nconst SYNC_STATUS_EVENT_TYPES = [\n\t'sync:connected',\n\t'sync:disconnected',\n\t'sync:schema-mismatch',\n\t'sync:auth-failed',\n\t'sync:sent',\n\t'sync:received',\n\t'sync:acknowledged',\n\t'sync:apply-failed',\n\t'sync:diagnostics',\n\t'sync:initial-sync-progress',\n] as const satisfies readonly KoraEventType[]\n\n/**\n * React hook for monitoring the sync engine's connection status.\n *\n * Subscribes to sync events via `app.sync.subscribeStatus` (or the sync engine\n * emitter when using store-only mode) and re-renders only when status changes.\n *\n * @returns Current sync status information\n *\n * @example\n * ```typescript\n * const status = useSyncStatus()\n * // status.status: 'connected' | 'syncing' | 'synced' | 'offline' | 'error'\n * ```\n */\nexport function useSyncStatus(): SyncStatusInfo {\n\tconst { syncEngine, subscribeSyncStatus, events } = useKoraContext()\n\tconst snapshotRef = useRef<SyncStatusInfo>(OFFLINE_STATUS)\n\tconst serializedRef = useRef<string>(JSON.stringify(OFFLINE_STATUS))\n\n\tconst subscribe = useCallback(\n\t\t(onStoreChange: () => void): (() => void) => {\n\t\t\tif (subscribeSyncStatus) {\n\t\t\t\treturn subscribeSyncStatus((status) => {\n\t\t\t\t\tsnapshotRef.current = status\n\t\t\t\t\tserializedRef.current = JSON.stringify(status)\n\t\t\t\t\tonStoreChange()\n\t\t\t\t})\n\t\t\t}\n\n\t\t\tif (!syncEngine) {\n\t\t\t\treturn () => {}\n\t\t\t}\n\n\t\t\tconst refresh = (): void => {\n\t\t\t\tconst newStatus = syncEngine.getStatus()\n\t\t\t\tconst newSerialized = JSON.stringify(newStatus)\n\t\t\t\tif (newSerialized !== serializedRef.current) {\n\t\t\t\t\tsnapshotRef.current = newStatus\n\t\t\t\t\tserializedRef.current = newSerialized\n\t\t\t\t\tonStoreChange()\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (events) {\n\t\t\t\tconst unsubs = SYNC_STATUS_EVENT_TYPES.map((type) => events.on(type, refresh))\n\t\t\t\trefresh()\n\t\t\t\treturn () => {\n\t\t\t\t\tfor (const unsub of unsubs) {\n\t\t\t\t\t\tunsub()\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\trefresh()\n\t\t\treturn () => {}\n\t\t},\n\t\t[syncEngine, subscribeSyncStatus, events],\n\t)\n\n\tconst getSnapshot = useCallback((): SyncStatusInfo => {\n\t\tif (subscribeSyncStatus) {\n\t\t\treturn snapshotRef.current\n\t\t}\n\t\treturn syncEngine ? syncEngine.getStatus() : OFFLINE_STATUS\n\t}, [syncEngine, subscribeSyncStatus])\n\n\treturn useSyncExternalStore(subscribe, getSnapshot)\n}\n","import type { CollectionAccessor } from '@korajs/store'\nimport { useMemo } from 'react'\nimport { useKoraContext } from '../context/kora-context'\n\n/**\n * React hook that returns a CollectionAccessor for the given collection name.\n * Convenience hook for accessing a collection without going through the store directly.\n *\n * @param name - The collection name (must match a collection in the schema)\n * @returns CollectionAccessor with insert, findById, update, delete, and where methods\n *\n * @example\n * ```typescript\n * const todos = useCollection('todos')\n * await todos.insert({ title: 'New todo' })\n * ```\n */\nexport function useCollection(name: string): CollectionAccessor {\n\tconst { store } = useKoraContext()\n\n\treturn useMemo(() => {\n\t\treturn store.collection(name)\n\t}, [store, name])\n}\n","import type { CollectionAccessor } from '@korajs/store'\nimport { encodeRichtext } from '@korajs/store'\nimport type { AwarenessState, AwarenessUser, CursorInfo } from '@korajs/sync'\nimport { useCallback, useEffect, useMemo, useRef, useState } from 'react'\nimport * as Y from 'yjs'\nimport { useKoraContext } from '../context/kora-context'\nimport type { UseRichTextResult } from '../types'\n\nconst LOAD_ORIGIN = 'kora-load'\nconst REMOTE_ORIGIN = 'kora-remote'\nconst DOC_CHANNEL_ORIGIN = 'kora-doc-channel'\nconst TEXT_KEY = 'content'\nconst COMPACT_AFTER_DELTAS = 20\nconst PERSIST_DEBOUNCE_MS = 400\n\nexport interface UseRichTextOptions {\n\t/** Presence identity broadcast with cursor updates. */\n\tuser?: AwarenessUser\n\t/**\n\t * Use the incremental Yjs doc channel for live edits (recommended for large documents).\n\t * When omitted, the channel activates automatically once the snapshot exceeds the sync threshold.\n\t */\n\tuseDocChannel?: boolean\n}\n\n/**\n * Binds a richtext field to a shared Yjs document for editor integration.\n */\nexport function useRichText(\n\tcollectionName: string,\n\trecordId: string,\n\tfieldName: string,\n\toptions?: UseRichTextOptions,\n): UseRichTextResult {\n\tconst { store, syncEngine } = useKoraContext()\n\tconst collection = useMemo<CollectionAccessor>(\n\t\t() => store.collection(collectionName),\n\t\t[store, collectionName],\n\t)\n\tconst [doc] = useState(() => new Y.Doc())\n\tconst [ready, setReady] = useState(false)\n\tconst [error, setError] = useState<Error | null>(null)\n\tconst [canUndo, setCanUndo] = useState(false)\n\tconst [canRedo, setCanRedo] = useState(false)\n\tconst [cursors, setCursors] = useState<CursorInfo[]>([])\n\tconst baseUpdateRef = useRef<Uint8Array | null>(null)\n\tconst pendingDeltasRef = useRef<Uint8Array[]>([])\n\tconst docChannelActiveRef = useRef(false)\n\tconst persistTimerRef = useRef<ReturnType<typeof setTimeout> | null>(null)\n\tconst userRef = useRef<AwarenessUser | undefined>(options?.user)\n\n\tuseEffect(() => {\n\t\tuserRef.current = options?.user\n\t}, [options?.user])\n\n\tconst text = useMemo(() => doc.getText(TEXT_KEY), [doc])\n\tconst undoManager = useMemo(() => new Y.UndoManager(text), [text])\n\n\tconst syncHistoryState = useCallback(() => {\n\t\tsetCanUndo(undoManager.undoStack.length > 0)\n\t\tsetCanRedo(undoManager.redoStack.length > 0)\n\t}, [undoManager])\n\n\tconst undo = useCallback(() => {\n\t\tundoManager.undo()\n\t\tsyncHistoryState()\n\t}, [syncHistoryState, undoManager])\n\n\tconst redo = useCallback(() => {\n\t\tundoManager.redo()\n\t\tsyncHistoryState()\n\t}, [syncHistoryState, undoManager])\n\n\tconst resolveAwarenessUser = useCallback((): AwarenessUser => {\n\t\tif (userRef.current) {\n\t\t\treturn userRef.current\n\t\t}\n\t\tif (syncEngine) {\n\t\t\tconst existing = syncEngine.getAwarenessManager().getLocalState()?.user\n\t\t\tif (existing) {\n\t\t\t\treturn existing\n\t\t\t}\n\t\t}\n\t\treturn {\n\t\t\tname: 'Anonymous',\n\t\t\tcolor: '#6366f1',\n\t\t}\n\t}, [syncEngine])\n\n\tconst setCursor = useCallback(\n\t\t(anchor: number, head: number) => {\n\t\t\tif (!syncEngine) {\n\t\t\t\treturn\n\t\t\t}\n\n\t\t\tconst awareness = syncEngine.getAwarenessManager()\n\t\t\tconst state: AwarenessState = {\n\t\t\t\tuser: resolveAwarenessUser(),\n\t\t\t\tcursor: {\n\t\t\t\t\tcollection: collectionName,\n\t\t\t\t\trecordId,\n\t\t\t\t\tfield: fieldName,\n\t\t\t\t\tanchor,\n\t\t\t\t\thead,\n\t\t\t\t},\n\t\t\t}\n\t\t\tawareness.setLocalState(state)\n\t\t},\n\t\t[collectionName, fieldName, recordId, resolveAwarenessUser, syncEngine],\n\t)\n\n\tconst clearCursor = useCallback(() => {\n\t\tif (!syncEngine) {\n\t\t\treturn\n\t\t}\n\n\t\tconst awareness = syncEngine.getAwarenessManager()\n\t\tconst current = awareness.getLocalState()\n\t\tif (!current?.cursor) {\n\t\t\treturn\n\t\t}\n\t\tif (\n\t\t\tcurrent.cursor.collection !== collectionName ||\n\t\t\tcurrent.cursor.recordId !== recordId ||\n\t\t\tcurrent.cursor.field !== fieldName\n\t\t) {\n\t\t\treturn\n\t\t}\n\n\t\tawareness.setLocalState({ user: current.user })\n\t}, [collectionName, fieldName, recordId, syncEngine])\n\n\tconst applyRemoteSnapshot = useCallback(\n\t\t(value: unknown) => {\n\t\t\tconst encoded = encodeRichtextInput(value)\n\t\t\tif (!encoded) {\n\t\t\t\treturn\n\t\t\t}\n\n\t\t\tconst currentSnapshot = Y.encodeStateAsUpdate(doc)\n\t\t\tif (updatesEqual(currentSnapshot, encoded)) {\n\t\t\t\treturn\n\t\t\t}\n\n\t\t\tY.applyUpdate(doc, encoded, REMOTE_ORIGIN)\n\t\t\tbaseUpdateRef.current = encoded\n\t\t\tpendingDeltasRef.current = []\n\t\t\tsyncHistoryState()\n\t\t},\n\t\t[doc, syncHistoryState],\n\t)\n\n\tuseEffect(() => {\n\t\tlet disposed = false\n\n\t\tconst initialize = async (): Promise<void> => {\n\t\t\tsetReady(false)\n\t\t\tsetError(null)\n\n\t\t\ttry {\n\t\t\t\tconst record = await collection.findById(recordId)\n\t\t\t\tif (disposed) return\n\n\t\t\t\tdoc.transact(() => {\n\t\t\t\t\tconst target = doc.getText(TEXT_KEY)\n\t\t\t\t\ttarget.delete(0, target.length)\n\t\t\t\t}, LOAD_ORIGIN)\n\n\t\t\t\tconst encoded = encodeRichtextInput(record?.[fieldName])\n\t\t\t\tbaseUpdateRef.current = encoded\n\t\t\t\tpendingDeltasRef.current = []\n\t\t\t\tif (encoded) {\n\t\t\t\t\tY.applyUpdate(doc, encoded, LOAD_ORIGIN)\n\t\t\t\t}\n\n\t\t\t\tconst channel = syncEngine?.getRichtextDocChannel?.()\n\t\t\t\tdocChannelActiveRef.current =\n\t\t\t\t\tchannel?.shouldUseChannel(encoded?.length ?? 0, options?.useDocChannel) ?? false\n\n\t\t\t\tsetReady(true)\n\t\t\t} catch (cause) {\n\t\t\t\tif (disposed) return\n\t\t\t\tsetError(cause instanceof Error ? cause : new Error(String(cause)))\n\t\t\t}\n\t\t}\n\n\t\tconst flushPersist = async (): Promise<void> => {\n\t\t\tconst snapshot = composeRichtextSnapshot(baseUpdateRef.current, pendingDeltasRef.current)\n\t\t\tif (pendingDeltasRef.current.length >= COMPACT_AFTER_DELTAS) {\n\t\t\t\tbaseUpdateRef.current = snapshot\n\t\t\t\tpendingDeltasRef.current = []\n\t\t\t}\n\n\t\t\ttry {\n\t\t\t\tawait collection.update(recordId, {\n\t\t\t\t\t[fieldName]: snapshot,\n\t\t\t\t})\n\t\t\t} catch (cause) {\n\t\t\t\tif (!disposed) {\n\t\t\t\t\tsetError(cause instanceof Error ? cause : new Error(String(cause)))\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tconst schedulePersist = (): void => {\n\t\t\tif (persistTimerRef.current) {\n\t\t\t\tclearTimeout(persistTimerRef.current)\n\t\t\t}\n\t\t\tpersistTimerRef.current = setTimeout(() => {\n\t\t\t\tpersistTimerRef.current = null\n\t\t\t\tvoid flushPersist()\n\t\t\t}, PERSIST_DEBOUNCE_MS)\n\t\t}\n\n\t\tconst persist = (_update: Uint8Array, origin: unknown): void => {\n\t\t\tsyncHistoryState()\n\t\t\tif (origin === LOAD_ORIGIN || origin === REMOTE_ORIGIN || origin === DOC_CHANNEL_ORIGIN) {\n\t\t\t\treturn\n\t\t\t}\n\n\t\t\tpendingDeltasRef.current.push(_update)\n\n\t\t\tif (docChannelActiveRef.current && syncEngine) {\n\t\t\t\tsyncEngine.getRichtextDocChannel().send(collectionName, recordId, fieldName, _update)\n\t\t\t\tschedulePersist()\n\t\t\t\treturn\n\t\t\t}\n\n\t\t\tvoid flushPersist()\n\t\t}\n\n\t\tdoc.on('update', persist)\n\t\tvoid initialize()\n\n\t\tsyncHistoryState()\n\n\t\treturn () => {\n\t\t\tdisposed = true\n\t\t\tif (persistTimerRef.current) {\n\t\t\t\tclearTimeout(persistTimerRef.current)\n\t\t\t\tpersistTimerRef.current = null\n\t\t\t}\n\t\t\tdoc.off('update', persist)\n\t\t\tundoManager.destroy()\n\t\t\tbaseUpdateRef.current = null\n\t\t\tpendingDeltasRef.current = []\n\t\t\tdocChannelActiveRef.current = false\n\t\t}\n\t}, [\n\t\tcollection,\n\t\tcollectionName,\n\t\tdoc,\n\t\tfieldName,\n\t\toptions?.useDocChannel,\n\t\trecordId,\n\t\tsyncEngine,\n\t\tsyncHistoryState,\n\t\tundoManager,\n\t])\n\n\t// Incremental Yjs updates from the doc channel (large-document side path).\n\tuseEffect(() => {\n\t\tif (!ready || !syncEngine || !docChannelActiveRef.current) {\n\t\t\treturn\n\t\t}\n\n\t\tconst channel = syncEngine.getRichtextDocChannel()\n\t\treturn channel.subscribe(collectionName, recordId, fieldName, (update) => {\n\t\t\tY.applyUpdate(doc, update, DOC_CHANNEL_ORIGIN)\n\t\t\tsyncHistoryState()\n\t\t})\n\t}, [collectionName, doc, fieldName, ready, recordId, syncEngine, syncHistoryState])\n\n\t// Merge remote richtext writes from sync into the live Y.Doc.\n\tuseEffect(() => {\n\t\tif (!ready) {\n\t\t\treturn\n\t\t}\n\n\t\tconst unsubscribe = store\n\t\t\t.collection(collectionName)\n\t\t\t.where({ id: recordId })\n\t\t\t.subscribe((results) => {\n\t\t\t\tconst record = results[0]\n\t\t\t\tif (!record) {\n\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\t\tapplyRemoteSnapshot(record[fieldName])\n\t\t\t})\n\n\t\treturn unsubscribe\n\t}, [applyRemoteSnapshot, collectionName, fieldName, ready, recordId, store])\n\n\t// Track remote collaborators' cursors for this specific field.\n\tuseEffect(() => {\n\t\tif (!syncEngine) return\n\n\t\tconst awareness = syncEngine.getAwarenessManager()\n\t\tconst localClientId = awareness.clientId\n\n\t\tconst updateCursors = (): void => {\n\t\t\tconst states = awareness.getStates()\n\t\t\tconst fieldCursors: CursorInfo[] = []\n\n\t\t\tfor (const [clientId, state] of states) {\n\t\t\t\tif (clientId === localClientId) continue\n\t\t\t\tif (!state.cursor) continue\n\t\t\t\tif (\n\t\t\t\t\tstate.cursor.collection !== collectionName ||\n\t\t\t\t\tstate.cursor.recordId !== recordId ||\n\t\t\t\t\tstate.cursor.field !== fieldName\n\t\t\t\t) {\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\t\t\t\tfieldCursors.push({\n\t\t\t\t\tclientId,\n\t\t\t\t\tuserName: state.user.name,\n\t\t\t\t\tcolor: state.user.color,\n\t\t\t\t\tanchor: state.cursor.anchor,\n\t\t\t\t\thead: state.cursor.head,\n\t\t\t\t})\n\t\t\t}\n\n\t\t\tsetCursors(fieldCursors)\n\t\t}\n\n\t\tconst unsubscribe = awareness.on('change', () => {\n\t\t\tupdateCursors()\n\t\t})\n\n\t\tupdateCursors()\n\n\t\treturn () => {\n\t\t\tunsubscribe()\n\t\t\tclearCursor()\n\t\t}\n\t}, [clearCursor, collectionName, fieldName, recordId, syncEngine])\n\n\treturn { doc, text, undo, redo, canUndo, canRedo, ready, error, cursors, setCursor, clearCursor }\n}\n\nfunction encodeRichtextInput(value: unknown): Uint8Array | null {\n\tif (value === null || value === undefined) {\n\t\treturn null\n\t}\n\n\ttry {\n\t\treturn encodeRichtext(value as Parameters<typeof encodeRichtext>[0])\n\t} catch {\n\t\tif (typeof value === 'string') {\n\t\t\tconst fallbackDoc = new Y.Doc()\n\t\t\tfallbackDoc.getText(TEXT_KEY).insert(0, value)\n\t\t\treturn Y.encodeStateAsUpdate(fallbackDoc)\n\t\t}\n\t\tthrow new Error('Richtext record value must be a string, Uint8Array, ArrayBuffer, or null.')\n\t}\n}\n\nfunction composeRichtextSnapshot(base: Uint8Array | null, deltas: Uint8Array[]): Uint8Array {\n\tconst mergedDoc = new Y.Doc()\n\tif (base) {\n\t\tY.applyUpdate(mergedDoc, base)\n\t}\n\n\tfor (const delta of deltas) {\n\t\tY.applyUpdate(mergedDoc, delta)\n\t}\n\n\treturn Y.encodeStateAsUpdate(mergedDoc)\n}\n\nfunction updatesEqual(left: Uint8Array, right: Uint8Array): boolean {\n\tif (left.length !== right.length) {\n\t\treturn false\n\t}\n\tfor (let index = 0; index < left.length; index++) {\n\t\tif (left[index] !== right[index]) {\n\t\t\treturn false\n\t\t}\n\t}\n\treturn true\n}\n"],"mappings":"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","names":["import_react","EMPTY_ARRAY","import_react","import_react","import_react","import_react"]}
|