๐ src/web3/subscription/sync-cursor.ts
D-OPEN SOVEREIGN
Documentation & Insights
IndexedDB-backed cursor store keyed by userId โ a sibling of sessionStore,
not a field on it (spec ยง15.8: SyncCursor is heavier than the counters
already on Web3SessionAccount, and bloating that struct isn't worth it for
5 component files that reference it as a prop/ref type).1import { del, get, set } from 'idb-keyval';
2import type { SubscriptionNode, SyncCursor } from './types';
3
4interface SerializedCursor {
5 rootUserId: number;
6 knownNodes: Array<[number, SubscriptionNode]>;
7 countSnapshot: Array<[number, number]>;
8 patchIndex: Array<[number, number]>;
9}
10
11export function serializeCursor(cursor: SyncCursor): SerializedCursor {
12 return {
13 rootUserId: cursor.rootUserId,
14 knownNodes: Array.from(cursor.knownNodes.entries()),
15 countSnapshot: Array.from(cursor.countSnapshot.entries()),
16 patchIndex: Array.from(cursor.patchIndex.entries()),
17 };
18}
19
20export function deserializeCursor(serialized: SerializedCursor): SyncCursor {
21 return {
22 rootUserId: serialized.rootUserId,
23 knownNodes: new Map(serialized.knownNodes),
24 countSnapshot: new Map(serialized.countSnapshot),
25 patchIndex: new Map(serialized.patchIndex),
26 };
27}
28
29export function emptyCursor(rootUserId: number): SyncCursor {
30 return {
31 rootUserId,
32 knownNodes: new Map(),
33 countSnapshot: new Map(),
34 patchIndex: new Map(),
35 };
36}
37
38const IDB_KEY_PREFIX = 'web3-sdk:subscription-cursor:';
39
40/**
41 * IndexedDB-backed cursor store keyed by userId โ a sibling of sessionStore,
42 * not a field on it (spec ยง15.8: SyncCursor is heavier than the counters
43 * already on Web3SessionAccount, and bloating that struct isn't worth it for
44 * 5 component files that reference it as a prop/ref type).
45 */
46export const cursorStore = {
47 async get(userId: number): Promise<SyncCursor | undefined> {
48 try {
49 const serialized = await get<SerializedCursor>(`${IDB_KEY_PREFIX}${userId}`);
50 return serialized ? deserializeCursor(serialized) : undefined;
51 } catch (e) {
52 console.warn(`[cursorStore] Failed to read cursor for userId ${userId}:`, e);
53 return undefined;
54 }
55 },
56
57 async set(userId: number, cursor: SyncCursor): Promise<void> {
58 try {
59 await set(`${IDB_KEY_PREFIX}${userId}`, serializeCursor(cursor));
60 } catch (e) {
61 console.warn(`[cursorStore] Failed to persist cursor for userId ${userId}:`, e);
62 }
63 },
64
65 async clear(userId: number): Promise<void> {
66 try {
67 await del(`${IDB_KEY_PREFIX}${userId}`);
68 } catch {
69 // ignore
70 }
71 },
72};
73