@automerge/automerge-repo-react-hooks 2.0.0 → 2.0.3

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/index.js CHANGED
@@ -1,22 +1,22 @@
1
- import q, { createContext as I, useContext as j, useRef as $, useState as x, useEffect as _, useCallback as S } from "react";
2
- function O(s) {
3
- let l = "pending", r, f;
4
- const h = s.then(
1
+ import q, { createContext as I, useContext as j, useRef as $, useState as b, useEffect as y, useCallback as S } from "react";
2
+ function O(o) {
3
+ let l = "pending", r, i;
4
+ const h = o.then(
5
5
  (p) => {
6
6
  l = "success", r = p;
7
7
  },
8
8
  (p) => {
9
- l = "error", f = p;
9
+ l = "error", i = p;
10
10
  }
11
11
  );
12
12
  return {
13
- promise: s,
13
+ promise: o,
14
14
  read() {
15
15
  switch (l) {
16
16
  case "pending":
17
17
  throw h;
18
18
  case "error":
19
- throw f;
19
+ throw i;
20
20
  case "success":
21
21
  return r;
22
22
  }
@@ -25,25 +25,25 @@ function O(s) {
25
25
  }
26
26
  const F = I(null);
27
27
  function k() {
28
- const s = j(F);
29
- if (!s) throw new Error("Repo was not found on RepoContext.");
30
- return s;
28
+ const o = j(F);
29
+ if (!o) throw new Error("Repo was not found on RepoContext.");
30
+ return o;
31
31
  }
32
- const E = /* @__PURE__ */ new Map();
33
- function N(s, { suspense: l } = { suspense: !1 }) {
34
- const r = k(), f = $(), [h, p] = x();
32
+ const x = /* @__PURE__ */ new Map();
33
+ function N(o, { suspense: l } = { suspense: !1 }) {
34
+ const r = k(), i = $(), [h, p] = b();
35
35
  let u = h;
36
- if (s && !u) {
37
- const t = r.findWithProgress(s);
36
+ if (o && !u) {
37
+ const t = r.findWithProgress(o);
38
38
  t.state === "ready" && (u = t.handle);
39
39
  }
40
- let n = s ? E.get(s) : void 0;
41
- if (!n && s) {
42
- f.current?.abort(), f.current = new AbortController();
43
- const t = r.find(s, { signal: f.current.signal });
44
- n = O(t), E.set(s, n);
40
+ let n = o ? x.get(o) : void 0;
41
+ if (!n && o) {
42
+ i.current?.abort(), i.current = new AbortController();
43
+ const t = r.find(o, { signal: i.current.signal });
44
+ n = O(t), x.set(o, n);
45
45
  }
46
- return _(() => {
46
+ return y(() => {
47
47
  l || !n || n.promise.then((t) => {
48
48
  p(t);
49
49
  }).catch(() => {
@@ -51,20 +51,20 @@ function N(s, { suspense: l } = { suspense: !1 }) {
51
51
  });
52
52
  }, [l, n]), u || !l || !n ? u : n.read();
53
53
  }
54
- function Q(s, l = { suspense: !1 }) {
55
- const r = N(s, l), [f, h] = x(() => r?.doc()), [p, u] = x();
56
- _(() => {
54
+ function Q(o, l = { suspense: !1 }) {
55
+ const r = N(o, l), [i, h] = b(() => r?.doc()), [p, u] = b();
56
+ y(() => {
57
57
  h(r?.doc());
58
- }, [r]), _(() => {
58
+ }, [r]), y(() => {
59
59
  if (!r)
60
60
  return;
61
61
  const t = () => h(r.doc()), e = () => {
62
- u(new Error(`Document ${s} was deleted`));
62
+ u(new Error(`Document ${o} was deleted`));
63
63
  };
64
64
  return r.on("change", t), r.on("delete", e), () => {
65
65
  r.removeListener("change", t), r.removeListener("delete", e);
66
66
  };
67
- }, [r, s]);
67
+ }, [r, o]);
68
68
  const n = S(
69
69
  (t, e) => {
70
70
  r.change(t, e);
@@ -73,28 +73,39 @@ function Q(s, l = { suspense: !1 }) {
73
73
  );
74
74
  if (p)
75
75
  throw p;
76
- return f ? [f, n] : [void 0, () => {
76
+ return i ? [i, n] : [void 0, () => {
77
77
  }];
78
78
  }
79
- function T(s, { suspense: l = !1 } = {}) {
80
- const r = k(), [f, h] = x(() => /* @__PURE__ */ new Map()), p = [], u = /* @__PURE__ */ new Map();
81
- for (const n of s) {
82
- let t = E.get(n);
83
- if (!t)
79
+ function W(o, { suspense: l = !1 } = {}) {
80
+ const r = k(), [i, h] = b(() => {
81
+ const n = /* @__PURE__ */ new Map();
82
+ for (const t of o) {
83
+ let e;
84
84
  try {
85
- const e = r.find(n);
86
- t = O(e), E.set(n, t);
85
+ e = r.findWithProgress(t);
86
+ } catch {
87
+ continue;
88
+ }
89
+ e.state === "ready" && n.set(t, e.handle);
90
+ }
91
+ return n;
92
+ }), p = [], u = /* @__PURE__ */ new Map();
93
+ for (const n of o) {
94
+ let t = i.get(n), e = x.get(n);
95
+ if (!e)
96
+ try {
97
+ const s = r.find(n);
98
+ e = O(s), x.set(n, e);
87
99
  } catch {
88
100
  continue;
89
101
  }
90
102
  try {
91
- const e = t.read();
92
- u.set(n, e);
93
- } catch (e) {
94
- e instanceof Promise ? p.push(t) : u.set(n, void 0);
103
+ t ??= e.read(), u.set(n, t);
104
+ } catch (s) {
105
+ s instanceof Promise ? p.push(e) : u.set(n, void 0);
95
106
  }
96
107
  }
97
- if (_(() => {
108
+ if (y(() => {
98
109
  p.length > 0 ? Promise.allSettled(p.map((n) => n.promise)).then(
99
110
  (n) => {
100
111
  n.forEach((t) => {
@@ -105,24 +116,30 @@ function T(s, { suspense: l = !1 } = {}) {
105
116
  }), h(u);
106
117
  }
107
118
  ) : h(u);
108
- }, [l, s]), l && p.length > 0)
119
+ }, [l, o]), l && p.length > 0)
109
120
  throw Promise.all(p.map((n) => n.promise));
110
- return f;
121
+ return i;
111
122
  }
112
- function V(s, { suspense: l = !0 } = {}) {
113
- const r = T(s, { suspense: l }), [f, h] = x(() => /* @__PURE__ */ new Map());
114
- _(() => {
123
+ function V(o, { suspense: l = !0 } = {}) {
124
+ const r = W(o, { suspense: l }), [i, h] = b(() => {
125
+ const u = /* @__PURE__ */ new Map();
126
+ return r.forEach((n) => {
127
+ const t = n?.url;
128
+ t && u.set(t, n?.doc());
129
+ }), u;
130
+ });
131
+ y(() => {
115
132
  const u = /* @__PURE__ */ new Map();
116
133
  return r.forEach((n, t) => {
117
134
  if (n) {
118
135
  const e = () => {
119
- h((o) => {
120
- const c = new Map(o);
136
+ h((s) => {
137
+ const c = new Map(s);
121
138
  return c.set(t, n.doc()), c;
122
139
  });
123
140
  };
124
- h((o) => {
125
- const c = new Map(o);
141
+ h((s) => {
142
+ const c = new Map(s);
126
143
  return c.set(t, n.doc()), c;
127
144
  }), n.on("change", e), u.set(t, e);
128
145
  }
@@ -145,189 +162,189 @@ function V(s, { suspense: l = !0 } = {}) {
145
162
  },
146
163
  [r]
147
164
  );
148
- return [f, p];
165
+ return [i, p];
149
166
  }
150
- function H(s) {
151
- return s && s.__esModule && Object.prototype.hasOwnProperty.call(s, "default") ? s.default : s;
167
+ function H(o) {
168
+ return o && o.__esModule && Object.prototype.hasOwnProperty.call(o, "default") ? o.default : o;
152
169
  }
153
- var C, R;
154
- function W() {
155
- if (R) return C;
156
- R = 1;
157
- var s = q, l = function(f) {
158
- return typeof f == "function";
159
- }, r = function(f) {
160
- var h = s.useState(f), p = h[0], u = h[1], n = s.useRef(p), t = s.useCallback(function(e) {
170
+ var C, P;
171
+ function T() {
172
+ if (P) return C;
173
+ P = 1;
174
+ var o = q, l = function(i) {
175
+ return typeof i == "function";
176
+ }, r = function(i) {
177
+ var h = o.useState(i), p = h[0], u = h[1], n = o.useRef(p), t = o.useCallback(function(e) {
161
178
  n.current = l(e) ? e(n.current) : e, u(n.current);
162
179
  }, []);
163
180
  return [p, t, n];
164
181
  };
165
182
  return C = r, C;
166
183
  }
167
- var z = W();
184
+ var z = T();
168
185
  const L = /* @__PURE__ */ H(z);
169
- var D = { exports: {} }, P;
186
+ var D = { exports: {} }, R;
170
187
  function B() {
171
- return P || (P = 1, function(s) {
188
+ return R || (R = 1, function(o) {
172
189
  var l = Object.prototype.hasOwnProperty, r = "~";
173
- function f() {
190
+ function i() {
174
191
  }
175
- Object.create && (f.prototype = /* @__PURE__ */ Object.create(null), new f().__proto__ || (r = !1));
176
- function h(t, e, o) {
177
- this.fn = t, this.context = e, this.once = o || !1;
192
+ Object.create && (i.prototype = /* @__PURE__ */ Object.create(null), new i().__proto__ || (r = !1));
193
+ function h(t, e, s) {
194
+ this.fn = t, this.context = e, this.once = s || !1;
178
195
  }
179
- function p(t, e, o, c, w) {
180
- if (typeof o != "function")
196
+ function p(t, e, s, c, w) {
197
+ if (typeof s != "function")
181
198
  throw new TypeError("The listener must be a function");
182
- var v = new h(o, c || t, w), i = r ? r + e : e;
183
- return t._events[i] ? t._events[i].fn ? t._events[i] = [t._events[i], v] : t._events[i].push(v) : (t._events[i] = v, t._eventsCount++), t;
199
+ var v = new h(s, c || t, w), f = r ? r + e : e;
200
+ return t._events[f] ? t._events[f].fn ? t._events[f] = [t._events[f], v] : t._events[f].push(v) : (t._events[f] = v, t._eventsCount++), t;
184
201
  }
185
202
  function u(t, e) {
186
- --t._eventsCount === 0 ? t._events = new f() : delete t._events[e];
203
+ --t._eventsCount === 0 ? t._events = new i() : delete t._events[e];
187
204
  }
188
205
  function n() {
189
- this._events = new f(), this._eventsCount = 0;
206
+ this._events = new i(), this._eventsCount = 0;
190
207
  }
191
208
  n.prototype.eventNames = function() {
192
- var e = [], o, c;
209
+ var e = [], s, c;
193
210
  if (this._eventsCount === 0) return e;
194
- for (c in o = this._events)
195
- l.call(o, c) && e.push(r ? c.slice(1) : c);
196
- return Object.getOwnPropertySymbols ? e.concat(Object.getOwnPropertySymbols(o)) : e;
211
+ for (c in s = this._events)
212
+ l.call(s, c) && e.push(r ? c.slice(1) : c);
213
+ return Object.getOwnPropertySymbols ? e.concat(Object.getOwnPropertySymbols(s)) : e;
197
214
  }, n.prototype.listeners = function(e) {
198
- var o = r ? r + e : e, c = this._events[o];
215
+ var s = r ? r + e : e, c = this._events[s];
199
216
  if (!c) return [];
200
217
  if (c.fn) return [c.fn];
201
- for (var w = 0, v = c.length, i = new Array(v); w < v; w++)
202
- i[w] = c[w].fn;
203
- return i;
218
+ for (var w = 0, v = c.length, f = new Array(v); w < v; w++)
219
+ f[w] = c[w].fn;
220
+ return f;
204
221
  }, n.prototype.listenerCount = function(e) {
205
- var o = r ? r + e : e, c = this._events[o];
222
+ var s = r ? r + e : e, c = this._events[s];
206
223
  return c ? c.fn ? 1 : c.length : 0;
207
- }, n.prototype.emit = function(e, o, c, w, v, i) {
224
+ }, n.prototype.emit = function(e, s, c, w, v, f) {
208
225
  var m = r ? r + e : e;
209
226
  if (!this._events[m]) return !1;
210
- var a = this._events[m], g = arguments.length, y, d;
227
+ var a = this._events[m], g = arguments.length, _, d;
211
228
  if (a.fn) {
212
229
  switch (a.once && this.removeListener(e, a.fn, void 0, !0), g) {
213
230
  case 1:
214
231
  return a.fn.call(a.context), !0;
215
232
  case 2:
216
- return a.fn.call(a.context, o), !0;
233
+ return a.fn.call(a.context, s), !0;
217
234
  case 3:
218
- return a.fn.call(a.context, o, c), !0;
235
+ return a.fn.call(a.context, s, c), !0;
219
236
  case 4:
220
- return a.fn.call(a.context, o, c, w), !0;
237
+ return a.fn.call(a.context, s, c, w), !0;
221
238
  case 5:
222
- return a.fn.call(a.context, o, c, w, v), !0;
239
+ return a.fn.call(a.context, s, c, w, v), !0;
223
240
  case 6:
224
- return a.fn.call(a.context, o, c, w, v, i), !0;
241
+ return a.fn.call(a.context, s, c, w, v, f), !0;
225
242
  }
226
- for (d = 1, y = new Array(g - 1); d < g; d++)
227
- y[d - 1] = arguments[d];
228
- a.fn.apply(a.context, y);
243
+ for (d = 1, _ = new Array(g - 1); d < g; d++)
244
+ _[d - 1] = arguments[d];
245
+ a.fn.apply(a.context, _);
229
246
  } else {
230
- var A = a.length, b;
247
+ var A = a.length, E;
231
248
  for (d = 0; d < A; d++)
232
249
  switch (a[d].once && this.removeListener(e, a[d].fn, void 0, !0), g) {
233
250
  case 1:
234
251
  a[d].fn.call(a[d].context);
235
252
  break;
236
253
  case 2:
237
- a[d].fn.call(a[d].context, o);
254
+ a[d].fn.call(a[d].context, s);
238
255
  break;
239
256
  case 3:
240
- a[d].fn.call(a[d].context, o, c);
257
+ a[d].fn.call(a[d].context, s, c);
241
258
  break;
242
259
  case 4:
243
- a[d].fn.call(a[d].context, o, c, w);
260
+ a[d].fn.call(a[d].context, s, c, w);
244
261
  break;
245
262
  default:
246
- if (!y) for (b = 1, y = new Array(g - 1); b < g; b++)
247
- y[b - 1] = arguments[b];
248
- a[d].fn.apply(a[d].context, y);
263
+ if (!_) for (E = 1, _ = new Array(g - 1); E < g; E++)
264
+ _[E - 1] = arguments[E];
265
+ a[d].fn.apply(a[d].context, _);
249
266
  }
250
267
  }
251
268
  return !0;
252
- }, n.prototype.on = function(e, o, c) {
253
- return p(this, e, o, c, !1);
254
- }, n.prototype.once = function(e, o, c) {
255
- return p(this, e, o, c, !0);
256
- }, n.prototype.removeListener = function(e, o, c, w) {
269
+ }, n.prototype.on = function(e, s, c) {
270
+ return p(this, e, s, c, !1);
271
+ }, n.prototype.once = function(e, s, c) {
272
+ return p(this, e, s, c, !0);
273
+ }, n.prototype.removeListener = function(e, s, c, w) {
257
274
  var v = r ? r + e : e;
258
275
  if (!this._events[v]) return this;
259
- if (!o)
276
+ if (!s)
260
277
  return u(this, v), this;
261
- var i = this._events[v];
262
- if (i.fn)
263
- i.fn === o && (!w || i.once) && (!c || i.context === c) && u(this, v);
278
+ var f = this._events[v];
279
+ if (f.fn)
280
+ f.fn === s && (!w || f.once) && (!c || f.context === c) && u(this, v);
264
281
  else {
265
- for (var m = 0, a = [], g = i.length; m < g; m++)
266
- (i[m].fn !== o || w && !i[m].once || c && i[m].context !== c) && a.push(i[m]);
282
+ for (var m = 0, a = [], g = f.length; m < g; m++)
283
+ (f[m].fn !== s || w && !f[m].once || c && f[m].context !== c) && a.push(f[m]);
267
284
  a.length ? this._events[v] = a.length === 1 ? a[0] : a : u(this, v);
268
285
  }
269
286
  return this;
270
287
  }, n.prototype.removeAllListeners = function(e) {
271
- var o;
272
- return e ? (o = r ? r + e : e, this._events[o] && u(this, o)) : (this._events = new f(), this._eventsCount = 0), this;
273
- }, n.prototype.off = n.prototype.removeListener, n.prototype.addListener = n.prototype.on, n.prefixed = r, n.EventEmitter = n, s.exports = n;
288
+ var s;
289
+ return e ? (s = r ? r + e : e, this._events[s] && u(this, s)) : (this._events = new i(), this._eventsCount = 0), this;
290
+ }, n.prototype.off = n.prototype.removeListener, n.prototype.addListener = n.prototype.on, n.prefixed = r, n.EventEmitter = n, o.exports = n;
274
291
  }(D)), D.exports;
275
292
  }
276
293
  var G = B();
277
294
  const J = /* @__PURE__ */ H(G), M = new J(), X = ({
278
- handle: s,
295
+ handle: o,
279
296
  localUserId: l,
280
297
  offlineTimeout: r = 3e4,
281
- getTime: f = () => (/* @__PURE__ */ new Date()).getTime()
298
+ getTime: i = () => (/* @__PURE__ */ new Date()).getTime()
282
299
  }) => {
283
300
  const [h, p, u] = L({}), [n, t, e] = L({});
284
- return _(() => {
285
- const o = (v) => {
286
- const [i, m] = v.message;
287
- i !== l && (e.current[i] || M.emit("new_peer", v), p({
301
+ return y(() => {
302
+ const s = (v) => {
303
+ const [f, m] = v.message;
304
+ f !== l && (e.current[f] || M.emit("new_peer", v), p({
288
305
  ...u.current,
289
- [i]: m
306
+ [f]: m
290
307
  }), t({
291
308
  ...e.current,
292
- [i]: f()
309
+ [f]: i()
293
310
  }));
294
311
  }, c = () => {
295
- const v = u.current, i = e.current, m = f();
296
- for (const a in i)
297
- m - i[a] > r && (delete v[a], delete i[a]);
298
- p(v), t(i);
312
+ const v = u.current, f = e.current, m = i();
313
+ for (const a in f)
314
+ m - f[a] > r && (delete v[a], delete f[a]);
315
+ p(v), t(f);
299
316
  };
300
- s.on("ephemeral-message", o);
317
+ o.on("ephemeral-message", s);
301
318
  const w = setInterval(
302
319
  c,
303
320
  r
304
321
  );
305
322
  return () => {
306
- s.removeListener("ephemeral-message", o), clearInterval(w);
323
+ o.removeListener("ephemeral-message", s), clearInterval(w);
307
324
  };
308
- }, [s, l, r, f]), [h, n];
325
+ }, [o, l, r, i]), [h, n];
309
326
  }, Y = ({
310
- handle: s,
327
+ handle: o,
311
328
  userId: l,
312
329
  initialState: r,
313
- heartbeatTime: f = 15e3
330
+ heartbeatTime: i = 15e3
314
331
  }) => {
315
332
  const [h, p, u] = L(r), n = (t) => {
316
333
  const e = typeof t == "function" ? t(u.current) : t;
317
- p(e), s.broadcast([l, e]);
334
+ p(e), o.broadcast([l, e]);
318
335
  };
319
- return _(() => {
336
+ return y(() => {
320
337
  if (!l)
321
338
  return;
322
- const t = () => void s.broadcast([l, u.current]);
339
+ const t = () => void o.broadcast([l, u.current]);
323
340
  t();
324
- const e = setInterval(t, f);
341
+ const e = setInterval(t, i);
325
342
  return () => void clearInterval(e);
326
- }, [s, l, f]), _(() => {
343
+ }, [o, l, i]), y(() => {
327
344
  let t;
328
345
  const e = M.on("new_peer", () => {
329
346
  t = setTimeout(
330
- () => s.broadcast([l, u.current]),
347
+ () => o.broadcast([l, u.current]),
331
348
  500
332
349
  // Wait for the peer to be ready
333
350
  );
@@ -335,12 +352,12 @@ const J = /* @__PURE__ */ H(G), M = new J(), X = ({
335
352
  return () => {
336
353
  e.off("new_peer"), t && clearTimeout(t);
337
354
  };
338
- }, [s, l, M]), [h, n];
355
+ }, [o, l, M]), [h, n];
339
356
  };
340
357
  export {
341
358
  F as RepoContext,
342
359
  N as useDocHandle,
343
- T as useDocHandles,
360
+ W as useDocHandles,
344
361
  Q as useDocument,
345
362
  V as useDocuments,
346
363
  Y as useLocalAwareness,
@@ -1 +1 @@
1
- {"version":3,"file":"useDocHandles.d.ts","sourceRoot":"","sources":["../src/useDocHandles.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,SAAS,EAAE,MAAM,gCAAgC,CAAA;AAMxE,UAAU,mBAAmB;IAC3B,QAAQ,CAAC,EAAE,OAAO,CAAA;CACnB;AAED,KAAK,YAAY,CAAC,CAAC,IAAI,GAAG,CAAC,YAAY,EAAE,SAAS,CAAC,CAAC,CAAC,GAAG,SAAS,CAAC,CAAA;AAElE,wBAAgB,aAAa,CAAC,CAAC,EAC7B,GAAG,EAAE,YAAY,EAAE,EACnB,EAAE,QAAgB,EAAE,GAAE,mBAAwB,GAC7C,YAAY,CAAC,CAAC,CAAC,CAkEjB"}
1
+ {"version":3,"file":"useDocHandles.d.ts","sourceRoot":"","sources":["../src/useDocHandles.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,SAAS,EAAE,MAAM,gCAAgC,CAAA;AAMxE,UAAU,mBAAmB;IAC3B,QAAQ,CAAC,EAAE,OAAO,CAAA;CACnB;AAED,KAAK,YAAY,CAAC,CAAC,IAAI,GAAG,CAAC,YAAY,EAAE,SAAS,CAAC,CAAC,CAAC,GAAG,SAAS,CAAC,CAAA;AAElE,wBAAgB,aAAa,CAAC,CAAC,EAC7B,GAAG,EAAE,YAAY,EAAE,EACnB,EAAE,QAAgB,EAAE,GAAE,mBAAwB,GAC7C,YAAY,CAAC,CAAC,CAAC,CAqFjB"}
@@ -1 +1 @@
1
- {"version":3,"file":"useDocuments.d.ts","sourceRoot":"","sources":["../src/useDocuments.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,gCAAgC,CAAA;AAC7D,OAAO,EAAE,QAAQ,EAAE,aAAa,EAAE,GAAG,EAAE,MAAM,2BAA2B,CAAA;AAIxE,KAAK,MAAM,CAAC,CAAC,IAAI,GAAG,CAAC,YAAY,EAAE,GAAG,CAAC,CAAC,CAAC,CAAC,CAAA;AAC1C,KAAK,WAAW,CAAC,CAAC,IAAI,CACpB,EAAE,EAAE,YAAY,EAChB,QAAQ,EAAE,QAAQ,CAAC,CAAC,CAAC,EACrB,OAAO,CAAC,EAAE,aAAa,CAAC,CAAC,CAAC,KACvB,IAAI,CAAA;AAET,UAAU,mBAAmB;IAC3B,QAAQ,CAAC,EAAE,OAAO,CAAA;CACnB;AAED,wBAAgB,YAAY,CAAC,CAAC,EAC5B,GAAG,EAAE,YAAY,EAAE,EACnB,EAAE,QAAe,EAAE,GAAE,mBAAwB,GAC5C,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,WAAW,CAAC,CAAC,CAAC,CAAC,CA6D7B"}
1
+ {"version":3,"file":"useDocuments.d.ts","sourceRoot":"","sources":["../src/useDocuments.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,gCAAgC,CAAA;AAC7D,OAAO,EAAE,QAAQ,EAAE,aAAa,EAAE,GAAG,EAAE,MAAM,2BAA2B,CAAA;AAIxE,KAAK,MAAM,CAAC,CAAC,IAAI,GAAG,CAAC,YAAY,EAAE,GAAG,CAAC,CAAC,CAAC,CAAC,CAAA;AAC1C,KAAK,WAAW,CAAC,CAAC,IAAI,CACpB,EAAE,EAAE,YAAY,EAChB,QAAQ,EAAE,QAAQ,CAAC,CAAC,CAAC,EACrB,OAAO,CAAC,EAAE,aAAa,CAAC,CAAC,CAAC,KACvB,IAAI,CAAA;AAET,UAAU,mBAAmB;IAC3B,QAAQ,CAAC,EAAE,OAAO,CAAA;CACnB;AAED,wBAAgB,YAAY,CAAC,CAAC,EAC5B,GAAG,EAAE,YAAY,EAAE,EACnB,EAAE,QAAe,EAAE,GAAE,mBAAwB,GAC5C,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,WAAW,CAAC,CAAC,CAAC,CAAC,CAsE7B"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@automerge/automerge-repo-react-hooks",
3
- "version": "2.0.0",
3
+ "version": "2.0.3",
4
4
  "description": "Hooks to access an Automerge Repo from your react app.",
5
5
  "repository": "https://github.com/automerge/automerge-repo/tree/master/packages/automerge-repo-react-hooks",
6
6
  "author": "Peter van Hardenberg <pvh@pvh.ca>",
@@ -15,7 +15,7 @@
15
15
  },
16
16
  "dependencies": {
17
17
  "@automerge/automerge": "^2.2.8",
18
- "@automerge/automerge-repo": "2.0.0",
18
+ "@automerge/automerge-repo": "2.0.3",
19
19
  "eventemitter3": "^5.0.1",
20
20
  "react-usestateref": "^1.0.8"
21
21
  },
@@ -46,5 +46,5 @@
46
46
  "publishConfig": {
47
47
  "access": "public"
48
48
  },
49
- "gitHead": "670d9e0d9d52451453e60a6b64e74c146f1f959c"
49
+ "gitHead": "47bbb8af119776522de99771b095b7351f8d3870"
50
50
  }
@@ -15,13 +15,32 @@ export function useDocHandles<T>(
15
15
  { suspense = false }: UseDocHandlesParams = {}
16
16
  ): DocHandleMap<T> {
17
17
  const repo = useRepo()
18
- const [handleMap, setHandleMap] = useState<DocHandleMap<T>>(() => new Map())
18
+ const [handleMap, setHandleMap] = useState<DocHandleMap<T>>(() => {
19
+ const map = new Map()
20
+
21
+ // Initialize the map with any handles that are ready
22
+ for (const id of ids) {
23
+ let progress
24
+ try {
25
+ progress = repo.findWithProgress<T>(id)
26
+ } catch (e) {
27
+ continue
28
+ }
29
+
30
+ if (progress.state === "ready") {
31
+ map.set(id, progress.handle)
32
+ }
33
+ }
34
+
35
+ return map
36
+ })
19
37
 
20
38
  const pendingPromises: PromiseWrapper<DocHandle<T>>[] = []
21
39
  const nextHandleMap = new Map<AutomergeUrl, DocHandle<T> | undefined>()
22
40
 
23
41
  // Check if we need any new wrappers
24
42
  for (const id of ids) {
43
+ let handle = handleMap.get(id)
25
44
  let wrapper = wrapperCache.get(id)
26
45
  if (!wrapper) {
27
46
  try {
@@ -37,7 +56,7 @@ export function useDocHandles<T>(
37
56
  // Update handleMap with any available handles,
38
57
  // and collect any pending promises
39
58
  try {
40
- const handle = wrapper.read() as DocHandle<T>
59
+ handle ??= wrapper.read() as DocHandle<T>
41
60
  nextHandleMap.set(id, handle)
42
61
  } catch (e) {
43
62
  if (e instanceof Promise) {
@@ -19,7 +19,16 @@ export function useDocuments<T>(
19
19
  { suspense = true }: UseDocumentsOptions = {}
20
20
  ): [DocMap<T>, ChangeDocFn<T>] {
21
21
  const handleMap = useDocHandles<T>(ids, { suspense })
22
- const [docMap, setDocMap] = useState<DocMap<T>>(() => new Map())
22
+ const [docMap, setDocMap] = useState<DocMap<T>>(() => {
23
+ const docs: DocMap<T> = new Map()
24
+ handleMap.forEach(handle => {
25
+ const url = handle?.url
26
+ if (url) {
27
+ docs.set(url, handle?.doc())
28
+ }
29
+ })
30
+ return docs
31
+ })
23
32
 
24
33
  useEffect(() => {
25
34
  const listeners = new Map<AutomergeUrl, () => void>()
@@ -0,0 +1,81 @@
1
+ import React from "react"
2
+
3
+ import { PeerId, Repo } from "@automerge/automerge-repo"
4
+ import "@testing-library/jest-dom"
5
+ import { cleanup } from "@testing-library/react"
6
+ import { afterEach } from "vitest"
7
+ import { RepoContext } from "../src/useRepo"
8
+ import { DummyNetworkAdapter } from "../src/helpers/DummyNetworkAdapter"
9
+
10
+ afterEach(() => {
11
+ cleanup()
12
+ })
13
+
14
+ export interface ExampleDoc {
15
+ foo: string
16
+ counter?: number
17
+ nested?: {
18
+ value: string
19
+ }
20
+ }
21
+
22
+ export function setup() {
23
+ const repo = new Repo({
24
+ peerId: "bob" as PeerId,
25
+ })
26
+
27
+ const handleA = repo.create<ExampleDoc>()
28
+ handleA.change(doc => (doc.foo = "A"))
29
+
30
+ const handleB = repo.create<ExampleDoc>()
31
+ handleB.change(doc => (doc.foo = "B"))
32
+
33
+ const handleC = repo.create<ExampleDoc>()
34
+ handleC.change(doc => (doc.foo = "C"))
35
+
36
+ const wrapper = ({ children }) => {
37
+ return <RepoContext.Provider value={repo}>{children}</RepoContext.Provider>
38
+ }
39
+
40
+ return {
41
+ repo,
42
+ handleA,
43
+ handleB,
44
+ handleC,
45
+ handles: [handleA, handleB, handleC],
46
+ urls: [handleA.url, handleB.url, handleC.url],
47
+ wrapper,
48
+ }
49
+ }
50
+
51
+ export function setupPairedRepos(latency = 10) {
52
+ // Create two connected repos with network delay
53
+ const [adapterCreator, adapterFinder] =
54
+ DummyNetworkAdapter.createConnectedPair({
55
+ latency,
56
+ })
57
+
58
+ const peerIdCreator = "peer-creator" as PeerId
59
+ const peerIdFinder = "peer-finder" as PeerId
60
+ const repoCreator = new Repo({
61
+ peerId: peerIdCreator,
62
+ network: [adapterCreator],
63
+ })
64
+ const repoFinder = new Repo({
65
+ peerId: peerIdFinder,
66
+ network: [adapterFinder],
67
+ })
68
+
69
+ // TODO: dummynetwork adapter should probably take care of this
70
+ // Initialize the network.
71
+ adapterCreator.peerCandidate(peerIdFinder)
72
+ adapterFinder.peerCandidate(peerIdCreator)
73
+
74
+ function Wrapper({ children }) {
75
+ return (
76
+ <RepoContext.Provider value={repoFinder}>{children}</RepoContext.Provider>
77
+ )
78
+ }
79
+
80
+ return { repoCreator, repoFinder, wrapper: Wrapper }
81
+ }
@@ -11,73 +11,14 @@ import "@testing-library/jest-dom"
11
11
 
12
12
  import { describe, expect, it, vi } from "vitest"
13
13
  import { useDocHandle } from "../src/useDocHandle"
14
- import { RepoContext } from "../src/useRepo"
15
14
  import { ErrorBoundary } from "react-error-boundary"
16
- import { DummyNetworkAdapter } from "../src/helpers/DummyNetworkAdapter"
17
-
18
- interface ExampleDoc {
19
- foo: string
20
- }
21
-
22
- function getRepoWrapper(repo: Repo) {
23
- return ({ children }) => (
24
- <RepoContext.Provider value={repo}>{children}</RepoContext.Provider>
25
- )
26
- }
15
+ import { setup, setupPairedRepos } from "./testSetup"
27
16
 
28
17
  describe("useDocHandle", () => {
29
18
  const repo = new Repo({
30
19
  peerId: "bob" as PeerId,
31
20
  })
32
21
 
33
- function setup() {
34
- const handleA = repo.create<ExampleDoc>()
35
- handleA.change(doc => (doc.foo = "A"))
36
-
37
- const handleB = repo.create<ExampleDoc>()
38
- handleB.change(doc => (doc.foo = "B"))
39
-
40
- return {
41
- repo,
42
- handleA,
43
- handleB,
44
- wrapper: getRepoWrapper(repo),
45
- }
46
- }
47
-
48
- function setupPairedRepos(latency = 10) {
49
- // Create two connected repos with network delay
50
- const [adapterCreator, adapterFinder] =
51
- DummyNetworkAdapter.createConnectedPair({
52
- latency,
53
- })
54
-
55
- // TODO: fix types here
56
- const repoCreator = new Repo({
57
- peerId: "peer-creator" as PeerId,
58
- network: [adapterCreator],
59
- })
60
- const repoFinder = new Repo({
61
- peerId: "peer-finder" as PeerId,
62
- network: [adapterFinder],
63
- })
64
-
65
- // TODO: dummynetwork adapter should probably take care of this
66
- // Initialize the network.
67
- adapterCreator.peerCandidate(`peer-finder` as PeerId)
68
- adapterFinder.peerCandidate(`peer-creator` as PeerId)
69
-
70
- const wrapper = ({ children }) => {
71
- return (
72
- <RepoContext.Provider value={repoFinder}>
73
- {children}
74
- </RepoContext.Provider>
75
- )
76
- }
77
-
78
- return { repoCreator, repoFinder, wrapper }
79
- }
80
-
81
22
  const Component = ({
82
23
  url,
83
24
  onHandle,
@@ -167,7 +108,7 @@ describe("useDocHandle", () => {
167
108
  })
168
109
 
169
110
  it("handles slow network correctly", async () => {
170
- const { repoCreator, wrapper } = await setupPairedRepos()
111
+ const { repoCreator, wrapper } = setupPairedRepos()
171
112
  const handleA = repoCreator.create({ foo: "A" })
172
113
  const onHandle = vi.fn()
173
114
 
@@ -2,10 +2,6 @@ import {
2
2
  AutomergeUrl,
3
3
  Doc,
4
4
  generateAutomergeUrl,
5
- PeerId,
6
- Repo,
7
- NetworkAdapter,
8
- Message,
9
5
  } from "@automerge/automerge-repo"
10
6
  import { render, screen, waitFor } from "@testing-library/react"
11
7
  import React, { Suspense } from "react"
@@ -13,74 +9,10 @@ import { describe, expect, it, vi } from "vitest"
13
9
  import "@testing-library/jest-dom"
14
10
 
15
11
  import { useDocument } from "../src/useDocument"
16
- import { RepoContext } from "../src/useRepo"
17
12
  import { ErrorBoundary } from "react-error-boundary"
18
- import { DummyNetworkAdapter, pause } from "../src/helpers/DummyNetworkAdapter"
19
- interface ExampleDoc {
20
- foo: string
21
- }
13
+ import { setup, setupPairedRepos, ExampleDoc } from "./testSetup"
22
14
 
23
15
  describe("useDocument", () => {
24
- function setup() {
25
- const repo = new Repo({
26
- peerId: "bob" as PeerId,
27
- })
28
-
29
- const handleA = repo.create<ExampleDoc>()
30
- handleA.change(doc => (doc.foo = "A"))
31
-
32
- const handleB = repo.create<ExampleDoc>()
33
- handleB.change(doc => (doc.foo = "B"))
34
-
35
- const handleC = repo.create<ExampleDoc>()
36
- handleC.change(doc => (doc.foo = "C"))
37
-
38
- const wrapper = ({ children }) => {
39
- return (
40
- <RepoContext.Provider value={repo}>{children}</RepoContext.Provider>
41
- )
42
- }
43
-
44
- return {
45
- repo,
46
- handleA,
47
- handleB,
48
- handleC,
49
- wrapper,
50
- }
51
- }
52
-
53
- function setupPairedRepos(latency = 10) {
54
- // Create two connected repos with network delay
55
- const [adapterCreator, adapterFinder] =
56
- DummyNetworkAdapter.createConnectedPair({
57
- latency,
58
- })
59
-
60
- const repoCreator = new Repo({
61
- peerId: "peer-creator" as PeerId,
62
- network: [adapterCreator],
63
- })
64
- const repoFinder = new Repo({
65
- peerId: "peer-finder" as PeerId,
66
- network: [adapterFinder],
67
- })
68
-
69
- // TODO: dummynetwork adapter should probably take care of this
70
- // Initialize the network.
71
- adapterCreator.peerCandidate(`peer-finder` as PeerId)
72
- adapterFinder.peerCandidate(`peer-creator` as PeerId)
73
-
74
- const wrapper = ({ children }) => {
75
- return (
76
- <RepoContext.Provider value={repoFinder}>
77
- {children}
78
- </RepoContext.Provider>
79
- )
80
- }
81
-
82
- return { repoCreator, repoFinder, wrapper }
83
- }
84
16
  const Component = ({
85
17
  url,
86
18
  onDoc,
@@ -3,49 +3,10 @@ import { AutomergeUrl, Repo, PeerId } from "@automerge/automerge-repo"
3
3
  import { render, act, waitFor } from "@testing-library/react"
4
4
  import { describe, expect, it, vi } from "vitest"
5
5
  import { useDocuments } from "../src/useDocuments"
6
- import { RepoContext } from "../src/useRepo"
7
6
  import { ErrorBoundary } from "react-error-boundary"
8
-
9
- interface ExampleDoc {
10
- foo: string
11
- counter?: number
12
- nested?: {
13
- value: string
14
- }
15
- }
16
-
17
- function getRepoWrapper(repo: Repo) {
18
- return ({ children }) => (
19
- <RepoContext.Provider value={repo}>{children}</RepoContext.Provider>
20
- )
21
- }
7
+ import { ExampleDoc, setup, setupPairedRepos } from "./testSetup"
22
8
 
23
9
  describe("useDocuments", () => {
24
- const repo = new Repo({
25
- peerId: "bob" as PeerId,
26
- })
27
-
28
- function setup() {
29
- const handleA = repo.create<ExampleDoc>()
30
- handleA.change(doc => (doc.foo = "A"))
31
-
32
- const handleB = repo.create<ExampleDoc>()
33
- handleB.change(doc => (doc.foo = "B"))
34
-
35
- const handleC = repo.create<ExampleDoc>()
36
- handleC.change(doc => (doc.foo = "C"))
37
-
38
- return {
39
- repo,
40
- handleA,
41
- handleB,
42
- handleC,
43
- handles: [handleA, handleB, handleC],
44
- urls: [handleA.url, handleB.url, handleC.url],
45
- wrapper: getRepoWrapper(repo),
46
- }
47
- }
48
-
49
10
  const DocumentsComponent = ({
50
11
  urls,
51
12
  onState,
@@ -235,31 +196,6 @@ describe("useDocuments", () => {
235
196
  })
236
197
 
237
198
  describe("useDocuments with suspense: false", () => {
238
- const repo = new Repo({
239
- peerId: "bob" as PeerId,
240
- })
241
-
242
- function setup() {
243
- const handleA = repo.create<ExampleDoc>()
244
- handleA.change(doc => (doc.foo = "A"))
245
-
246
- const handleB = repo.create<ExampleDoc>()
247
- handleB.change(doc => (doc.foo = "B"))
248
-
249
- const handleC = repo.create<ExampleDoc>()
250
- handleC.change(doc => (doc.foo = "C"))
251
-
252
- return {
253
- repo,
254
- handleA,
255
- handleB,
256
- handleC,
257
- handles: [handleA, handleB, handleC],
258
- urls: [handleA.url, handleB.url, handleC.url],
259
- wrapper: getRepoWrapper(repo),
260
- }
261
- }
262
-
263
199
  const NonSuspendingDocumentsComponent = ({
264
200
  urls,
265
201
  onState,
@@ -272,14 +208,17 @@ describe("useDocuments", () => {
272
208
  return null
273
209
  }
274
210
 
275
- it("should start with empty map and load documents asynchronously", async () => {
276
- const { handleA, wrapper } = setup()
211
+ it("should start with already-loaded documents and load other documents asynchronously", async () => {
212
+ const { repoCreator, repoFinder, wrapper } = setupPairedRepos()
213
+ const handleA = repoFinder.create({ foo: "A" })
214
+ const handleB = repoCreator.create({ foo: "B" })
215
+
277
216
  const onState = vi.fn()
278
217
 
279
218
  const Wrapped = () => (
280
219
  <ErrorBoundary fallback={<div>Error!</div>}>
281
220
  <NonSuspendingDocumentsComponent
282
- urls={[handleA.url]}
221
+ urls={[handleA.url, handleB.url]}
283
222
  onState={onState}
284
223
  />
285
224
  </ErrorBoundary>
@@ -287,23 +226,28 @@ describe("useDocuments", () => {
287
226
 
288
227
  render(<Wrapped />, { wrapper })
289
228
 
290
- // Initial state should be empty map
229
+ // Initial state should include local document
291
230
  expect(onState).toHaveBeenCalled()
292
- let [docs] = onState.mock.lastCall || []
293
- expect(docs.size).toBe(0)
231
+ let docs = onState.mock.lastCall?.[0]
232
+ expect(docs.size).toBe(1)
233
+ expect(docs.get(handleA.url)?.foo).toBe("A")
294
234
 
295
- // Wait for document to load
235
+ // Wait for remote document to load
296
236
  await act(async () => {
297
- await Promise.resolve()
237
+ await repoFinder.find(handleB.url)
298
238
  })
299
239
 
300
- // Document should now be loaded
240
+ // Both should now be loaded
301
241
  docs = onState.mock.lastCall?.[0]
242
+ expect(docs.size).toBe(2)
302
243
  expect(docs.get(handleA.url)?.foo).toBe("A")
244
+ expect(docs.get(handleB.url)?.foo).toBe("B")
303
245
  })
304
246
 
305
247
  it("should handle loading multiple documents asynchronously", async () => {
306
- const { handleA, handleB, wrapper } = setup()
248
+ const { repoCreator, repoFinder, wrapper } = setupPairedRepos()
249
+ const handleA = repoCreator.create({ foo: "A" })
250
+ const handleB = repoCreator.create({ foo: "B" })
307
251
  const onState = vi.fn()
308
252
 
309
253
  const Wrapped = () => (
@@ -318,12 +262,15 @@ describe("useDocuments", () => {
318
262
  render(<Wrapped />, { wrapper })
319
263
 
320
264
  // Initial state should be empty
321
- let [docs] = onState.mock.lastCall || []
265
+ let docs = onState.mock.lastCall?.[0]
322
266
  expect(docs.size).toBe(0)
323
267
 
324
268
  // Wait for documents to load
325
269
  await act(async () => {
326
- await Promise.resolve()
270
+ await Promise.all([
271
+ repoFinder.find(handleA.url),
272
+ repoFinder.find(handleB.url),
273
+ ])
327
274
  })
328
275
 
329
276
  // Check loaded state
@@ -360,7 +307,10 @@ describe("useDocuments", () => {
360
307
  })
361
308
 
362
309
  it("should handle document removal with pending loads", async () => {
363
- const { handleA, handleB, wrapper } = setup()
310
+ const { repoCreator, repoFinder, wrapper } = setupPairedRepos()
311
+ const handleA = repoCreator.create({ foo: "A" })
312
+ const handleB = repoCreator.create({ foo: "B" })
313
+
364
314
  const onState = vi.fn()
365
315
 
366
316
  const Wrapped = ({ urls }: { urls: AutomergeUrl[] }) => (
@@ -375,7 +325,8 @@ describe("useDocuments", () => {
375
325
  )
376
326
 
377
327
  // Initial state should be empty
378
- let [docs] = onState.mock.lastCall || []
328
+ let docs = onState.mock.lastCall?.[0]
329
+ expect(docs).toBeDefined()
379
330
  expect(docs.size).toBe(0)
380
331
 
381
332
  // Remove one document before load completes
@@ -383,7 +334,10 @@ describe("useDocuments", () => {
383
334
 
384
335
  // Wait for remaining document to load
385
336
  await act(async () => {
386
- await Promise.resolve()
337
+ await Promise.all([
338
+ repoFinder.find(handleA.url),
339
+ repoFinder.find(handleB.url),
340
+ ])
387
341
  })
388
342
 
389
343
  // Should only have loaded the remaining document
@@ -395,10 +349,11 @@ describe("useDocuments", () => {
395
349
  })
396
350
  })
397
351
 
398
- it("should cleanup listeners when unmounting with pending loads", async () => {
399
- const { handleA, wrapper } = setup()
352
+ it("should clean up listeners when unmounting with pending loads", async () => {
353
+ const { repoCreator, repoFinder, wrapper } = setupPairedRepos()
400
354
  const onState = vi.fn()
401
355
 
356
+ const handleA = repoCreator.create({ foo: "bar" })
402
357
  const Wrapped = () => (
403
358
  <ErrorBoundary fallback={<div>Error!</div>}>
404
359
  <NonSuspendingDocumentsComponent
@@ -417,13 +372,14 @@ describe("useDocuments", () => {
417
372
  unmount()
418
373
 
419
374
  // Wait for what would have been load completion
375
+ let finderHandleA
420
376
  await act(async () => {
421
- await Promise.resolve()
377
+ finderHandleA = await repoFinder.find(handleA.url)
422
378
  })
423
379
 
424
380
  // Should not have received any updates after unmount
425
381
  const callCount = onState.mock.calls.length
426
- handleA.change(doc => (doc.foo = "Changed after unmount"))
382
+ finderHandleA.change(doc => (doc.foo = "Changed after unmount"))
427
383
  expect(onState.mock.calls.length).toBe(callCount)
428
384
  })
429
385
 
@@ -475,7 +431,8 @@ describe("useDocuments", () => {
475
431
  render(<Wrapped />, { wrapper })
476
432
 
477
433
  // Initial state empty
478
- let [docs] = onState.mock.lastCall || []
434
+ let docs = onState.mock.lastCall?.[0]
435
+ expect(docs).toBeDefined()
479
436
  expect(docs.size).toBe(0)
480
437
 
481
438
  // Should remain empty after attempted load
@@ -484,6 +441,7 @@ describe("useDocuments", () => {
484
441
  })
485
442
 
486
443
  docs = onState.mock.lastCall?.[0]
444
+ expect(docs).toBeDefined()
487
445
  expect(docs.size).toBe(0)
488
446
  })
489
447
  })
package/vitest.config.ts CHANGED
@@ -4,7 +4,7 @@ import path from "path"
4
4
  export default defineConfig({
5
5
  test: {
6
6
  globals: true,
7
- setupFiles: [path.join(__dirname, "./test/testSetup.ts")],
7
+ setupFiles: [path.join(__dirname, "./test/testSetup.tsx")],
8
8
  environment: "jsdom",
9
9
  coverage: {
10
10
  provider: "v8",
package/test/testSetup.ts DELETED
@@ -1,7 +0,0 @@
1
- import "@testing-library/jest-dom"
2
- import { cleanup } from "@testing-library/react"
3
- import { afterEach } from "vitest"
4
-
5
- afterEach(() => {
6
- cleanup()
7
- })