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

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-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';
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-B2GQerqJ.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-B2GQerqJ.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';
@@ -825,6 +825,13 @@ declare class OutboundQueue {
825
825
  * Acknowledge a batch, removing its operations permanently.
826
826
  */
827
827
  acknowledge(batchId: string): Promise<void>;
828
+ /**
829
+ * Acknowledge only operations in an in-flight batch whose local sequence number is
830
+ * covered by the server's ack. Any later operations return to the front of the
831
+ * queue for retry. This is required when the server accepts a prefix but rejects a
832
+ * later operation retriably (for example a stale-scope push after auth changed).
833
+ */
834
+ acknowledgeThrough(batchId: string, lastSequenceNumber: number): Promise<void>;
828
835
  /**
829
836
  * Return a failed batch to the front of the queue for retry.
830
837
  * Prepends the operations to maintain priority.
@@ -927,6 +934,8 @@ interface SyncDiagnostics {
927
934
  pendingOperations: number;
928
935
  hasInFlightBatch: boolean;
929
936
  reconnecting: boolean;
937
+ deliveryWatermark: number;
938
+ deliveryGapRepeatCount: number;
930
939
  timestamp: number;
931
940
  }
932
941
  /**
@@ -961,6 +970,7 @@ declare class SyncEngine {
961
970
  private lastSuccessfulPull;
962
971
  private conflictCount;
963
972
  private currentBatch;
973
+ private currentBatchRequiresPartialAck;
964
974
  private reconnecting;
965
975
  private schemaBlocked;
966
976
  private clockBlocked;
@@ -969,6 +979,7 @@ declare class SyncEngine {
969
979
  private outboundAckTimer;
970
980
  private startPromise;
971
981
  private stopPromise;
982
+ private reconnectPromise;
972
983
  private deltaBatchesReceived;
973
984
  private deltaReceiveComplete;
974
985
  private deltaSendComplete;
@@ -1007,6 +1018,8 @@ declare class SyncEngine {
1007
1018
  * so a view switch resolves synchronously (no race with the reconnect it triggers).
1008
1019
  */
1009
1020
  private readonly deliverySignatureWatermarks;
1021
+ private deliveryGapRepeatCount;
1022
+ private lastDeliveryGapKey;
1010
1023
  constructor(options: SyncEngineOptions);
1011
1024
  /**
1012
1025
  * Start the sync engine: connect → handshake → delta exchange → streaming.
@@ -1075,6 +1088,13 @@ declare class SyncEngine {
1075
1088
  * or in error state, restarts the sync. If already connected, no-op.
1076
1089
  */
1077
1090
  retryNow(): Promise<void>;
1091
+ /**
1092
+ * Atomically refresh the transport session. Overlapping callers share one
1093
+ * reconnect, in-flight outbound batches are returned to the queue by stop(), and the
1094
+ * next start handshakes with the latest auth token, sync scope, and query subsets.
1095
+ */
1096
+ reconnect(): Promise<void>;
1097
+ private reconnectInternal;
1078
1098
  /**
1079
1099
  * Export a diagnostics snapshot for debugging and support tickets.
1080
1100
  * Contains connection state, timing info, and queue metrics.
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-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';
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-B2GQerqJ.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-B2GQerqJ.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';
@@ -825,6 +825,13 @@ declare class OutboundQueue {
825
825
  * Acknowledge a batch, removing its operations permanently.
826
826
  */
827
827
  acknowledge(batchId: string): Promise<void>;
828
+ /**
829
+ * Acknowledge only operations in an in-flight batch whose local sequence number is
830
+ * covered by the server's ack. Any later operations return to the front of the
831
+ * queue for retry. This is required when the server accepts a prefix but rejects a
832
+ * later operation retriably (for example a stale-scope push after auth changed).
833
+ */
834
+ acknowledgeThrough(batchId: string, lastSequenceNumber: number): Promise<void>;
828
835
  /**
829
836
  * Return a failed batch to the front of the queue for retry.
830
837
  * Prepends the operations to maintain priority.
@@ -927,6 +934,8 @@ interface SyncDiagnostics {
927
934
  pendingOperations: number;
928
935
  hasInFlightBatch: boolean;
929
936
  reconnecting: boolean;
937
+ deliveryWatermark: number;
938
+ deliveryGapRepeatCount: number;
930
939
  timestamp: number;
931
940
  }
932
941
  /**
@@ -961,6 +970,7 @@ declare class SyncEngine {
961
970
  private lastSuccessfulPull;
962
971
  private conflictCount;
963
972
  private currentBatch;
973
+ private currentBatchRequiresPartialAck;
964
974
  private reconnecting;
965
975
  private schemaBlocked;
966
976
  private clockBlocked;
@@ -969,6 +979,7 @@ declare class SyncEngine {
969
979
  private outboundAckTimer;
970
980
  private startPromise;
971
981
  private stopPromise;
982
+ private reconnectPromise;
972
983
  private deltaBatchesReceived;
973
984
  private deltaReceiveComplete;
974
985
  private deltaSendComplete;
@@ -1007,6 +1018,8 @@ declare class SyncEngine {
1007
1018
  * so a view switch resolves synchronously (no race with the reconnect it triggers).
1008
1019
  */
1009
1020
  private readonly deliverySignatureWatermarks;
1021
+ private deliveryGapRepeatCount;
1022
+ private lastDeliveryGapKey;
1010
1023
  constructor(options: SyncEngineOptions);
1011
1024
  /**
1012
1025
  * Start the sync engine: connect → handshake → delta exchange → streaming.
@@ -1075,6 +1088,13 @@ declare class SyncEngine {
1075
1088
  * or in error state, restarts the sync. If already connected, no-op.
1076
1089
  */
1077
1090
  retryNow(): Promise<void>;
1091
+ /**
1092
+ * Atomically refresh the transport session. Overlapping callers share one
1093
+ * reconnect, in-flight outbound batches are returned to the queue by stop(), and the
1094
+ * next start handshakes with the latest auth token, sync scope, and query subsets.
1095
+ */
1096
+ reconnect(): Promise<void>;
1097
+ private reconnectInternal;
1078
1098
  /**
1079
1099
  * Export a diagnostics snapshot for debugging and support tickets.
1080
1100
  * Contains connection state, timing info, and queue metrics.
package/dist/index.js CHANGED
@@ -14,6 +14,7 @@ var SYNC_STATES = [
14
14
  ];
15
15
  var SYNC_STATUSES = [
16
16
  "connected",
17
+ "reconnecting",
17
18
  "syncing",
18
19
  "synced",
19
20
  "offline",
@@ -2500,6 +2501,32 @@ var OutboundQueue = class {
2500
2501
  const ids = ops.map((op) => op.id);
2501
2502
  await this.storage.dequeue(ids);
2502
2503
  }
2504
+ /**
2505
+ * Acknowledge only operations in an in-flight batch whose local sequence number is
2506
+ * covered by the server's ack. Any later operations return to the front of the
2507
+ * queue for retry. This is required when the server accepts a prefix but rejects a
2508
+ * later operation retriably (for example a stale-scope push after auth changed).
2509
+ */
2510
+ async acknowledgeThrough(batchId, lastSequenceNumber) {
2511
+ const ops = this.inFlight.get(batchId);
2512
+ if (!ops) return;
2513
+ this.inFlight.delete(batchId);
2514
+ const acknowledged = ops.filter((op) => op.sequenceNumber <= lastSequenceNumber);
2515
+ const retry = ops.filter((op) => op.sequenceNumber > lastSequenceNumber);
2516
+ if (retry.length > 0) {
2517
+ this.queue.unshift(...retry);
2518
+ if (this.queue.length > 1) {
2519
+ this.queue = topologicalSort(this.queue);
2520
+ }
2521
+ }
2522
+ if (acknowledged.length > 0) {
2523
+ const ids = acknowledged.map((op) => op.id);
2524
+ for (const id of ids) {
2525
+ this.seen.delete(id);
2526
+ }
2527
+ await this.storage.dequeue(ids);
2528
+ }
2529
+ }
2503
2530
  /**
2504
2531
  * Return a failed batch to the front of the queue for retry.
2505
2532
  * Prepends the operations to maintain priority.
@@ -2681,6 +2708,7 @@ var SyncEngine = class {
2681
2708
  lastSuccessfulPull = null;
2682
2709
  conflictCount = 0;
2683
2710
  currentBatch = null;
2711
+ currentBatchRequiresPartialAck = false;
2684
2712
  reconnecting = false;
2685
2713
  schemaBlocked = false;
2686
2714
  clockBlocked = false;
@@ -2689,6 +2717,7 @@ var SyncEngine = class {
2689
2717
  outboundAckTimer = null;
2690
2718
  startPromise = null;
2691
2719
  stopPromise = null;
2720
+ reconnectPromise = null;
2692
2721
  // Track delta exchange state
2693
2722
  deltaBatchesReceived = 0;
2694
2723
  deltaReceiveComplete = false;
@@ -2728,6 +2757,8 @@ var SyncEngine = class {
2728
2757
  * so a view switch resolves synchronously (no race with the reconnect it triggers).
2729
2758
  */
2730
2759
  deliverySignatureWatermarks = /* @__PURE__ */ new Map();
2760
+ deliveryGapRepeatCount = 0;
2761
+ lastDeliveryGapKey = null;
2731
2762
  constructor(options) {
2732
2763
  this.transport = options.transport;
2733
2764
  this.store = options.store;
@@ -2875,6 +2906,7 @@ var SyncEngine = class {
2875
2906
  if (this.currentBatch) {
2876
2907
  this.outboundQueue.returnBatch(this.currentBatch.batchId);
2877
2908
  this.currentBatch = null;
2909
+ this.currentBatchRequiresPartialAck = false;
2878
2910
  }
2879
2911
  try {
2880
2912
  await this.transport.disconnect();
@@ -2924,6 +2956,7 @@ var SyncEngine = class {
2924
2956
  getStatus() {
2925
2957
  const pendingOperations = this.syncState ? this.cachedUnsyncedCount : this.outboundQueue.totalPending;
2926
2958
  const base = {
2959
+ reconnecting: this.reconnecting,
2927
2960
  pendingOperations,
2928
2961
  lastSyncedAt: this.lastSyncedAt,
2929
2962
  lastSuccessfulPush: this.lastSuccessfulPush,
@@ -2943,7 +2976,7 @@ var SyncEngine = class {
2943
2976
  case "connecting":
2944
2977
  case "handshaking":
2945
2978
  case "syncing":
2946
- return { ...base, status: this.reconnecting ? "offline" : "syncing" };
2979
+ return { ...base, status: this.reconnecting ? "reconnecting" : "syncing" };
2947
2980
  case "streaming":
2948
2981
  return { ...base, status: pendingOperations > 0 ? "syncing" : "synced" };
2949
2982
  case "error":
@@ -3028,6 +3061,24 @@ var SyncEngine = class {
3028
3061
  await this.start();
3029
3062
  }
3030
3063
  }
3064
+ /**
3065
+ * Atomically refresh the transport session. Overlapping callers share one
3066
+ * reconnect, in-flight outbound batches are returned to the queue by stop(), and the
3067
+ * next start handshakes with the latest auth token, sync scope, and query subsets.
3068
+ */
3069
+ async reconnect() {
3070
+ if (this.schemaBlocked) return;
3071
+ if (this.reconnectPromise) return this.reconnectPromise;
3072
+ this.reconnecting = false;
3073
+ this.reconnectPromise = this.reconnectInternal().finally(() => {
3074
+ this.reconnectPromise = null;
3075
+ });
3076
+ return this.reconnectPromise;
3077
+ }
3078
+ async reconnectInternal() {
3079
+ await this.stop();
3080
+ await this.start();
3081
+ }
3031
3082
  /**
3032
3083
  * Export a diagnostics snapshot for debugging and support tickets.
3033
3084
  * Contains connection state, timing info, and queue metrics.
@@ -3046,6 +3097,8 @@ var SyncEngine = class {
3046
3097
  pendingOperations: this.outboundQueue.totalPending,
3047
3098
  hasInFlightBatch: this.currentBatch !== null,
3048
3099
  reconnecting: this.reconnecting,
3100
+ deliveryWatermark: this.deliveryWatermark,
3101
+ deliveryGapRepeatCount: this.deliveryGapRepeatCount,
3049
3102
  timestamp: Date.now()
3050
3103
  };
3051
3104
  }
@@ -3235,7 +3288,7 @@ var SyncEngine = class {
3235
3288
  await this.handleOperationBatch(message);
3236
3289
  break;
3237
3290
  case "acknowledgment":
3238
- this.handleAcknowledgment(message);
3291
+ await this.handleAcknowledgment(message);
3239
3292
  break;
3240
3293
  case "error":
3241
3294
  this.handleError(message);
@@ -3474,6 +3527,17 @@ var SyncEngine = class {
3474
3527
  if (isDeliveryBatch) {
3475
3528
  const base = msg.baseDeliverySequence ?? 0;
3476
3529
  if (base > this.deliveryWatermark) {
3530
+ const gapKey = `${this.deliveryWatermark}:${base}`;
3531
+ this.deliveryGapRepeatCount = this.lastDeliveryGapKey === gapKey ? this.deliveryGapRepeatCount + 1 : 1;
3532
+ this.lastDeliveryGapKey = gapKey;
3533
+ this.emitter?.emit({
3534
+ type: "sync:delivery-gap",
3535
+ expectedBase: this.deliveryWatermark,
3536
+ receivedBase: base,
3537
+ currentWatermark: this.deliveryWatermark,
3538
+ messageId: msg.messageId,
3539
+ repeatCount: this.deliveryGapRepeatCount
3540
+ });
3477
3541
  return;
3478
3542
  }
3479
3543
  if (base < this.deliveryWatermark) {
@@ -3538,6 +3602,8 @@ var SyncEngine = class {
3538
3602
  });
3539
3603
  }
3540
3604
  if (isDeliveryBatch && fullyApplied && msg.maxDeliverySequence !== void 0) {
3605
+ this.deliveryGapRepeatCount = 0;
3606
+ this.lastDeliveryGapKey = null;
3541
3607
  this.deliveryWatermark = msg.maxDeliverySequence;
3542
3608
  this.setViewWatermark(this.deliverySignature(), this.deliveryWatermark);
3543
3609
  await this.persistDeliveryWatermark(this.deliveryWatermark);
@@ -3579,22 +3645,29 @@ var SyncEngine = class {
3579
3645
  };
3580
3646
  this.transport.send(ack);
3581
3647
  }
3582
- handleAcknowledgment(msg) {
3648
+ async handleAcknowledgment(msg) {
3583
3649
  this.clearOutboundAckTimer();
3584
3650
  if (this.state === "syncing" && this.config.strictHandshake) {
3585
3651
  this.pendingDeltaBatchAcks.delete(msg.acknowledgedMessageId);
3586
3652
  this.markDeltaSendCompleteIfReady();
3587
3653
  }
3588
3654
  if (this.currentBatch) {
3589
- this.outboundQueue.acknowledge(this.currentBatch.batchId);
3655
+ if (this.currentBatchRequiresPartialAck) {
3656
+ await this.outboundQueue.acknowledgeThrough(
3657
+ this.currentBatch.batchId,
3658
+ msg.lastSequenceNumber
3659
+ );
3660
+ } else {
3661
+ await this.outboundQueue.acknowledge(this.currentBatch.batchId);
3662
+ }
3590
3663
  this.currentBatch = null;
3664
+ this.currentBatchRequiresPartialAck = false;
3591
3665
  const now = Date.now();
3592
3666
  this.lastSyncedAt = now;
3593
3667
  this.lastSuccessfulPush = now;
3594
3668
  }
3595
- void this.advanceLastAckedForLocalNode(msg.lastSequenceNumber).then(
3596
- () => this.refreshPendingCount()
3597
- );
3669
+ await this.advanceLastAckedForLocalNode(msg.lastSequenceNumber);
3670
+ await this.refreshPendingCount();
3598
3671
  if (this.state === "streaming" && this.outboundQueue.hasOperations) {
3599
3672
  this.flushQueue();
3600
3673
  }
@@ -3604,6 +3677,7 @@ var SyncEngine = class {
3604
3677
  if (this.currentBatch) {
3605
3678
  this.outboundQueue.returnBatch(this.currentBatch.batchId);
3606
3679
  this.currentBatch = null;
3680
+ this.currentBatchRequiresPartialAck = false;
3607
3681
  }
3608
3682
  this.transitionTo("error");
3609
3683
  if (msg.code === "AUTH_FAILED") {
@@ -3631,6 +3705,20 @@ var SyncEngine = class {
3631
3705
  * signal, so the connection stays up.
3632
3706
  */
3633
3707
  async handleOperationRejected(msg) {
3708
+ if (msg.retriable) {
3709
+ this.currentBatchRequiresPartialAck = true;
3710
+ await this.refreshPendingCount();
3711
+ this.emitter?.emit({
3712
+ type: "sync:operation-rejected",
3713
+ operationId: msg.operationId,
3714
+ collection: msg.collection,
3715
+ recordId: msg.recordId,
3716
+ code: msg.code,
3717
+ message: msg.message,
3718
+ retriable: true
3719
+ });
3720
+ return;
3721
+ }
3634
3722
  await this.outboundQueue.reject(msg.operationId);
3635
3723
  await this.rejectedStorage.record({
3636
3724
  operationId: msg.operationId,
@@ -3776,6 +3864,7 @@ var SyncEngine = class {
3776
3864
  const batch = this.outboundQueue.takeBatch(this.batchSize);
3777
3865
  if (!batch) return;
3778
3866
  this.currentBatch = batch;
3867
+ this.currentBatchRequiresPartialAck = false;
3779
3868
  if (this.encryptor) {
3780
3869
  this.encryptor.encryptBatch(batch.operations).then(
3781
3870
  (encrypted) => {
@@ -3798,6 +3887,7 @@ var SyncEngine = class {
3798
3887
  (err) => {
3799
3888
  this.outboundQueue.returnBatch(batch.batchId);
3800
3889
  this.currentBatch = null;
3890
+ this.currentBatchRequiresPartialAck = false;
3801
3891
  this.emitter?.emit({
3802
3892
  type: "sync:disconnected",
3803
3893
  reason: err instanceof Error ? err.message : "Encryption failed"
@@ -3838,6 +3928,7 @@ var SyncEngine = class {
3838
3928
  if (!this.currentBatch || this.currentBatch.batchId !== batchId) return;
3839
3929
  this.outboundQueue.returnBatch(batchId);
3840
3930
  this.currentBatch = null;
3931
+ this.currentBatchRequiresPartialAck = false;
3841
3932
  this.clearOutboundAckTimer();
3842
3933
  await this.refreshPendingCount();
3843
3934
  if (this.state === "disconnected") return;
@@ -3862,6 +3953,7 @@ var SyncEngine = class {
3862
3953
  if (this.currentBatch) {
3863
3954
  this.outboundQueue.returnBatch(this.currentBatch.batchId);
3864
3955
  this.currentBatch = null;
3956
+ this.currentBatchRequiresPartialAck = false;
3865
3957
  }
3866
3958
  if (this.schemaBlocked) {
3867
3959
  return;
@@ -3952,8 +4044,7 @@ var SyncEngine = class {
3952
4044
  }, 500);
3953
4045
  }
3954
4046
  async reconnectForQuerySubsets() {
3955
- await this.stop();
3956
- await this.start();
4047
+ await this.reconnect();
3957
4048
  }
3958
4049
  };
3959
4050
  function awarenessStatesToWire(states) {
@@ -4634,6 +4725,7 @@ function fromBase642(str) {
4634
4725
  // src/reactivity/sync-status-controller.ts
4635
4726
  var OFFLINE_SYNC_STATUS = Object.freeze({
4636
4727
  status: "offline",
4728
+ reconnecting: false,
4637
4729
  pendingOperations: 0,
4638
4730
  lastSyncedAt: null,
4639
4731
  lastSuccessfulPush: null,
@@ -4651,6 +4743,7 @@ var SYNC_STATUS_EVENT_TYPES = [
4651
4743
  "sync:received",
4652
4744
  "sync:acknowledged",
4653
4745
  "sync:apply-failed",
4746
+ "sync:delivery-gap",
4654
4747
  "sync:diagnostics",
4655
4748
  "sync:initial-sync-progress"
4656
4749
  ];