@korajs/sync 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.d.cts CHANGED
@@ -1,6 +1,8 @@
1
- import { S as SyncScopeMap, a as SyncMessage, b as SerializedOperation, W as WireFormat, c as SyncTransport, T as TransportOptions, d as TransportMessageHandler, e as TransportCloseHandler, f as TransportErrorHandler, g as SyncEncryptionConfig, V as VersionedKey, Q as QueueStorage, h as SyncState, i as SyncStatusInfo, j as SyncConfig } from './transport-B-BIguq9.cjs';
2
- export { A as AcknowledgmentMessage, k as AwarenessStateWire, l as AwarenessUpdateMessage, E as EncryptedPayload, m as ErrorMessage, H as HandshakeMessage, n as HandshakeResponseMessage, I as InvalidScopeError, O as OperationBatchMessage, o as SYNC_STATES, p as SYNC_STATUSES, q as ScopeViolationError, r as SyncEncryptionAlgorithm, s as SyncScopeContext, t as SyncStatus, u as isAcknowledgmentMessage, v as isAwarenessUpdateMessage, w as isErrorMessage, x as isHandshakeMessage, y as isHandshakeResponseMessage, z as isOperationBatchMessage, B as isSyncMessage } from './transport-B-BIguq9.cjs';
3
- import { Operation, VersionVector, TimeSource, SyncError, KoraEventEmitter, ConnectionQuality } from '@korajs/core';
1
+ import { S as SyncScopeMap, D as DeltaCursor, a as SyncMessage, b as SerializedOperation, W as WireFormat, c as SyncTransport, T as TransportOptions, d as TransportMessageHandler, e as TransportCloseHandler, f as TransportErrorHandler, g as SyncEncryptionConfig, V as VersionedKey, Y as YjsDocUpdateMessage, Q as QueueStorage, h as SyncState, i as SyncStatusInfo, j as SyncConfig, k as SyncStatePersistence, l as SyncQuerySubset } from './transport-BrdBj7rH.cjs';
2
+ export { A as AcknowledgmentMessage, m as AwarenessStateWire, n as AwarenessUpdateMessage, E as EncryptedPayload, o as ErrorMessage, H as HandshakeMessage, p as HandshakeResponseMessage, I as InvalidScopeError, O as OperationBatchMessage, q as SYNC_STATES, r as SYNC_STATUSES, s as ScopeViolationError, t as SyncEncryptionAlgorithm, u as SyncScopeContext, v as SyncStatus, w as dedupeQuerySubsets, x as isAcknowledgmentMessage, y as isAwarenessUpdateMessage, z as isErrorMessage, B as isHandshakeMessage, C as isHandshakeResponseMessage, F as isOperationBatchMessage, G as isSyncMessage, J as isYjsDocUpdateMessage, K as operationMatchesQuerySubsets } from './transport-BrdBj7rH.cjs';
3
+ import * as _korajs_core from '@korajs/core';
4
+ import { Operation, VersionVector, ApplyResult, KoraEventEmitter, TimeSource, SyncError, ConnectionQuality } from '@korajs/core';
5
+ export { ApplyFailureReason, ApplyResult } from '@korajs/core';
4
6
 
5
7
  /**
6
8
  * Check whether an operation matches the given scope map.
@@ -12,13 +14,18 @@ import { Operation, VersionVector, TimeSource, SyncError, KoraEventEmitter, Conn
12
14
  * - Scope has field/value pairs: all must match in the operation's data snapshot.
13
15
  *
14
16
  * For updates, the snapshot is built by merging `previousData` and `data` (data wins),
15
- * which represents the record's state after the operation is applied.
17
+ * which represents the record's state after the operation is applied. When an optional
18
+ * `fullRecord` is provided (e.g., from the local store), its values fill in scope fields
19
+ * that weren't included in the operation's data (critical for update operations where
20
+ * `data` only contains changed fields).
16
21
  *
17
22
  * @param op - The operation to check
18
23
  * @param scopeMap - Per-collection scope filters, or undefined for no filtering
24
+ * @param fullRecord - Optional full record state from the store, used to fill in scope
25
+ * fields not present in the operation's partial data
19
26
  * @returns true if the operation is within scope
20
27
  */
21
- declare function operationMatchesScope(op: Operation, scopeMap: SyncScopeMap | undefined): boolean;
28
+ declare function operationMatchesScope(op: Operation, scopeMap: SyncScopeMap | undefined, fullRecord?: Record<string, unknown> | null): boolean;
22
29
  /**
23
30
  * Filter operations to only those matching the given scope map.
24
31
  *
@@ -29,9 +36,22 @@ declare function operationMatchesScope(op: Operation, scopeMap: SyncScopeMap | u
29
36
  declare function filterOperationsByScope(operations: Operation[], scopeMap: SyncScopeMap | undefined): Operation[];
30
37
 
31
38
  /**
32
- * Result of applying a remote operation to the store.
39
+ * Encode a delta cursor for wire transfer or `_kora_meta` persistence.
33
40
  */
34
- type ApplyResult = 'applied' | 'duplicate' | 'skipped';
41
+ declare function encodeDeltaCursor(cursor: DeltaCursor): string;
42
+ /**
43
+ * Decode a delta cursor string. Returns null when malformed.
44
+ */
45
+ declare function decodeDeltaCursor(value: string | undefined | null): DeltaCursor | null;
46
+ /**
47
+ * Slice a causally sorted operation list to resume after a cursor.
48
+ */
49
+ declare function sliceOperationsAfterCursor(operations: _korajs_core.Operation[], cursor: DeltaCursor | null): _korajs_core.Operation[];
50
+ /**
51
+ * Build a cursor from the last operation in a delta batch.
52
+ */
53
+ declare function createDeltaCursorFromBatch(operations: _korajs_core.Operation[], batchIndex: number): DeltaCursor | null;
54
+
35
55
  /**
36
56
  * Interface that the local store must implement for sync.
37
57
  * This decouples @korajs/sync from @korajs/store — the store satisfies this interface.
@@ -63,6 +83,20 @@ interface SyncStore {
63
83
  getOperationRange(nodeId: string, fromSeq: number, toSeq: number): Promise<Operation[]>;
64
84
  }
65
85
 
86
+ /** Prefix for handshake rejection when client schema version is unsupported. */
87
+ declare const SCHEMA_MISMATCH_PREFIX = "SCHEMA_MISMATCH";
88
+ /**
89
+ * Returns true when a handshake `rejectReason` indicates unsupported schema version.
90
+ */
91
+ declare function isSchemaMismatchReject(rejectReason: string | undefined): boolean;
92
+ /**
93
+ * Returns true when the client schema version is within the inclusive supported range.
94
+ */
95
+ declare function isClientSchemaVersionSupported(clientVersion: number, supported: {
96
+ min: number;
97
+ max: number;
98
+ }): boolean;
99
+
66
100
  type EncodedMessage = string | Uint8Array;
67
101
  /**
68
102
  * Interface for encoding/decoding sync protocol messages.
@@ -255,6 +289,173 @@ declare class ChaosTransport implements SyncTransport {
255
289
  private flushReorderBuffer;
256
290
  }
257
291
 
292
+ /**
293
+ * Cursor position within a richtext field.
294
+ * Uses Yjs-compatible anchor/head positions for editor interop.
295
+ */
296
+ interface AwarenessCursor {
297
+ /** Collection containing the record being edited */
298
+ collection: string;
299
+ /** ID of the record being edited */
300
+ recordId: string;
301
+ /** Field name of the richtext field */
302
+ field: string;
303
+ /** Cursor anchor position in Y.Text (start of selection) */
304
+ anchor: number;
305
+ /** Cursor head position in Y.Text (end of selection, same as anchor if no selection) */
306
+ head: number;
307
+ }
308
+ /**
309
+ * User identity information for presence display.
310
+ */
311
+ interface AwarenessUser {
312
+ /** Display name of the user */
313
+ name: string;
314
+ /** Hex color for cursor/selection rendering (e.g. '#ff0000') */
315
+ color: string;
316
+ /** Optional avatar URL */
317
+ avatar?: string;
318
+ }
319
+ /**
320
+ * Per-client awareness state. Ephemeral -- not persisted, only shared with connected peers.
321
+ * Compatible with Yjs awareness protocol format for interop with existing editors.
322
+ */
323
+ interface AwarenessState {
324
+ /** User identity information */
325
+ user: AwarenessUser;
326
+ /** Current cursor position, if any */
327
+ cursor?: AwarenessCursor;
328
+ }
329
+ /**
330
+ * Internal awareness message format used between AwarenessManager and transport layer.
331
+ */
332
+ interface AwarenessMessage {
333
+ type: 'awareness';
334
+ /** Client ID of the sender */
335
+ clientId: number;
336
+ /** All known awareness states. null value means removal. */
337
+ states: Record<number, AwarenessState | null>;
338
+ }
339
+ /**
340
+ * Describes a change in awareness states.
341
+ * Emitted when remote clients update or remove their presence.
342
+ */
343
+ interface AwarenessChange {
344
+ /** Client IDs whose states were added */
345
+ added: number[];
346
+ /** Client IDs whose states were updated */
347
+ updated: number[];
348
+ /** Client IDs whose states were removed */
349
+ removed: number[];
350
+ }
351
+ /**
352
+ * Cursor information for rendering in editors.
353
+ * This is the developer-facing type -- simplified from internal awareness state.
354
+ * Editor-agnostic: provides data that can be rendered by TipTap, ProseMirror, Quill, etc.
355
+ */
356
+ interface CursorInfo {
357
+ /** Unique client ID */
358
+ clientId: number;
359
+ /** User display name */
360
+ userName: string;
361
+ /** Hex color for cursor rendering */
362
+ color: string;
363
+ /** Cursor anchor position (start of selection) */
364
+ anchor: number;
365
+ /** Cursor head position (end of selection) */
366
+ head: number;
367
+ }
368
+
369
+ /**
370
+ * Callback for awareness change events.
371
+ */
372
+ type AwarenessChangeListener = (change: AwarenessChange) => void;
373
+ /**
374
+ * Manages collaborative awareness state (cursor positions, user presence).
375
+ *
376
+ * Awareness is ephemeral -- states are never persisted, only shared with
377
+ * currently connected peers via the sync transport. This implements a
378
+ * lightweight protocol compatible with Yjs awareness semantics.
379
+ *
380
+ * Each AwarenessManager has a unique client ID and tracks both its own
381
+ * local state and all remote clients' states.
382
+ */
383
+ declare class AwarenessManager {
384
+ /** Unique client ID for this instance */
385
+ readonly clientId: number;
386
+ private localState;
387
+ private readonly remoteStates;
388
+ private readonly listeners;
389
+ private readonly emitter;
390
+ private readonly timeoutMs;
391
+ private readonly lastUpdated;
392
+ private cleanupTimer;
393
+ private sendHandler;
394
+ private destroyed;
395
+ constructor(options?: {
396
+ clientId?: number;
397
+ emitter?: KoraEventEmitter;
398
+ timeoutMs?: number;
399
+ });
400
+ /**
401
+ * Set the local user's awareness state and broadcast to peers.
402
+ *
403
+ * @param state - The awareness state to share. Pass null to clear presence.
404
+ */
405
+ setLocalState(state: AwarenessState | null): void;
406
+ /**
407
+ * Get the local awareness state.
408
+ */
409
+ getLocalState(): AwarenessState | null;
410
+ /**
411
+ * Get all known awareness states (local + remote).
412
+ * Returns a new Map on each call.
413
+ */
414
+ getStates(): Map<number, AwarenessState>;
415
+ /**
416
+ * Handle an incoming awareness message from the transport.
417
+ * This processes remote state updates and notifies listeners.
418
+ */
419
+ handleRemoteMessage(message: AwarenessMessage): void;
420
+ /**
421
+ * Remove a specific remote client's awareness state.
422
+ * Called when the server notifies that a client has disconnected.
423
+ */
424
+ removeClient(clientId: number): void;
425
+ /**
426
+ * Register a handler for sending awareness messages through the transport.
427
+ * The sync engine calls this to wire outgoing awareness messages to the transport.
428
+ */
429
+ onSend(handler: (message: AwarenessMessage) => void): void;
430
+ /**
431
+ * Register a listener for awareness state changes.
432
+ * Returns an unsubscribe function.
433
+ */
434
+ on(_event: 'change', listener: AwarenessChangeListener): () => void;
435
+ /**
436
+ * Remove a specific change listener.
437
+ */
438
+ off(_event: 'change', listener: AwarenessChangeListener): void;
439
+ /**
440
+ * Start the cleanup timer that removes stale remote states.
441
+ * Called when the sync engine transitions to streaming state.
442
+ */
443
+ startCleanupTimer(): void;
444
+ /**
445
+ * Stop the cleanup timer.
446
+ */
447
+ stopCleanupTimer(): void;
448
+ /**
449
+ * Clean up all resources. After calling destroy(), the manager
450
+ * will no longer send or receive awareness updates.
451
+ * Broadcasts removal of local state before shutting down.
452
+ */
453
+ destroy(): void;
454
+ private cleanupStaleStates;
455
+ private notifyListeners;
456
+ private emitAwarenessEvent;
457
+ }
458
+
258
459
  /**
259
460
  * Configuration for the SyncMetricsCollector.
260
461
  */
@@ -440,171 +641,42 @@ declare class SyncEncryptor {
440
641
  */
441
642
  declare function isEncryptedPayload(field: Record<string, unknown> | null): boolean;
442
643
 
443
- /**
444
- * Cursor position within a richtext field.
445
- * Uses Yjs-compatible anchor/head positions for editor interop.
446
- */
447
- interface AwarenessCursor {
448
- /** Collection containing the record being edited */
449
- collection: string;
450
- /** ID of the record being edited */
451
- recordId: string;
452
- /** Field name of the richtext field */
453
- field: string;
454
- /** Cursor anchor position in Y.Text (start of selection) */
455
- anchor: number;
456
- /** Cursor head position in Y.Text (end of selection, same as anchor if no selection) */
457
- head: number;
458
- }
459
- /**
460
- * User identity information for presence display.
461
- */
462
- interface AwarenessUser {
463
- /** Display name of the user */
464
- name: string;
465
- /** Hex color for cursor/selection rendering (e.g. '#ff0000') */
466
- color: string;
467
- /** Optional avatar URL */
468
- avatar?: string;
644
+ /** Default snapshot size above which the doc channel is preferred. */
645
+ declare const DEFAULT_RICHTEXT_DOC_CHANNEL_THRESHOLD = 4096;
646
+ type RichtextDocListener = (update: Uint8Array) => void;
647
+ interface RichtextDocChannelOptions {
648
+ /** Snapshot byte length at which incremental channel sync is recommended. */
649
+ largeDocThreshold?: number;
650
+ /** Called when a local update should be sent on the wire. */
651
+ onSend?: (message: YjsDocUpdateMessage) => void;
469
652
  }
470
653
  /**
471
- * Per-client awareness state. Ephemeral -- not persisted, only shared with connected peers.
472
- * Compatible with Yjs awareness protocol format for interop with existing editors.
473
- */
474
- interface AwarenessState {
475
- /** User identity information */
476
- user: AwarenessUser;
477
- /** Current cursor position, if any */
478
- cursor?: AwarenessCursor;
479
- }
480
- /**
481
- * Internal awareness message format used between AwarenessManager and transport layer.
654
+ * Side channel for incremental Yjs updates on large richtext fields.
655
+ * Ephemeral not persisted in the operation log (durable state still flows via ops).
482
656
  */
483
- interface AwarenessMessage {
484
- type: 'awareness';
485
- /** Client ID of the sender */
486
- clientId: number;
487
- /** All known awareness states. null value means removal. */
488
- states: Record<number, AwarenessState | null>;
489
- }
490
- /**
491
- * Describes a change in awareness states.
492
- * Emitted when remote clients update or remove their presence.
493
- */
494
- interface AwarenessChange {
495
- /** Client IDs whose states were added */
496
- added: number[];
497
- /** Client IDs whose states were updated */
498
- updated: number[];
499
- /** Client IDs whose states were removed */
500
- removed: number[];
501
- }
502
- /**
503
- * Cursor information for rendering in editors.
504
- * This is the developer-facing type -- simplified from internal awareness state.
505
- * Editor-agnostic: provides data that can be rendered by TipTap, ProseMirror, Quill, etc.
506
- */
507
- interface CursorInfo {
508
- /** Unique client ID */
509
- clientId: number;
510
- /** User display name */
511
- userName: string;
512
- /** Hex color for cursor rendering */
513
- color: string;
514
- /** Cursor anchor position (start of selection) */
515
- anchor: number;
516
- /** Cursor head position (end of selection) */
517
- head: number;
518
- }
519
-
520
- /**
521
- * Callback for awareness change events.
522
- */
523
- type AwarenessChangeListener = (change: AwarenessChange) => void;
524
- /**
525
- * Manages collaborative awareness state (cursor positions, user presence).
526
- *
527
- * Awareness is ephemeral -- states are never persisted, only shared with
528
- * currently connected peers via the sync transport. This implements a
529
- * lightweight protocol compatible with Yjs awareness semantics.
530
- *
531
- * Each AwarenessManager has a unique client ID and tracks both its own
532
- * local state and all remote clients' states.
533
- */
534
- declare class AwarenessManager {
535
- /** Unique client ID for this instance */
536
- readonly clientId: number;
537
- private localState;
538
- private readonly remoteStates;
657
+ declare class RichtextDocChannel {
658
+ private readonly threshold;
659
+ private readonly onSend;
539
660
  private readonly listeners;
540
- private readonly emitter;
541
- private readonly timeoutMs;
542
- private readonly lastUpdated;
543
- private cleanupTimer;
544
- private sendHandler;
545
- private destroyed;
546
- constructor(options?: {
547
- clientId?: number;
548
- emitter?: KoraEventEmitter;
549
- timeoutMs?: number;
550
- });
551
- /**
552
- * Set the local user's awareness state and broadcast to peers.
553
- *
554
- * @param state - The awareness state to share. Pass null to clear presence.
555
- */
556
- setLocalState(state: AwarenessState | null): void;
557
- /**
558
- * Get the local awareness state.
559
- */
560
- getLocalState(): AwarenessState | null;
561
- /**
562
- * Get all known awareness states (local + remote).
563
- * Returns a new Map on each call.
564
- */
565
- getStates(): Map<number, AwarenessState>;
566
- /**
567
- * Handle an incoming awareness message from the transport.
568
- * This processes remote state updates and notifies listeners.
569
- */
570
- handleRemoteMessage(message: AwarenessMessage): void;
571
- /**
572
- * Remove a specific remote client's awareness state.
573
- * Called when the server notifies that a client has disconnected.
574
- */
575
- removeClient(clientId: number): void;
576
- /**
577
- * Register a handler for sending awareness messages through the transport.
578
- * The sync engine calls this to wire outgoing awareness messages to the transport.
579
- */
580
- onSend(handler: (message: AwarenessMessage) => void): void;
581
- /**
582
- * Register a listener for awareness state changes.
583
- * Returns an unsubscribe function.
584
- */
585
- on(_event: 'change', listener: AwarenessChangeListener): () => void;
661
+ constructor(options?: RichtextDocChannelOptions);
586
662
  /**
587
- * Remove a specific change listener.
663
+ * Whether the doc channel should be used for a field.
664
+ * @param preference - `true` forces on, `false` forces off, `undefined` uses size threshold
588
665
  */
589
- off(_event: 'change', listener: AwarenessChangeListener): void;
666
+ shouldUseChannel(snapshotByteLength: number, preference?: boolean): boolean;
667
+ getThreshold(): number;
590
668
  /**
591
- * Start the cleanup timer that removes stale remote states.
592
- * Called when the sync engine transitions to streaming state.
669
+ * Subscribe to incremental updates for a document key.
593
670
  */
594
- startCleanupTimer(): void;
671
+ subscribe(collection: string, recordId: string, field: string, listener: RichtextDocListener): () => void;
595
672
  /**
596
- * Stop the cleanup timer.
673
+ * Publish a local incremental update to peers.
597
674
  */
598
- stopCleanupTimer(): void;
675
+ send(collection: string, recordId: string, field: string, update: Uint8Array): void;
599
676
  /**
600
- * Clean up all resources. After calling destroy(), the manager
601
- * will no longer send or receive awareness updates.
602
- * Broadcasts removal of local state before shutting down.
677
+ * Deliver a remote incremental update to local subscribers.
603
678
  */
604
- destroy(): void;
605
- private cleanupStaleStates;
606
- private notifyListeners;
607
- private emitAwarenessEvent;
679
+ deliver(message: YjsDocUpdateMessage): void;
608
680
  }
609
681
 
610
682
  /**
@@ -677,6 +749,11 @@ declare class OutboundQueue {
677
749
  * Whether initialize() has been called.
678
750
  */
679
751
  get isInitialized(): boolean;
752
+ /**
753
+ * Remove operations by id from queue and persistent storage.
754
+ * Used when ops were already sent during handshake delta exchange.
755
+ */
756
+ removeByIds(ids: string[]): Promise<void>;
680
757
  }
681
758
 
682
759
  /**
@@ -703,6 +780,8 @@ interface SyncEngineOptions {
703
780
  encryptor?: SyncEncryptor;
704
781
  /** Optional configuration for the metrics collector. */
705
782
  metricsConfig?: MetricsCollectorConfig;
783
+ /** Op-log backed sync state (last acked server vector, unsynced counts). */
784
+ syncState?: SyncStatePersistence;
706
785
  }
707
786
  /**
708
787
  * Diagnostics snapshot for debugging and support.
@@ -740,23 +819,38 @@ declare class SyncEngine {
740
819
  private readonly batchSize;
741
820
  private readonly encryptor;
742
821
  private readonly awarenessManager;
822
+ private readonly richtextDocChannel;
743
823
  private readonly metricsCollector;
824
+ private readonly syncState;
744
825
  private remoteVector;
826
+ private lastAckedServerVector;
827
+ private cachedUnsyncedCount;
745
828
  private lastSyncedAt;
746
829
  private lastSuccessfulPush;
747
830
  private lastSuccessfulPull;
748
831
  private conflictCount;
749
832
  private currentBatch;
750
833
  private reconnecting;
834
+ private schemaBlocked;
751
835
  private deltaBatchesReceived;
752
836
  private deltaReceiveComplete;
753
837
  private deltaSendComplete;
838
+ /** Op ids sent during handshake delta — removed from outbound queue to avoid duplicate send */
839
+ private deltaSentOpIds;
840
+ /** Outbound delta batch message IDs awaiting ACK when strictHandshake is enabled */
841
+ private pendingDeltaBatchAcks;
754
842
  /**
755
843
  * The effective scope for this sync session.
756
844
  * Starts as the configured scopeMap. After handshake, may be replaced
757
845
  * with the server-accepted scope (server is authoritative).
758
846
  */
759
847
  private activeScope;
848
+ /** Live query subsets registered from reactive subscriptions */
849
+ private querySubsets;
850
+ private querySubsetReconnectTimer;
851
+ /** Resume cursor for paginated initial sync (persisted across reconnects) */
852
+ private resumeDeltaCursor;
853
+ private initialSyncTotalBatches;
760
854
  constructor(options: SyncEngineOptions);
761
855
  /**
762
856
  * Start the sync engine: connect → handshake → delta exchange → streaming.
@@ -786,11 +880,26 @@ declare class SyncEngine {
786
880
  * Get the current developer-facing sync status.
787
881
  */
788
882
  getStatus(): SyncStatusInfo;
883
+ /**
884
+ * True when the server rejected the client's schema version at handshake.
885
+ * Sync stays blocked until the app schema is upgraded or sync config changes.
886
+ */
887
+ isSchemaBlocked(): boolean;
888
+ /**
889
+ * Clears schema-mismatch block after upgrading the local schema / sync config.
890
+ * Moves the engine back to `disconnected` so `start()` can run again.
891
+ */
892
+ clearSchemaBlock(): void;
789
893
  /**
790
894
  * Record a merge conflict. Called by the merge-aware sync store
791
895
  * to increment the conflict counter for status reporting.
792
896
  */
793
897
  recordConflict(): void;
898
+ /**
899
+ * Count of local operations not yet covered by the last acked server vector (op-log source of truth).
900
+ */
901
+ getUnsyncedOperationCount(): Promise<number>;
902
+ private emitApplyFailure;
794
903
  /**
795
904
  * Force an immediate reconnection attempt. If the engine is disconnected
796
905
  * or in error state, restarts the sync. If already connected, no-op.
@@ -826,26 +935,65 @@ declare class SyncEngine {
826
935
  * Get the currently active scope map. Returns undefined if no scope is configured.
827
936
  */
828
937
  getActiveScope(): SyncScopeMap | undefined;
938
+ /**
939
+ * Register a live query subset that narrows synced data for a collection.
940
+ * Takes effect on the next connection; reconnects when already connected.
941
+ */
942
+ registerQuerySubset(subset: SyncQuerySubset): () => void;
943
+ /**
944
+ * Returns deduplicated active query subsets from registered subscriptions.
945
+ */
946
+ getActiveQuerySubsets(): SyncQuerySubset[];
829
947
  /**
830
948
  * Get the awareness manager for collaborative presence.
831
949
  * Use this to set local presence, observe remote collaborators,
832
950
  * and track cursor positions in richtext fields.
833
951
  */
834
952
  getAwarenessManager(): AwarenessManager;
835
- private handleMessage;
953
+ /**
954
+ * Optional side channel for incremental Yjs updates on large richtext fields.
955
+ */
956
+ getRichtextDocChannel(): RichtextDocChannel;
957
+ private messageChain;
958
+ private enqueueMessage;
959
+ private handleMessageAsync;
960
+ private handleMessageFailure;
836
961
  private handleHandshakeResponse;
837
962
  private sendDelta;
963
+ private markDeltaSendCompleteIfReady;
838
964
  private collectDelta;
839
965
  private handleOperationBatch;
840
966
  private handleAcknowledgment;
841
967
  private handleError;
842
968
  private checkDeltaComplete;
969
+ /**
970
+ * Effective server vector: persisted last-ack merged with live handshake remote vector.
971
+ */
972
+ private getEffectiveServerVector;
973
+ private persistLastAckedServerVector;
974
+ private advanceLastAckedForLocalNode;
975
+ /**
976
+ * Refresh cached unsynced count from op log vs effective server vector.
977
+ */
978
+ refreshPendingCount(): Promise<void>;
979
+ /**
980
+ * Returns operations the server has not yet acknowledged (op-log source of truth).
981
+ */
982
+ getPendingSyncOperations(): Promise<Operation[]>;
983
+ /**
984
+ * Hydrate the outbound queue from unsynced ops in the op log (deduped by operation id).
985
+ */
986
+ private reconcileOutboundFromOpLog;
843
987
  private flushQueue;
844
988
  private handleAwarenessUpdate;
845
989
  private handleTransportClose;
846
990
  private handleTransportError;
847
991
  private transitionTo;
848
992
  private setSerializerWireFormat;
993
+ private operationAllowedForSync;
994
+ private persistDeltaCursor;
995
+ private scheduleQuerySubsetReconnect;
996
+ private reconnectForQuerySubsets;
849
997
  }
850
998
 
851
999
  /**
@@ -952,6 +1100,11 @@ declare class ReconnectionManager {
952
1100
  * @returns Promise that resolves when reconnection succeeds or maxAttempts reached.
953
1101
  */
954
1102
  start(onReconnect: () => Promise<boolean>): Promise<boolean>;
1103
+ /**
1104
+ * Cancel the current backoff wait and proceed to the next reconnect attempt immediately.
1105
+ * No-op if not waiting.
1106
+ */
1107
+ wake(): void;
955
1108
  /**
956
1109
  * Stop any pending reconnection attempt.
957
1110
  */
@@ -976,6 +1129,19 @@ declare class ReconnectionManager {
976
1129
  private wait;
977
1130
  }
978
1131
 
1132
+ /**
1133
+ * Encode a Yjs update for the doc channel wire (base64).
1134
+ */
1135
+ declare function encodeYjsUpdate(update: Uint8Array): string;
1136
+ /**
1137
+ * Decode a base64 Yjs update from the doc channel wire.
1138
+ */
1139
+ declare function decodeYjsUpdate(encoded: string): Uint8Array;
1140
+ /**
1141
+ * Build a stable key for a richtext field document.
1142
+ */
1143
+ declare function richtextDocKey(collection: string, recordId: string, field: string): string;
1144
+
979
1145
  /**
980
1146
  * Thrown when key derivation fails.
981
1147
  */
@@ -1029,4 +1195,4 @@ declare function deriveKey(passphrase: string, salt?: Uint8Array): Promise<{
1029
1195
  */
1030
1196
  declare function deriveVersionedKey(passphrase: string, version: number, salt?: Uint8Array): Promise<VersionedKey>;
1031
1197
 
1032
- export { type ApplyResult, type AwarenessChange, type AwarenessCursor, AwarenessManager, type AwarenessMessage, type AwarenessState, type AwarenessUser, type ChaosConfig, ChaosTransport, ConnectionMonitor, type ConnectionMonitorConfig, type CursorInfo, DecryptionError, EncryptionError, HttpLongPollingTransport, type HttpLongPollingTransportOptions, JsonMessageSerializer, KeyDerivationError, type MessageSerializer, NegotiatedMessageSerializer, type OutboundBatch, OutboundQueue, ProtobufMessageSerializer, QueueStorage, type ReconnectionConfig, ReconnectionManager, SerializedOperation, SyncConfig, type SyncDiagnostics, SyncEncryptionConfig, SyncEncryptor, SyncEngine, type SyncEngineOptions, SyncMessage, SyncScopeMap, SyncState, SyncStatusInfo, type SyncStore, SyncTransport, TransportCloseHandler, TransportErrorHandler, TransportMessageHandler, TransportOptions, VersionedKey, type WebSocketConstructor, type WebSocketLike, WebSocketTransport, type WebSocketTransportOptions, WireFormat, deriveKey, deriveVersionedKey, filterOperationsByScope, generateSalt, isEncryptedPayload, operationMatchesScope, versionVectorToWire, wireToVersionVector };
1198
+ export { type AwarenessChange, type AwarenessCursor, AwarenessManager, type AwarenessMessage, type AwarenessState, type AwarenessUser, type ChaosConfig, ChaosTransport, ConnectionMonitor, type ConnectionMonitorConfig, type CursorInfo, DEFAULT_RICHTEXT_DOC_CHANNEL_THRESHOLD, DecryptionError, DeltaCursor, EncryptionError, HttpLongPollingTransport, type HttpLongPollingTransportOptions, JsonMessageSerializer, KeyDerivationError, type MessageSerializer, NegotiatedMessageSerializer, type OutboundBatch, OutboundQueue, ProtobufMessageSerializer, QueueStorage, type ReconnectionConfig, ReconnectionManager, RichtextDocChannel, type RichtextDocChannelOptions, type RichtextDocListener, SCHEMA_MISMATCH_PREFIX, SerializedOperation, SyncConfig, type SyncDiagnostics, SyncEncryptionConfig, SyncEncryptor, SyncEngine, type SyncEngineOptions, SyncMessage, SyncQuerySubset, SyncScopeMap, SyncState, SyncStatePersistence, SyncStatusInfo, type SyncStore, SyncTransport, TransportCloseHandler, TransportErrorHandler, TransportMessageHandler, TransportOptions, VersionedKey, type WebSocketConstructor, type WebSocketLike, WebSocketTransport, type WebSocketTransportOptions, WireFormat, YjsDocUpdateMessage, createDeltaCursorFromBatch, decodeDeltaCursor, decodeYjsUpdate, deriveKey, deriveVersionedKey, encodeDeltaCursor, encodeYjsUpdate, filterOperationsByScope, generateSalt, isClientSchemaVersionSupported, isEncryptedPayload, isSchemaMismatchReject, operationMatchesScope, richtextDocKey, sliceOperationsAfterCursor, versionVectorToWire, wireToVersionVector };