@korajs/sync 1.0.0-beta.5 → 1.0.0-beta.6

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,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, 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, R as RejectedOperationStorage, m as SyncStatePersistence, n as SyncQuerySubset, o as RejectedOperation } from './transport-DXmZ8kyI.cjs';
2
- export { A as AcknowledgmentMessage, p as AwarenessStateWire, q as AwarenessUpdateMessage, E as EncryptedPayload, r as ErrorMessage, H as HandshakeMessage, s as HandshakeResponseMessage, I as InvalidScopeError, M as MemoryRejectedOperationStorage, O as OperationBatchMessage, t as OperationRejectedMessage, u as SYNC_STATES, v as SYNC_STATUSES, w as ScopeViolationError, x as SyncEncryptionAlgorithm, y as SyncScopeContext, z as SyncStatus, C as dedupeQuerySubsets, F as isAcknowledgmentMessage, G as isAwarenessUpdateMessage, J as isBlobChunkPushMessage, K as isBlobChunkRequestMessage, L as isBlobChunkResponseMessage, N as isErrorMessage, P as isHandshakeMessage, U as isHandshakeResponseMessage, X as isOperationBatchMessage, Z as isOperationRejectedMessage, _ as isSyncMessage, $ as isYjsDocUpdateMessage, a0 as operationMatchesQuerySubsets } from './transport-DXmZ8kyI.cjs';
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, R as RejectedOperationStorage, m as SyncStatePersistence, n as SyncQuerySubset, o as RejectedOperation } from './transport-ByWfWBbB.cjs';
2
+ export { A as AcknowledgmentMessage, p as AwarenessStateWire, q as AwarenessUpdateMessage, E as EncryptedPayload, r as ErrorMessage, H as HandshakeMessage, s as HandshakeResponseMessage, I as InvalidScopeError, M as MemoryRejectedOperationStorage, O as OperationBatchMessage, t as OperationRejectedMessage, u as SYNC_STATES, v as SYNC_STATUSES, w as ScopeViolationError, x as SyncEncryptionAlgorithm, y as SyncScopeContext, z as SyncStatus, C as dedupeQuerySubsets, F as isAcknowledgmentMessage, G as isAwarenessUpdateMessage, J as isBlobChunkPushMessage, K as isBlobChunkRequestMessage, L as isBlobChunkResponseMessage, N as isErrorMessage, P as isHandshakeMessage, U as isHandshakeResponseMessage, X as isOperationBatchMessage, Z as isOperationRejectedMessage, _ as isSyncMessage, $ as isYjsDocUpdateMessage, a0 as operationMatchesQuerySubsets } from './transport-ByWfWBbB.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';
@@ -946,6 +946,7 @@ declare class SyncEngine {
946
946
  private readonly outboundQueue;
947
947
  private readonly rejectedStorage;
948
948
  private readonly batchSize;
949
+ private readonly outboundAckTimeoutMs;
949
950
  private readonly encryptor;
950
951
  private readonly awarenessManager;
951
952
  private readonly richtextDocChannel;
@@ -965,6 +966,9 @@ declare class SyncEngine {
965
966
  private clockBlocked;
966
967
  private clockSkewMs;
967
968
  private blobStorageEnabled;
969
+ private outboundAckTimer;
970
+ private startPromise;
971
+ private stopPromise;
968
972
  private deltaBatchesReceived;
969
973
  private deltaReceiveComplete;
970
974
  private deltaSendComplete;
@@ -1008,10 +1012,12 @@ declare class SyncEngine {
1008
1012
  * Start the sync engine: connect → handshake → delta exchange → streaming.
1009
1013
  */
1010
1014
  start(): Promise<void>;
1015
+ private startInternal;
1011
1016
  /**
1012
1017
  * Stop the sync engine. Disconnects the transport.
1013
1018
  */
1014
1019
  stop(): Promise<void>;
1020
+ private stopInternal;
1015
1021
  private ensureDisconnected;
1016
1022
  /**
1017
1023
  * Push a local operation to the outbound queue.
@@ -1236,6 +1242,9 @@ declare class SyncEngine {
1236
1242
  */
1237
1243
  private reconcileOutboundFromOpLog;
1238
1244
  private flushQueue;
1245
+ private startOutboundAckTimer;
1246
+ private clearOutboundAckTimer;
1247
+ private handleOutboundAckTimeout;
1239
1248
  private handleAwarenessUpdate;
1240
1249
  private handleTransportClose;
1241
1250
  private handleTransportError;
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, 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, R as RejectedOperationStorage, m as SyncStatePersistence, n as SyncQuerySubset, o as RejectedOperation } from './transport-DXmZ8kyI.js';
2
- export { A as AcknowledgmentMessage, p as AwarenessStateWire, q as AwarenessUpdateMessage, E as EncryptedPayload, r as ErrorMessage, H as HandshakeMessage, s as HandshakeResponseMessage, I as InvalidScopeError, M as MemoryRejectedOperationStorage, O as OperationBatchMessage, t as OperationRejectedMessage, u as SYNC_STATES, v as SYNC_STATUSES, w as ScopeViolationError, x as SyncEncryptionAlgorithm, y as SyncScopeContext, z as SyncStatus, C as dedupeQuerySubsets, F as isAcknowledgmentMessage, G as isAwarenessUpdateMessage, J as isBlobChunkPushMessage, K as isBlobChunkRequestMessage, L as isBlobChunkResponseMessage, N as isErrorMessage, P as isHandshakeMessage, U as isHandshakeResponseMessage, X as isOperationBatchMessage, Z as isOperationRejectedMessage, _ as isSyncMessage, $ as isYjsDocUpdateMessage, a0 as operationMatchesQuerySubsets } from './transport-DXmZ8kyI.js';
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, R as RejectedOperationStorage, m as SyncStatePersistence, n as SyncQuerySubset, o as RejectedOperation } from './transport-ByWfWBbB.js';
2
+ export { A as AcknowledgmentMessage, p as AwarenessStateWire, q as AwarenessUpdateMessage, E as EncryptedPayload, r as ErrorMessage, H as HandshakeMessage, s as HandshakeResponseMessage, I as InvalidScopeError, M as MemoryRejectedOperationStorage, O as OperationBatchMessage, t as OperationRejectedMessage, u as SYNC_STATES, v as SYNC_STATUSES, w as ScopeViolationError, x as SyncEncryptionAlgorithm, y as SyncScopeContext, z as SyncStatus, C as dedupeQuerySubsets, F as isAcknowledgmentMessage, G as isAwarenessUpdateMessage, J as isBlobChunkPushMessage, K as isBlobChunkRequestMessage, L as isBlobChunkResponseMessage, N as isErrorMessage, P as isHandshakeMessage, U as isHandshakeResponseMessage, X as isOperationBatchMessage, Z as isOperationRejectedMessage, _ as isSyncMessage, $ as isYjsDocUpdateMessage, a0 as operationMatchesQuerySubsets } from './transport-ByWfWBbB.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';
@@ -946,6 +946,7 @@ declare class SyncEngine {
946
946
  private readonly outboundQueue;
947
947
  private readonly rejectedStorage;
948
948
  private readonly batchSize;
949
+ private readonly outboundAckTimeoutMs;
949
950
  private readonly encryptor;
950
951
  private readonly awarenessManager;
951
952
  private readonly richtextDocChannel;
@@ -965,6 +966,9 @@ declare class SyncEngine {
965
966
  private clockBlocked;
966
967
  private clockSkewMs;
967
968
  private blobStorageEnabled;
969
+ private outboundAckTimer;
970
+ private startPromise;
971
+ private stopPromise;
968
972
  private deltaBatchesReceived;
969
973
  private deltaReceiveComplete;
970
974
  private deltaSendComplete;
@@ -1008,10 +1012,12 @@ declare class SyncEngine {
1008
1012
  * Start the sync engine: connect → handshake → delta exchange → streaming.
1009
1013
  */
1010
1014
  start(): Promise<void>;
1015
+ private startInternal;
1011
1016
  /**
1012
1017
  * Stop the sync engine. Disconnects the transport.
1013
1018
  */
1014
1019
  stop(): Promise<void>;
1020
+ private stopInternal;
1015
1021
  private ensureDisconnected;
1016
1022
  /**
1017
1023
  * Push a local operation to the outbound queue.
@@ -1236,6 +1242,9 @@ declare class SyncEngine {
1236
1242
  */
1237
1243
  private reconcileOutboundFromOpLog;
1238
1244
  private flushQueue;
1245
+ private startOutboundAckTimer;
1246
+ private clearOutboundAckTimer;
1247
+ private handleOutboundAckTimeout;
1239
1248
  private handleAwarenessUpdate;
1240
1249
  private handleTransportClose;
1241
1250
  private handleTransportError;
package/dist/index.js CHANGED
@@ -2631,6 +2631,7 @@ var OutboundQueue = class {
2631
2631
  // src/engine/sync-engine.ts
2632
2632
  var DEFAULT_BATCH_SIZE = 100;
2633
2633
  var DEFAULT_SCHEMA_VERSION = 1;
2634
+ var DEFAULT_OUTBOUND_ACK_TIMEOUT_MS = 3e4;
2634
2635
  var VALID_TRANSITIONS = {
2635
2636
  disconnected: ["connecting"],
2636
2637
  connecting: ["handshaking", "error", "disconnected"],
@@ -2665,6 +2666,7 @@ var SyncEngine = class {
2665
2666
  outboundQueue;
2666
2667
  rejectedStorage;
2667
2668
  batchSize;
2669
+ outboundAckTimeoutMs;
2668
2670
  encryptor;
2669
2671
  awarenessManager;
2670
2672
  richtextDocChannel;
@@ -2684,6 +2686,9 @@ var SyncEngine = class {
2684
2686
  clockBlocked = false;
2685
2687
  clockSkewMs = null;
2686
2688
  blobStorageEnabled = false;
2689
+ outboundAckTimer = null;
2690
+ startPromise = null;
2691
+ stopPromise = null;
2687
2692
  // Track delta exchange state
2688
2693
  deltaBatchesReceived = 0;
2689
2694
  deltaReceiveComplete = false;
@@ -2730,6 +2735,7 @@ var SyncEngine = class {
2730
2735
  this.serializer = options.serializer ?? new NegotiatedMessageSerializer("json");
2731
2736
  this.emitter = options.emitter ?? null;
2732
2737
  this.batchSize = options.config.batchSize ?? DEFAULT_BATCH_SIZE;
2738
+ this.outboundAckTimeoutMs = options.config.outboundAckTimeoutMs ?? DEFAULT_OUTBOUND_ACK_TIMEOUT_MS;
2733
2739
  this.encryptor = options.encryptor ?? null;
2734
2740
  this.syncState = options.syncState ?? null;
2735
2741
  this.activeScope = options.config.scopeMap;
@@ -2775,10 +2781,20 @@ var SyncEngine = class {
2775
2781
  * Start the sync engine: connect → handshake → delta exchange → streaming.
2776
2782
  */
2777
2783
  async start() {
2784
+ if (this.state === "streaming") return;
2785
+ if (this.startPromise) return this.startPromise;
2786
+ this.startPromise = this.startInternal().finally(() => {
2787
+ this.startPromise = null;
2788
+ });
2789
+ return this.startPromise;
2790
+ }
2791
+ async startInternal() {
2792
+ if (this.stopPromise) {
2793
+ await this.stopPromise;
2794
+ }
2795
+ if (this.state === "streaming") return;
2778
2796
  if (this.state !== "disconnected") {
2779
- throw new SyncError4("Cannot start sync engine: not in disconnected state", {
2780
- currentState: this.state
2781
- });
2797
+ await this.stop();
2782
2798
  }
2783
2799
  await this.outboundQueue.initialize();
2784
2800
  if (this.syncState) {
@@ -2811,7 +2827,12 @@ var SyncEngine = class {
2811
2827
  this.transitionTo("connecting");
2812
2828
  try {
2813
2829
  const authToken = this.config.auth ? (await this.config.auth()).token : void 0;
2830
+ if (this.state !== "connecting") return;
2814
2831
  await this.transport.connect(this.config.url, { authToken });
2832
+ if (this.state !== "connecting") {
2833
+ await this.transport.disconnect();
2834
+ return;
2835
+ }
2815
2836
  this.transitionTo("handshaking");
2816
2837
  const localVector = this.store.getVersionVector();
2817
2838
  const activeQuerySubsets = this.getActiveQuerySubsets();
@@ -2841,7 +2862,15 @@ var SyncEngine = class {
2841
2862
  * Stop the sync engine. Disconnects the transport.
2842
2863
  */
2843
2864
  async stop() {
2865
+ if (this.stopPromise) return this.stopPromise;
2866
+ this.stopPromise = this.stopInternal().finally(() => {
2867
+ this.stopPromise = null;
2868
+ });
2869
+ return this.stopPromise;
2870
+ }
2871
+ async stopInternal() {
2844
2872
  if (this.state === "disconnected") return;
2873
+ this.clearOutboundAckTimer();
2845
2874
  this.awarenessManager.stopCleanupTimer();
2846
2875
  if (this.currentBatch) {
2847
2876
  this.outboundQueue.returnBatch(this.currentBatch.batchId);
@@ -3551,6 +3580,7 @@ var SyncEngine = class {
3551
3580
  this.transport.send(ack);
3552
3581
  }
3553
3582
  handleAcknowledgment(msg) {
3583
+ this.clearOutboundAckTimer();
3554
3584
  if (this.state === "syncing" && this.config.strictHandshake) {
3555
3585
  this.pendingDeltaBatchAcks.delete(msg.acknowledgedMessageId);
3556
3586
  this.markDeltaSendCompleteIfReady();
@@ -3570,6 +3600,7 @@ var SyncEngine = class {
3570
3600
  }
3571
3601
  }
3572
3602
  handleError(msg) {
3603
+ this.clearOutboundAckTimer();
3573
3604
  if (this.currentBatch) {
3574
3605
  this.outboundQueue.returnBatch(this.currentBatch.batchId);
3575
3606
  this.currentBatch = null;
@@ -3757,6 +3788,7 @@ var SyncEngine = class {
3757
3788
  batchIndex: 0
3758
3789
  };
3759
3790
  this.transport.send(batchMsg);
3791
+ this.startOutboundAckTimer(batch.batchId);
3760
3792
  this.emitter?.emit({
3761
3793
  type: "sync:sent",
3762
3794
  operations: batch.operations,
@@ -3782,6 +3814,7 @@ var SyncEngine = class {
3782
3814
  batchIndex: 0
3783
3815
  };
3784
3816
  this.transport.send(batchMsg);
3817
+ this.startOutboundAckTimer(batch.batchId);
3785
3818
  this.emitter?.emit({
3786
3819
  type: "sync:sent",
3787
3820
  operations: batch.operations,
@@ -3789,6 +3822,33 @@ var SyncEngine = class {
3789
3822
  });
3790
3823
  }
3791
3824
  }
3825
+ startOutboundAckTimer(batchId) {
3826
+ this.clearOutboundAckTimer();
3827
+ if (this.outboundAckTimeoutMs <= 0) return;
3828
+ this.outboundAckTimer = setTimeout(() => {
3829
+ void this.handleOutboundAckTimeout(batchId);
3830
+ }, this.outboundAckTimeoutMs);
3831
+ }
3832
+ clearOutboundAckTimer() {
3833
+ if (!this.outboundAckTimer) return;
3834
+ clearTimeout(this.outboundAckTimer);
3835
+ this.outboundAckTimer = null;
3836
+ }
3837
+ async handleOutboundAckTimeout(batchId) {
3838
+ if (!this.currentBatch || this.currentBatch.batchId !== batchId) return;
3839
+ this.outboundQueue.returnBatch(batchId);
3840
+ this.currentBatch = null;
3841
+ this.clearOutboundAckTimer();
3842
+ await this.refreshPendingCount();
3843
+ if (this.state === "disconnected") return;
3844
+ const reason = `Timed out waiting for acknowledgment of outbound batch ${batchId}`;
3845
+ try {
3846
+ await this.transport.disconnect();
3847
+ } finally {
3848
+ this.ensureDisconnected();
3849
+ this.emitter?.emit({ type: "sync:disconnected", reason });
3850
+ }
3851
+ }
3792
3852
  handleAwarenessUpdate(msg) {
3793
3853
  const awarenessMessage = {
3794
3854
  type: "awareness",
@@ -3798,6 +3858,7 @@ var SyncEngine = class {
3798
3858
  this.awarenessManager.handleRemoteMessage(awarenessMessage);
3799
3859
  }
3800
3860
  handleTransportClose(reason) {
3861
+ this.clearOutboundAckTimer();
3801
3862
  if (this.currentBatch) {
3802
3863
  this.outboundQueue.returnBatch(this.currentBatch.batchId);
3803
3864
  this.currentBatch = null;