canvu-react 0.4.80 → 0.4.81
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/realtime.cjs +86 -11
- package/dist/realtime.cjs.map +1 -1
- package/dist/realtime.d.cts +5 -0
- package/dist/realtime.d.ts +5 -0
- package/dist/realtime.js +86 -11
- package/dist/realtime.js.map +1 -1
- package/dist/realtimeNative.cjs +86 -11
- package/dist/realtimeNative.cjs.map +1 -1
- package/dist/realtimeNative.d.cts +5 -0
- package/dist/realtimeNative.d.ts +5 -0
- package/dist/realtimeNative.js +86 -11
- package/dist/realtimeNative.js.map +1 -1
- package/package.json +1 -1
package/dist/realtime.cjs
CHANGED
|
@@ -526,12 +526,16 @@ function parseDocumentSnapshot(value) {
|
|
|
526
526
|
const updatedAt = getNumber(value.updatedAt);
|
|
527
527
|
const items = parseItems(value.items);
|
|
528
528
|
if (revision == null || updatedAt == null || items == null) return void 0;
|
|
529
|
+
const deletedItemIds = Array.isArray(value.deletedItemIds) ? value.deletedItemIds.filter(
|
|
530
|
+
(entry) => typeof entry === "string"
|
|
531
|
+
) : void 0;
|
|
529
532
|
return {
|
|
530
533
|
revision,
|
|
531
534
|
updatedAt,
|
|
532
535
|
items,
|
|
533
536
|
...getString(value.updatedByClientId) ? { updatedByClientId: getString(value.updatedByClientId) } : {},
|
|
534
|
-
...getNumber(value.persistedRevision) != null ? { persistedRevision: getNumber(value.persistedRevision) } : {}
|
|
537
|
+
...getNumber(value.persistedRevision) != null ? { persistedRevision: getNumber(value.persistedRevision) } : {},
|
|
538
|
+
...deletedItemIds ? { deletedItemIds } : {}
|
|
535
539
|
};
|
|
536
540
|
}
|
|
537
541
|
function parseRealtimeSessionPeer(value) {
|
|
@@ -2029,6 +2033,16 @@ var CLIENT_ONLY_IMAGE_KEYS = /* @__PURE__ */ new Set([
|
|
|
2029
2033
|
"imageThumbnailHref"
|
|
2030
2034
|
]);
|
|
2031
2035
|
var SERVER_ADD_RACE_WINDOW_MS = 2e3;
|
|
2036
|
+
var LOCAL_DELETE_TOMBSTONE_TTL_MS = 1e4;
|
|
2037
|
+
function isLocalDeleteTombstoneActive(board, id, now) {
|
|
2038
|
+
const deletedAt = board.locallyDeletedItemIds.get(id);
|
|
2039
|
+
if (deletedAt == null) return false;
|
|
2040
|
+
if (now - deletedAt >= LOCAL_DELETE_TOMBSTONE_TTL_MS) {
|
|
2041
|
+
board.locallyDeletedItemIds.delete(id);
|
|
2042
|
+
return false;
|
|
2043
|
+
}
|
|
2044
|
+
return true;
|
|
2045
|
+
}
|
|
2032
2046
|
function createYjsBoardDoc() {
|
|
2033
2047
|
const doc = new Y__namespace.Doc();
|
|
2034
2048
|
const yItems = doc.getArray(ITEMS_KEY);
|
|
@@ -2037,9 +2051,22 @@ function createYjsBoardDoc() {
|
|
|
2037
2051
|
yItems,
|
|
2038
2052
|
lastServerConfirmedIds: /* @__PURE__ */ new Set(),
|
|
2039
2053
|
lastServerConfirmedItemSerializations: /* @__PURE__ */ new Map(),
|
|
2040
|
-
serverItemSeenAt: /* @__PURE__ */ new Map()
|
|
2054
|
+
serverItemSeenAt: /* @__PURE__ */ new Map(),
|
|
2055
|
+
serverDeletedItemIds: /* @__PURE__ */ new Set(),
|
|
2056
|
+
locallyDeletedItemIds: /* @__PURE__ */ new Map(),
|
|
2057
|
+
consumerSeenItemIds: /* @__PURE__ */ new Set()
|
|
2041
2058
|
};
|
|
2042
2059
|
}
|
|
2060
|
+
function getRealtimeDeletedItemIds(board) {
|
|
2061
|
+
const now = Date.now();
|
|
2062
|
+
const result = new Set(board.serverDeletedItemIds);
|
|
2063
|
+
for (const id of Array.from(board.locallyDeletedItemIds.keys())) {
|
|
2064
|
+
if (isLocalDeleteTombstoneActive(board, id, now)) {
|
|
2065
|
+
result.add(id);
|
|
2066
|
+
}
|
|
2067
|
+
}
|
|
2068
|
+
return result;
|
|
2069
|
+
}
|
|
2043
2070
|
function getItemId(item) {
|
|
2044
2071
|
if (item instanceof Y__namespace.Map) {
|
|
2045
2072
|
const id2 = item.get("id");
|
|
@@ -2168,9 +2195,11 @@ function applyLocalItemsToYDoc(board, options) {
|
|
|
2168
2195
|
const toDelete = [];
|
|
2169
2196
|
for (const [id, entry] of currentIndex) {
|
|
2170
2197
|
if (nextIds.has(id)) continue;
|
|
2171
|
-
|
|
2172
|
-
|
|
2173
|
-
|
|
2198
|
+
if (!board.consumerSeenItemIds.has(id)) {
|
|
2199
|
+
const serverSeenAt = board.serverItemSeenAt.get(id);
|
|
2200
|
+
if (serverSeenAt != null && now - serverSeenAt < SERVER_ADD_RACE_WINDOW_MS) {
|
|
2201
|
+
continue;
|
|
2202
|
+
}
|
|
2174
2203
|
}
|
|
2175
2204
|
toDelete.push({ id, index: entry.index });
|
|
2176
2205
|
}
|
|
@@ -2178,6 +2207,7 @@ function applyLocalItemsToYDoc(board, options) {
|
|
|
2178
2207
|
for (const { id, index } of toDelete) {
|
|
2179
2208
|
board.yItems.delete(index, 1);
|
|
2180
2209
|
board.serverItemSeenAt.delete(id);
|
|
2210
|
+
board.locallyDeletedItemIds.set(id, now);
|
|
2181
2211
|
removedIds.push(id);
|
|
2182
2212
|
}
|
|
2183
2213
|
const refreshedIndex = indexYItemsById(board.yItems);
|
|
@@ -2186,6 +2216,9 @@ function applyLocalItemsToYDoc(board, options) {
|
|
|
2186
2216
|
if (!item) continue;
|
|
2187
2217
|
const id = getItemId(item);
|
|
2188
2218
|
if (!id) continue;
|
|
2219
|
+
board.consumerSeenItemIds.add(id);
|
|
2220
|
+
if (board.serverDeletedItemIds.has(id)) continue;
|
|
2221
|
+
board.locallyDeletedItemIds.delete(id);
|
|
2189
2222
|
const existing = refreshedIndex.get(id);
|
|
2190
2223
|
if (existing) {
|
|
2191
2224
|
updateYMapInPlace(existing.yMap, item);
|
|
@@ -2200,12 +2233,33 @@ function applyLocalItemsToYDoc(board, options) {
|
|
|
2200
2233
|
return { addedIds, removedIds };
|
|
2201
2234
|
}
|
|
2202
2235
|
function applyServerSnapshotToYDoc(board, options) {
|
|
2203
|
-
const { items: snapshotItems, origin } = options;
|
|
2236
|
+
const { items: snapshotItems, deletedItemIds, origin } = options;
|
|
2204
2237
|
const now = Date.now();
|
|
2238
|
+
const snapshotItemIds = /* @__PURE__ */ new Set();
|
|
2239
|
+
for (const item of snapshotItems) {
|
|
2240
|
+
const id = getItemId(item);
|
|
2241
|
+
if (id) snapshotItemIds.add(id);
|
|
2242
|
+
}
|
|
2205
2243
|
board.doc.transact(() => {
|
|
2244
|
+
if (deletedItemIds) {
|
|
2245
|
+
for (const id of deletedItemIds) {
|
|
2246
|
+
if (snapshotItemIds.has(id)) continue;
|
|
2247
|
+
board.serverDeletedItemIds.add(id);
|
|
2248
|
+
board.locallyDeletedItemIds.delete(id);
|
|
2249
|
+
board.serverItemSeenAt.delete(id);
|
|
2250
|
+
const existing = indexYItemsById(board.yItems).get(id);
|
|
2251
|
+
if (existing) {
|
|
2252
|
+
board.yItems.delete(existing.index, 1);
|
|
2253
|
+
}
|
|
2254
|
+
}
|
|
2255
|
+
}
|
|
2206
2256
|
for (const item of snapshotItems) {
|
|
2207
2257
|
const id = getItemId(item);
|
|
2208
2258
|
if (!id) continue;
|
|
2259
|
+
if (board.serverDeletedItemIds.has(id)) continue;
|
|
2260
|
+
if (isLocalDeleteTombstoneActive(board, id, now)) {
|
|
2261
|
+
continue;
|
|
2262
|
+
}
|
|
2209
2263
|
const existing = indexYItemsById(board.yItems).get(id);
|
|
2210
2264
|
if (existing) {
|
|
2211
2265
|
const currentSerialized = serializeItem(existing.yMap);
|
|
@@ -2236,7 +2290,7 @@ function applyServerSnapshotToYDoc(board, options) {
|
|
|
2236
2290
|
}
|
|
2237
2291
|
}
|
|
2238
2292
|
function replaceYDocWithSnapshot(board, options) {
|
|
2239
|
-
const { items: snapshotItems, origin } = options;
|
|
2293
|
+
const { items: snapshotItems, deletedItemIds, origin } = options;
|
|
2240
2294
|
const now = Date.now();
|
|
2241
2295
|
board.doc.transact(() => {
|
|
2242
2296
|
if (board.yItems.length > 0) {
|
|
@@ -2249,6 +2303,12 @@ function replaceYDocWithSnapshot(board, options) {
|
|
|
2249
2303
|
board.lastServerConfirmedIds = /* @__PURE__ */ new Set();
|
|
2250
2304
|
board.lastServerConfirmedItemSerializations = /* @__PURE__ */ new Map();
|
|
2251
2305
|
board.serverItemSeenAt = /* @__PURE__ */ new Map();
|
|
2306
|
+
board.locallyDeletedItemIds = /* @__PURE__ */ new Map();
|
|
2307
|
+
if (deletedItemIds) {
|
|
2308
|
+
for (const id of deletedItemIds) {
|
|
2309
|
+
board.serverDeletedItemIds.add(id);
|
|
2310
|
+
}
|
|
2311
|
+
}
|
|
2252
2312
|
for (const item of snapshotItems) {
|
|
2253
2313
|
const id = getItemId(item);
|
|
2254
2314
|
if (id) {
|
|
@@ -2611,7 +2671,7 @@ function useRealtimeSession(options) {
|
|
|
2611
2671
|
const applyDocument = react.useCallback(
|
|
2612
2672
|
(snapshot, options2) => {
|
|
2613
2673
|
const currentSnapshot = latestDocumentRef.current;
|
|
2614
|
-
if (currentSnapshot && currentSnapshot.revision === snapshot.revision && currentSnapshot.updatedByClientId === snapshot.updatedByClientId && currentSnapshot.persistedRevision === snapshot.persistedRevision && sameSerializedItems(currentSnapshot.items, snapshot.items)) {
|
|
2674
|
+
if (currentSnapshot && currentSnapshot.revision === snapshot.revision && currentSnapshot.updatedByClientId === snapshot.updatedByClientId && currentSnapshot.persistedRevision === snapshot.persistedRevision && (currentSnapshot.deletedItemIds?.length ?? 0) === (snapshot.deletedItemIds?.length ?? 0) && sameSerializedItems(currentSnapshot.items, snapshot.items)) {
|
|
2615
2675
|
return;
|
|
2616
2676
|
}
|
|
2617
2677
|
const board = boardRef.current;
|
|
@@ -2630,11 +2690,13 @@ function useRealtimeSession(options) {
|
|
|
2630
2690
|
if (options2?.replace) {
|
|
2631
2691
|
replaceYDocWithSnapshot(board, {
|
|
2632
2692
|
items: snapshot.items,
|
|
2693
|
+
deletedItemIds: snapshot.deletedItemIds,
|
|
2633
2694
|
origin: ORIGIN_REMOTE
|
|
2634
2695
|
});
|
|
2635
2696
|
} else {
|
|
2636
2697
|
applyServerSnapshotToYDoc(board, {
|
|
2637
2698
|
items: snapshot.items,
|
|
2699
|
+
deletedItemIds: snapshot.deletedItemIds,
|
|
2638
2700
|
origin: ORIGIN_REMOTE
|
|
2639
2701
|
});
|
|
2640
2702
|
}
|
|
@@ -2642,7 +2704,11 @@ function useRealtimeSession(options) {
|
|
|
2642
2704
|
const mergedItems = board ? readVectorItems(board.yItems) : snapshot.items;
|
|
2643
2705
|
const mergedSnapshot = {
|
|
2644
2706
|
...snapshot,
|
|
2645
|
-
items: mergedItems
|
|
2707
|
+
items: mergedItems,
|
|
2708
|
+
// Expose the accumulated tombstones (server-confirmed + pending
|
|
2709
|
+
// local deletes) so document consumers can prune stale local
|
|
2710
|
+
// views instead of re-sending deleted items.
|
|
2711
|
+
...board ? { deletedItemIds: Array.from(getRealtimeDeletedItemIds(board)) } : {}
|
|
2646
2712
|
};
|
|
2647
2713
|
currentRevisionRef.current = snapshot.revision;
|
|
2648
2714
|
latestDocumentRef.current = mergedSnapshot;
|
|
@@ -3767,6 +3833,7 @@ function useRealtimeCanvasDocument(options) {
|
|
|
3767
3833
|
const realtimeEnabled = enabled && session != null;
|
|
3768
3834
|
const documentRevision = session?.document?.revision ?? null;
|
|
3769
3835
|
const documentItems = session?.document?.items;
|
|
3836
|
+
const documentDeletedItemIds = session?.document?.deletedItemIds;
|
|
3770
3837
|
const documentUpdatedByClientId = session?.document?.updatedByClientId ?? null;
|
|
3771
3838
|
const connectionClientId = session?.connection.clientId ?? null;
|
|
3772
3839
|
const hasLocalOfflineDraft = session?.hasLocalOfflineDraft ?? false;
|
|
@@ -3819,15 +3886,22 @@ function useRealtimeCanvasDocument(options) {
|
|
|
3819
3886
|
if (cancelled) return;
|
|
3820
3887
|
if (inFlightRevisionRef.current !== documentRevision) return;
|
|
3821
3888
|
lastAppliedRevisionRef.current = documentRevision;
|
|
3822
|
-
const
|
|
3889
|
+
const rawLocalItems = latestItemsRef.current;
|
|
3890
|
+
const deletedIds = new Set(documentDeletedItemIds ?? []);
|
|
3891
|
+
const localItems = deletedIds.size > 0 ? rawLocalItems.filter((item) => {
|
|
3892
|
+
const id = getSceneItemId(item);
|
|
3893
|
+
return !id || !deletedIds.has(id);
|
|
3894
|
+
}) : rawLocalItems;
|
|
3823
3895
|
const hasLocalItems = localItems.length > 0;
|
|
3824
3896
|
const hasPendingLocalChanges = hasLocalOfflineDraft || hasPendingDocumentSync || hasLocalChangeInFlightRef.current;
|
|
3825
3897
|
if (resolvedItems.length === 0 && (hasEverPropagatedItemsRef.current || hasLocalItems || hasPendingLocalChanges)) {
|
|
3826
3898
|
if (hasLocalItems) {
|
|
3827
3899
|
const normalizedLocalItems = normalizeItems ? normalizeItems(localItems) : [...localItems];
|
|
3828
3900
|
session.remoteAdapter.send?.(normalizedLocalItems);
|
|
3901
|
+
return;
|
|
3829
3902
|
}
|
|
3830
|
-
|
|
3903
|
+
const emptinessExplainedByDeletes = rawLocalItems.length > 0 && localItems.length === 0;
|
|
3904
|
+
if (!emptinessExplainedByDeletes) return;
|
|
3831
3905
|
}
|
|
3832
3906
|
if (shouldPreserveLocalRealtimeItems({
|
|
3833
3907
|
localItems,
|
|
@@ -3855,6 +3929,7 @@ function useRealtimeCanvasDocument(options) {
|
|
|
3855
3929
|
}, [
|
|
3856
3930
|
applyIncomingItems,
|
|
3857
3931
|
connectionClientId,
|
|
3932
|
+
documentDeletedItemIds,
|
|
3858
3933
|
documentItems,
|
|
3859
3934
|
documentRevision,
|
|
3860
3935
|
documentUpdatedByClientId,
|