@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.
@@ -25,7 +25,47 @@ var t = "t", n = (t) => e.encodeStateAsUpdate(t), r = (t) => {
25
25
  let n = r(e), i = n.getText(t).toString();
26
26
  return n.destroy(), i;
27
27
  },
28
- emptyState: () => n(new e.Doc())
28
+ emptyState: () => n(new e.Doc()),
29
+ createDoc: (t) => {
30
+ let n = new e.Doc();
31
+ return t !== void 0 && t.length > 0 && e.applyUpdate(n, t), {
32
+ raw: n,
33
+ encodeState: () => e.encodeStateAsUpdate(n),
34
+ stateVector: () => e.encodeStateVector(n),
35
+ encodeUpdateSince: (t) => e.encodeStateAsUpdate(n, t),
36
+ applyState: (t) => {
37
+ t.length > 0 && e.applyUpdate(n, t);
38
+ },
39
+ onUpdate: (e) => {
40
+ let t = (t) => {
41
+ e(t);
42
+ };
43
+ return n.on("update", t), () => {
44
+ n.off("update", t);
45
+ };
46
+ },
47
+ encodeAnchor: (t, r) => {
48
+ let i = e.createRelativePositionFromTypeIndex(n.getText(t), r);
49
+ return e.encodeRelativePosition(i);
50
+ },
51
+ resolveAnchor: (t) => {
52
+ let r = e.createAbsolutePositionFromRelativePosition(e.decodeRelativePosition(t), n);
53
+ return r === null ? void 0 : r.index;
54
+ }
55
+ };
56
+ },
57
+ compact: (t) => {
58
+ let n = r(t), i = e.encodeStateAsUpdate(n);
59
+ return n.destroy(), i;
60
+ },
61
+ rebaseText: (n) => {
62
+ let i = r(n), a = i.getText(t).toString();
63
+ i.destroy();
64
+ let o = new e.Doc();
65
+ a.length > 0 && o.getText(t).insert(0, a);
66
+ let s = e.encodeStateAsUpdate(o);
67
+ return o.destroy(), s;
68
+ }
29
69
  }, o = a, s = (e, t = o) => {
30
70
  let n = t.createText(e), r = {
31
71
  backend: t.name,
@@ -36,15 +76,22 @@ var t = "t", n = (t) => e.encodeStateAsUpdate(t), r = (t) => {
36
76
  encode: () => n.encodeState()
37
77
  };
38
78
  return r;
39
- }, c = (e, t, n = o) => n.merge(e, t), l = (e, t = o) => t.decodeText(e), u = (e = o) => e.emptyState(), d = (e = !0) => ({
79
+ }, c = (e, t, n) => {
80
+ if (!(n instanceof Uint8Array) || !(t instanceof Uint8Array)) return n;
81
+ try {
82
+ return l(t, n);
83
+ } catch {
84
+ return n;
85
+ }
86
+ }, l = (e, t, n = o) => n.merge(e, t), u = (e, t = o) => t.decodeText(e), d = (e = o) => e.emptyState(), f = (e = !0) => ({
40
87
  online: e,
41
88
  pending: [],
42
89
  inFlight: void 0,
43
90
  lastError: void 0
44
- }), f = (e) => e.online && e.inFlight === void 0 && e.pending.length > 0, p = (e) => e.pending.length + (e.inFlight === void 0 ? 0 : 1), m = (e) => ({
91
+ }), p = (e) => e.online && e.inFlight === void 0 && e.pending.length > 0, m = (e) => e.pending.length + (e.inFlight === void 0 ? 0 : 1), h = (e) => ({
45
92
  ...e,
46
93
  attempts: e.attempts + 1
47
- }), h = (e, t) => {
94
+ }), g = (e, t) => {
48
95
  switch (t.type) {
49
96
  case "enqueue": return {
50
97
  ...e,
@@ -64,7 +111,7 @@ var t = "t", n = (t) => e.encodeStateAsUpdate(t), r = (t) => {
64
111
  inFlight: void 0
65
112
  };
66
113
  case "drain-start": {
67
- if (!f(e)) return e;
114
+ if (!p(e)) return e;
68
115
  let [t, ...n] = e.pending;
69
116
  return t === void 0 ? e : {
70
117
  ...e,
@@ -80,15 +127,15 @@ var t = "t", n = (t) => e.encodeStateAsUpdate(t), r = (t) => {
80
127
  };
81
128
  case "fail": return e.inFlight === void 0 || e.inFlight.id !== t.id ? e : {
82
129
  ...e,
83
- pending: [m(e.inFlight), ...e.pending],
130
+ pending: [h(e.inFlight), ...e.pending],
84
131
  inFlight: void 0,
85
132
  lastError: t.error
86
133
  };
87
134
  }
88
- }, g = (e = "online") => ({
135
+ }, _ = (e = "online") => ({
89
136
  phase: e,
90
137
  attempt: 0
91
- }), _ = (e, t) => {
138
+ }), v = (e, t) => {
92
139
  switch (t.type) {
93
140
  case "network-down": return e.phase === "offline" ? e : {
94
141
  phase: "offline",
@@ -107,87 +154,102 @@ var t = "t", n = (t) => e.encodeStateAsUpdate(t), r = (t) => {
107
154
  attempt: e.attempt + 1
108
155
  } : e;
109
156
  }
110
- }, v = (e, t) => e === "offline" ? "offline" : e === "reconnecting" || t > 0 ? "syncing" : "synced", y = (e) => `${e.table}\u0000${e.id}\u0000${e.column}`, b = 0, x = () => {
157
+ }, y = (e, t) => e === "offline" ? "offline" : e === "reconnecting" || t > 0 ? "syncing" : "synced", b = (e) => `${e.table}\u0000${e.id}\u0000${e.column}`, x = 0, S = () => {
111
158
  let e = typeof crypto < "u" ? crypto : void 0;
112
- return e && typeof e.randomUUID == "function" ? e.randomUUID() : `w_${Date.now().toString(36)}_${b += 1}`;
113
- }, S = (e) => {
114
- let { transport: t, adapter: n } = e, r = e.backend ?? o, i = e.now ?? Date.now, a = e.maxAttempts ?? 5, s = d(e.online ?? !0), l = /* @__PURE__ */ new Map(), u = /* @__PURE__ */ new Set(), m = () => {
159
+ return e && typeof e.randomUUID == "function" ? e.randomUUID() : `w_${Date.now().toString(36)}_${x += 1}`;
160
+ }, C = (e) => {
161
+ let { transport: t, adapter: n } = e, r = e.backend ?? o, i = e.now ?? Date.now, a = e.maxAttempts ?? 5, s = f(e.online ?? !0), c = /* @__PURE__ */ new Map(), u = /* @__PURE__ */ new Set(), d = () => {
115
162
  for (let e of u) try {
116
163
  e();
117
164
  } catch {}
118
- }, g = (e, t) => {
119
- let i = y(e), a = l.get(i), o = a === void 0 ? t : c(a, t, r);
120
- l.set(i, o), n?.saveCrdtState(e.table, e.id, o).catch(() => void 0);
165
+ }, h = (e, t) => {
166
+ let i = b(e), a = c.get(i), o = a === void 0 ? t : l(a, t, r);
167
+ c.set(i, o), n?.saveCrdtState(e.table, e.id, o).catch(() => void 0);
121
168
  }, _ = () => {
122
169
  if (n === void 0) return;
123
170
  let e = s.inFlight === void 0 ? s.pending : [s.inFlight, ...s.pending];
124
171
  n.saveQueue(e).catch(() => void 0);
125
- }, v = !1, b = async () => {
172
+ }, v = !1, y = async () => {
126
173
  if (!v) {
127
174
  v = !0;
128
175
  try {
129
- for (; f(s);) {
130
- s = h(s, { type: "drain-start" });
176
+ for (; p(s);) {
177
+ s = g(s, { type: "drain-start" });
131
178
  let e = s.inFlight;
132
179
  if (e === void 0) break;
133
180
  try {
134
- await t.push(e), s = h(s, {
181
+ await t.push(e), s = g(s, {
135
182
  type: "ack",
136
183
  id: e.id
137
- }), _(), m();
184
+ }), _(), d();
138
185
  } catch (t) {
139
- if (s = h(s, {
186
+ if (s = g(s, {
140
187
  type: "fail",
141
188
  id: e.id,
142
189
  error: t instanceof Error ? t.message : String(t)
143
- }), _(), m(), e.attempts + 1 >= a) break;
190
+ }), _(), d(), e.attempts + 1 >= a) break;
144
191
  }
145
192
  }
146
193
  } finally {
147
194
  v = !1;
148
195
  }
149
196
  }
150
- }, S = t.onRemoteState((e) => {
151
- g(e, e.state), m();
197
+ }, x = t.onRemoteState((e) => {
198
+ h(e, e.state), d();
152
199
  });
153
200
  return {
154
201
  enqueue: (e) => {
155
- g(e, e.update);
156
- let t = {
157
- id: x(),
202
+ h(e, e.update);
203
+ let t = b(e), n = s.pending.findIndex((e) => b(e.payload) === t);
204
+ if (n !== -1) {
205
+ let t = s.pending[n], i = {
206
+ ...t,
207
+ payload: {
208
+ ...t.payload,
209
+ update: l(t.payload.update, e.update, r)
210
+ }
211
+ };
212
+ s = {
213
+ ...s,
214
+ pending: s.pending.map((e, t) => t === n ? i : e)
215
+ }, _(), d(), y();
216
+ return;
217
+ }
218
+ let a = {
219
+ id: S(),
158
220
  payload: e,
159
221
  enqueuedAt: i(),
160
222
  attempts: 0
161
223
  };
162
- s = h(s, {
224
+ s = g(s, {
163
225
  type: "enqueue",
164
- write: t
165
- }), _(), m(), b();
226
+ write: a
227
+ }), _(), d(), y();
166
228
  },
167
229
  setOnline: (e) => {
168
- s = h(s, e ? { type: "online" } : { type: "offline" }), _(), m(), e && b();
230
+ s = g(s, e ? { type: "online" } : { type: "offline" }), _(), d(), e && y();
169
231
  },
170
232
  queueState: () => s,
171
- outstanding: () => p(s),
233
+ outstanding: () => m(s),
172
234
  getState: (e) => {
173
- let t = l.get(y(e));
235
+ let t = c.get(b(e));
174
236
  return t === void 0 ? void 0 : t.slice();
175
237
  },
176
238
  getText: (e) => {
177
- let t = l.get(y(e));
239
+ let t = c.get(b(e));
178
240
  return t === void 0 ? "" : r.decodeText(t);
179
241
  },
180
242
  subscribe: (e) => (u.add(e), () => {
181
243
  u.delete(e);
182
244
  }),
183
245
  flush: async () => {
184
- await b();
246
+ await y();
185
247
  },
186
248
  close: () => {
187
- S(), u.clear();
249
+ x(), u.clear();
188
250
  }
189
251
  };
190
- }, C = (e) => {
252
+ }, w = (e) => {
191
253
  let { channel: t, roomId: n, peerId: r, initial: i } = e, a = e.ttlMs ?? 3e4, o = e.now ?? Date.now, s = i, c = 0, l = !1, u = /* @__PURE__ */ new Map(), d = /* @__PURE__ */ new Map(), f = /* @__PURE__ */ new Set(), p = () => {
192
254
  for (let e of f) try {
193
255
  e();
@@ -248,4 +310,4 @@ var t = "t", n = (t) => e.encodeStateAsUpdate(t), r = (t) => {
248
310
  };
249
311
  };
250
312
  //#endregion
251
- export { g as a, p as c, l as d, o as f, a as h, v as i, h as l, c as m, S as n, f as o, u as p, _ as r, d as s, C as t, s as u };
313
+ export { _ as a, m as c, s as d, u as f, a as g, l as h, y as i, g as l, d as m, C as n, p as o, o as p, v as r, f as s, w as t, c as u };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@voltro/local-first",
3
- "version": "0.52.0",
3
+ "version": "0.53.0",
4
4
  "description": "Local-first / CRDT primitives (opt-in): a Yjs-backed crdtText() field and its deterministic merge, the bi-directional sync wire (createSyncClient + the useCrdtText React binding), durable IndexedDB persistence, presence rooms, connection-status state machine, and last-write-wins conflict policy. Remaining seams: the two app-level tags the sync wire is pointed at (the mutation that writes the column, the query that streams the row), the presence channel's broker binding, and an optional wa-sqlite/Turso adapter.",
5
5
  "keywords": [
6
6
  "voltro",
@@ -38,14 +38,31 @@
38
38
  "node": ">=24.0.0"
39
39
  },
40
40
  "dependencies": {
41
- "yjs": "^13.6.32"
41
+ "yjs": "^13.6.32",
42
+ "y-protocols": "^1.0.6"
42
43
  },
43
44
  "peerDependencies": {
44
- "react": "^19.0.0"
45
+ "react": "^19.0.0",
46
+ "@tiptap/react": "^3.0.0",
47
+ "@tiptap/starter-kit": "^3.0.0",
48
+ "@tiptap/extension-collaboration": "^3.0.0",
49
+ "@tiptap/extension-collaboration-caret": "^3.0.0"
45
50
  },
46
51
  "peerDependenciesMeta": {
47
52
  "react": {
48
53
  "optional": true
54
+ },
55
+ "@tiptap/react": {
56
+ "optional": true
57
+ },
58
+ "@tiptap/starter-kit": {
59
+ "optional": true
60
+ },
61
+ "@tiptap/extension-collaboration": {
62
+ "optional": true
63
+ },
64
+ "@tiptap/extension-collaboration-caret": {
65
+ "optional": true
49
66
  }
50
67
  },
51
68
  "publishConfig": {