@peerbit/react 0.0.33 → 0.0.35
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/src/index.d.ts +5 -0
- package/dist/src/index.d.ts.map +1 -0
- package/dist/src/index.js +5 -0
- package/dist/src/index.js.map +1 -0
- package/{lib/esm → dist/src}/lockstorage.d.ts +1 -0
- package/dist/src/lockstorage.d.ts.map +1 -0
- package/{lib/esm → dist/src}/lockstorage.js +2 -5
- package/dist/src/lockstorage.js.map +1 -0
- package/{lib/esm → dist/src}/useMount.d.ts +1 -0
- package/dist/src/useMount.d.ts.map +1 -0
- package/dist/src/useMount.js.map +1 -0
- package/{lib/esm → dist/src}/usePeer.d.ts +7 -3
- package/dist/src/usePeer.d.ts.map +1 -0
- package/{lib/esm → dist/src}/usePeer.js +25 -20
- package/dist/src/usePeer.js.map +1 -0
- package/{lib/esm → dist/src}/utils.d.ts +2 -4
- package/dist/src/utils.d.ts.map +1 -0
- package/{lib/esm → dist/src}/utils.js +17 -69
- package/dist/src/utils.js.map +1 -0
- package/package.json +59 -56
- package/src/index.ts +4 -14
- package/src/lockstorage.ts +224 -233
- package/src/useMount.ts +15 -0
- package/src/usePeer.tsx +406 -419
- package/src/utils.ts +99 -168
- package/README.md +0 -24
- package/lib/esm/__tests__/lockstorage.test.d.ts +0 -1
- package/lib/esm/__tests__/lockstorage.test.js +0 -237
- package/lib/esm/__tests__/lockstorage.test.js.map +0 -1
- package/lib/esm/__tests__/singletonLock.test.d.ts +0 -1
- package/lib/esm/__tests__/singletonLock.test.js +0 -71
- package/lib/esm/__tests__/singletonLock.test.js.map +0 -1
- package/lib/esm/__tests__/useQuery.dom.test.d.ts +0 -1
- package/lib/esm/__tests__/useQuery.dom.test.js +0 -433
- package/lib/esm/__tests__/useQuery.dom.test.js.map +0 -1
- package/lib/esm/__tests__/utils.test.d.ts +0 -1
- package/lib/esm/__tests__/utils.test.js +0 -66
- package/lib/esm/__tests__/utils.test.js.map +0 -1
- package/lib/esm/index.d.ts +0 -8
- package/lib/esm/index.js +0 -9
- package/lib/esm/index.js.map +0 -1
- package/lib/esm/lockstorage.js.map +0 -1
- package/lib/esm/useCount.d.ts +0 -11
- package/lib/esm/useCount.js +0 -43
- package/lib/esm/useCount.js.map +0 -1
- package/lib/esm/useLocal.d.ts +0 -20
- package/lib/esm/useLocal.js +0 -73
- package/lib/esm/useLocal.js.map +0 -1
- package/lib/esm/useMount.js.map +0 -1
- package/lib/esm/useOnline.d.ts +0 -11
- package/lib/esm/useOnline.js +0 -65
- package/lib/esm/useOnline.js.map +0 -1
- package/lib/esm/usePeer.js.map +0 -1
- package/lib/esm/useProgram.d.ts +0 -16
- package/lib/esm/useProgram.js +0 -114
- package/lib/esm/useProgram.js.map +0 -1
- package/lib/esm/useQuery.d.ts +0 -49
- package/lib/esm/useQuery.js +0 -418
- package/lib/esm/useQuery.js.map +0 -1
- package/lib/esm/utils.js.map +0 -1
- package/src/__tests__/lockstorage.test.ts +0 -285
- package/src/__tests__/singletonLock.test.ts +0 -85
- package/src/__tests__/useQuery.dom.test.ts +0 -518
- package/src/__tests__/utils.test.ts +0 -90
- package/src/useCount.tsx +0 -63
- package/src/useLocal.tsx +0 -125
- package/src/useMount.tsx +0 -15
- package/src/useOnline.tsx +0 -85
- package/src/useProgram.tsx +0 -148
- package/src/useQuery.tsx +0 -548
- /package/{lib/esm → dist/src}/useMount.js +0 -0
|
@@ -1,518 +0,0 @@
|
|
|
1
|
-
import { describe, it, beforeEach, afterEach, expect } from "vitest";
|
|
2
|
-
import { Peerbit } from "peerbit";
|
|
3
|
-
import { field, variant } from "@dao-xyz/borsh";
|
|
4
|
-
import { Documents, WithContext, WithIndexedContext } from "@peerbit/document";
|
|
5
|
-
import { Program } from "@peerbit/program";
|
|
6
|
-
import React, { useEffect } from "react";
|
|
7
|
-
import { render, act, waitFor } from "@testing-library/react";
|
|
8
|
-
import { useQuery, UseQuerySharedOptions } from "../useQuery.js";
|
|
9
|
-
import sodium from "libsodium-wrappers";
|
|
10
|
-
|
|
11
|
-
// Minimal Post model and Program with Documents for integration-like tests
|
|
12
|
-
@variant(0)
|
|
13
|
-
class Post {
|
|
14
|
-
@field({ type: "string" })
|
|
15
|
-
id!: string;
|
|
16
|
-
@field({ type: "string" })
|
|
17
|
-
message!: string;
|
|
18
|
-
constructor(props?: { id?: string; message?: string }) {
|
|
19
|
-
if (!props) return; // borsh
|
|
20
|
-
this.id = props.id ?? `${Date.now()}-${Math.random()}`;
|
|
21
|
-
this.message = props.message ?? "";
|
|
22
|
-
}
|
|
23
|
-
}
|
|
24
|
-
@variant(0)
|
|
25
|
-
class PostIndexed {
|
|
26
|
-
@field({ type: "string" })
|
|
27
|
-
id!: string;
|
|
28
|
-
@field({ type: "string" })
|
|
29
|
-
indexedMessage!: string;
|
|
30
|
-
constructor(props?: Post) {
|
|
31
|
-
if (!props) return; // borsh
|
|
32
|
-
this.id = props.id ?? `${Date.now()}-${Math.random()}`;
|
|
33
|
-
this.indexedMessage = props.message ?? "";
|
|
34
|
-
}
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
@variant("posts-db")
|
|
38
|
-
class PostsDB extends Program<{ replicate?: boolean }> {
|
|
39
|
-
@field({ type: Documents })
|
|
40
|
-
posts: Documents<Post, PostIndexed>;
|
|
41
|
-
constructor() {
|
|
42
|
-
super();
|
|
43
|
-
this.posts = new Documents<Post, PostIndexed>();
|
|
44
|
-
}
|
|
45
|
-
async open(args?: { replicate?: boolean }): Promise<void> {
|
|
46
|
-
await this.posts.open({
|
|
47
|
-
type: Post,
|
|
48
|
-
index: { type: PostIndexed },
|
|
49
|
-
replicate: args?.replicate ? { factor: 1 } : false,
|
|
50
|
-
});
|
|
51
|
-
}
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
describe("useQuery (integration with Documents)", () => {
|
|
55
|
-
let peerWriter: Peerbit;
|
|
56
|
-
let peerReader: Peerbit;
|
|
57
|
-
let peerReader2: Peerbit | undefined;
|
|
58
|
-
let dbWriter: PostsDB;
|
|
59
|
-
let dbReader: PostsDB;
|
|
60
|
-
let dbReader2: PostsDB | undefined;
|
|
61
|
-
let autoUnmount: undefined | (() => void);
|
|
62
|
-
|
|
63
|
-
beforeEach(async () => {
|
|
64
|
-
await sodium.ready;
|
|
65
|
-
peerWriter = await Peerbit.create();
|
|
66
|
-
peerReader = await Peerbit.create();
|
|
67
|
-
peerReader2 = undefined;
|
|
68
|
-
dbReader2 = undefined;
|
|
69
|
-
});
|
|
70
|
-
const setupConnected = async () => {
|
|
71
|
-
await peerWriter.dial(peerReader);
|
|
72
|
-
dbWriter = await peerWriter.open(new PostsDB(), {
|
|
73
|
-
existing: "reuse",
|
|
74
|
-
args: { replicate: true },
|
|
75
|
-
});
|
|
76
|
-
dbReader = await peerReader.open<PostsDB>(dbWriter.address, {
|
|
77
|
-
args: { replicate: false },
|
|
78
|
-
});
|
|
79
|
-
// ensure reader knows about writer as replicator for the log
|
|
80
|
-
await dbReader.posts.log.waitForReplicator(
|
|
81
|
-
peerWriter.identity.publicKey
|
|
82
|
-
);
|
|
83
|
-
};
|
|
84
|
-
|
|
85
|
-
const setupDisconnected = async () => {
|
|
86
|
-
dbWriter = await peerWriter.open(new PostsDB(), {
|
|
87
|
-
existing: "reuse",
|
|
88
|
-
args: { replicate: true },
|
|
89
|
-
});
|
|
90
|
-
dbReader = await peerReader.open<PostsDB>(dbWriter.clone(), {
|
|
91
|
-
args: { replicate: false },
|
|
92
|
-
});
|
|
93
|
-
};
|
|
94
|
-
|
|
95
|
-
afterEach(async () => {
|
|
96
|
-
// Unmount React trees before tearing down peers
|
|
97
|
-
autoUnmount?.();
|
|
98
|
-
autoUnmount = undefined;
|
|
99
|
-
await peerWriter?.stop();
|
|
100
|
-
await peerReader?.stop();
|
|
101
|
-
await peerReader2?.stop();
|
|
102
|
-
});
|
|
103
|
-
|
|
104
|
-
function renderUseQuery<R extends boolean>(
|
|
105
|
-
db: PostsDB,
|
|
106
|
-
options: UseQuerySharedOptions<Post, PostIndexed, R>
|
|
107
|
-
) {
|
|
108
|
-
const result: {
|
|
109
|
-
current: ReturnType<typeof useQuery<Post, PostIndexed, R>>;
|
|
110
|
-
} = {} as any;
|
|
111
|
-
|
|
112
|
-
function HookCmp({
|
|
113
|
-
opts,
|
|
114
|
-
}: {
|
|
115
|
-
opts: UseQuerySharedOptions<Post, PostIndexed, R>;
|
|
116
|
-
}) {
|
|
117
|
-
const hook = useQuery<Post, PostIndexed, R>(db.posts, opts);
|
|
118
|
-
useEffect(() => {
|
|
119
|
-
result.current = hook;
|
|
120
|
-
}, [hook]);
|
|
121
|
-
return null;
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
const api = render(React.createElement(HookCmp, { opts: options }));
|
|
125
|
-
const rerender = (opts: UseQuerySharedOptions<Post, PostIndexed, R>) =>
|
|
126
|
-
api.rerender(React.createElement(HookCmp, { opts }));
|
|
127
|
-
let hasUnmounted = false;
|
|
128
|
-
const doUnmount = () => {
|
|
129
|
-
if (hasUnmounted) return;
|
|
130
|
-
hasUnmounted = true;
|
|
131
|
-
api.unmount();
|
|
132
|
-
if (autoUnmount === doUnmount) {
|
|
133
|
-
// clear outer reference if we still own it
|
|
134
|
-
autoUnmount = undefined;
|
|
135
|
-
}
|
|
136
|
-
};
|
|
137
|
-
// Expose to outer afterEach so tests don't need to remember calling unmount
|
|
138
|
-
autoUnmount = doUnmount;
|
|
139
|
-
return { result, rerender, unmount: doUnmount };
|
|
140
|
-
}
|
|
141
|
-
|
|
142
|
-
it("local query", async () => {
|
|
143
|
-
await setupConnected();
|
|
144
|
-
await dbWriter.posts.put(new Post({ message: "hello" }));
|
|
145
|
-
const { result } = renderUseQuery(dbWriter, {
|
|
146
|
-
query: {},
|
|
147
|
-
resolve: true,
|
|
148
|
-
local: true,
|
|
149
|
-
prefetch: true,
|
|
150
|
-
});
|
|
151
|
-
await waitFor(() => expect(result.current?.items?.length ?? 0).toBe(1));
|
|
152
|
-
|
|
153
|
-
await act(async () => {
|
|
154
|
-
expect(result.current.items.length).toBe(1);
|
|
155
|
-
expect(result.current.items[0].message).toBe("hello");
|
|
156
|
-
});
|
|
157
|
-
});
|
|
158
|
-
|
|
159
|
-
it("does not mutate the options object passed in", async () => {
|
|
160
|
-
await setupConnected();
|
|
161
|
-
const cfg = {
|
|
162
|
-
query: {},
|
|
163
|
-
resolve: true,
|
|
164
|
-
local: true,
|
|
165
|
-
remote: { reach: { eager: true }, wait: { timeout: 10_000 } },
|
|
166
|
-
prefetch: false,
|
|
167
|
-
batchSize: 10,
|
|
168
|
-
};
|
|
169
|
-
const cfgOrg = { ...cfg };
|
|
170
|
-
renderUseQuery(dbReader, cfg);
|
|
171
|
-
// expect that cfg has not been modified
|
|
172
|
-
expect(cfg).to.deep.equal(cfgOrg);
|
|
173
|
-
});
|
|
174
|
-
|
|
175
|
-
it("respects remote warmup before iterating", async () => {
|
|
176
|
-
await setupConnected();
|
|
177
|
-
await dbWriter.posts.put(new Post({ message: "hello" }));
|
|
178
|
-
|
|
179
|
-
const cfg: UseQuerySharedOptions<Post, PostIndexed, true> = {
|
|
180
|
-
query: {},
|
|
181
|
-
resolve: true,
|
|
182
|
-
local: true,
|
|
183
|
-
remote: { reach: { eager: true }, wait: { timeout: 10_000 } },
|
|
184
|
-
prefetch: false,
|
|
185
|
-
batchSize: 10,
|
|
186
|
-
};
|
|
187
|
-
const { result, rerender } = renderUseQuery(dbReader, cfg);
|
|
188
|
-
|
|
189
|
-
await waitFor(() => {
|
|
190
|
-
if (!result.current) throw new Error("no result yet");
|
|
191
|
-
return true;
|
|
192
|
-
});
|
|
193
|
-
|
|
194
|
-
expect(result.current.items.length).toBe(0);
|
|
195
|
-
|
|
196
|
-
await act(async () => {
|
|
197
|
-
await result.current.loadMore();
|
|
198
|
-
});
|
|
199
|
-
|
|
200
|
-
expect(result.current.items.length).toBe(1);
|
|
201
|
-
expect(result.current.items[0].message).toBe("hello");
|
|
202
|
-
|
|
203
|
-
await act(async () => {
|
|
204
|
-
rerender(cfg);
|
|
205
|
-
});
|
|
206
|
-
await act(async () => {
|
|
207
|
-
await result.current.loadMore();
|
|
208
|
-
});
|
|
209
|
-
await waitFor(() => expect(result.current.items.length).toBe(1));
|
|
210
|
-
});
|
|
211
|
-
|
|
212
|
-
it("honors remote.wait.timeout by resolving after connection", async () => {
|
|
213
|
-
// create isolated peers not connected yet
|
|
214
|
-
await setupDisconnected();
|
|
215
|
-
|
|
216
|
-
const { result } = renderUseQuery(dbReader, {
|
|
217
|
-
query: {},
|
|
218
|
-
resolve: true,
|
|
219
|
-
local: false,
|
|
220
|
-
remote: {
|
|
221
|
-
reach: { eager: true },
|
|
222
|
-
wait: { behavior: "block", timeout: 5_000 },
|
|
223
|
-
},
|
|
224
|
-
prefetch: true,
|
|
225
|
-
});
|
|
226
|
-
|
|
227
|
-
await waitFor(() => expect(result.current).toBeDefined());
|
|
228
|
-
|
|
229
|
-
// Now connect and write
|
|
230
|
-
|
|
231
|
-
await act(async () => {
|
|
232
|
-
await dbReader.node.dial(dbWriter.node.getMultiaddrs());
|
|
233
|
-
await dbWriter.posts.put(new Post({ message: "late" }));
|
|
234
|
-
await dbReader.posts.log.waitForReplicator(
|
|
235
|
-
dbWriter.node.identity.publicKey
|
|
236
|
-
);
|
|
237
|
-
});
|
|
238
|
-
|
|
239
|
-
await waitFor(() => expect(result.current.items.length).toBe(1));
|
|
240
|
-
expect(result.current.items[0].message).toBe("late");
|
|
241
|
-
});
|
|
242
|
-
|
|
243
|
-
it("pushes remote writes from replicator to non-replicator", async () => {
|
|
244
|
-
await setupConnected();
|
|
245
|
-
|
|
246
|
-
const { result } = renderUseQuery(dbReader, {
|
|
247
|
-
query: {},
|
|
248
|
-
resolve: true,
|
|
249
|
-
local: false,
|
|
250
|
-
remote: { reach: { eager: true } },
|
|
251
|
-
updates: { merge: true },
|
|
252
|
-
prefetch: false,
|
|
253
|
-
});
|
|
254
|
-
|
|
255
|
-
await waitFor(() => {
|
|
256
|
-
expect(result.current).toBeDefined();
|
|
257
|
-
});
|
|
258
|
-
|
|
259
|
-
expect(result.current.items.length).toBe(0);
|
|
260
|
-
|
|
261
|
-
await act(async () => {
|
|
262
|
-
await dbWriter.posts.put(new Post({ message: "replicator-push" }));
|
|
263
|
-
});
|
|
264
|
-
|
|
265
|
-
await act(async () => {
|
|
266
|
-
await result.current.loadMore();
|
|
267
|
-
});
|
|
268
|
-
|
|
269
|
-
await waitFor(
|
|
270
|
-
() =>
|
|
271
|
-
expect(
|
|
272
|
-
result.current.items.map((p) => (p as Post).message)
|
|
273
|
-
).toContain("replicator-push"),
|
|
274
|
-
{ timeout: 10_000 }
|
|
275
|
-
);
|
|
276
|
-
});
|
|
277
|
-
|
|
278
|
-
it("fanouts pushed updates to multiple observers", async () => {
|
|
279
|
-
await setupConnected();
|
|
280
|
-
|
|
281
|
-
peerReader2 = await Peerbit.create();
|
|
282
|
-
await peerReader2.dial(peerWriter);
|
|
283
|
-
dbReader2 = await peerReader2.open<PostsDB>(dbWriter.address, {
|
|
284
|
-
args: { replicate: false },
|
|
285
|
-
});
|
|
286
|
-
|
|
287
|
-
await dbReader2.posts.log.waitForReplicator(
|
|
288
|
-
peerWriter.identity.publicKey
|
|
289
|
-
);
|
|
290
|
-
|
|
291
|
-
const hookOne = renderUseQuery(dbReader, {
|
|
292
|
-
query: {},
|
|
293
|
-
resolve: true,
|
|
294
|
-
local: false,
|
|
295
|
-
remote: { reach: { eager: true } },
|
|
296
|
-
updates: { merge: true },
|
|
297
|
-
prefetch: false,
|
|
298
|
-
});
|
|
299
|
-
|
|
300
|
-
const hookTwo = renderUseQuery(dbReader2, {
|
|
301
|
-
query: {},
|
|
302
|
-
resolve: true,
|
|
303
|
-
local: false,
|
|
304
|
-
remote: { reach: { eager: true } },
|
|
305
|
-
updates: { merge: true },
|
|
306
|
-
prefetch: false,
|
|
307
|
-
});
|
|
308
|
-
|
|
309
|
-
await waitFor(() => {
|
|
310
|
-
expect(hookOne.result.current).toBeDefined();
|
|
311
|
-
expect(hookTwo.result.current).toBeDefined();
|
|
312
|
-
});
|
|
313
|
-
|
|
314
|
-
await act(async () => {
|
|
315
|
-
await dbWriter.posts.put(new Post({ message: "broadcast" }));
|
|
316
|
-
});
|
|
317
|
-
|
|
318
|
-
await act(async () => {
|
|
319
|
-
await hookOne.result.current.loadMore();
|
|
320
|
-
await hookTwo.result.current.loadMore();
|
|
321
|
-
});
|
|
322
|
-
|
|
323
|
-
await waitFor(
|
|
324
|
-
() =>
|
|
325
|
-
expect(
|
|
326
|
-
hookOne.result.current.items.some(
|
|
327
|
-
(p) => (p as Post).message === "broadcast"
|
|
328
|
-
)
|
|
329
|
-
).toBe(true),
|
|
330
|
-
{ timeout: 10_000 }
|
|
331
|
-
);
|
|
332
|
-
|
|
333
|
-
await waitFor(
|
|
334
|
-
() =>
|
|
335
|
-
expect(
|
|
336
|
-
hookTwo.result.current.items.some(
|
|
337
|
-
(p) => (p as Post).message === "broadcast"
|
|
338
|
-
)
|
|
339
|
-
).toBe(true),
|
|
340
|
-
{ timeout: 10_000 }
|
|
341
|
-
);
|
|
342
|
-
|
|
343
|
-
await act(async () => {
|
|
344
|
-
await dbReader.posts.put(new Post({ message: "observer-origin" }));
|
|
345
|
-
});
|
|
346
|
-
|
|
347
|
-
await act(async () => {
|
|
348
|
-
await hookOne.result.current.loadMore();
|
|
349
|
-
await hookTwo.result.current.loadMore();
|
|
350
|
-
});
|
|
351
|
-
|
|
352
|
-
await waitFor(
|
|
353
|
-
() =>
|
|
354
|
-
expect(
|
|
355
|
-
hookTwo.result.current.items.some(
|
|
356
|
-
(p) => (p as Post).message === "observer-origin"
|
|
357
|
-
)
|
|
358
|
-
).toBe(true),
|
|
359
|
-
{ timeout: 10_000 }
|
|
360
|
-
);
|
|
361
|
-
|
|
362
|
-
hookOne.unmount();
|
|
363
|
-
hookTwo.unmount();
|
|
364
|
-
});
|
|
365
|
-
|
|
366
|
-
describe("merge", () => {
|
|
367
|
-
const checkAsResolvedResults = async <R extends boolean>(
|
|
368
|
-
out: ReturnType<typeof renderUseQuery<R>>,
|
|
369
|
-
resolved: R
|
|
370
|
-
) => {
|
|
371
|
-
const { result } = out;
|
|
372
|
-
await waitFor(() => expect(result.current).toBeDefined());
|
|
373
|
-
|
|
374
|
-
// Initially empty
|
|
375
|
-
expect(result.current.items.length).toBe(0);
|
|
376
|
-
|
|
377
|
-
// Create a post on writer and expect reader hook to merge it automatically
|
|
378
|
-
const id = `${Date.now()}-merge`;
|
|
379
|
-
await act(async () => {
|
|
380
|
-
// the reader actually does the put (a user)
|
|
381
|
-
await dbReader.posts.put(new Post({ id, message: "first" }));
|
|
382
|
-
});
|
|
383
|
-
|
|
384
|
-
await waitFor(() => expect(result.current.items.length).toBe(1), {
|
|
385
|
-
timeout: 1e4,
|
|
386
|
-
});
|
|
387
|
-
if (resolved) {
|
|
388
|
-
expect((result.current.items[0] as Post).message).toBe("first");
|
|
389
|
-
expect(result.current.items[0]).to.be.instanceOf(Post);
|
|
390
|
-
} else {
|
|
391
|
-
expect(
|
|
392
|
-
(result.current.items[0] as PostIndexed).indexedMessage
|
|
393
|
-
).toBe("first");
|
|
394
|
-
expect(result.current.items[0]).to.be.instanceOf(PostIndexed);
|
|
395
|
-
}
|
|
396
|
-
};
|
|
397
|
-
|
|
398
|
-
it("updates.merge merges new writes into state without manual iteration, as resolved", async () => {
|
|
399
|
-
await setupConnected();
|
|
400
|
-
|
|
401
|
-
// resolved undefined means we should resolve
|
|
402
|
-
await checkAsResolvedResults(
|
|
403
|
-
renderUseQuery<true>(dbReader, {
|
|
404
|
-
query: {},
|
|
405
|
-
local: false,
|
|
406
|
-
remote: { reach: { eager: true } },
|
|
407
|
-
prefetch: false,
|
|
408
|
-
updates: { merge: true },
|
|
409
|
-
}),
|
|
410
|
-
true
|
|
411
|
-
);
|
|
412
|
-
|
|
413
|
-
// resolved true means we should resolve
|
|
414
|
-
await checkAsResolvedResults(
|
|
415
|
-
renderUseQuery<true>(dbReader, {
|
|
416
|
-
query: {},
|
|
417
|
-
local: false,
|
|
418
|
-
resolve: true,
|
|
419
|
-
remote: { reach: { eager: true } },
|
|
420
|
-
prefetch: false,
|
|
421
|
-
updates: { merge: true },
|
|
422
|
-
}),
|
|
423
|
-
true
|
|
424
|
-
);
|
|
425
|
-
|
|
426
|
-
// resolved false means we should NOT resolve
|
|
427
|
-
await checkAsResolvedResults(
|
|
428
|
-
renderUseQuery<false>(dbReader, {
|
|
429
|
-
query: {},
|
|
430
|
-
local: false,
|
|
431
|
-
resolve: false,
|
|
432
|
-
remote: { reach: { eager: true } },
|
|
433
|
-
prefetch: false,
|
|
434
|
-
updates: { merge: true },
|
|
435
|
-
}),
|
|
436
|
-
false
|
|
437
|
-
);
|
|
438
|
-
});
|
|
439
|
-
});
|
|
440
|
-
|
|
441
|
-
/*
|
|
442
|
-
it("updates.merge reflects document mutation in hook state", async () => {
|
|
443
|
-
await setupConnected();
|
|
444
|
-
|
|
445
|
-
const id = `${Date.now()}-mut`;
|
|
446
|
-
await dbWriter.posts.put(new Post({ id, message: "v1" }));
|
|
447
|
-
|
|
448
|
-
const { result } = renderUseQuery(dbReader, {
|
|
449
|
-
query: {},
|
|
450
|
-
resolve: true,
|
|
451
|
-
local: false,
|
|
452
|
-
remote: { reach: { eager: true } },
|
|
453
|
-
prefetch: true,
|
|
454
|
-
updates: { merge: true },
|
|
455
|
-
});
|
|
456
|
-
|
|
457
|
-
await waitFor(() => expect(result.current.items.length).toBe(1), {
|
|
458
|
-
timeout: 1e4,
|
|
459
|
-
});
|
|
460
|
-
expect(result.current.items[0].message).toBe("v1");
|
|
461
|
-
|
|
462
|
-
// Mutate by putting a new version with the same id
|
|
463
|
-
await act(async () => {
|
|
464
|
-
// the reader actually does the put (a user)
|
|
465
|
-
await dbReader.posts.put(new Post({ id, message: "v2" }));
|
|
466
|
-
});
|
|
467
|
-
|
|
468
|
-
// Expect the hook state to reflect the updated content
|
|
469
|
-
await waitFor(
|
|
470
|
-
() => {
|
|
471
|
-
const found = result.current.items.find((p) => p.id === id);
|
|
472
|
-
expect(found?.message).toBe("v2");
|
|
473
|
-
},
|
|
474
|
-
{ timeout: 1e4 }
|
|
475
|
-
);
|
|
476
|
-
});
|
|
477
|
-
*/
|
|
478
|
-
it("clears results when props change (e.g. reverse toggled)", async () => {
|
|
479
|
-
await setupConnected();
|
|
480
|
-
await dbWriter.posts.put(new Post({ message: "one" }));
|
|
481
|
-
await dbWriter.posts.put(new Post({ message: "two" }));
|
|
482
|
-
|
|
483
|
-
const { result, rerender } = renderUseQuery(dbReader, {
|
|
484
|
-
query: {},
|
|
485
|
-
resolve: true,
|
|
486
|
-
local: true,
|
|
487
|
-
remote: false,
|
|
488
|
-
prefetch: true,
|
|
489
|
-
reverse: false,
|
|
490
|
-
});
|
|
491
|
-
|
|
492
|
-
await waitFor(() =>
|
|
493
|
-
expect(result.current.items.length).toBeGreaterThan(0)
|
|
494
|
-
);
|
|
495
|
-
|
|
496
|
-
// Toggle a prop that triggers iterator rebuild
|
|
497
|
-
await act(async () => {
|
|
498
|
-
rerender({
|
|
499
|
-
query: {},
|
|
500
|
-
resolve: true,
|
|
501
|
-
local: true,
|
|
502
|
-
remote: false,
|
|
503
|
-
prefetch: false,
|
|
504
|
-
reverse: true,
|
|
505
|
-
});
|
|
506
|
-
});
|
|
507
|
-
|
|
508
|
-
// After reset we expect cleared results until re-fetched
|
|
509
|
-
await waitFor(() => expect(result.current.items.length).toBe(0));
|
|
510
|
-
|
|
511
|
-
await act(async () => {
|
|
512
|
-
await result.current.loadMore();
|
|
513
|
-
});
|
|
514
|
-
await waitFor(() =>
|
|
515
|
-
expect(result.current.items.length).toBeGreaterThan(0)
|
|
516
|
-
);
|
|
517
|
-
});
|
|
518
|
-
});
|
|
@@ -1,90 +0,0 @@
|
|
|
1
|
-
import { getAllKeyPairs, getFreeKeypair, releaseKey } from "../utils.js";
|
|
2
|
-
import nodelocalstorage from "node-localstorage";
|
|
3
|
-
import { FastMutex } from "../lockstorage.js";
|
|
4
|
-
import { delay } from "@peerbit/time";
|
|
5
|
-
import { default as sodium } from "libsodium-wrappers";
|
|
6
|
-
import { v4 as uuid } from "uuid";
|
|
7
|
-
import { expect } from "chai";
|
|
8
|
-
import { beforeAll, afterAll, describe, it } from "vitest";
|
|
9
|
-
|
|
10
|
-
describe("getKeypair", () => {
|
|
11
|
-
beforeAll(async () => {
|
|
12
|
-
await sodium.ready;
|
|
13
|
-
|
|
14
|
-
var LocalStorage = nodelocalstorage.LocalStorage;
|
|
15
|
-
var localStorage = new LocalStorage("./tmp/getKeypair");
|
|
16
|
-
globalThis.localStorage = localStorage;
|
|
17
|
-
});
|
|
18
|
-
|
|
19
|
-
afterAll(() => {
|
|
20
|
-
globalThis.localStorage.clear();
|
|
21
|
-
});
|
|
22
|
-
|
|
23
|
-
it("can aquire multiple keypairs", async () => {
|
|
24
|
-
let timeout = 1000;
|
|
25
|
-
let mutex = new FastMutex({ localStorage, timeout });
|
|
26
|
-
let lock = true;
|
|
27
|
-
const lockCondition = () => lock;
|
|
28
|
-
let id = uuid();
|
|
29
|
-
const { key: keypair, path: path1 } = await getFreeKeypair(
|
|
30
|
-
id,
|
|
31
|
-
mutex,
|
|
32
|
-
lockCondition
|
|
33
|
-
);
|
|
34
|
-
const { key: keypair2, path: path2 } = await getFreeKeypair(id, mutex);
|
|
35
|
-
expect(keypair!.equals(keypair2!)).to.be.false;
|
|
36
|
-
expect(path1).not.to.eq(path2);
|
|
37
|
-
lock = false;
|
|
38
|
-
await delay(timeout);
|
|
39
|
-
const { path: path3, key: keypair3 } = await getFreeKeypair(id, mutex);
|
|
40
|
-
expect(path3).to.eq(path1);
|
|
41
|
-
expect(keypair3.equals(keypair)).to.be.true;
|
|
42
|
-
|
|
43
|
-
const allKeypair = await getAllKeyPairs(id);
|
|
44
|
-
expect(allKeypair.map((x) => x.publicKey.hashcode())).to.deep.eq([
|
|
45
|
-
keypair3.publicKey.hashcode(),
|
|
46
|
-
keypair2.publicKey.hashcode(),
|
|
47
|
-
]);
|
|
48
|
-
});
|
|
49
|
-
|
|
50
|
-
it("can release if same id", async () => {
|
|
51
|
-
let timeout = 1000;
|
|
52
|
-
let mutex = new FastMutex({ localStorage, timeout });
|
|
53
|
-
let lock = true;
|
|
54
|
-
const lockCondition = () => lock;
|
|
55
|
-
let id = uuid();
|
|
56
|
-
const { key: keypair, path: path1 } = await getFreeKeypair(
|
|
57
|
-
id,
|
|
58
|
-
mutex,
|
|
59
|
-
lockCondition,
|
|
60
|
-
{ releaseLockIfSameId: true }
|
|
61
|
-
);
|
|
62
|
-
const { key: keypair2, path: path2 } = await getFreeKeypair(
|
|
63
|
-
id,
|
|
64
|
-
mutex,
|
|
65
|
-
undefined,
|
|
66
|
-
{ releaseLockIfSameId: true }
|
|
67
|
-
);
|
|
68
|
-
expect(keypair!.equals(keypair2!)).to.be.true;
|
|
69
|
-
expect(path1).to.eq(path2);
|
|
70
|
-
const allKeypair = await getAllKeyPairs(id);
|
|
71
|
-
expect(allKeypair).to.have.length(1);
|
|
72
|
-
});
|
|
73
|
-
|
|
74
|
-
it("releases manually", async () => {
|
|
75
|
-
let timeout = 1000;
|
|
76
|
-
let mutex = new FastMutex({ localStorage, timeout });
|
|
77
|
-
const id = uuid();
|
|
78
|
-
|
|
79
|
-
const { key: keypair, path: path1 } = await getFreeKeypair(id, mutex);
|
|
80
|
-
|
|
81
|
-
const { key: keypair2, path: path2 } = await getFreeKeypair(id, mutex);
|
|
82
|
-
|
|
83
|
-
expect(path1).not.to.eq(path2);
|
|
84
|
-
releaseKey(path1, mutex);
|
|
85
|
-
expect(mutex.getLockedInfo(path1)).to.be.undefined;
|
|
86
|
-
const { key: keypair3, path: path3 } = await getFreeKeypair(id, mutex);
|
|
87
|
-
|
|
88
|
-
expect(path1).to.eq(path3); // we can now acquire key at path1 again, since we released it
|
|
89
|
-
});
|
|
90
|
-
});
|
package/src/useCount.tsx
DELETED
|
@@ -1,63 +0,0 @@
|
|
|
1
|
-
import { ClosedError, Documents, WithContext } from "@peerbit/document";
|
|
2
|
-
import { useEffect, useRef, useState } from "react";
|
|
3
|
-
import * as indexerTypes from "@peerbit/indexer-interface";
|
|
4
|
-
import { debounceLeadingTrailing } from "./utils.js";
|
|
5
|
-
|
|
6
|
-
type QueryOptons = {
|
|
7
|
-
query: indexerTypes.Query[] | indexerTypes.QueryLike;
|
|
8
|
-
id?: string;
|
|
9
|
-
};
|
|
10
|
-
export const useCount = <T extends Record<string, any>>(
|
|
11
|
-
db?: Documents<T, any, any>,
|
|
12
|
-
options?: {
|
|
13
|
-
debounce?: number;
|
|
14
|
-
debug?: boolean; // add debug option here
|
|
15
|
-
} & QueryOptons
|
|
16
|
-
) => {
|
|
17
|
-
const [count, setCount] = useState<number>(0);
|
|
18
|
-
const countRef = useRef<number>(0);
|
|
19
|
-
|
|
20
|
-
useEffect(() => {
|
|
21
|
-
if (!db || db.closed) {
|
|
22
|
-
return;
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
const _l = async (args?: any) => {
|
|
26
|
-
try {
|
|
27
|
-
const count = await db.count({
|
|
28
|
-
query: options?.query,
|
|
29
|
-
approximate: true,
|
|
30
|
-
});
|
|
31
|
-
countRef.current = count;
|
|
32
|
-
setCount(count);
|
|
33
|
-
} catch (error) {
|
|
34
|
-
if (error instanceof ClosedError) {
|
|
35
|
-
return;
|
|
36
|
-
}
|
|
37
|
-
throw error;
|
|
38
|
-
}
|
|
39
|
-
};
|
|
40
|
-
|
|
41
|
-
const debounced = debounceLeadingTrailing(
|
|
42
|
-
_l,
|
|
43
|
-
options?.debounce ?? 1000
|
|
44
|
-
);
|
|
45
|
-
|
|
46
|
-
const handleChange = () => {
|
|
47
|
-
debounced();
|
|
48
|
-
};
|
|
49
|
-
|
|
50
|
-
debounced();
|
|
51
|
-
db.events.addEventListener("change", handleChange);
|
|
52
|
-
|
|
53
|
-
return () => {
|
|
54
|
-
db.events.removeEventListener("change", handleChange);
|
|
55
|
-
debounced.cancel();
|
|
56
|
-
};
|
|
57
|
-
}, [
|
|
58
|
-
db?.closed ? undefined : db?.rootAddress,
|
|
59
|
-
options?.id ?? options?.query,
|
|
60
|
-
]);
|
|
61
|
-
|
|
62
|
-
return count;
|
|
63
|
-
};
|