@peerbit/react 0.0.31 → 0.0.33
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/lib/esm/__tests__/useQuery.dom.test.d.ts +1 -0
- package/lib/esm/__tests__/useQuery.dom.test.js +433 -0
- package/lib/esm/__tests__/useQuery.dom.test.js.map +1 -0
- package/lib/esm/usePeer.d.ts +1 -1
- package/lib/esm/usePeer.js +35 -11
- package/lib/esm/usePeer.js.map +1 -1
- package/lib/esm/useQuery.d.ts +6 -17
- package/lib/esm/useQuery.js +269 -76
- package/lib/esm/useQuery.js.map +1 -1
- package/package.json +74 -73
- package/src/__tests__/useQuery.dom.test.ts +518 -0
- package/src/useLocal.tsx +1 -1
- package/src/useOnline.tsx +1 -1
- package/src/usePeer.tsx +42 -19
- package/src/useProgram.tsx +1 -1
- package/src/useQuery.tsx +321 -113
- package/src/utils.ts +1 -1
package/src/useQuery.tsx
CHANGED
|
@@ -1,16 +1,18 @@
|
|
|
1
1
|
import { useState, useEffect, useRef, useMemo } from "react";
|
|
2
2
|
import {
|
|
3
|
+
AbstractSearchRequest,
|
|
4
|
+
AbstractSearchResult,
|
|
3
5
|
ClosedError,
|
|
6
|
+
Context,
|
|
4
7
|
Documents,
|
|
5
|
-
|
|
8
|
+
RemoteQueryOptions,
|
|
6
9
|
ResultsIterator,
|
|
7
10
|
WithContext,
|
|
8
11
|
} from "@peerbit/document";
|
|
9
12
|
import * as indexerTypes from "@peerbit/indexer-interface";
|
|
10
|
-
import { AbortError } from "@peerbit/time";
|
|
11
|
-
import { NoPeersError } from "@peerbit/shared-log";
|
|
12
13
|
import { v4 as uuid } from "uuid";
|
|
13
14
|
import { WithIndexedContext } from "@peerbit/document";
|
|
15
|
+
import { UpdateOptions } from "@peerbit/document";
|
|
14
16
|
|
|
15
17
|
type QueryOptions = { query: QueryLike; id?: string };
|
|
16
18
|
|
|
@@ -26,38 +28,39 @@ export type QueryLike = {
|
|
|
26
28
|
* All the non-DB-specific options supported by the original single-DB hook.
|
|
27
29
|
* They stay fully backward-compatible.
|
|
28
30
|
*/
|
|
29
|
-
export type UseQuerySharedOptions<
|
|
31
|
+
export type UseQuerySharedOptions<
|
|
32
|
+
T,
|
|
33
|
+
I,
|
|
34
|
+
R extends boolean | undefined,
|
|
35
|
+
RT = R extends false ? WithContext<I> : WithIndexedContext<T, I>,
|
|
36
|
+
> = {
|
|
30
37
|
/* original behavioural flags */
|
|
31
38
|
resolve?: R;
|
|
32
39
|
transform?: (r: RT) => Promise<RT>;
|
|
33
40
|
debounce?: number;
|
|
34
|
-
debug?: boolean |
|
|
41
|
+
debug?: boolean | string;
|
|
35
42
|
reverse?: boolean;
|
|
36
43
|
batchSize?: number;
|
|
37
44
|
prefetch?: boolean;
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
45
|
+
/* onChange?: {
|
|
46
|
+
merge?:
|
|
47
|
+
| boolean
|
|
48
|
+
| ((
|
|
49
|
+
c: DocumentsChange<T, I>
|
|
50
|
+
) =>
|
|
51
|
+
| DocumentsChange<T, I>
|
|
52
|
+
| Promise<DocumentsChange<T, I>>
|
|
53
|
+
| undefined);
|
|
54
|
+
update?: (
|
|
55
|
+
prev: RT[],
|
|
56
|
+
change: DocumentsChange<T, I>
|
|
57
|
+
) => RT[] | Promise<RT[]>;
|
|
58
|
+
}; */
|
|
59
|
+
updates?: UpdateOptions<T, I, R>;
|
|
53
60
|
local?: boolean;
|
|
54
61
|
remote?:
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
warmup?: number;
|
|
58
|
-
joining?: { waitFor?: number };
|
|
59
|
-
eager?: boolean;
|
|
60
|
-
};
|
|
62
|
+
| boolean
|
|
63
|
+
| RemoteQueryOptions<AbstractSearchRequest, AbstractSearchResult, any>;
|
|
61
64
|
} & QueryOptions;
|
|
62
65
|
|
|
63
66
|
/* ────────────────────────── Main Hook ────────────────────────── */
|
|
@@ -74,7 +77,7 @@ export const useQuery = <
|
|
|
74
77
|
T extends Record<string, any>,
|
|
75
78
|
I extends Record<string, any>,
|
|
76
79
|
R extends boolean | undefined = true,
|
|
77
|
-
RT = R extends false ? WithContext<I> : WithIndexedContext<T, I
|
|
80
|
+
RT = R extends false ? WithContext<I> : WithIndexedContext<T, I>,
|
|
78
81
|
>(
|
|
79
82
|
/** Single DB or list of DBs. 100 % backward-compatible with the old single param. */
|
|
80
83
|
dbOrDbs: Documents<T, I> | Documents<T, I>[] | undefined,
|
|
@@ -82,6 +85,12 @@ export const useQuery = <
|
|
|
82
85
|
) => {
|
|
83
86
|
/* ─────── internal type alias for convenience ─────── */
|
|
84
87
|
type Item = RT;
|
|
88
|
+
type IteratorRef = {
|
|
89
|
+
id: string;
|
|
90
|
+
db: Documents<T, I>;
|
|
91
|
+
iterator: ResultsIterator<Item>;
|
|
92
|
+
itemsConsumed: number;
|
|
93
|
+
};
|
|
85
94
|
|
|
86
95
|
/* ────────────── normalise DBs input ────────────── */
|
|
87
96
|
const dbs = useMemo<(Documents<T, I> | undefined)[]>(() => {
|
|
@@ -94,14 +103,8 @@ export const useQuery = <
|
|
|
94
103
|
const [all, setAll] = useState<Item[]>([]);
|
|
95
104
|
const allRef = useRef<Item[]>([]);
|
|
96
105
|
const [isLoading, setIsLoading] = useState(false);
|
|
97
|
-
const iteratorRefs = useRef<
|
|
98
|
-
|
|
99
|
-
id: string;
|
|
100
|
-
db: Documents<T, I>;
|
|
101
|
-
iterator: ResultsIterator<Item>;
|
|
102
|
-
itemsConsumed: number;
|
|
103
|
-
}[]
|
|
104
|
-
>([]);
|
|
106
|
+
const iteratorRefs = useRef<IteratorRef[]>([]);
|
|
107
|
+
const itemIdRef = useRef(new WeakMap<object, string>());
|
|
105
108
|
const emptyResultsRef = useRef(false);
|
|
106
109
|
const closeControllerRef = useRef<AbortController | null>(null);
|
|
107
110
|
const waitedOnceRef = useRef(false);
|
|
@@ -118,7 +121,7 @@ export const useQuery = <
|
|
|
118
121
|
const log = (...a: any[]) => {
|
|
119
122
|
if (!options.debug) return;
|
|
120
123
|
if (typeof options.debug === "boolean") console.log(...a);
|
|
121
|
-
else console.log(options.debug
|
|
124
|
+
else console.log(options.debug, ...a);
|
|
122
125
|
};
|
|
123
126
|
|
|
124
127
|
const updateAll = (combined: Item[]) => {
|
|
@@ -127,15 +130,16 @@ export const useQuery = <
|
|
|
127
130
|
};
|
|
128
131
|
|
|
129
132
|
const reset = () => {
|
|
130
|
-
iteratorRefs.current
|
|
133
|
+
iteratorRefs.current?.forEach(({ iterator }) => iterator.close());
|
|
131
134
|
iteratorRefs.current = [];
|
|
132
135
|
|
|
133
|
-
closeControllerRef.current?.abort();
|
|
136
|
+
closeControllerRef.current?.abort(new Error("Reset"));
|
|
134
137
|
closeControllerRef.current = new AbortController();
|
|
135
138
|
emptyResultsRef.current = false;
|
|
136
139
|
waitedOnceRef.current = false;
|
|
137
140
|
|
|
138
141
|
allRef.current = [];
|
|
142
|
+
itemIdRef.current = new WeakMap();
|
|
139
143
|
setAll([]);
|
|
140
144
|
setIsLoading(false);
|
|
141
145
|
log("Iterators reset");
|
|
@@ -156,24 +160,104 @@ export const useQuery = <
|
|
|
156
160
|
|
|
157
161
|
reset();
|
|
158
162
|
const abortSignal = closeControllerRef.current?.signal;
|
|
163
|
+
const onMissedResults = (evt: { amount: number }) => {
|
|
164
|
+
console.error("Not effective yet: missed results", evt);
|
|
165
|
+
/* if (allRef.current.length > 0 || typeof options.remote !== "object" || !options.updates) {
|
|
166
|
+
return;
|
|
167
|
+
}
|
|
168
|
+
console.log("Missed results, loading more", evt.amount);
|
|
169
|
+
loadMore(evt.amount); */
|
|
170
|
+
};
|
|
171
|
+
let draining = false;
|
|
172
|
+
const scheduleDrain = (ref: ResultsIterator<RT>, amount: number) => {
|
|
173
|
+
log("Schedule drain", draining, ref, amount);
|
|
174
|
+
if (draining) return;
|
|
175
|
+
draining = true;
|
|
176
|
+
loadMore(amount)
|
|
177
|
+
.catch((e) => {
|
|
178
|
+
if (!(e instanceof ClosedError)) throw e;
|
|
179
|
+
})
|
|
180
|
+
.finally(() => {
|
|
181
|
+
draining = false;
|
|
182
|
+
});
|
|
183
|
+
};
|
|
159
184
|
|
|
160
185
|
iteratorRefs.current = openDbs.map((db) => {
|
|
186
|
+
let currentRef: IteratorRef | undefined;
|
|
161
187
|
const iterator = db.index.iterate(query ?? {}, {
|
|
188
|
+
closePolicy: "manual",
|
|
162
189
|
local: options.local ?? true,
|
|
163
|
-
remote: options.remote
|
|
190
|
+
remote: options.remote
|
|
191
|
+
? {
|
|
192
|
+
...(typeof options?.remote === "object"
|
|
193
|
+
? {
|
|
194
|
+
...options.remote,
|
|
195
|
+
onLateResults: onMissedResults,
|
|
196
|
+
wait: {
|
|
197
|
+
...options?.remote?.wait,
|
|
198
|
+
timeout:
|
|
199
|
+
options?.remote?.wait?.timeout ??
|
|
200
|
+
5000,
|
|
201
|
+
},
|
|
202
|
+
}
|
|
203
|
+
: options?.remote
|
|
204
|
+
? {
|
|
205
|
+
onLateResults: onMissedResults,
|
|
206
|
+
}
|
|
207
|
+
: undefined),
|
|
208
|
+
}
|
|
209
|
+
: undefined,
|
|
164
210
|
resolve,
|
|
165
211
|
signal: abortSignal,
|
|
212
|
+
updates: {
|
|
213
|
+
push: true,
|
|
214
|
+
merge:
|
|
215
|
+
typeof options.updates === "boolean" && options.updates
|
|
216
|
+
? true
|
|
217
|
+
: typeof options.updates === "object" &&
|
|
218
|
+
options.updates.merge
|
|
219
|
+
? true
|
|
220
|
+
: false,
|
|
221
|
+
onChange: (evt) => {
|
|
222
|
+
log("Live update", evt);
|
|
223
|
+
if (evt.added.length > 0) {
|
|
224
|
+
scheduleDrain(
|
|
225
|
+
iterator as ResultsIterator<RT>,
|
|
226
|
+
evt.added.length
|
|
227
|
+
);
|
|
228
|
+
}
|
|
229
|
+
},
|
|
230
|
+
onResults: (batch, props) => {
|
|
231
|
+
log("onResults", { batch, props, currentRef: !!currentRef });
|
|
232
|
+
if (
|
|
233
|
+
props.reason === "join" ||
|
|
234
|
+
props.reason === "change"
|
|
235
|
+
) {
|
|
236
|
+
if (!currentRef) return;
|
|
237
|
+
handleBatch(iteratorRefs.current, [
|
|
238
|
+
{ ref: currentRef, items: batch as Item[] },
|
|
239
|
+
]);
|
|
240
|
+
}
|
|
241
|
+
},
|
|
242
|
+
},
|
|
166
243
|
}) as ResultsIterator<Item>;
|
|
167
244
|
|
|
168
|
-
const ref = {
|
|
245
|
+
const ref: IteratorRef = {
|
|
246
|
+
id: uuid(),
|
|
247
|
+
db,
|
|
248
|
+
iterator,
|
|
249
|
+
itemsConsumed: 0,
|
|
250
|
+
};
|
|
251
|
+
currentRef = ref;
|
|
169
252
|
log("Iterator init", ref.id, "db", db.address);
|
|
170
253
|
return ref;
|
|
171
254
|
});
|
|
172
255
|
|
|
173
|
-
/* prefetch if requested */
|
|
174
|
-
if (options.prefetch) void loadMore();
|
|
175
256
|
/* store a deterministic id (useful for external keys) */
|
|
176
257
|
setId(uuid());
|
|
258
|
+
|
|
259
|
+
/* prefetch if requested */
|
|
260
|
+
if (options.prefetch) void loadMore();
|
|
177
261
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
178
262
|
}, [
|
|
179
263
|
dbs.map((d) => d?.address).join("|"),
|
|
@@ -195,82 +279,200 @@ export const useQuery = <
|
|
|
195
279
|
waitedOnceRef.current = true;
|
|
196
280
|
};
|
|
197
281
|
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
282
|
+
/* helper to turn primitive ids into stable map keys */
|
|
283
|
+
const idToKey = (value: indexerTypes.IdPrimitive): string => {
|
|
284
|
+
switch (typeof value) {
|
|
285
|
+
case "string":
|
|
286
|
+
return `s:${value}`;
|
|
287
|
+
case "number":
|
|
288
|
+
return `n:${value}`;
|
|
289
|
+
default:
|
|
290
|
+
return `b:${value.toString()}`;
|
|
291
|
+
}
|
|
292
|
+
};
|
|
201
293
|
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
294
|
+
const handleBatch = async (
|
|
295
|
+
iterators: IteratorRef[],
|
|
296
|
+
batches: { ref: IteratorRef; items: Item[] }[]
|
|
297
|
+
): Promise<boolean> => {
|
|
298
|
+
if (!iterators.length) {
|
|
299
|
+
log("No iterators in handleBatch");
|
|
300
|
+
return false;
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
const totalFetched = batches.reduce(
|
|
304
|
+
(sum, batch) => sum + batch.items.length,
|
|
305
|
+
0
|
|
306
|
+
);
|
|
307
|
+
if (totalFetched === 0) {
|
|
308
|
+
log("No items fetched");
|
|
309
|
+
emptyResultsRef.current = iterators.every((i) => i.iterator.done());
|
|
310
|
+
return !emptyResultsRef.current;
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
let processed = batches;
|
|
314
|
+
if (options.transform) {
|
|
315
|
+
const transform = options.transform;
|
|
316
|
+
processed = await Promise.all(
|
|
317
|
+
batches.map(async ({ ref, items }) => ({
|
|
318
|
+
ref,
|
|
319
|
+
items: await Promise.all(items.map(transform)),
|
|
320
|
+
}))
|
|
321
|
+
);
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
const prev = allRef.current;
|
|
325
|
+
const next = [...prev];
|
|
326
|
+
const keyIndex = new Map<string, number>();
|
|
327
|
+
prev.forEach((item, idx) => {
|
|
328
|
+
const key = itemIdRef.current.get(item as object);
|
|
329
|
+
if (key) keyIndex.set(key, idx);
|
|
330
|
+
});
|
|
331
|
+
|
|
332
|
+
const seenHeads = new Set(prev.map((x) => (x as any).__context?.head));
|
|
333
|
+
const freshItems: Item[] = [];
|
|
334
|
+
let hasMutations = false;
|
|
335
|
+
|
|
336
|
+
log("Processing batches", { processed, keyIndex });
|
|
337
|
+
for (const { ref, items } of processed) {
|
|
338
|
+
const db = ref.db;
|
|
339
|
+
for (const item of items) {
|
|
340
|
+
const ctx = (item as WithContext<any>).__context;
|
|
341
|
+
const head = ctx?.head;
|
|
342
|
+
|
|
343
|
+
let key: string | null = null;
|
|
344
|
+
try {
|
|
345
|
+
key = idToKey(
|
|
346
|
+
db.index.resolveId(
|
|
347
|
+
item as WithContext<I> | WithIndexedContext<T, I>
|
|
348
|
+
).primitive
|
|
227
349
|
);
|
|
350
|
+
} catch (error) {
|
|
351
|
+
log("useQuery: failed to resolve id", error);
|
|
228
352
|
}
|
|
229
|
-
markWaited();
|
|
230
|
-
}
|
|
231
353
|
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
354
|
+
if (key && keyIndex.has(key)) {
|
|
355
|
+
const existingIndex = keyIndex.get(key)!;
|
|
356
|
+
const current = next[existingIndex];
|
|
357
|
+
const currentContext: Context | undefined = (
|
|
358
|
+
current as WithContext<any>
|
|
359
|
+
)?.__context;
|
|
360
|
+
const incomingContext: Context | undefined = ctx;
|
|
361
|
+
const shouldReplace =
|
|
362
|
+
!currentContext ||
|
|
363
|
+
!incomingContext ||
|
|
364
|
+
currentContext.modified <= incomingContext.modified;
|
|
365
|
+
|
|
366
|
+
if (shouldReplace && current !== item) {
|
|
367
|
+
itemIdRef.current.delete(current as object);
|
|
368
|
+
next[existingIndex] = item;
|
|
369
|
+
hasMutations = true;
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
if (key) {
|
|
373
|
+
itemIdRef.current.set(item as object, key);
|
|
374
|
+
keyIndex.set(key, existingIndex);
|
|
375
|
+
}
|
|
376
|
+
if (head != null) seenHeads.add(head);
|
|
377
|
+
continue;
|
|
240
378
|
}
|
|
241
|
-
}
|
|
242
379
|
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
i.iterator.done()
|
|
246
|
-
);
|
|
247
|
-
return !emptyResultsRef.current;
|
|
248
|
-
}
|
|
380
|
+
if (head != null && seenHeads.has(head)) continue;
|
|
381
|
+
if (head != null) seenHeads.add(head);
|
|
249
382
|
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
383
|
+
freshItems.push(item);
|
|
384
|
+
if (key) {
|
|
385
|
+
itemIdRef.current.set(item as object, key);
|
|
386
|
+
keyIndex.set(key, prev.length + freshItems.length - 1);
|
|
387
|
+
}
|
|
254
388
|
}
|
|
389
|
+
}
|
|
255
390
|
|
|
256
|
-
/* deduplicate & merge */
|
|
257
|
-
const prev = allRef.current;
|
|
258
|
-
const dedupHeads = new Set(
|
|
259
|
-
prev.map((x) => (x as any).__context.head)
|
|
260
|
-
);
|
|
261
|
-
const unique = processed.filter(
|
|
262
|
-
(x) => !dedupHeads.has((x as any).__context.head)
|
|
263
|
-
);
|
|
264
|
-
if (!unique.length)
|
|
265
|
-
return !iterators.every((i) => i.iterator.done());
|
|
266
|
-
|
|
267
|
-
const combined = reverseRef.current
|
|
268
|
-
? [...unique.reverse(), ...prev]
|
|
269
|
-
: [...prev, ...unique];
|
|
270
|
-
updateAll(combined);
|
|
271
391
|
|
|
392
|
+
if (!freshItems.length && !hasMutations) {
|
|
272
393
|
emptyResultsRef.current = iterators.every((i) => i.iterator.done());
|
|
394
|
+
log("No new items or mutations");
|
|
273
395
|
return !emptyResultsRef.current;
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
|
|
399
|
+
const combined = reverseRef.current
|
|
400
|
+
? [...freshItems.reverse(), ...next]
|
|
401
|
+
: [...next, ...freshItems];
|
|
402
|
+
|
|
403
|
+
log("Updating all with", {
|
|
404
|
+
prevLength: prev.length,
|
|
405
|
+
freshLength: freshItems.length,
|
|
406
|
+
combinedLength: combined.length,
|
|
407
|
+
});
|
|
408
|
+
updateAll(combined);
|
|
409
|
+
|
|
410
|
+
emptyResultsRef.current = iterators.every((i) => i.iterator.done());
|
|
411
|
+
return !emptyResultsRef.current;
|
|
412
|
+
};
|
|
413
|
+
|
|
414
|
+
const drainRoundRobin = async (
|
|
415
|
+
iterators: IteratorRef[],
|
|
416
|
+
n: number
|
|
417
|
+
): Promise<boolean> => {
|
|
418
|
+
const batches: { ref: IteratorRef; items: Item[] }[] = [];
|
|
419
|
+
for (const ref of iterators) {
|
|
420
|
+
if (ref.iterator.done()) continue;
|
|
421
|
+
const batch = await ref.iterator.next(n);
|
|
422
|
+
log("Iterator", ref.id, "fetched", batch.length, "items");
|
|
423
|
+
if (batch.length) {
|
|
424
|
+
ref.itemsConsumed += batch.length;
|
|
425
|
+
batches.push({ ref, items: batch });
|
|
426
|
+
}
|
|
427
|
+
}
|
|
428
|
+
return handleBatch(iterators, batches);
|
|
429
|
+
};
|
|
430
|
+
|
|
431
|
+
/* maybe make the rule that if results are empty and we get results from joining
|
|
432
|
+
set the results to the joining results
|
|
433
|
+
when results are not empty use onMerge option to merge the results ? */
|
|
434
|
+
|
|
435
|
+
const loadMore = async (n: number = batchSize): Promise<boolean> => {
|
|
436
|
+
const iterators = iteratorRefs.current;
|
|
437
|
+
if (!iterators.length || emptyResultsRef.current) {
|
|
438
|
+
log("No iterators or already empty", {
|
|
439
|
+
length: iterators.length,
|
|
440
|
+
emptyResultsRef: emptyResultsRef.current,
|
|
441
|
+
});
|
|
442
|
+
return false;
|
|
443
|
+
}
|
|
444
|
+
|
|
445
|
+
setIsLoading(true);
|
|
446
|
+
try {
|
|
447
|
+
/* one-time replicator warm-up across all DBs */
|
|
448
|
+
if (shouldWait()) {
|
|
449
|
+
/* if (
|
|
450
|
+
typeof options.remote === "object" &&
|
|
451
|
+
options.remote.wait
|
|
452
|
+
) {
|
|
453
|
+
await Promise.all(
|
|
454
|
+
iterators.map(async ({ db }) => {
|
|
455
|
+
try {
|
|
456
|
+
await db.log.waitForReplicators({
|
|
457
|
+
timeout: (options.remote as { warmup })
|
|
458
|
+
.warmup,
|
|
459
|
+
signal: closeControllerRef.current?.signal,
|
|
460
|
+
});
|
|
461
|
+
} catch (e) {
|
|
462
|
+
if (
|
|
463
|
+
e instanceof AbortError ||
|
|
464
|
+
e instanceof NoPeersError
|
|
465
|
+
)
|
|
466
|
+
return;
|
|
467
|
+
console.warn("Remote replicators not ready", e);
|
|
468
|
+
}
|
|
469
|
+
})
|
|
470
|
+
);
|
|
471
|
+
}*/
|
|
472
|
+
markWaited();
|
|
473
|
+
}
|
|
474
|
+
|
|
475
|
+
return drainRoundRobin(iterators, n);
|
|
274
476
|
} catch (e) {
|
|
275
477
|
if (!(e instanceof ClosedError)) throw e;
|
|
276
478
|
return false;
|
|
@@ -281,13 +483,15 @@ export const useQuery = <
|
|
|
281
483
|
|
|
282
484
|
/* ────────────── live-merge listeners ────────────── */
|
|
283
485
|
useEffect(() => {
|
|
284
|
-
if (!options.
|
|
486
|
+
if (!options.updates) {
|
|
487
|
+
return;
|
|
488
|
+
}
|
|
285
489
|
|
|
286
|
-
const listeners = iteratorRefs.current.map(({ db, id: itId }) => {
|
|
287
|
-
|
|
490
|
+
/* const listeners = iteratorRefs.current.map(({ db, id: itId }) => {
|
|
491
|
+
const mergeFn =
|
|
288
492
|
typeof options.onChange?.merge === "function"
|
|
289
493
|
? options.onChange.merge
|
|
290
|
-
: (c: DocumentsChange<T, I>) => c;
|
|
494
|
+
: (c: DocumentsChange<T, I>) => c;
|
|
291
495
|
|
|
292
496
|
const handler = async (e: CustomEvent<DocumentsChange<T, I>>) => {
|
|
293
497
|
log("Merge change", e.detail, "it", itId);
|
|
@@ -314,7 +518,7 @@ export const useQuery = <
|
|
|
314
518
|
}
|
|
315
519
|
updateAll(options.reverse ? merged.reverse() : merged);
|
|
316
520
|
};
|
|
317
|
-
db.events.addEventListener("change", handler);
|
|
521
|
+
db.events.addEventListener("change", handler);
|
|
318
522
|
return { db, handler };
|
|
319
523
|
});
|
|
320
524
|
|
|
@@ -322,11 +526,15 @@ export const useQuery = <
|
|
|
322
526
|
listeners.forEach(({ db, handler }) =>
|
|
323
527
|
db.events.removeEventListener("change", handler)
|
|
324
528
|
);
|
|
325
|
-
};
|
|
529
|
+
}; */
|
|
530
|
+
|
|
326
531
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
327
532
|
}, [
|
|
328
533
|
iteratorRefs.current.map((r) => r.db.address).join("|"),
|
|
329
|
-
options.
|
|
534
|
+
options.updates,
|
|
535
|
+
options.query,
|
|
536
|
+
options.resolve,
|
|
537
|
+
options.reverse,
|
|
330
538
|
]);
|
|
331
539
|
|
|
332
540
|
/* ────────────── public API – unchanged from the caller's perspective ────────────── */
|
package/src/utils.ts
CHANGED