@voltro/local-first 0.52.0 → 0.53.0
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/CHANGELOG.md +229 -0
- package/THIRD-PARTY-NOTICES.md +29 -1
- package/dist/index.d.ts +329 -20
- package/dist/index.js +192 -27
- package/dist/react.d.ts +66 -7
- package/dist/react.js +20 -20
- package/dist/{room-wFNUwEgi.js → room-DZzqWoJq.js} +100 -38
- package/package.json +20 -3
package/dist/index.js
CHANGED
|
@@ -1,16 +1,16 @@
|
|
|
1
|
-
import { a as e, c as t, d as n, f as r,
|
|
1
|
+
import { a as e, c as t, d as n, f as r, g as i, h as a, i as o, l as s, m as c, n as l, o as u, p as d, r as f, s as p, t as m, u as h } from "./room-DZzqWoJq.js";
|
|
2
2
|
//#region src/conflict/policy.ts
|
|
3
|
-
var
|
|
3
|
+
var g = (e) => {
|
|
4
4
|
if (e.writer !== void 0) return `w:${e.writer}`;
|
|
5
5
|
try {
|
|
6
6
|
return `v:${JSON.stringify(e.value)}`;
|
|
7
7
|
} catch {
|
|
8
8
|
return `v:${String(e.value)}`;
|
|
9
9
|
}
|
|
10
|
-
},
|
|
10
|
+
}, _ = (e, t) => t.updatedAt > e.updatedAt ? t.value : t.updatedAt < e.updatedAt ? e.value : g(t) > g(e) ? t.value : e.value, v = (e) => e === "lastWriteWins" ? _ : e, y = (e) => {
|
|
11
11
|
let t = (t, n, r) => {
|
|
12
12
|
let i = e[t];
|
|
13
|
-
return (i === void 0 ?
|
|
13
|
+
return (i === void 0 ? _ : v(i))(n, r);
|
|
14
14
|
};
|
|
15
15
|
return {
|
|
16
16
|
resolveField: t,
|
|
@@ -31,32 +31,32 @@ var h = (e) => {
|
|
|
31
31
|
return r;
|
|
32
32
|
}
|
|
33
33
|
};
|
|
34
|
-
},
|
|
34
|
+
}, b = (e, t) => `${e}\u0001${t}`, x = () => {
|
|
35
35
|
let e = /* @__PURE__ */ new Map(), t = [], n = /* @__PURE__ */ new Set(), r = (e) => e.slice();
|
|
36
36
|
return {
|
|
37
37
|
kind: "in-memory",
|
|
38
38
|
loadCrdtState: (t, n) => {
|
|
39
|
-
let i = e.get(
|
|
39
|
+
let i = e.get(b(t, n));
|
|
40
40
|
return Promise.resolve(i === void 0 ? void 0 : r(i));
|
|
41
41
|
},
|
|
42
|
-
saveCrdtState: (t, n, i) => (e.set(
|
|
42
|
+
saveCrdtState: (t, n, i) => (e.set(b(t, n), r(i)), Promise.resolve()),
|
|
43
43
|
loadQueue: () => Promise.resolve(t.map((e) => ({ ...e }))),
|
|
44
44
|
saveQueue: (e) => (t = e.map((e) => ({ ...e })), Promise.resolve()),
|
|
45
|
-
pin: (e, t) => (n.add(
|
|
46
|
-
isPinned: (e, t) => Promise.resolve(n.has(
|
|
45
|
+
pin: (e, t) => (n.add(b(e, t)), Promise.resolve()),
|
|
46
|
+
isPinned: (e, t) => Promise.resolve(n.has(b(e, t)))
|
|
47
47
|
};
|
|
48
|
-
},
|
|
48
|
+
}, S = "voltro_local_first", C = "\0queue", w = "\0pin\0", T = (e, t) => `cell\u0000${e}\u0000${t}`, E = (e, t) => `${w}${e}\u0000${t}`, D = (e) => new Promise((t, n) => {
|
|
49
49
|
e.onsuccess = () => t(e.result), e.onerror = () => n(e.error ?? /* @__PURE__ */ Error("IndexedDB request failed"));
|
|
50
|
-
}),
|
|
50
|
+
}), O = async (e) => {
|
|
51
51
|
let t = e?.factory ?? globalThis.indexedDB;
|
|
52
52
|
if (t === void 0) throw Error("createIndexedDbPersistence: no IndexedDB available. Pass options.factory (e.g. in a non-browser host) or use createInMemoryPersistence().");
|
|
53
53
|
let n = e?.databaseName ?? "voltro-local-first", r = t.open(n, 1), i = await new Promise((e, t) => {
|
|
54
54
|
r.onupgradeneeded = () => {
|
|
55
55
|
let e = r.result;
|
|
56
|
-
e.objectStoreNames.contains(
|
|
56
|
+
e.objectStoreNames.contains(S) || e.createObjectStore(S);
|
|
57
57
|
}, r.onsuccess = () => e(r.result), r.onerror = () => t(r.error ?? /* @__PURE__ */ Error("IndexedDB open failed"));
|
|
58
|
-
}), a = async (e) =>
|
|
59
|
-
await
|
|
58
|
+
}), a = async (e) => D(i.transaction(S, "readonly").objectStore(S).get(e)), o = async (e, t) => {
|
|
59
|
+
await D(i.transaction(S, "readwrite").objectStore(S).put(t, e));
|
|
60
60
|
}, s = (e) => {
|
|
61
61
|
if (e instanceof Uint8Array) return e.slice();
|
|
62
62
|
if (e instanceof ArrayBuffer) return new Uint8Array(e.slice(0));
|
|
@@ -64,33 +64,198 @@ var h = (e) => {
|
|
|
64
64
|
};
|
|
65
65
|
return {
|
|
66
66
|
kind: "indexeddb",
|
|
67
|
-
loadCrdtState: async (e, t) => s(await a(
|
|
67
|
+
loadCrdtState: async (e, t) => s(await a(T(e, t))),
|
|
68
68
|
saveCrdtState: async (e, t, n) => {
|
|
69
|
-
await o(
|
|
69
|
+
await o(T(e, t), n.slice());
|
|
70
70
|
},
|
|
71
71
|
loadQueue: async () => {
|
|
72
|
-
let e = await a(
|
|
72
|
+
let e = await a(C);
|
|
73
73
|
return e === void 0 || !Array.isArray(e.writes) ? [] : e.writes.map((e) => ({ ...e }));
|
|
74
74
|
},
|
|
75
75
|
saveQueue: async (e) => {
|
|
76
76
|
let t = { writes: e.map((e) => ({ ...e })) };
|
|
77
|
-
await o(
|
|
77
|
+
await o(C, t);
|
|
78
78
|
},
|
|
79
79
|
pin: async (e, t) => {
|
|
80
|
-
await o(
|
|
80
|
+
await o(E(e, t), !0);
|
|
81
81
|
},
|
|
82
|
-
isPinned: async (e, t) => await a(
|
|
82
|
+
isPinned: async (e, t) => await a(E(e, t)) === !0
|
|
83
83
|
};
|
|
84
|
-
},
|
|
84
|
+
}, k = (e) => e.inFlight === void 0 ? e.pending : [e.inFlight, ...e.pending], A = async (e, t = !0) => {
|
|
85
85
|
let n = await e.loadQueue(), r = {
|
|
86
|
-
...
|
|
86
|
+
...p(t),
|
|
87
87
|
pending: n
|
|
88
88
|
};
|
|
89
89
|
return {
|
|
90
90
|
getState: () => r,
|
|
91
|
-
dispatch: async (t) => (r =
|
|
91
|
+
dispatch: async (t) => (r = s(r, t), await e.saveQueue(k(r)), r)
|
|
92
92
|
};
|
|
93
|
-
},
|
|
93
|
+
}, j = (e) => {
|
|
94
|
+
let t = e;
|
|
95
|
+
return typeof t == "object" && !!t && typeof t.id == "string" && typeof t.tag == "string" && (t.status === "pending" || t.status === "sent" || t.status === "failed" || t.status === "conflict");
|
|
96
|
+
}, M = (e, t = Date.now) => ({
|
|
97
|
+
load: async () => (await e.loadQueue()).map((e) => e.payload).filter(j),
|
|
98
|
+
save: async (n) => {
|
|
99
|
+
let r = n.map((e) => ({
|
|
100
|
+
id: e.id,
|
|
101
|
+
payload: e,
|
|
102
|
+
enqueuedAt: t(),
|
|
103
|
+
attempts: e.attempts
|
|
104
|
+
}));
|
|
105
|
+
await e.saveQueue(r);
|
|
106
|
+
}
|
|
107
|
+
}), N = () => {
|
|
108
|
+
let e = /* @__PURE__ */ new Map();
|
|
109
|
+
return {
|
|
110
|
+
kind: "in-memory",
|
|
111
|
+
get: (t) => Promise.resolve(e.get(t)),
|
|
112
|
+
put: (t, n) => (e.set(t, n), Promise.resolve()),
|
|
113
|
+
del: (t) => (e.delete(t), Promise.resolve()),
|
|
114
|
+
keysWithPrefix: (t) => Promise.resolve([...e.keys()].filter((e) => e.startsWith(t)))
|
|
115
|
+
};
|
|
116
|
+
}, P = (e) => new Promise((t, n) => {
|
|
117
|
+
e.onsuccess = () => t(e.result), e.onerror = () => n(e.error ?? /* @__PURE__ */ Error("IndexedDB request failed"));
|
|
118
|
+
}), F = "kv", I = async (e) => {
|
|
119
|
+
let t = e?.factory ?? globalThis.indexedDB;
|
|
120
|
+
if (t === void 0) throw Error("createIndexedDbKv: no IndexedDB available. Pass options.factory or use createInMemoryKv().");
|
|
121
|
+
let n = t.open(e?.databaseName ?? "voltro-local-first-kv", 1), r = await new Promise((e, t) => {
|
|
122
|
+
n.onupgradeneeded = () => {
|
|
123
|
+
n.result.objectStoreNames.contains(F) || n.result.createObjectStore(F);
|
|
124
|
+
}, n.onsuccess = () => e(n.result), n.onerror = () => t(n.error ?? /* @__PURE__ */ Error("IndexedDB open failed"));
|
|
125
|
+
}), i = (e) => r.transaction(F, e).objectStore(F);
|
|
126
|
+
return {
|
|
127
|
+
kind: "indexeddb",
|
|
128
|
+
get: (e) => P(i("readonly").get(e)),
|
|
129
|
+
put: async (e, t) => {
|
|
130
|
+
await P(i("readwrite").put(t, e));
|
|
131
|
+
},
|
|
132
|
+
del: async (e) => {
|
|
133
|
+
await P(i("readwrite").delete(e));
|
|
134
|
+
},
|
|
135
|
+
keysWithPrefix: async (e) => {
|
|
136
|
+
let t = globalThis.IDBKeyRange;
|
|
137
|
+
return t === void 0 ? (await P(i("readonly").getAllKeys())).filter((t) => t.startsWith(e)) : await P(i("readonly").getAllKeys(t.bound(e, `${e}\uffff`)));
|
|
138
|
+
}
|
|
139
|
+
};
|
|
140
|
+
}, L = async (e) => {
|
|
141
|
+
try {
|
|
142
|
+
return {
|
|
143
|
+
kv: await I(e),
|
|
144
|
+
durability: "durable"
|
|
145
|
+
};
|
|
146
|
+
} catch {
|
|
147
|
+
return {
|
|
148
|
+
kv: N(),
|
|
149
|
+
durability: "memory"
|
|
150
|
+
};
|
|
151
|
+
}
|
|
152
|
+
}, R = "mirror", z = "\0", B = (e) => `${R}${z}${e.subjectId}${z}${e.tenantId}${z}`, V = (e, t) => `${B(e)}q${z}${t}`, H = (e, t) => {
|
|
153
|
+
if (t.length === 0) return e;
|
|
154
|
+
let n = (e) => {
|
|
155
|
+
if (typeof e != "object" || !e || Array.isArray(e)) return e;
|
|
156
|
+
let n = {};
|
|
157
|
+
for (let [r, i] of Object.entries(e)) t.includes(r) || (n[r] = i);
|
|
158
|
+
return n;
|
|
159
|
+
};
|
|
160
|
+
return Array.isArray(e) ? e.map(n) : n(e);
|
|
161
|
+
}, U = (e) => {
|
|
162
|
+
try {
|
|
163
|
+
return JSON.stringify(e)?.length ?? 0;
|
|
164
|
+
} catch {
|
|
165
|
+
return 0;
|
|
166
|
+
}
|
|
167
|
+
}, W = (e, t, n = {}, r = Date.now) => ({
|
|
168
|
+
partition: t,
|
|
169
|
+
save: async (i, a) => {
|
|
170
|
+
let o = n.encryptedColumnsOf?.(a.tag) ?? [], s = a.fingerprint, c = {
|
|
171
|
+
tag: a.tag,
|
|
172
|
+
data: H(a.data, o),
|
|
173
|
+
revision: a.revision,
|
|
174
|
+
savedAt: r(),
|
|
175
|
+
...s === void 0 ? {} : { fingerprint: s }
|
|
176
|
+
};
|
|
177
|
+
await e.put(V(t, i), c);
|
|
178
|
+
},
|
|
179
|
+
load: async (n) => {
|
|
180
|
+
let r = await e.get(V(t, n));
|
|
181
|
+
if (typeof r != "object" || !r) return;
|
|
182
|
+
let i = r;
|
|
183
|
+
return typeof i.revision == "number" && typeof i.tag == "string" ? i : void 0;
|
|
184
|
+
},
|
|
185
|
+
purge: async () => {
|
|
186
|
+
let n = await e.keysWithPrefix(B(t));
|
|
187
|
+
for (let t of n) await e.del(t);
|
|
188
|
+
},
|
|
189
|
+
enforceBudget: async (n) => {
|
|
190
|
+
let r = await e.keysWithPrefix(B(t)), i = [];
|
|
191
|
+
for (let t of r) {
|
|
192
|
+
let n = await e.get(t);
|
|
193
|
+
i.push({
|
|
194
|
+
key: t,
|
|
195
|
+
savedAt: n?.savedAt ?? 0,
|
|
196
|
+
bytes: U(n)
|
|
197
|
+
});
|
|
198
|
+
}
|
|
199
|
+
let a = i.reduce((e, t) => e + t.bytes, 0), o = [];
|
|
200
|
+
i.sort((e, t) => e.savedAt - t.savedAt);
|
|
201
|
+
for (let t of i) {
|
|
202
|
+
if (a <= n) break;
|
|
203
|
+
await e.del(t.key), a -= t.bytes, o.push(t.key);
|
|
204
|
+
}
|
|
205
|
+
return o;
|
|
206
|
+
}
|
|
207
|
+
}), G = (e, t) => {
|
|
208
|
+
let n = (e) => {
|
|
209
|
+
let n = t.tags[e];
|
|
210
|
+
if (n !== void 0) return t.metadata?.[n]?.encryptedColumns;
|
|
211
|
+
};
|
|
212
|
+
return {
|
|
213
|
+
load: async (n) => {
|
|
214
|
+
let r = await e.load(n);
|
|
215
|
+
if (r === void 0 || t.tags[r.tag] === void 0) return;
|
|
216
|
+
let i = r.fingerprint;
|
|
217
|
+
if (t.schemaFingerprint === void 0 || i === t.schemaFingerprint) return {
|
|
218
|
+
data: r.data,
|
|
219
|
+
revision: r.revision
|
|
220
|
+
};
|
|
221
|
+
},
|
|
222
|
+
save: async (r, i) => {
|
|
223
|
+
if (i.tag === void 0 || t.tags[i.tag] === void 0) return;
|
|
224
|
+
let a = n(i.tag) ?? [], o = a.length === 0 ? i.data : K(i.data, a);
|
|
225
|
+
await e.save(r, {
|
|
226
|
+
tag: i.tag,
|
|
227
|
+
data: o,
|
|
228
|
+
revision: i.revision,
|
|
229
|
+
...t.schemaFingerprint === void 0 ? {} : { fingerprint: t.schemaFingerprint }
|
|
230
|
+
});
|
|
231
|
+
}
|
|
232
|
+
};
|
|
233
|
+
}, K = (e, t) => {
|
|
234
|
+
let n = (e) => {
|
|
235
|
+
if (typeof e != "object" || !e || Array.isArray(e)) return e;
|
|
236
|
+
let n = {};
|
|
237
|
+
for (let [r, i] of Object.entries(e)) t.includes(r) || (n[r] = i);
|
|
238
|
+
return n;
|
|
239
|
+
};
|
|
240
|
+
return Array.isArray(e) ? e.map(n) : n(e);
|
|
241
|
+
}, q = (e, t, n, r = {}) => {
|
|
242
|
+
let i = new Set(r.crdtColumns ?? []), o = r.backend ?? d, s = e.resolveRecord(Object.fromEntries(Object.entries(t).filter(([e]) => !i.has(e))), Object.fromEntries(Object.entries(n).filter(([e]) => !i.has(e))));
|
|
243
|
+
for (let e of i) {
|
|
244
|
+
let r = t[e]?.value, i = n[e]?.value;
|
|
245
|
+
r !== void 0 && i !== void 0 ? s[e] = a(r, i, o) : r === void 0 ? i !== void 0 && (s[e] = i) : s[e] = r;
|
|
246
|
+
}
|
|
247
|
+
return s;
|
|
248
|
+
}, J = async (e, t) => {
|
|
249
|
+
let n = e.locks === null ? void 0 : e.locks ?? globalThis.navigator?.locks;
|
|
250
|
+
if (n === void 0) return await t(), "no-locks";
|
|
251
|
+
let r = "held-elsewhere";
|
|
252
|
+
return await n.request(`voltro:outbox:${e.name}`, {
|
|
253
|
+
mode: "exclusive",
|
|
254
|
+
ifAvailable: !0
|
|
255
|
+
}, async (e) => {
|
|
256
|
+
e !== null && (await t(), r = "drained");
|
|
257
|
+
}), r;
|
|
258
|
+
}, Y = () => ({ rooms: /* @__PURE__ */ new Map() }), X = (e = Y()) => {
|
|
94
259
|
let t = /* @__PURE__ */ new Set(), n = (t) => {
|
|
95
260
|
let n = e.rooms.get(t);
|
|
96
261
|
return n === void 0 && (n = /* @__PURE__ */ new Set(), e.rooms.set(t, n)), n;
|
|
@@ -112,16 +277,16 @@ var h = (e) => {
|
|
|
112
277
|
};
|
|
113
278
|
}
|
|
114
279
|
};
|
|
115
|
-
},
|
|
280
|
+
}, Z = [
|
|
116
281
|
"durable-persistence-indexeddb",
|
|
117
282
|
"sync-transport-syncclient",
|
|
118
283
|
"sync-transport-react-binding",
|
|
119
284
|
"presence-room",
|
|
120
285
|
"localFirst-mixin-discovery"
|
|
121
|
-
],
|
|
286
|
+
], Q = [
|
|
122
287
|
"sync-transport-app-tags",
|
|
123
288
|
"presence-broker-binding",
|
|
124
289
|
"wasm-sqlite-durable-adapter"
|
|
125
290
|
];
|
|
126
291
|
//#endregion
|
|
127
|
-
export {
|
|
292
|
+
export { Z as IMPLEMENTED_SINCE_SEAMS, Q as RUNTIME_SEAMS, u as canDrain, y as conflictPolicy, f as connectionReducer, h as crdtMergeCell, n as crdtText, L as createDurableKv, N as createInMemoryKv, x as createInMemoryPersistence, Y as createInMemoryPresenceBus, X as createInMemoryPresenceChannel, I as createIndexedDbKv, O as createIndexedDbPersistence, m as createPresenceRoom, W as createQueryMirror, G as createSubscriptionMirrorBinding, l as createSyncClient, r as decodeCrdtText, d as defaultCrdtBackend, o as deriveSyncStatus, c as emptyCrdtState, e as initialConnection, p as initialSyncQueue, _ as lastWriteWins, A as loadPersistedSyncQueue, a as mergeCrdtStates, M as outboxPersistence, t as outstandingCount, q as resolveWithPolicy, s as syncQueueReducer, J as withDrainLock, i as yjsBackend };
|
package/dist/react.d.ts
CHANGED
|
@@ -6,18 +6,29 @@ declare interface ConnectionState {
|
|
|
6
6
|
readonly attempt: number;
|
|
7
7
|
}
|
|
8
8
|
|
|
9
|
-
/**
|
|
10
|
-
* A CRDT implementation. One per library. The default is {@link yjsBackend}.
|
|
11
|
-
*
|
|
12
|
-
* `merge` and `decodeText` are STATELESS with respect to any live handle: they
|
|
13
|
-
* operate purely on encoded bytes, which is what lets the sync layer and tests
|
|
14
|
-
* converge two states without holding a document open.
|
|
15
|
-
*/
|
|
16
9
|
declare interface CrdtBackend {
|
|
17
10
|
/** Identifies the library, e.g. `"yjs"`. Surfaced for diagnostics/telemetry. */
|
|
18
11
|
readonly name: string;
|
|
19
12
|
/** Create a fresh text buffer, optionally seeded with `initial`. */
|
|
20
13
|
createText: (initial?: string) => CrdtTextHandle;
|
|
14
|
+
/** Create a whole-document handle, optionally seeded from encoded state. */
|
|
15
|
+
createDoc: (initial?: CrdtState) => CrdtDocHandle;
|
|
16
|
+
/**
|
|
17
|
+
* Re-encode a state through a live document so the backend garbage-collects
|
|
18
|
+
* what it can (Yjs: `mergeUpdates` never GCs tombstones, so a hot document's
|
|
19
|
+
* blob grows monotonically without this). Loses UPDATE HISTORY by design —
|
|
20
|
+
* a version-history feature must snapshot BEFORE compacting.
|
|
21
|
+
*/
|
|
22
|
+
compact: (state: CrdtState) => CrdtState;
|
|
23
|
+
/**
|
|
24
|
+
* HARD compaction for a text state: re-create the document from its
|
|
25
|
+
* MATERIALISED text, dropping all history, tombstones and client ids. The
|
|
26
|
+
* result is minimal — and a NEW EPOCH: it does not merge with updates from
|
|
27
|
+
* the old lineage, so the server treats a rebase as a fresh snapshot for
|
|
28
|
+
* every subscriber (which the subscription lane already handles as a
|
|
29
|
+
* reset). Use when `compact` no longer bounds a hot document.
|
|
30
|
+
*/
|
|
31
|
+
rebaseText: (state: CrdtState) => CrdtState;
|
|
21
32
|
/**
|
|
22
33
|
* Merge two encoded states into one converged encoded state.
|
|
23
34
|
*
|
|
@@ -33,6 +44,54 @@ declare interface CrdtBackend {
|
|
|
33
44
|
emptyState: () => CrdtState;
|
|
34
45
|
}
|
|
35
46
|
|
|
47
|
+
/**
|
|
48
|
+
* A CRDT implementation. One per library. The default is {@link yjsBackend}.
|
|
49
|
+
*
|
|
50
|
+
* `merge` and `decodeText` are STATELESS with respect to any live handle: they
|
|
51
|
+
* operate purely on encoded bytes, which is what lets the sync layer and tests
|
|
52
|
+
* converge two states without holding a document open.
|
|
53
|
+
*/
|
|
54
|
+
/**
|
|
55
|
+
* A live, mutable CRDT DOCUMENT — the whole-doc generalisation `crdtDoc()`
|
|
56
|
+
* stores (rich text, maps, arrays; whatever the backend's document model
|
|
57
|
+
* holds). `crdtText()` remains the single-field specialisation over the same
|
|
58
|
+
* backend.
|
|
59
|
+
*/
|
|
60
|
+
declare interface CrdtDocHandle {
|
|
61
|
+
/**
|
|
62
|
+
* The underlying library document (a `Y.Doc` for the yjs backend), typed
|
|
63
|
+
* opaquely: an EDITOR BINDING needs the real object (Tiptap's Collaboration
|
|
64
|
+
* extension takes a Y.Doc), while every sync-layer consumer only moves
|
|
65
|
+
* encoded bytes and must never reach in. Cast at the binding, nowhere else.
|
|
66
|
+
*/
|
|
67
|
+
readonly raw: unknown;
|
|
68
|
+
/** Full state, encoded for transport or merge. */
|
|
69
|
+
encodeState: () => CrdtState;
|
|
70
|
+
/** This doc's state VECTOR — the compact "what I have" summary a peer
|
|
71
|
+
* diffs against. */
|
|
72
|
+
stateVector: () => Uint8Array;
|
|
73
|
+
/**
|
|
74
|
+
* Encode only what a peer holding `sinceVector` is missing — the
|
|
75
|
+
* INCREMENTAL update lane. A 1-character edit against a 100 KB doc encodes
|
|
76
|
+
* to a few dozen bytes, not the full state.
|
|
77
|
+
*/
|
|
78
|
+
encodeUpdateSince: (sinceVector: Uint8Array) => CrdtState;
|
|
79
|
+
/** Fold an encoded state or incremental update into this doc, in place. */
|
|
80
|
+
applyState: (state: CrdtState) => void;
|
|
81
|
+
/** Subscribe to update blobs this doc produces (local edits AND applied
|
|
82
|
+
* remote states). Returns the unsubscribe. */
|
|
83
|
+
onUpdate: (handler: (update: CrdtState) => void) => () => void;
|
|
84
|
+
/**
|
|
85
|
+
* Encode a stable ANCHOR at `index` of the named text field — a position
|
|
86
|
+
* that survives concurrent edits (the primitive an inline-comment UI pins
|
|
87
|
+
* threads with; the UI itself is the comments plugin's business).
|
|
88
|
+
*/
|
|
89
|
+
encodeAnchor: (field: string, index: number) => Uint8Array;
|
|
90
|
+
/** Resolve an anchor back to its current index, or `undefined` when the
|
|
91
|
+
* anchored region was deleted. */
|
|
92
|
+
resolveAnchor: (encoded: Uint8Array) => number | undefined;
|
|
93
|
+
}
|
|
94
|
+
|
|
36
95
|
/** Identifies one CRDT-managed cell: a `crdtText()` COLUMN of one ROW of one TABLE. */
|
|
37
96
|
declare interface CrdtDocKey {
|
|
38
97
|
readonly table: string;
|
package/dist/react.js
CHANGED
|
@@ -1,19 +1,19 @@
|
|
|
1
|
-
import { a as e, c as t,
|
|
1
|
+
import { a as e, c as t, i as n, l as r, n as i, p as a, r as o, s, t as c } from "./room-DZzqWoJq.js";
|
|
2
2
|
import { useCallback as l, useEffect as u, useMemo as d, useReducer as f, useRef as p, useState as m } from "react";
|
|
3
3
|
//#region src/react/useSyncQueue.ts
|
|
4
4
|
var h = (e = !0) => {
|
|
5
|
-
let [n,
|
|
6
|
-
|
|
5
|
+
let [n, i] = f(r, e, s), a = l((e) => {
|
|
6
|
+
i({
|
|
7
7
|
type: "enqueue",
|
|
8
8
|
write: e
|
|
9
9
|
});
|
|
10
10
|
}, []), o = l((e) => {
|
|
11
|
-
|
|
12
|
-
}, []), c = l(() =>
|
|
11
|
+
i(e ? { type: "online" } : { type: "offline" });
|
|
12
|
+
}, []), c = l(() => i({ type: "drain-start" }), []), u = l((e) => i({
|
|
13
13
|
type: "ack",
|
|
14
14
|
id: e
|
|
15
15
|
}), []), p = l((e, t) => {
|
|
16
|
-
|
|
16
|
+
i({
|
|
17
17
|
type: "fail",
|
|
18
18
|
id: e,
|
|
19
19
|
error: t
|
|
@@ -37,7 +37,7 @@ var h = (e = !0) => {
|
|
|
37
37
|
p
|
|
38
38
|
]);
|
|
39
39
|
}, g = () => typeof navigator > "u" || navigator.onLine ? "online" : "offline", _ = (t = 0) => {
|
|
40
|
-
let [
|
|
40
|
+
let [r, i] = f(o, void 0, () => e(g()));
|
|
41
41
|
u(() => {
|
|
42
42
|
if (typeof window > "u") return;
|
|
43
43
|
let e = () => i({ type: "network-up" }), t = () => i({ type: "network-down" });
|
|
@@ -45,15 +45,15 @@ var h = (e = !0) => {
|
|
|
45
45
|
window.removeEventListener("online", e), window.removeEventListener("offline", t);
|
|
46
46
|
};
|
|
47
47
|
}, []);
|
|
48
|
-
let a = l(() => i({ type: "confirmed" }), []), s = l(() => i({ type: "confirm-failed" }), []), c = r
|
|
48
|
+
let a = l(() => i({ type: "confirmed" }), []), s = l(() => i({ type: "confirm-failed" }), []), c = n(r.phase, t);
|
|
49
49
|
return d(() => ({
|
|
50
|
-
state:
|
|
51
|
-
phase:
|
|
50
|
+
state: r,
|
|
51
|
+
phase: r.phase,
|
|
52
52
|
status: c,
|
|
53
53
|
confirm: a,
|
|
54
54
|
confirmFailed: s
|
|
55
55
|
}), [
|
|
56
|
-
|
|
56
|
+
r,
|
|
57
57
|
c,
|
|
58
58
|
a,
|
|
59
59
|
s
|
|
@@ -113,13 +113,13 @@ var h = (e = !0) => {
|
|
|
113
113
|
state: void 0,
|
|
114
114
|
outstanding: 0
|
|
115
115
|
}, w = (e) => {
|
|
116
|
-
let { cell: t, remote:
|
|
116
|
+
let { cell: t, remote: n, adapter: r, online: o, maxAttempts: s } = e, c = e.backend ?? a, f = S(t), h = p(e.push);
|
|
117
117
|
h.current = e.push;
|
|
118
118
|
let g = p(t);
|
|
119
119
|
g.current = t;
|
|
120
120
|
let _ = p(null), v = p(null), y = p(null), [b, w] = m(C);
|
|
121
121
|
u(() => {
|
|
122
|
-
let t = g.current, n =
|
|
122
|
+
let t = g.current, n = i({
|
|
123
123
|
transport: {
|
|
124
124
|
kind: "sync-transport",
|
|
125
125
|
push: async (e) => {
|
|
@@ -140,33 +140,33 @@ var h = (e = !0) => {
|
|
|
140
140
|
v.current = null;
|
|
141
141
|
})
|
|
142
142
|
},
|
|
143
|
-
...
|
|
143
|
+
...r === void 0 ? {} : { adapter: r },
|
|
144
144
|
...e.backend === void 0 ? {} : { backend: e.backend },
|
|
145
145
|
...o === void 0 ? {} : { online: o },
|
|
146
146
|
...s === void 0 ? {} : { maxAttempts: s }
|
|
147
147
|
});
|
|
148
148
|
_.current = n, y.current = c.createText();
|
|
149
|
-
let
|
|
149
|
+
let a = () => {
|
|
150
150
|
let e = n.getText(t), r = n.outstanding();
|
|
151
151
|
w((i) => i.text === e && i.outstanding === r ? i : {
|
|
152
152
|
text: e,
|
|
153
153
|
state: n.getState(t),
|
|
154
154
|
outstanding: r
|
|
155
155
|
});
|
|
156
|
-
}, l = n.subscribe(
|
|
157
|
-
return
|
|
156
|
+
}, l = n.subscribe(a);
|
|
157
|
+
return a(), () => {
|
|
158
158
|
l(), n.close(), _.current = null, y.current = null, v.current = null, w(C);
|
|
159
159
|
};
|
|
160
160
|
}, [
|
|
161
161
|
f,
|
|
162
|
-
|
|
162
|
+
r,
|
|
163
163
|
c,
|
|
164
164
|
e.backend,
|
|
165
165
|
o,
|
|
166
166
|
s
|
|
167
167
|
]), u(() => {
|
|
168
|
-
|
|
169
|
-
}, [f,
|
|
168
|
+
n != null && v.current?.(n);
|
|
169
|
+
}, [f, n]);
|
|
170
170
|
let T = l((e) => {
|
|
171
171
|
let t = _.current, n = y.current;
|
|
172
172
|
if (t === null || n === null) return;
|