@pylonsync/sync 0.3.192 → 0.3.195
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/package.json +1 -1
- package/src/index.ts +81 -0
package/package.json
CHANGED
package/src/index.ts
CHANGED
|
@@ -274,6 +274,15 @@ export class SyncEngine {
|
|
|
274
274
|
private reactiveHandlers: Map<string, (msg: ReactiveMessage) => void> =
|
|
275
275
|
new Map();
|
|
276
276
|
|
|
277
|
+
/**
|
|
278
|
+
* Listeners notified when the server signals a per-subscriber row
|
|
279
|
+
* revocation (`row-revoked` envelope). Used by `@pylonsync/loro`
|
|
280
|
+
* to evict the LoroDoc registry entry for a row whose policy was
|
|
281
|
+
* revoked mid-session. Plain Set so identity is the dedup key.
|
|
282
|
+
*/
|
|
283
|
+
private rowEvictionListeners: Set<(entity: string, rowId: string) => void> =
|
|
284
|
+
new Set();
|
|
285
|
+
|
|
277
286
|
/**
|
|
278
287
|
* Register a binary-frame handler. Returns an unsubscribe fn that
|
|
279
288
|
* pulls the handler back out — call on hook unmount / module
|
|
@@ -731,6 +740,28 @@ export class SyncEngine {
|
|
|
731
740
|
return;
|
|
732
741
|
}
|
|
733
742
|
|
|
743
|
+
// Server-driven revocation: a subscriber whose read policy
|
|
744
|
+
// was revoked mid-session for a specific row. Drop the row
|
|
745
|
+
// from the local replica at the current cursor seq so the
|
|
746
|
+
// tombstone supersedes any racing late-arriving WS update
|
|
747
|
+
// for the same row, and notify any LoroDoc subscriber
|
|
748
|
+
// (registered via `addRowEvictionListener`) so collaborative
|
|
749
|
+
// doc handles unmount cleanly.
|
|
750
|
+
//
|
|
751
|
+
// Distinct from a regular Delete change event because this
|
|
752
|
+
// envelope has no global seq — the row's underlying data
|
|
753
|
+
// hasn't been deleted, only the recipient's visibility of
|
|
754
|
+
// it. Other subscribers (with matching policy) keep their
|
|
755
|
+
// row intact.
|
|
756
|
+
if (
|
|
757
|
+
msg.type === "row-revoked" &&
|
|
758
|
+
typeof msg.entity === "string" &&
|
|
759
|
+
typeof msg.row_id === "string"
|
|
760
|
+
) {
|
|
761
|
+
this.handleRowRevocation(msg.entity, msg.row_id);
|
|
762
|
+
return;
|
|
763
|
+
}
|
|
764
|
+
|
|
734
765
|
// Session mutated server-side. Fires for select-org / clear-org
|
|
735
766
|
// / session revoke — every tab connected as this user gets the
|
|
736
767
|
// envelope (cross-machine too via the cluster bus). Trigger
|
|
@@ -1456,6 +1487,56 @@ export class SyncEngine {
|
|
|
1456
1487
|
return this.refreshResolvedSession();
|
|
1457
1488
|
}
|
|
1458
1489
|
|
|
1490
|
+
/**
|
|
1491
|
+
* Drop a row from the local replica because the server signaled
|
|
1492
|
+
* that the current subscriber's read policy was revoked for it.
|
|
1493
|
+
*
|
|
1494
|
+
* Calls `LocalStore.reconcileRemove(entity, id, cursor.last_seq)`
|
|
1495
|
+
* to record a tombstone at the current cursor. A future server
|
|
1496
|
+
* issuing seqs strictly greater than the cursor can re-create
|
|
1497
|
+
* the row if policy is re-granted; replays older than the cursor
|
|
1498
|
+
* are filtered by the tombstone (closing the "WS update lands
|
|
1499
|
+
* between revoke and tombstone" race).
|
|
1500
|
+
*
|
|
1501
|
+
* Also notifies row-eviction listeners so external row-bound
|
|
1502
|
+
* resources (LoroDoc registries, etc.) can unmount.
|
|
1503
|
+
*/
|
|
1504
|
+
private handleRowRevocation(entity: string, rowId: string): void {
|
|
1505
|
+
const removed = this.store.reconcileRemove(
|
|
1506
|
+
entity,
|
|
1507
|
+
rowId,
|
|
1508
|
+
this.cursor.last_seq,
|
|
1509
|
+
);
|
|
1510
|
+
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
|
+
if (this.persistence) {
|
|
1514
|
+
void this.persistence.deleteRow(entity, rowId).catch(() => {
|
|
1515
|
+
/* best-effort */
|
|
1516
|
+
});
|
|
1517
|
+
}
|
|
1518
|
+
this.store.notify();
|
|
1519
|
+
}
|
|
1520
|
+
for (const listener of this.rowEvictionListeners) {
|
|
1521
|
+
listener(entity, rowId);
|
|
1522
|
+
}
|
|
1523
|
+
}
|
|
1524
|
+
|
|
1525
|
+
/**
|
|
1526
|
+
* Register a listener invoked when the server signals a per-
|
|
1527
|
+
* subscriber row revocation. Used by `@pylonsync/loro` to evict
|
|
1528
|
+
* the LoroDoc registry entry for the row so collaborative doc
|
|
1529
|
+
* handles unmount cleanly. Returns an unsubscribe function.
|
|
1530
|
+
*/
|
|
1531
|
+
addRowEvictionListener(
|
|
1532
|
+
listener: (entity: string, rowId: string) => void,
|
|
1533
|
+
): () => void {
|
|
1534
|
+
this.rowEvictionListeners.add(listener);
|
|
1535
|
+
return () => {
|
|
1536
|
+
this.rowEvictionListeners.delete(listener);
|
|
1537
|
+
};
|
|
1538
|
+
}
|
|
1539
|
+
|
|
1459
1540
|
/**
|
|
1460
1541
|
* Switch the caller's active tenant (organization) and refresh the
|
|
1461
1542
|
* resolved session in one shot. Membership is verified server-side
|