@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 +155 -138
- package/dist/useDocHandles.d.ts.map +1 -1
- package/dist/useDocuments.d.ts.map +1 -1
- package/package.json +3 -3
- package/src/useDocHandles.ts +21 -2
- package/src/useDocuments.ts +10 -1
- package/test/testSetup.tsx +81 -0
- package/test/useDocHandle.test.tsx +2 -61
- package/test/useDocument.test.tsx +1 -69
- package/test/useDocuments.test.tsx +43 -85
- package/vitest.config.ts +1 -1
- package/test/testSetup.ts +0 -7
package/dist/index.js
CHANGED
|
@@ -1,22 +1,22 @@
|
|
|
1
|
-
import q, { createContext as I, useContext as j, useRef as $, useState as
|
|
2
|
-
function O(
|
|
3
|
-
let l = "pending", r,
|
|
4
|
-
const h =
|
|
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",
|
|
9
|
+
l = "error", i = p;
|
|
10
10
|
}
|
|
11
11
|
);
|
|
12
12
|
return {
|
|
13
|
-
promise:
|
|
13
|
+
promise: o,
|
|
14
14
|
read() {
|
|
15
15
|
switch (l) {
|
|
16
16
|
case "pending":
|
|
17
17
|
throw h;
|
|
18
18
|
case "error":
|
|
19
|
-
throw
|
|
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
|
|
29
|
-
if (!
|
|
30
|
-
return
|
|
28
|
+
const o = j(F);
|
|
29
|
+
if (!o) throw new Error("Repo was not found on RepoContext.");
|
|
30
|
+
return o;
|
|
31
31
|
}
|
|
32
|
-
const
|
|
33
|
-
function N(
|
|
34
|
-
const r = k(),
|
|
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 (
|
|
37
|
-
const t = r.findWithProgress(
|
|
36
|
+
if (o && !u) {
|
|
37
|
+
const t = r.findWithProgress(o);
|
|
38
38
|
t.state === "ready" && (u = t.handle);
|
|
39
39
|
}
|
|
40
|
-
let n =
|
|
41
|
-
if (!n &&
|
|
42
|
-
|
|
43
|
-
const t = r.find(
|
|
44
|
-
n = O(t),
|
|
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(
|
|
55
|
-
const r = N(
|
|
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 ${
|
|
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,
|
|
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
|
|
76
|
+
return i ? [i, n] : [void 0, () => {
|
|
77
77
|
}];
|
|
78
78
|
}
|
|
79
|
-
function
|
|
80
|
-
const r = k(), [
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
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
|
-
|
|
86
|
-
|
|
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
|
-
|
|
92
|
-
|
|
93
|
-
|
|
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,
|
|
119
|
+
}, [l, o]), l && p.length > 0)
|
|
109
120
|
throw Promise.all(p.map((n) => n.promise));
|
|
110
|
-
return
|
|
121
|
+
return i;
|
|
111
122
|
}
|
|
112
|
-
function V(
|
|
113
|
-
const r =
|
|
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((
|
|
120
|
-
const c = new Map(
|
|
136
|
+
h((s) => {
|
|
137
|
+
const c = new Map(s);
|
|
121
138
|
return c.set(t, n.doc()), c;
|
|
122
139
|
});
|
|
123
140
|
};
|
|
124
|
-
h((
|
|
125
|
-
const c = new Map(
|
|
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 [
|
|
165
|
+
return [i, p];
|
|
149
166
|
}
|
|
150
|
-
function H(
|
|
151
|
-
return
|
|
167
|
+
function H(o) {
|
|
168
|
+
return o && o.__esModule && Object.prototype.hasOwnProperty.call(o, "default") ? o.default : o;
|
|
152
169
|
}
|
|
153
|
-
var C,
|
|
154
|
-
function
|
|
155
|
-
if (
|
|
156
|
-
|
|
157
|
-
var
|
|
158
|
-
return typeof
|
|
159
|
-
}, r = function(
|
|
160
|
-
var h =
|
|
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 =
|
|
184
|
+
var z = T();
|
|
168
185
|
const L = /* @__PURE__ */ H(z);
|
|
169
|
-
var D = { exports: {} },
|
|
186
|
+
var D = { exports: {} }, R;
|
|
170
187
|
function B() {
|
|
171
|
-
return
|
|
188
|
+
return R || (R = 1, function(o) {
|
|
172
189
|
var l = Object.prototype.hasOwnProperty, r = "~";
|
|
173
|
-
function
|
|
190
|
+
function i() {
|
|
174
191
|
}
|
|
175
|
-
Object.create && (
|
|
176
|
-
function h(t, e,
|
|
177
|
-
this.fn = t, this.context = e, this.once =
|
|
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,
|
|
180
|
-
if (typeof
|
|
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(
|
|
183
|
-
return t._events[
|
|
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
|
|
203
|
+
--t._eventsCount === 0 ? t._events = new i() : delete t._events[e];
|
|
187
204
|
}
|
|
188
205
|
function n() {
|
|
189
|
-
this._events = new
|
|
206
|
+
this._events = new i(), this._eventsCount = 0;
|
|
190
207
|
}
|
|
191
208
|
n.prototype.eventNames = function() {
|
|
192
|
-
var e = [],
|
|
209
|
+
var e = [], s, c;
|
|
193
210
|
if (this._eventsCount === 0) return e;
|
|
194
|
-
for (c in
|
|
195
|
-
l.call(
|
|
196
|
-
return Object.getOwnPropertySymbols ? e.concat(Object.getOwnPropertySymbols(
|
|
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
|
|
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,
|
|
202
|
-
|
|
203
|
-
return
|
|
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
|
|
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,
|
|
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,
|
|
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,
|
|
233
|
+
return a.fn.call(a.context, s), !0;
|
|
217
234
|
case 3:
|
|
218
|
-
return a.fn.call(a.context,
|
|
235
|
+
return a.fn.call(a.context, s, c), !0;
|
|
219
236
|
case 4:
|
|
220
|
-
return a.fn.call(a.context,
|
|
237
|
+
return a.fn.call(a.context, s, c, w), !0;
|
|
221
238
|
case 5:
|
|
222
|
-
return a.fn.call(a.context,
|
|
239
|
+
return a.fn.call(a.context, s, c, w, v), !0;
|
|
223
240
|
case 6:
|
|
224
|
-
return a.fn.call(a.context,
|
|
241
|
+
return a.fn.call(a.context, s, c, w, v, f), !0;
|
|
225
242
|
}
|
|
226
|
-
for (d = 1,
|
|
227
|
-
|
|
228
|
-
a.fn.apply(a.context,
|
|
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,
|
|
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,
|
|
254
|
+
a[d].fn.call(a[d].context, s);
|
|
238
255
|
break;
|
|
239
256
|
case 3:
|
|
240
|
-
a[d].fn.call(a[d].context,
|
|
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,
|
|
260
|
+
a[d].fn.call(a[d].context, s, c, w);
|
|
244
261
|
break;
|
|
245
262
|
default:
|
|
246
|
-
if (!
|
|
247
|
-
|
|
248
|
-
a[d].fn.apply(a[d].context,
|
|
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,
|
|
253
|
-
return p(this, e,
|
|
254
|
-
}, n.prototype.once = function(e,
|
|
255
|
-
return p(this, e,
|
|
256
|
-
}, n.prototype.removeListener = function(e,
|
|
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 (!
|
|
276
|
+
if (!s)
|
|
260
277
|
return u(this, v), this;
|
|
261
|
-
var
|
|
262
|
-
if (
|
|
263
|
-
|
|
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 =
|
|
266
|
-
(
|
|
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
|
|
272
|
-
return e ? (
|
|
273
|
-
}, n.prototype.off = n.prototype.removeListener, n.prototype.addListener = n.prototype.on, n.prefixed = r, n.EventEmitter = 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:
|
|
295
|
+
handle: o,
|
|
279
296
|
localUserId: l,
|
|
280
297
|
offlineTimeout: r = 3e4,
|
|
281
|
-
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
|
|
286
|
-
const [
|
|
287
|
-
|
|
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
|
-
[
|
|
306
|
+
[f]: m
|
|
290
307
|
}), t({
|
|
291
308
|
...e.current,
|
|
292
|
-
[
|
|
309
|
+
[f]: i()
|
|
293
310
|
}));
|
|
294
311
|
}, c = () => {
|
|
295
|
-
const v = u.current,
|
|
296
|
-
for (const a in
|
|
297
|
-
m -
|
|
298
|
-
p(v), t(
|
|
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
|
-
|
|
317
|
+
o.on("ephemeral-message", s);
|
|
301
318
|
const w = setInterval(
|
|
302
319
|
c,
|
|
303
320
|
r
|
|
304
321
|
);
|
|
305
322
|
return () => {
|
|
306
|
-
|
|
323
|
+
o.removeListener("ephemeral-message", s), clearInterval(w);
|
|
307
324
|
};
|
|
308
|
-
}, [
|
|
325
|
+
}, [o, l, r, i]), [h, n];
|
|
309
326
|
}, Y = ({
|
|
310
|
-
handle:
|
|
327
|
+
handle: o,
|
|
311
328
|
userId: l,
|
|
312
329
|
initialState: r,
|
|
313
|
-
heartbeatTime:
|
|
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),
|
|
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
|
|
339
|
+
const t = () => void o.broadcast([l, u.current]);
|
|
323
340
|
t();
|
|
324
|
-
const e = setInterval(t,
|
|
341
|
+
const e = setInterval(t, i);
|
|
325
342
|
return () => void clearInterval(e);
|
|
326
|
-
}, [
|
|
343
|
+
}, [o, l, i]), y(() => {
|
|
327
344
|
let t;
|
|
328
345
|
const e = M.on("new_peer", () => {
|
|
329
346
|
t = setTimeout(
|
|
330
|
-
() =>
|
|
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
|
-
}, [
|
|
355
|
+
}, [o, l, M]), [h, n];
|
|
339
356
|
};
|
|
340
357
|
export {
|
|
341
358
|
F as RepoContext,
|
|
342
359
|
N as useDocHandle,
|
|
343
|
-
|
|
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,
|
|
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,
|
|
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.
|
|
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.
|
|
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": "
|
|
49
|
+
"gitHead": "47bbb8af119776522de99771b095b7351f8d3870"
|
|
50
50
|
}
|
package/src/useDocHandles.ts
CHANGED
|
@@ -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>>(() =>
|
|
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
|
-
|
|
59
|
+
handle ??= wrapper.read() as DocHandle<T>
|
|
41
60
|
nextHandleMap.set(id, handle)
|
|
42
61
|
} catch (e) {
|
|
43
62
|
if (e instanceof Promise) {
|
package/src/useDocuments.ts
CHANGED
|
@@ -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>>(() =>
|
|
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 {
|
|
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 } =
|
|
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 {
|
|
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
|
|
276
|
-
const {
|
|
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
|
|
229
|
+
// Initial state should include local document
|
|
291
230
|
expect(onState).toHaveBeenCalled()
|
|
292
|
-
let
|
|
293
|
-
expect(docs.size).toBe(
|
|
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
|
|
237
|
+
await repoFinder.find(handleB.url)
|
|
298
238
|
})
|
|
299
239
|
|
|
300
|
-
//
|
|
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 {
|
|
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
|
|
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.
|
|
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 {
|
|
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
|
|
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.
|
|
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
|
|
399
|
-
const {
|
|
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
|
|
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
|
-
|
|
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
|
|
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