@korajs/sync 0.6.1 → 1.0.0-beta.1
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/LICENSE +21 -0
- package/dist/index.cjs +444 -34
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +154 -7
- package/dist/index.d.ts +154 -7
- package/dist/index.js +423 -19
- package/dist/index.js.map +1 -1
- package/dist/internal.d.cts +1 -1
- package/dist/internal.d.ts +1 -1
- package/dist/{transport-BrdBj7rH.d.cts → transport-CZLMdZ0k.d.cts} +70 -3
- package/dist/{transport-BrdBj7rH.d.ts → transport-CZLMdZ0k.d.ts} +70 -3
- package/package.json +4 -3
package/dist/index.d.cts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
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,
|
|
2
|
-
export { A as AcknowledgmentMessage,
|
|
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, B as BlobChunkRequestMessage, g as BlobChunkResponseMessage, h as BlobChunkPushMessage, i as SyncEncryptionConfig, V as VersionedKey, Y as YjsDocUpdateMessage, Q as QueueStorage, j as SyncState, k as SyncStatusInfo, l as SyncConfig, m as SyncStatePersistence, n as SyncQuerySubset } from './transport-CZLMdZ0k.cjs';
|
|
2
|
+
export { A as AcknowledgmentMessage, o as AwarenessStateWire, p as AwarenessUpdateMessage, E as EncryptedPayload, q as ErrorMessage, H as HandshakeMessage, r as HandshakeResponseMessage, I as InvalidScopeError, O as OperationBatchMessage, s as SYNC_STATES, t as SYNC_STATUSES, u as ScopeViolationError, v as SyncEncryptionAlgorithm, w as SyncScopeContext, x as SyncStatus, y as dedupeQuerySubsets, z as isAcknowledgmentMessage, C as isAwarenessUpdateMessage, F as isBlobChunkPushMessage, G as isBlobChunkRequestMessage, J as isBlobChunkResponseMessage, K as isErrorMessage, L as isHandshakeMessage, M as isHandshakeResponseMessage, N as isOperationBatchMessage, P as isSyncMessage, R as isYjsDocUpdateMessage, U as operationMatchesQuerySubsets } from './transport-CZLMdZ0k.cjs';
|
|
3
3
|
import * as _korajs_core from '@korajs/core';
|
|
4
4
|
import { Operation, VersionVector, ApplyResult, KoraEventEmitter, TimeSource, SyncError, ConnectionQuality } from '@korajs/core';
|
|
5
5
|
export { ApplyFailureReason, ApplyResult } from '@korajs/core';
|
|
@@ -81,6 +81,20 @@ interface SyncStore {
|
|
|
81
81
|
* @param toSeq - End sequence number (inclusive)
|
|
82
82
|
*/
|
|
83
83
|
getOperationRange(nodeId: string, fromSeq: number, toSeq: number): Promise<Operation[]>;
|
|
84
|
+
/**
|
|
85
|
+
* Optional: re-stamp never-acknowledged local operations after a fast device
|
|
86
|
+
* clock was corrected (timestamp rebase). Optional so hand-rolled SyncStore
|
|
87
|
+
* implementations keep working; when absent the engine simply skips the
|
|
88
|
+
* rebase and falls back to the clock-block behavior.
|
|
89
|
+
*
|
|
90
|
+
* @param ids - Operation ids that are candidates for re-stamping
|
|
91
|
+
* @param correctedNowMs - Trusted "now" (server time at handshake) in ms
|
|
92
|
+
*/
|
|
93
|
+
rebaseUnsyncedOperations?(ids: string[], correctedNowMs: number): Promise<{
|
|
94
|
+
operations: Operation[];
|
|
95
|
+
idMapping: Record<string, string>;
|
|
96
|
+
rebasedCount: number;
|
|
97
|
+
}>;
|
|
84
98
|
}
|
|
85
99
|
|
|
86
100
|
/** Prefix for handshake rejection when client schema version is unsupported. */
|
|
@@ -275,7 +289,7 @@ declare class ChaosTransport implements SyncTransport {
|
|
|
275
289
|
private readonly random;
|
|
276
290
|
private messageHandler;
|
|
277
291
|
private reorderBuffer;
|
|
278
|
-
private
|
|
292
|
+
private pending;
|
|
279
293
|
constructor(inner: SyncTransport, config?: ChaosConfig);
|
|
280
294
|
connect(url: string, options?: TransportOptions): Promise<void>;
|
|
281
295
|
disconnect(): Promise<void>;
|
|
@@ -456,6 +470,71 @@ declare class AwarenessManager {
|
|
|
456
470
|
private emitAwarenessEvent;
|
|
457
471
|
}
|
|
458
472
|
|
|
473
|
+
/**
|
|
474
|
+
* A blob-chunk message in decoded form (chunk bytes as a `Uint8Array` rather
|
|
475
|
+
* than the base64 wire string). This is the shape callers of the channel work
|
|
476
|
+
* with; the channel handles base64 encode/decode at the wire boundary.
|
|
477
|
+
*
|
|
478
|
+
* The shape mirrors `@korajs/store`'s `ChunkMessage` so an app-layer adapter can
|
|
479
|
+
* bridge the two with no field translation.
|
|
480
|
+
*/
|
|
481
|
+
type BlobChunkChannelMessage = {
|
|
482
|
+
type: 'blob-chunk-request';
|
|
483
|
+
requestId: string;
|
|
484
|
+
hash: string;
|
|
485
|
+
} | {
|
|
486
|
+
type: 'blob-chunk-response';
|
|
487
|
+
requestId: string;
|
|
488
|
+
bytes: Uint8Array | null;
|
|
489
|
+
} | {
|
|
490
|
+
type: 'blob-chunk-push';
|
|
491
|
+
hash: string;
|
|
492
|
+
bytes: Uint8Array;
|
|
493
|
+
};
|
|
494
|
+
/** Encode chunk bytes to a base64 string for the JSON wire. */
|
|
495
|
+
declare function encodeBlobChunkBytes(bytes: Uint8Array): string;
|
|
496
|
+
/** Decode base64 chunk bytes from the wire back to a `Uint8Array`. */
|
|
497
|
+
declare function decodeBlobChunkBytes(encoded: string): Uint8Array;
|
|
498
|
+
interface BlobChunkChannelOptions {
|
|
499
|
+
/**
|
|
500
|
+
* Called when a local blob-chunk message should go out on the wire. The
|
|
501
|
+
* caller (the sync engine) decides whether the connection is ready to send.
|
|
502
|
+
*/
|
|
503
|
+
onSend?: (message: BlobChunkRequestMessage | BlobChunkResponseMessage | BlobChunkPushMessage) => void;
|
|
504
|
+
}
|
|
505
|
+
/**
|
|
506
|
+
* Ephemeral side channel for out-of-band blob chunk transfer over the sync
|
|
507
|
+
* connection. Symmetric: a peer can both request chunks it needs and answer
|
|
508
|
+
* requests for chunks it holds. Never persisted in the operation log — durable
|
|
509
|
+
* state is the `BlobRef` inside a record's fields; this channel only moves the
|
|
510
|
+
* referenced bytes.
|
|
511
|
+
*
|
|
512
|
+
* The channel owns the base64 wire encoding so callers work purely in
|
|
513
|
+
* `Uint8Array`, and its {@link BlobChunkChannelMessage} shape mirrors
|
|
514
|
+
* `@korajs/store`'s `ChunkMessage` so the app layer can expose it as a
|
|
515
|
+
* `ChunkMessagePort` without translating fields.
|
|
516
|
+
*/
|
|
517
|
+
declare class BlobChunkChannel {
|
|
518
|
+
private readonly onSend;
|
|
519
|
+
private readonly handlers;
|
|
520
|
+
constructor(options?: BlobChunkChannelOptions);
|
|
521
|
+
/**
|
|
522
|
+
* Send a blob-chunk message to the peer. Response bytes are base64-encoded
|
|
523
|
+
* onto the wire here; callers pass a `Uint8Array`.
|
|
524
|
+
*/
|
|
525
|
+
send(message: BlobChunkChannelMessage): void;
|
|
526
|
+
/**
|
|
527
|
+
* Register a handler for inbound blob-chunk messages. Multiple handlers are
|
|
528
|
+
* allowed; each receives every delivered message.
|
|
529
|
+
*/
|
|
530
|
+
onMessage(handler: (message: BlobChunkChannelMessage) => void): () => void;
|
|
531
|
+
/**
|
|
532
|
+
* Deliver an inbound wire message to registered handlers, decoding response
|
|
533
|
+
* bytes from base64 back to a `Uint8Array` first.
|
|
534
|
+
*/
|
|
535
|
+
deliver(message: BlobChunkRequestMessage | BlobChunkResponseMessage | BlobChunkPushMessage): void;
|
|
536
|
+
}
|
|
537
|
+
|
|
459
538
|
/**
|
|
460
539
|
* Configuration for the SyncMetricsCollector.
|
|
461
540
|
*/
|
|
@@ -529,11 +608,13 @@ declare class SyncEncryptor {
|
|
|
529
608
|
* @param config - Encryption configuration with passphrase
|
|
530
609
|
* @param salt - Optional salt for deterministic key derivation (mainly for testing).
|
|
531
610
|
* If omitted, a random salt is generated.
|
|
611
|
+
* @param iterations - Optional PBKDF2 iteration count. Defaults to the
|
|
612
|
+
* production-strength value. Lower it only in tests.
|
|
532
613
|
* @returns A configured SyncEncryptor instance
|
|
533
614
|
* @throws {EncryptionError} If configuration is invalid
|
|
534
615
|
* @throws {KeyDerivationError} If key derivation fails
|
|
535
616
|
*/
|
|
536
|
-
static create(config: SyncEncryptionConfig, salt?: Uint8Array): Promise<SyncEncryptor>;
|
|
617
|
+
static create(config: SyncEncryptionConfig, salt?: Uint8Array, iterations?: number): Promise<SyncEncryptor>;
|
|
537
618
|
/**
|
|
538
619
|
* Creates a SyncEncryptor from pre-derived versioned keys.
|
|
539
620
|
*
|
|
@@ -745,6 +826,20 @@ declare class OutboundQueue {
|
|
|
745
826
|
* Peek at the first `count` operations without removing them.
|
|
746
827
|
*/
|
|
747
828
|
peek(count: number): Operation[];
|
|
829
|
+
/**
|
|
830
|
+
* All queued operations (not counting in-flight), in causal order.
|
|
831
|
+
*/
|
|
832
|
+
getAll(): Operation[];
|
|
833
|
+
/**
|
|
834
|
+
* Atomically replace the entire queue with a new set of operations.
|
|
835
|
+
*
|
|
836
|
+
* Used after a timestamp rebase rewrote the queued operations under new
|
|
837
|
+
* content-addressed ids: the old entries must vanish from memory AND from
|
|
838
|
+
* persistent storage in one step, or a page refresh could resurrect
|
|
839
|
+
* stale-stamped ops the server would reject. Resets the seen set to exactly
|
|
840
|
+
* the new ids so the rewritten ops are not treated as duplicates.
|
|
841
|
+
*/
|
|
842
|
+
replaceAll(ops: Operation[]): Promise<void>;
|
|
748
843
|
/**
|
|
749
844
|
* Whether initialize() has been called.
|
|
750
845
|
*/
|
|
@@ -820,6 +915,7 @@ declare class SyncEngine {
|
|
|
820
915
|
private readonly encryptor;
|
|
821
916
|
private readonly awarenessManager;
|
|
822
917
|
private readonly richtextDocChannel;
|
|
918
|
+
private readonly blobChunkChannel;
|
|
823
919
|
private readonly metricsCollector;
|
|
824
920
|
private readonly syncState;
|
|
825
921
|
private remoteVector;
|
|
@@ -832,6 +928,9 @@ declare class SyncEngine {
|
|
|
832
928
|
private currentBatch;
|
|
833
929
|
private reconnecting;
|
|
834
930
|
private schemaBlocked;
|
|
931
|
+
private clockBlocked;
|
|
932
|
+
private clockSkewMs;
|
|
933
|
+
private blobStorageEnabled;
|
|
835
934
|
private deltaBatchesReceived;
|
|
836
935
|
private deltaReceiveComplete;
|
|
837
936
|
private deltaSendComplete;
|
|
@@ -885,6 +984,18 @@ declare class SyncEngine {
|
|
|
885
984
|
* Sync stays blocked until the app schema is upgraded or sync config changes.
|
|
886
985
|
*/
|
|
887
986
|
isSchemaBlocked(): boolean;
|
|
987
|
+
/**
|
|
988
|
+
* True when sync is blocked because this device's clock is too far ahead.
|
|
989
|
+
* Local writes continue to work and queue; only sync is paused.
|
|
990
|
+
*/
|
|
991
|
+
isClockBlocked(): boolean;
|
|
992
|
+
/** serverTime - localTime measured at the last handshake, or null before first connect. */
|
|
993
|
+
getClockSkewMs(): number | null;
|
|
994
|
+
/**
|
|
995
|
+
* Clears the clock block after the user corrects the device clock.
|
|
996
|
+
* Moves the engine back to `disconnected` so `start()` can run again.
|
|
997
|
+
*/
|
|
998
|
+
clearClockBlock(): void;
|
|
888
999
|
/**
|
|
889
1000
|
* Clears schema-mismatch block after upgrading the local schema / sync config.
|
|
890
1001
|
* Moves the engine back to `disconnected` so `start()` can run again.
|
|
@@ -954,11 +1065,42 @@ declare class SyncEngine {
|
|
|
954
1065
|
* Optional side channel for incremental Yjs updates on large richtext fields.
|
|
955
1066
|
*/
|
|
956
1067
|
getRichtextDocChannel(): RichtextDocChannel;
|
|
1068
|
+
/**
|
|
1069
|
+
* Side channel for out-of-band blob chunk transfer over the sync connection.
|
|
1070
|
+
* The app layer bridges this to a `ChunkMessagePort` to pull/serve blob bytes.
|
|
1071
|
+
*/
|
|
1072
|
+
getBlobChunkChannel(): BlobChunkChannel;
|
|
1073
|
+
/**
|
|
1074
|
+
* Whether the connected server persists blob bytes centrally (learned from the
|
|
1075
|
+
* handshake response). When true, the app uploads the bytes behind `blob`
|
|
1076
|
+
* fields so they stay available after the authoring device goes offline.
|
|
1077
|
+
*/
|
|
1078
|
+
isBlobStorageEnabled(): boolean;
|
|
1079
|
+
/**
|
|
1080
|
+
* Upload a blob chunk (or manifest) to the server for central persistence.
|
|
1081
|
+
* A no-op unless connected and the server advertised blob storage.
|
|
1082
|
+
*/
|
|
1083
|
+
uploadBlobChunk(hash: string, bytes: Uint8Array): void;
|
|
957
1084
|
private messageChain;
|
|
958
1085
|
private enqueueMessage;
|
|
959
1086
|
private handleMessageAsync;
|
|
960
1087
|
private handleMessageFailure;
|
|
1088
|
+
/**
|
|
1089
|
+
* Compares server wall-clock time from the handshake with local time.
|
|
1090
|
+
* Negative skew = this device's clock is fast (dangerous for LWW and rejected
|
|
1091
|
+
* by server ingest beyond 60s). Positive skew = slow (accepted but surfaced).
|
|
1092
|
+
* Zero developer work required: the result flows into sync status and events.
|
|
1093
|
+
*/
|
|
1094
|
+
private evaluateClockSkew;
|
|
961
1095
|
private handleHandshakeResponse;
|
|
1096
|
+
/**
|
|
1097
|
+
* Re-stamps queued (never-acknowledged) operations whose timestamps are far
|
|
1098
|
+
* enough in the future that the server would reject them, using the server's
|
|
1099
|
+
* own handshake time as the trusted "now". This is the automatic recovery
|
|
1100
|
+
* path after a user corrects a fast device clock: the queue drains
|
|
1101
|
+
* immediately instead of waiting for real time to catch up.
|
|
1102
|
+
*/
|
|
1103
|
+
private maybeRebaseQueuedOperations;
|
|
962
1104
|
private sendDelta;
|
|
963
1105
|
private markDeltaSendCompleteIfReady;
|
|
964
1106
|
private collectDelta;
|
|
@@ -1164,6 +1306,9 @@ declare function generateSalt(): Uint8Array;
|
|
|
1164
1306
|
*
|
|
1165
1307
|
* @param passphrase - The user's passphrase (must be non-empty)
|
|
1166
1308
|
* @param salt - Optional salt bytes. If omitted, a random 32-byte salt is generated.
|
|
1309
|
+
* @param iterations - Optional PBKDF2 iteration count. Defaults to
|
|
1310
|
+
* {@link DEFAULT_PBKDF2_ITERATIONS}. Lower it only in tests; production must
|
|
1311
|
+
* keep the default so derived keys stay brute-force resistant.
|
|
1167
1312
|
* @returns The derived CryptoKey and the salt used
|
|
1168
1313
|
* @throws {KeyDerivationError} If the passphrase is empty, crypto is unavailable, or derivation fails
|
|
1169
1314
|
*
|
|
@@ -1176,7 +1321,7 @@ declare function generateSalt(): Uint8Array;
|
|
|
1176
1321
|
* const { key: sameKey } = await deriveKey('my-secure-passphrase', salt)
|
|
1177
1322
|
* ```
|
|
1178
1323
|
*/
|
|
1179
|
-
declare function deriveKey(passphrase: string, salt?: Uint8Array): Promise<{
|
|
1324
|
+
declare function deriveKey(passphrase: string, salt?: Uint8Array, iterations?: number): Promise<{
|
|
1180
1325
|
key: CryptoKey;
|
|
1181
1326
|
salt: Uint8Array;
|
|
1182
1327
|
}>;
|
|
@@ -1190,10 +1335,12 @@ declare function deriveKey(passphrase: string, salt?: Uint8Array): Promise<{
|
|
|
1190
1335
|
* @param passphrase - The user's passphrase
|
|
1191
1336
|
* @param version - Key version number (must be a positive integer)
|
|
1192
1337
|
* @param salt - Optional salt bytes. If omitted, a random salt is generated.
|
|
1338
|
+
* @param iterations - Optional PBKDF2 iteration count. Defaults to
|
|
1339
|
+
* {@link DEFAULT_PBKDF2_ITERATIONS}. Lower it only in tests.
|
|
1193
1340
|
* @returns A {@link VersionedKey} containing the key, version, and salt
|
|
1194
1341
|
* @throws {KeyDerivationError} If parameters are invalid or derivation fails
|
|
1195
1342
|
*/
|
|
1196
|
-
declare function deriveVersionedKey(passphrase: string, version: number, salt?: Uint8Array): Promise<VersionedKey>;
|
|
1343
|
+
declare function deriveVersionedKey(passphrase: string, version: number, salt?: Uint8Array, iterations?: number): Promise<VersionedKey>;
|
|
1197
1344
|
|
|
1198
1345
|
/** Default status when sync is not configured or the engine is unavailable. */
|
|
1199
1346
|
declare const OFFLINE_SYNC_STATUS: SyncStatusInfo;
|
|
@@ -1228,4 +1375,4 @@ declare function getRemoteAwarenessStates(awareness: AwarenessManager): Awarenes
|
|
|
1228
1375
|
*/
|
|
1229
1376
|
declare function subscribeRemoteAwarenessStates(awareness: AwarenessManager, listener: (states: AwarenessState[]) => void): () => void;
|
|
1230
1377
|
|
|
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 };
|
|
1378
|
+
export { type AwarenessChange, type AwarenessCursor, AwarenessManager, type AwarenessMessage, type AwarenessState, type AwarenessUser, BlobChunkChannel, type BlobChunkChannelMessage, type BlobChunkChannelOptions, BlobChunkPushMessage, BlobChunkRequestMessage, BlobChunkResponseMessage, 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, decodeBlobChunkBytes, decodeDeltaCursor, decodeYjsUpdate, deriveKey, deriveVersionedKey, encodeBlobChunkBytes, encodeDeltaCursor, encodeYjsUpdate, filterOperationsByScope, generateSalt, getRemoteAwarenessStates, isClientSchemaVersionSupported, isEncryptedPayload, isSchemaMismatchReject, operationMatchesScope, richtextDocKey, sliceOperationsAfterCursor, subscribeRemoteAwarenessStates, versionVectorToWire, wireToVersionVector };
|
package/dist/index.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
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,
|
|
2
|
-
export { A as AcknowledgmentMessage,
|
|
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, B as BlobChunkRequestMessage, g as BlobChunkResponseMessage, h as BlobChunkPushMessage, i as SyncEncryptionConfig, V as VersionedKey, Y as YjsDocUpdateMessage, Q as QueueStorage, j as SyncState, k as SyncStatusInfo, l as SyncConfig, m as SyncStatePersistence, n as SyncQuerySubset } from './transport-CZLMdZ0k.js';
|
|
2
|
+
export { A as AcknowledgmentMessage, o as AwarenessStateWire, p as AwarenessUpdateMessage, E as EncryptedPayload, q as ErrorMessage, H as HandshakeMessage, r as HandshakeResponseMessage, I as InvalidScopeError, O as OperationBatchMessage, s as SYNC_STATES, t as SYNC_STATUSES, u as ScopeViolationError, v as SyncEncryptionAlgorithm, w as SyncScopeContext, x as SyncStatus, y as dedupeQuerySubsets, z as isAcknowledgmentMessage, C as isAwarenessUpdateMessage, F as isBlobChunkPushMessage, G as isBlobChunkRequestMessage, J as isBlobChunkResponseMessage, K as isErrorMessage, L as isHandshakeMessage, M as isHandshakeResponseMessage, N as isOperationBatchMessage, P as isSyncMessage, R as isYjsDocUpdateMessage, U as operationMatchesQuerySubsets } from './transport-CZLMdZ0k.js';
|
|
3
3
|
import * as _korajs_core from '@korajs/core';
|
|
4
4
|
import { Operation, VersionVector, ApplyResult, KoraEventEmitter, TimeSource, SyncError, ConnectionQuality } from '@korajs/core';
|
|
5
5
|
export { ApplyFailureReason, ApplyResult } from '@korajs/core';
|
|
@@ -81,6 +81,20 @@ interface SyncStore {
|
|
|
81
81
|
* @param toSeq - End sequence number (inclusive)
|
|
82
82
|
*/
|
|
83
83
|
getOperationRange(nodeId: string, fromSeq: number, toSeq: number): Promise<Operation[]>;
|
|
84
|
+
/**
|
|
85
|
+
* Optional: re-stamp never-acknowledged local operations after a fast device
|
|
86
|
+
* clock was corrected (timestamp rebase). Optional so hand-rolled SyncStore
|
|
87
|
+
* implementations keep working; when absent the engine simply skips the
|
|
88
|
+
* rebase and falls back to the clock-block behavior.
|
|
89
|
+
*
|
|
90
|
+
* @param ids - Operation ids that are candidates for re-stamping
|
|
91
|
+
* @param correctedNowMs - Trusted "now" (server time at handshake) in ms
|
|
92
|
+
*/
|
|
93
|
+
rebaseUnsyncedOperations?(ids: string[], correctedNowMs: number): Promise<{
|
|
94
|
+
operations: Operation[];
|
|
95
|
+
idMapping: Record<string, string>;
|
|
96
|
+
rebasedCount: number;
|
|
97
|
+
}>;
|
|
84
98
|
}
|
|
85
99
|
|
|
86
100
|
/** Prefix for handshake rejection when client schema version is unsupported. */
|
|
@@ -275,7 +289,7 @@ declare class ChaosTransport implements SyncTransport {
|
|
|
275
289
|
private readonly random;
|
|
276
290
|
private messageHandler;
|
|
277
291
|
private reorderBuffer;
|
|
278
|
-
private
|
|
292
|
+
private pending;
|
|
279
293
|
constructor(inner: SyncTransport, config?: ChaosConfig);
|
|
280
294
|
connect(url: string, options?: TransportOptions): Promise<void>;
|
|
281
295
|
disconnect(): Promise<void>;
|
|
@@ -456,6 +470,71 @@ declare class AwarenessManager {
|
|
|
456
470
|
private emitAwarenessEvent;
|
|
457
471
|
}
|
|
458
472
|
|
|
473
|
+
/**
|
|
474
|
+
* A blob-chunk message in decoded form (chunk bytes as a `Uint8Array` rather
|
|
475
|
+
* than the base64 wire string). This is the shape callers of the channel work
|
|
476
|
+
* with; the channel handles base64 encode/decode at the wire boundary.
|
|
477
|
+
*
|
|
478
|
+
* The shape mirrors `@korajs/store`'s `ChunkMessage` so an app-layer adapter can
|
|
479
|
+
* bridge the two with no field translation.
|
|
480
|
+
*/
|
|
481
|
+
type BlobChunkChannelMessage = {
|
|
482
|
+
type: 'blob-chunk-request';
|
|
483
|
+
requestId: string;
|
|
484
|
+
hash: string;
|
|
485
|
+
} | {
|
|
486
|
+
type: 'blob-chunk-response';
|
|
487
|
+
requestId: string;
|
|
488
|
+
bytes: Uint8Array | null;
|
|
489
|
+
} | {
|
|
490
|
+
type: 'blob-chunk-push';
|
|
491
|
+
hash: string;
|
|
492
|
+
bytes: Uint8Array;
|
|
493
|
+
};
|
|
494
|
+
/** Encode chunk bytes to a base64 string for the JSON wire. */
|
|
495
|
+
declare function encodeBlobChunkBytes(bytes: Uint8Array): string;
|
|
496
|
+
/** Decode base64 chunk bytes from the wire back to a `Uint8Array`. */
|
|
497
|
+
declare function decodeBlobChunkBytes(encoded: string): Uint8Array;
|
|
498
|
+
interface BlobChunkChannelOptions {
|
|
499
|
+
/**
|
|
500
|
+
* Called when a local blob-chunk message should go out on the wire. The
|
|
501
|
+
* caller (the sync engine) decides whether the connection is ready to send.
|
|
502
|
+
*/
|
|
503
|
+
onSend?: (message: BlobChunkRequestMessage | BlobChunkResponseMessage | BlobChunkPushMessage) => void;
|
|
504
|
+
}
|
|
505
|
+
/**
|
|
506
|
+
* Ephemeral side channel for out-of-band blob chunk transfer over the sync
|
|
507
|
+
* connection. Symmetric: a peer can both request chunks it needs and answer
|
|
508
|
+
* requests for chunks it holds. Never persisted in the operation log — durable
|
|
509
|
+
* state is the `BlobRef` inside a record's fields; this channel only moves the
|
|
510
|
+
* referenced bytes.
|
|
511
|
+
*
|
|
512
|
+
* The channel owns the base64 wire encoding so callers work purely in
|
|
513
|
+
* `Uint8Array`, and its {@link BlobChunkChannelMessage} shape mirrors
|
|
514
|
+
* `@korajs/store`'s `ChunkMessage` so the app layer can expose it as a
|
|
515
|
+
* `ChunkMessagePort` without translating fields.
|
|
516
|
+
*/
|
|
517
|
+
declare class BlobChunkChannel {
|
|
518
|
+
private readonly onSend;
|
|
519
|
+
private readonly handlers;
|
|
520
|
+
constructor(options?: BlobChunkChannelOptions);
|
|
521
|
+
/**
|
|
522
|
+
* Send a blob-chunk message to the peer. Response bytes are base64-encoded
|
|
523
|
+
* onto the wire here; callers pass a `Uint8Array`.
|
|
524
|
+
*/
|
|
525
|
+
send(message: BlobChunkChannelMessage): void;
|
|
526
|
+
/**
|
|
527
|
+
* Register a handler for inbound blob-chunk messages. Multiple handlers are
|
|
528
|
+
* allowed; each receives every delivered message.
|
|
529
|
+
*/
|
|
530
|
+
onMessage(handler: (message: BlobChunkChannelMessage) => void): () => void;
|
|
531
|
+
/**
|
|
532
|
+
* Deliver an inbound wire message to registered handlers, decoding response
|
|
533
|
+
* bytes from base64 back to a `Uint8Array` first.
|
|
534
|
+
*/
|
|
535
|
+
deliver(message: BlobChunkRequestMessage | BlobChunkResponseMessage | BlobChunkPushMessage): void;
|
|
536
|
+
}
|
|
537
|
+
|
|
459
538
|
/**
|
|
460
539
|
* Configuration for the SyncMetricsCollector.
|
|
461
540
|
*/
|
|
@@ -529,11 +608,13 @@ declare class SyncEncryptor {
|
|
|
529
608
|
* @param config - Encryption configuration with passphrase
|
|
530
609
|
* @param salt - Optional salt for deterministic key derivation (mainly for testing).
|
|
531
610
|
* If omitted, a random salt is generated.
|
|
611
|
+
* @param iterations - Optional PBKDF2 iteration count. Defaults to the
|
|
612
|
+
* production-strength value. Lower it only in tests.
|
|
532
613
|
* @returns A configured SyncEncryptor instance
|
|
533
614
|
* @throws {EncryptionError} If configuration is invalid
|
|
534
615
|
* @throws {KeyDerivationError} If key derivation fails
|
|
535
616
|
*/
|
|
536
|
-
static create(config: SyncEncryptionConfig, salt?: Uint8Array): Promise<SyncEncryptor>;
|
|
617
|
+
static create(config: SyncEncryptionConfig, salt?: Uint8Array, iterations?: number): Promise<SyncEncryptor>;
|
|
537
618
|
/**
|
|
538
619
|
* Creates a SyncEncryptor from pre-derived versioned keys.
|
|
539
620
|
*
|
|
@@ -745,6 +826,20 @@ declare class OutboundQueue {
|
|
|
745
826
|
* Peek at the first `count` operations without removing them.
|
|
746
827
|
*/
|
|
747
828
|
peek(count: number): Operation[];
|
|
829
|
+
/**
|
|
830
|
+
* All queued operations (not counting in-flight), in causal order.
|
|
831
|
+
*/
|
|
832
|
+
getAll(): Operation[];
|
|
833
|
+
/**
|
|
834
|
+
* Atomically replace the entire queue with a new set of operations.
|
|
835
|
+
*
|
|
836
|
+
* Used after a timestamp rebase rewrote the queued operations under new
|
|
837
|
+
* content-addressed ids: the old entries must vanish from memory AND from
|
|
838
|
+
* persistent storage in one step, or a page refresh could resurrect
|
|
839
|
+
* stale-stamped ops the server would reject. Resets the seen set to exactly
|
|
840
|
+
* the new ids so the rewritten ops are not treated as duplicates.
|
|
841
|
+
*/
|
|
842
|
+
replaceAll(ops: Operation[]): Promise<void>;
|
|
748
843
|
/**
|
|
749
844
|
* Whether initialize() has been called.
|
|
750
845
|
*/
|
|
@@ -820,6 +915,7 @@ declare class SyncEngine {
|
|
|
820
915
|
private readonly encryptor;
|
|
821
916
|
private readonly awarenessManager;
|
|
822
917
|
private readonly richtextDocChannel;
|
|
918
|
+
private readonly blobChunkChannel;
|
|
823
919
|
private readonly metricsCollector;
|
|
824
920
|
private readonly syncState;
|
|
825
921
|
private remoteVector;
|
|
@@ -832,6 +928,9 @@ declare class SyncEngine {
|
|
|
832
928
|
private currentBatch;
|
|
833
929
|
private reconnecting;
|
|
834
930
|
private schemaBlocked;
|
|
931
|
+
private clockBlocked;
|
|
932
|
+
private clockSkewMs;
|
|
933
|
+
private blobStorageEnabled;
|
|
835
934
|
private deltaBatchesReceived;
|
|
836
935
|
private deltaReceiveComplete;
|
|
837
936
|
private deltaSendComplete;
|
|
@@ -885,6 +984,18 @@ declare class SyncEngine {
|
|
|
885
984
|
* Sync stays blocked until the app schema is upgraded or sync config changes.
|
|
886
985
|
*/
|
|
887
986
|
isSchemaBlocked(): boolean;
|
|
987
|
+
/**
|
|
988
|
+
* True when sync is blocked because this device's clock is too far ahead.
|
|
989
|
+
* Local writes continue to work and queue; only sync is paused.
|
|
990
|
+
*/
|
|
991
|
+
isClockBlocked(): boolean;
|
|
992
|
+
/** serverTime - localTime measured at the last handshake, or null before first connect. */
|
|
993
|
+
getClockSkewMs(): number | null;
|
|
994
|
+
/**
|
|
995
|
+
* Clears the clock block after the user corrects the device clock.
|
|
996
|
+
* Moves the engine back to `disconnected` so `start()` can run again.
|
|
997
|
+
*/
|
|
998
|
+
clearClockBlock(): void;
|
|
888
999
|
/**
|
|
889
1000
|
* Clears schema-mismatch block after upgrading the local schema / sync config.
|
|
890
1001
|
* Moves the engine back to `disconnected` so `start()` can run again.
|
|
@@ -954,11 +1065,42 @@ declare class SyncEngine {
|
|
|
954
1065
|
* Optional side channel for incremental Yjs updates on large richtext fields.
|
|
955
1066
|
*/
|
|
956
1067
|
getRichtextDocChannel(): RichtextDocChannel;
|
|
1068
|
+
/**
|
|
1069
|
+
* Side channel for out-of-band blob chunk transfer over the sync connection.
|
|
1070
|
+
* The app layer bridges this to a `ChunkMessagePort` to pull/serve blob bytes.
|
|
1071
|
+
*/
|
|
1072
|
+
getBlobChunkChannel(): BlobChunkChannel;
|
|
1073
|
+
/**
|
|
1074
|
+
* Whether the connected server persists blob bytes centrally (learned from the
|
|
1075
|
+
* handshake response). When true, the app uploads the bytes behind `blob`
|
|
1076
|
+
* fields so they stay available after the authoring device goes offline.
|
|
1077
|
+
*/
|
|
1078
|
+
isBlobStorageEnabled(): boolean;
|
|
1079
|
+
/**
|
|
1080
|
+
* Upload a blob chunk (or manifest) to the server for central persistence.
|
|
1081
|
+
* A no-op unless connected and the server advertised blob storage.
|
|
1082
|
+
*/
|
|
1083
|
+
uploadBlobChunk(hash: string, bytes: Uint8Array): void;
|
|
957
1084
|
private messageChain;
|
|
958
1085
|
private enqueueMessage;
|
|
959
1086
|
private handleMessageAsync;
|
|
960
1087
|
private handleMessageFailure;
|
|
1088
|
+
/**
|
|
1089
|
+
* Compares server wall-clock time from the handshake with local time.
|
|
1090
|
+
* Negative skew = this device's clock is fast (dangerous for LWW and rejected
|
|
1091
|
+
* by server ingest beyond 60s). Positive skew = slow (accepted but surfaced).
|
|
1092
|
+
* Zero developer work required: the result flows into sync status and events.
|
|
1093
|
+
*/
|
|
1094
|
+
private evaluateClockSkew;
|
|
961
1095
|
private handleHandshakeResponse;
|
|
1096
|
+
/**
|
|
1097
|
+
* Re-stamps queued (never-acknowledged) operations whose timestamps are far
|
|
1098
|
+
* enough in the future that the server would reject them, using the server's
|
|
1099
|
+
* own handshake time as the trusted "now". This is the automatic recovery
|
|
1100
|
+
* path after a user corrects a fast device clock: the queue drains
|
|
1101
|
+
* immediately instead of waiting for real time to catch up.
|
|
1102
|
+
*/
|
|
1103
|
+
private maybeRebaseQueuedOperations;
|
|
962
1104
|
private sendDelta;
|
|
963
1105
|
private markDeltaSendCompleteIfReady;
|
|
964
1106
|
private collectDelta;
|
|
@@ -1164,6 +1306,9 @@ declare function generateSalt(): Uint8Array;
|
|
|
1164
1306
|
*
|
|
1165
1307
|
* @param passphrase - The user's passphrase (must be non-empty)
|
|
1166
1308
|
* @param salt - Optional salt bytes. If omitted, a random 32-byte salt is generated.
|
|
1309
|
+
* @param iterations - Optional PBKDF2 iteration count. Defaults to
|
|
1310
|
+
* {@link DEFAULT_PBKDF2_ITERATIONS}. Lower it only in tests; production must
|
|
1311
|
+
* keep the default so derived keys stay brute-force resistant.
|
|
1167
1312
|
* @returns The derived CryptoKey and the salt used
|
|
1168
1313
|
* @throws {KeyDerivationError} If the passphrase is empty, crypto is unavailable, or derivation fails
|
|
1169
1314
|
*
|
|
@@ -1176,7 +1321,7 @@ declare function generateSalt(): Uint8Array;
|
|
|
1176
1321
|
* const { key: sameKey } = await deriveKey('my-secure-passphrase', salt)
|
|
1177
1322
|
* ```
|
|
1178
1323
|
*/
|
|
1179
|
-
declare function deriveKey(passphrase: string, salt?: Uint8Array): Promise<{
|
|
1324
|
+
declare function deriveKey(passphrase: string, salt?: Uint8Array, iterations?: number): Promise<{
|
|
1180
1325
|
key: CryptoKey;
|
|
1181
1326
|
salt: Uint8Array;
|
|
1182
1327
|
}>;
|
|
@@ -1190,10 +1335,12 @@ declare function deriveKey(passphrase: string, salt?: Uint8Array): Promise<{
|
|
|
1190
1335
|
* @param passphrase - The user's passphrase
|
|
1191
1336
|
* @param version - Key version number (must be a positive integer)
|
|
1192
1337
|
* @param salt - Optional salt bytes. If omitted, a random salt is generated.
|
|
1338
|
+
* @param iterations - Optional PBKDF2 iteration count. Defaults to
|
|
1339
|
+
* {@link DEFAULT_PBKDF2_ITERATIONS}. Lower it only in tests.
|
|
1193
1340
|
* @returns A {@link VersionedKey} containing the key, version, and salt
|
|
1194
1341
|
* @throws {KeyDerivationError} If parameters are invalid or derivation fails
|
|
1195
1342
|
*/
|
|
1196
|
-
declare function deriveVersionedKey(passphrase: string, version: number, salt?: Uint8Array): Promise<VersionedKey>;
|
|
1343
|
+
declare function deriveVersionedKey(passphrase: string, version: number, salt?: Uint8Array, iterations?: number): Promise<VersionedKey>;
|
|
1197
1344
|
|
|
1198
1345
|
/** Default status when sync is not configured or the engine is unavailable. */
|
|
1199
1346
|
declare const OFFLINE_SYNC_STATUS: SyncStatusInfo;
|
|
@@ -1228,4 +1375,4 @@ declare function getRemoteAwarenessStates(awareness: AwarenessManager): Awarenes
|
|
|
1228
1375
|
*/
|
|
1229
1376
|
declare function subscribeRemoteAwarenessStates(awareness: AwarenessManager, listener: (states: AwarenessState[]) => void): () => void;
|
|
1230
1377
|
|
|
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 };
|
|
1378
|
+
export { type AwarenessChange, type AwarenessCursor, AwarenessManager, type AwarenessMessage, type AwarenessState, type AwarenessUser, BlobChunkChannel, type BlobChunkChannelMessage, type BlobChunkChannelOptions, BlobChunkPushMessage, BlobChunkRequestMessage, BlobChunkResponseMessage, 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, decodeBlobChunkBytes, decodeDeltaCursor, decodeYjsUpdate, deriveKey, deriveVersionedKey, encodeBlobChunkBytes, encodeDeltaCursor, encodeYjsUpdate, filterOperationsByScope, generateSalt, getRemoteAwarenessStates, isClientSchemaVersionSupported, isEncryptedPayload, isSchemaMismatchReject, operationMatchesScope, richtextDocKey, sliceOperationsAfterCursor, subscribeRemoteAwarenessStates, versionVectorToWire, wireToVersionVector };
|