@korajs/sync 0.4.0 → 0.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.cjs +1118 -243
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +361 -162
- package/dist/index.d.ts +361 -162
- package/dist/index.js +1097 -234
- package/dist/index.js.map +1 -1
- package/dist/internal.d.cts +1 -1
- package/dist/internal.d.ts +1 -1
- package/dist/{transport-B-BIguq9.d.cts → transport-BrdBj7rH.d.cts} +84 -4
- package/dist/{transport-B-BIguq9.d.ts → transport-BrdBj7rH.d.ts} +84 -4
- package/package.json +5 -4
package/dist/index.d.ts
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-
|
|
2
|
-
export { A as AcknowledgmentMessage,
|
|
3
|
-
import
|
|
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.js';
|
|
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.js';
|
|
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
|
-
*
|
|
39
|
+
* Encode a delta cursor for wire transfer or `_kora_meta` persistence.
|
|
33
40
|
*/
|
|
34
|
-
|
|
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
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
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;
|
|
469
|
-
}
|
|
470
|
-
/**
|
|
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.
|
|
482
|
-
*/
|
|
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;
|
|
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;
|
|
518
652
|
}
|
|
519
|
-
|
|
520
653
|
/**
|
|
521
|
-
*
|
|
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).
|
|
522
656
|
*/
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
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
|
-
|
|
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;
|
|
661
|
+
constructor(options?: RichtextDocChannelOptions);
|
|
561
662
|
/**
|
|
562
|
-
*
|
|
563
|
-
*
|
|
663
|
+
* Whether the doc channel should be used for a field.
|
|
664
|
+
* @param preference - `true` forces on, `false` forces off, `undefined` uses size threshold
|
|
564
665
|
*/
|
|
565
|
-
|
|
666
|
+
shouldUseChannel(snapshotByteLength: number, preference?: boolean): boolean;
|
|
667
|
+
getThreshold(): number;
|
|
566
668
|
/**
|
|
567
|
-
*
|
|
568
|
-
* This processes remote state updates and notifies listeners.
|
|
669
|
+
* Subscribe to incremental updates for a document key.
|
|
569
670
|
*/
|
|
570
|
-
|
|
671
|
+
subscribe(collection: string, recordId: string, field: string, listener: RichtextDocListener): () => void;
|
|
571
672
|
/**
|
|
572
|
-
*
|
|
573
|
-
* Called when the server notifies that a client has disconnected.
|
|
673
|
+
* Publish a local incremental update to peers.
|
|
574
674
|
*/
|
|
575
|
-
|
|
675
|
+
send(collection: string, recordId: string, field: string, update: Uint8Array): void;
|
|
576
676
|
/**
|
|
577
|
-
*
|
|
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;
|
|
586
|
-
/**
|
|
587
|
-
* Remove a specific change listener.
|
|
588
|
-
*/
|
|
589
|
-
off(_event: 'change', listener: AwarenessChangeListener): void;
|
|
590
|
-
/**
|
|
591
|
-
* Start the cleanup timer that removes stale remote states.
|
|
592
|
-
* Called when the sync engine transitions to streaming state.
|
|
593
|
-
*/
|
|
594
|
-
startCleanupTimer(): void;
|
|
595
|
-
/**
|
|
596
|
-
* Stop the cleanup timer.
|
|
597
|
-
*/
|
|
598
|
-
stopCleanupTimer(): void;
|
|
599
|
-
/**
|
|
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
|
-
|
|
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
|
-
|
|
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,37 @@ 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
|
-
|
|
1198
|
+
/** Default status when sync is not configured or the engine is unavailable. */
|
|
1199
|
+
declare const OFFLINE_SYNC_STATUS: SyncStatusInfo;
|
|
1200
|
+
interface SyncStatusControllerOptions {
|
|
1201
|
+
syncEngine?: {
|
|
1202
|
+
getStatus(): SyncStatusInfo;
|
|
1203
|
+
} | null;
|
|
1204
|
+
getSyncEngine?: () => {
|
|
1205
|
+
getStatus(): SyncStatusInfo;
|
|
1206
|
+
} | null;
|
|
1207
|
+
subscribeSyncStatus: ((listener: (status: SyncStatusInfo) => void) => () => void) | null;
|
|
1208
|
+
events: KoraEventEmitter | null;
|
|
1209
|
+
}
|
|
1210
|
+
interface SyncStatusController {
|
|
1211
|
+
getSnapshot(): SyncStatusInfo;
|
|
1212
|
+
subscribe(listener: () => void): () => void;
|
|
1213
|
+
refresh(): void;
|
|
1214
|
+
destroy(): void;
|
|
1215
|
+
}
|
|
1216
|
+
/**
|
|
1217
|
+
* Framework-agnostic sync status subscription with live reads when no bridge exists.
|
|
1218
|
+
*/
|
|
1219
|
+
declare function createSyncStatusController(options: SyncStatusControllerOptions): SyncStatusController;
|
|
1220
|
+
|
|
1221
|
+
/**
|
|
1222
|
+
* Returns remote collaborators' awareness states (excludes the local client).
|
|
1223
|
+
*/
|
|
1224
|
+
declare function getRemoteAwarenessStates(awareness: AwarenessManager): AwarenessState[];
|
|
1225
|
+
/**
|
|
1226
|
+
* Subscribes to awareness changes and invokes `listener` only when the remote
|
|
1227
|
+
* collaborator set changes.
|
|
1228
|
+
*/
|
|
1229
|
+
declare function subscribeRemoteAwarenessStates(awareness: AwarenessManager, listener: (states: AwarenessState[]) => void): () => void;
|
|
1230
|
+
|
|
1231
|
+
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, OFFLINE_SYNC_STATUS, 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, type SyncStatusController, type SyncStatusControllerOptions, SyncStatusInfo, type SyncStore, SyncTransport, TransportCloseHandler, TransportErrorHandler, TransportMessageHandler, TransportOptions, VersionedKey, type WebSocketConstructor, type WebSocketLike, WebSocketTransport, type WebSocketTransportOptions, WireFormat, YjsDocUpdateMessage, createDeltaCursorFromBatch, createSyncStatusController, decodeDeltaCursor, decodeYjsUpdate, deriveKey, deriveVersionedKey, encodeDeltaCursor, encodeYjsUpdate, filterOperationsByScope, generateSalt, getRemoteAwarenessStates, isClientSchemaVersionSupported, isEncryptedPayload, isSchemaMismatchReject, operationMatchesScope, richtextDocKey, sliceOperationsAfterCursor, subscribeRemoteAwarenessStates, versionVectorToWire, wireToVersionVector };
|