@pylonsync/sync 0.3.196 → 0.3.197

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.
Files changed (2) hide show
  1. package/package.json +1 -1
  2. package/src/index.ts +31 -18
package/package.json CHANGED
@@ -3,7 +3,7 @@
3
3
  "publishConfig": {
4
4
  "access": "public"
5
5
  },
6
- "version": "0.3.196",
6
+ "version": "0.3.197",
7
7
  "type": "module",
8
8
  "main": "src/index.ts",
9
9
  "types": "src/index.ts",
package/src/index.ts CHANGED
@@ -758,7 +758,15 @@ export class SyncEngine {
758
758
  typeof msg.entity === "string" &&
759
759
  typeof msg.row_id === "string"
760
760
  ) {
761
- this.handleRowRevocation(msg.entity, msg.row_id);
761
+ // Server includes its current high-water seq when known —
762
+ // use it as the tombstone seq so an in-flight stale frame
763
+ // with `seq <= server_seq` is filtered locally. A
764
+ // legitimate re-grant at a higher seq still lands.
765
+ const revokeSeq =
766
+ typeof msg.seq === "number" && msg.seq > 0
767
+ ? msg.seq
768
+ : this.cursor.last_seq;
769
+ this.handleRowRevocation(msg.entity, msg.row_id, revokeSeq);
762
770
  return;
763
771
  }
764
772
 
@@ -1491,28 +1499,28 @@ export class SyncEngine {
1491
1499
  * Drop a row from the local replica because the server signaled
1492
1500
  * that the current subscriber's read policy was revoked for it.
1493
1501
  *
1494
- * Calls `LocalStore.revokeRow(entity, id, cursor.last_seq)`
1495
- * NOT `reconcileRemove` because the latter early-returns when
1496
- * the row isn't in memory (common for CRDT-only consumers using
1497
- * `useLoroDoc` without a JSON row). `revokeRow` always records
1498
- * the tombstone so a stale insert/update arriving after the
1499
- * revocation can't resurrect the row.
1502
+ * `tombstoneSeq` is the server's high-water seq at the time of
1503
+ * revocation (from the envelope). Stale in-flight WS frames with
1504
+ * `seq <= tombstoneSeq` are filtered locally; legitimate
1505
+ * re-grant + re-insert at higher seqs still land. Also fires a
1506
+ * catch-up pull on revocation so any frame with `seq >
1507
+ * tombstoneSeq` that arrives before the next legitimate event
1508
+ * gets reconciled against server truth.
1509
+ *
1510
+ * Uses `LocalStore.revokeRow` (not `reconcileRemove`) so the
1511
+ * tombstone is recorded even for CRDT-only consumers whose row
1512
+ * was never materialized into `tables`.
1500
1513
  *
1501
1514
  * Also notifies row-eviction listeners so external row-bound
1502
1515
  * resources (LoroDoc registries, etc.) can unmount.
1503
1516
  */
1504
- private handleRowRevocation(entity: string, rowId: string): void {
1505
- const removed = this.store.revokeRow(
1506
- entity,
1507
- rowId,
1508
- this.cursor.last_seq,
1509
- );
1517
+ private handleRowRevocation(
1518
+ entity: string,
1519
+ rowId: string,
1520
+ tombstoneSeq: number,
1521
+ ): void {
1522
+ const removed = this.store.revokeRow(entity, rowId, tombstoneSeq);
1510
1523
  if (removed) {
1511
- // Persist the deletion through the same pipe as a real Delete
1512
- // event so on-disk replica + in-memory replica stay aligned.
1513
- // For CRDT-only consumers `removed` is false (the row was
1514
- // never materialized into `tables`), but the tombstone is
1515
- // still recorded above so future replays are filtered.
1516
1524
  if (this.persistence) {
1517
1525
  void this.persistence.deleteRow(entity, rowId).catch(() => {
1518
1526
  /* best-effort */
@@ -1523,6 +1531,11 @@ export class SyncEngine {
1523
1531
  for (const listener of this.rowEvictionListeners) {
1524
1532
  listener(entity, rowId);
1525
1533
  }
1534
+ // Fire a catch-up pull so any in-flight frame with seq above
1535
+ // the revocation tombstone is resolved against server truth.
1536
+ // Fire-and-forget — pull is internally serialized so concurrent
1537
+ // triggers coalesce.
1538
+ void this.pull();
1526
1539
  }
1527
1540
 
1528
1541
  /**