@peerbit/react 0.0.33 → 0.0.34
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
package/src/useQuery.tsx
DELETED
|
@@ -1,548 +0,0 @@
|
|
|
1
|
-
import { useState, useEffect, useRef, useMemo } from "react";
|
|
2
|
-
import {
|
|
3
|
-
AbstractSearchRequest,
|
|
4
|
-
AbstractSearchResult,
|
|
5
|
-
ClosedError,
|
|
6
|
-
Context,
|
|
7
|
-
Documents,
|
|
8
|
-
RemoteQueryOptions,
|
|
9
|
-
ResultsIterator,
|
|
10
|
-
WithContext,
|
|
11
|
-
} from "@peerbit/document";
|
|
12
|
-
import * as indexerTypes from "@peerbit/indexer-interface";
|
|
13
|
-
import { v4 as uuid } from "uuid";
|
|
14
|
-
import { WithIndexedContext } from "@peerbit/document";
|
|
15
|
-
import { UpdateOptions } from "@peerbit/document";
|
|
16
|
-
|
|
17
|
-
type QueryOptions = { query: QueryLike; id?: string };
|
|
18
|
-
|
|
19
|
-
/* ────────────── helper types ────────────── */
|
|
20
|
-
export type QueryLike = {
|
|
21
|
-
/** Mongo-style selector or array of selectors */
|
|
22
|
-
query?: indexerTypes.QueryLike | indexerTypes.Query[];
|
|
23
|
-
/** Sort definition compatible with `@peerbit/indexer-interface` */
|
|
24
|
-
sort?: indexerTypes.SortLike | indexerTypes.Sort | indexerTypes.Sort[];
|
|
25
|
-
};
|
|
26
|
-
|
|
27
|
-
/**
|
|
28
|
-
* All the non-DB-specific options supported by the original single-DB hook.
|
|
29
|
-
* They stay fully backward-compatible.
|
|
30
|
-
*/
|
|
31
|
-
export type UseQuerySharedOptions<
|
|
32
|
-
T,
|
|
33
|
-
I,
|
|
34
|
-
R extends boolean | undefined,
|
|
35
|
-
RT = R extends false ? WithContext<I> : WithIndexedContext<T, I>,
|
|
36
|
-
> = {
|
|
37
|
-
/* original behavioural flags */
|
|
38
|
-
resolve?: R;
|
|
39
|
-
transform?: (r: RT) => Promise<RT>;
|
|
40
|
-
debounce?: number;
|
|
41
|
-
debug?: boolean | string;
|
|
42
|
-
reverse?: boolean;
|
|
43
|
-
batchSize?: number;
|
|
44
|
-
prefetch?: boolean;
|
|
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>;
|
|
60
|
-
local?: boolean;
|
|
61
|
-
remote?:
|
|
62
|
-
| boolean
|
|
63
|
-
| RemoteQueryOptions<AbstractSearchRequest, AbstractSearchResult, any>;
|
|
64
|
-
} & QueryOptions;
|
|
65
|
-
|
|
66
|
-
/* ────────────────────────── Main Hook ────────────────────────── */
|
|
67
|
-
/**
|
|
68
|
-
* `useQuery` – unified hook that accepts **either**
|
|
69
|
-
* 1. a single `Documents` instance
|
|
70
|
-
* 2. an array of `Documents` instances
|
|
71
|
-
* 3. *or* omits the first argument and provides `dbs` inside the `options` object.
|
|
72
|
-
*
|
|
73
|
-
* It supersedes the original single-DB version as well as the experimental
|
|
74
|
-
* `useMultiQuery` so callers never have to choose between two APIs.
|
|
75
|
-
*/
|
|
76
|
-
export const useQuery = <
|
|
77
|
-
T extends Record<string, any>,
|
|
78
|
-
I extends Record<string, any>,
|
|
79
|
-
R extends boolean | undefined = true,
|
|
80
|
-
RT = R extends false ? WithContext<I> : WithIndexedContext<T, I>,
|
|
81
|
-
>(
|
|
82
|
-
/** Single DB or list of DBs. 100 % backward-compatible with the old single param. */
|
|
83
|
-
dbOrDbs: Documents<T, I> | Documents<T, I>[] | undefined,
|
|
84
|
-
options: UseQuerySharedOptions<T, I, R, RT>
|
|
85
|
-
) => {
|
|
86
|
-
/* ─────── internal type alias for convenience ─────── */
|
|
87
|
-
type Item = RT;
|
|
88
|
-
type IteratorRef = {
|
|
89
|
-
id: string;
|
|
90
|
-
db: Documents<T, I>;
|
|
91
|
-
iterator: ResultsIterator<Item>;
|
|
92
|
-
itemsConsumed: number;
|
|
93
|
-
};
|
|
94
|
-
|
|
95
|
-
/* ────────────── normalise DBs input ────────────── */
|
|
96
|
-
const dbs = useMemo<(Documents<T, I> | undefined)[]>(() => {
|
|
97
|
-
if (Array.isArray(dbOrDbs)) return dbOrDbs;
|
|
98
|
-
if (dbOrDbs) return [dbOrDbs];
|
|
99
|
-
return [];
|
|
100
|
-
}, [dbOrDbs]);
|
|
101
|
-
|
|
102
|
-
/* ────────────── state & refs ────────────── */
|
|
103
|
-
const [all, setAll] = useState<Item[]>([]);
|
|
104
|
-
const allRef = useRef<Item[]>([]);
|
|
105
|
-
const [isLoading, setIsLoading] = useState(false);
|
|
106
|
-
const iteratorRefs = useRef<IteratorRef[]>([]);
|
|
107
|
-
const itemIdRef = useRef(new WeakMap<object, string>());
|
|
108
|
-
const emptyResultsRef = useRef(false);
|
|
109
|
-
const closeControllerRef = useRef<AbortController | null>(null);
|
|
110
|
-
const waitedOnceRef = useRef(false);
|
|
111
|
-
|
|
112
|
-
/* keep an id mostly for debugging – mirrors original behaviour */
|
|
113
|
-
const [id, setId] = useState<string | undefined>(options.id);
|
|
114
|
-
|
|
115
|
-
const reverseRef = useRef(options.reverse);
|
|
116
|
-
useEffect(() => {
|
|
117
|
-
reverseRef.current = options.reverse;
|
|
118
|
-
}, [options.reverse]);
|
|
119
|
-
|
|
120
|
-
/* ────────────── utilities ────────────── */
|
|
121
|
-
const log = (...a: any[]) => {
|
|
122
|
-
if (!options.debug) return;
|
|
123
|
-
if (typeof options.debug === "boolean") console.log(...a);
|
|
124
|
-
else console.log(options.debug, ...a);
|
|
125
|
-
};
|
|
126
|
-
|
|
127
|
-
const updateAll = (combined: Item[]) => {
|
|
128
|
-
allRef.current = combined;
|
|
129
|
-
setAll(combined);
|
|
130
|
-
};
|
|
131
|
-
|
|
132
|
-
const reset = () => {
|
|
133
|
-
iteratorRefs.current?.forEach(({ iterator }) => iterator.close());
|
|
134
|
-
iteratorRefs.current = [];
|
|
135
|
-
|
|
136
|
-
closeControllerRef.current?.abort(new Error("Reset"));
|
|
137
|
-
closeControllerRef.current = new AbortController();
|
|
138
|
-
emptyResultsRef.current = false;
|
|
139
|
-
waitedOnceRef.current = false;
|
|
140
|
-
|
|
141
|
-
allRef.current = [];
|
|
142
|
-
itemIdRef.current = new WeakMap();
|
|
143
|
-
setAll([]);
|
|
144
|
-
setIsLoading(false);
|
|
145
|
-
log("Iterators reset");
|
|
146
|
-
};
|
|
147
|
-
|
|
148
|
-
/* ────────── rebuild iterators when db list / query etc. change ────────── */
|
|
149
|
-
useEffect(() => {
|
|
150
|
-
/* derive canonical list of open DBs */
|
|
151
|
-
const openDbs = dbs.filter((d): d is Documents<T, I> =>
|
|
152
|
-
Boolean(d && !d.closed)
|
|
153
|
-
);
|
|
154
|
-
const { query, resolve } = options;
|
|
155
|
-
|
|
156
|
-
if (!openDbs.length || query == null) {
|
|
157
|
-
reset();
|
|
158
|
-
return;
|
|
159
|
-
}
|
|
160
|
-
|
|
161
|
-
reset();
|
|
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
|
-
};
|
|
184
|
-
|
|
185
|
-
iteratorRefs.current = openDbs.map((db) => {
|
|
186
|
-
let currentRef: IteratorRef | undefined;
|
|
187
|
-
const iterator = db.index.iterate(query ?? {}, {
|
|
188
|
-
closePolicy: "manual",
|
|
189
|
-
local: options.local ?? true,
|
|
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,
|
|
210
|
-
resolve,
|
|
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
|
-
},
|
|
243
|
-
}) as ResultsIterator<Item>;
|
|
244
|
-
|
|
245
|
-
const ref: IteratorRef = {
|
|
246
|
-
id: uuid(),
|
|
247
|
-
db,
|
|
248
|
-
iterator,
|
|
249
|
-
itemsConsumed: 0,
|
|
250
|
-
};
|
|
251
|
-
currentRef = ref;
|
|
252
|
-
log("Iterator init", ref.id, "db", db.address);
|
|
253
|
-
return ref;
|
|
254
|
-
});
|
|
255
|
-
|
|
256
|
-
/* store a deterministic id (useful for external keys) */
|
|
257
|
-
setId(uuid());
|
|
258
|
-
|
|
259
|
-
/* prefetch if requested */
|
|
260
|
-
if (options.prefetch) void loadMore();
|
|
261
|
-
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
262
|
-
}, [
|
|
263
|
-
dbs.map((d) => d?.address).join("|"),
|
|
264
|
-
options.query,
|
|
265
|
-
options.resolve,
|
|
266
|
-
options.reverse,
|
|
267
|
-
]);
|
|
268
|
-
|
|
269
|
-
/* ────────────── loadMore implementation ────────────── */
|
|
270
|
-
const batchSize = options.batchSize ?? 10;
|
|
271
|
-
|
|
272
|
-
const shouldWait = (): boolean => {
|
|
273
|
-
if (waitedOnceRef.current) return false;
|
|
274
|
-
if (options.remote === false) return false;
|
|
275
|
-
return true; // mimic original behaviour – wait once if remote allowed
|
|
276
|
-
};
|
|
277
|
-
|
|
278
|
-
const markWaited = () => {
|
|
279
|
-
waitedOnceRef.current = true;
|
|
280
|
-
};
|
|
281
|
-
|
|
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
|
-
};
|
|
293
|
-
|
|
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
|
|
349
|
-
);
|
|
350
|
-
} catch (error) {
|
|
351
|
-
log("useQuery: failed to resolve id", error);
|
|
352
|
-
}
|
|
353
|
-
|
|
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;
|
|
378
|
-
}
|
|
379
|
-
|
|
380
|
-
if (head != null && seenHeads.has(head)) continue;
|
|
381
|
-
if (head != null) seenHeads.add(head);
|
|
382
|
-
|
|
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
|
-
}
|
|
388
|
-
}
|
|
389
|
-
}
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
if (!freshItems.length && !hasMutations) {
|
|
393
|
-
emptyResultsRef.current = iterators.every((i) => i.iterator.done());
|
|
394
|
-
log("No new items or mutations");
|
|
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);
|
|
476
|
-
} catch (e) {
|
|
477
|
-
if (!(e instanceof ClosedError)) throw e;
|
|
478
|
-
return false;
|
|
479
|
-
} finally {
|
|
480
|
-
setIsLoading(false);
|
|
481
|
-
}
|
|
482
|
-
};
|
|
483
|
-
|
|
484
|
-
/* ────────────── live-merge listeners ────────────── */
|
|
485
|
-
useEffect(() => {
|
|
486
|
-
if (!options.updates) {
|
|
487
|
-
return;
|
|
488
|
-
}
|
|
489
|
-
|
|
490
|
-
/* const listeners = iteratorRefs.current.map(({ db, id: itId }) => {
|
|
491
|
-
const mergeFn =
|
|
492
|
-
typeof options.onChange?.merge === "function"
|
|
493
|
-
? options.onChange.merge
|
|
494
|
-
: (c: DocumentsChange<T, I>) => c;
|
|
495
|
-
|
|
496
|
-
const handler = async (e: CustomEvent<DocumentsChange<T, I>>) => {
|
|
497
|
-
log("Merge change", e.detail, "it", itId);
|
|
498
|
-
const filtered = await mergeFn(e.detail);
|
|
499
|
-
if (
|
|
500
|
-
!filtered ||
|
|
501
|
-
(!filtered.added.length && !filtered.removed.length)
|
|
502
|
-
)
|
|
503
|
-
return;
|
|
504
|
-
|
|
505
|
-
let merged: Item[];
|
|
506
|
-
if (options.onChange?.update) {
|
|
507
|
-
merged = await options.onChange.update(
|
|
508
|
-
allRef.current,
|
|
509
|
-
filtered
|
|
510
|
-
);
|
|
511
|
-
} else {
|
|
512
|
-
merged = await db.index.updateResults(
|
|
513
|
-
allRef.current as WithContext<RT>[],
|
|
514
|
-
filtered,
|
|
515
|
-
options.query || {},
|
|
516
|
-
options.resolve ?? true
|
|
517
|
-
);
|
|
518
|
-
}
|
|
519
|
-
updateAll(options.reverse ? merged.reverse() : merged);
|
|
520
|
-
};
|
|
521
|
-
db.events.addEventListener("change", handler);
|
|
522
|
-
return { db, handler };
|
|
523
|
-
});
|
|
524
|
-
|
|
525
|
-
return () => {
|
|
526
|
-
listeners.forEach(({ db, handler }) =>
|
|
527
|
-
db.events.removeEventListener("change", handler)
|
|
528
|
-
);
|
|
529
|
-
}; */
|
|
530
|
-
|
|
531
|
-
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
532
|
-
}, [
|
|
533
|
-
iteratorRefs.current.map((r) => r.db.address).join("|"),
|
|
534
|
-
options.updates,
|
|
535
|
-
options.query,
|
|
536
|
-
options.resolve,
|
|
537
|
-
options.reverse,
|
|
538
|
-
]);
|
|
539
|
-
|
|
540
|
-
/* ────────────── public API – unchanged from the caller's perspective ────────────── */
|
|
541
|
-
return {
|
|
542
|
-
items: all,
|
|
543
|
-
loadMore,
|
|
544
|
-
isLoading,
|
|
545
|
-
empty: () => emptyResultsRef.current,
|
|
546
|
-
id,
|
|
547
|
-
};
|
|
548
|
-
};
|
|
File without changes
|