@peerbit/react 0.0.21 → 0.0.23

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.
@@ -2,7 +2,7 @@ import { Documents } from "@peerbit/document";
2
2
  import * as indexerTypes from "@peerbit/indexer-interface";
3
3
  type QueryOptons = {
4
4
  query: indexerTypes.Query[] | indexerTypes.QueryLike;
5
- id: string;
5
+ id?: string;
6
6
  };
7
7
  export declare const useCount: <T extends Record<string, any>>(db?: Documents<T, any, any>, options?: {
8
8
  debounce?: number;
@@ -34,7 +34,10 @@ export const useCount = (db, options) => {
34
34
  db.events.removeEventListener("change", handleChange);
35
35
  debounced.cancel();
36
36
  };
37
- }, [db?.closed ? undefined : db?.rootAddress, options?.id]);
37
+ }, [
38
+ db?.closed ? undefined : db?.rootAddress,
39
+ options?.id ?? options?.query,
40
+ ]);
38
41
  return count;
39
42
  };
40
43
  //# sourceMappingURL=useCount.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"useCount.js","sourceRoot":"","sources":["../../src/useCount.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAA0B,MAAM,mBAAmB,CAAC;AACxE,OAAO,EAAE,SAAS,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAC;AAEpD,OAAO,EAAE,uBAAuB,EAAE,MAAM,SAAS,CAAC;AAMlD,MAAM,CAAC,MAAM,QAAQ,GAAG,CACpB,EAA2B,EAC3B,OAGe,EACjB,EAAE;IACA,MAAM,CAAC,KAAK,EAAE,QAAQ,CAAC,GAAG,QAAQ,CAAS,CAAC,CAAC,CAAC;IAC9C,MAAM,QAAQ,GAAG,MAAM,CAAS,CAAC,CAAC,CAAC;IAEnC,SAAS,CAAC,GAAG,EAAE;QACX,IAAI,CAAC,EAAE,IAAI,EAAE,CAAC,MAAM,EAAE,CAAC;YACnB,OAAO;QACX,CAAC;QAED,MAAM,EAAE,GAAG,KAAK,EAAE,IAAU,EAAE,EAAE;YAC5B,IAAI,CAAC;gBACD,MAAM,KAAK,GAAG,MAAM,EAAE,CAAC,KAAK,CAAC;oBACzB,KAAK,EAAE,OAAO,EAAE,KAAK;oBACrB,WAAW,EAAE,IAAI;iBACpB,CAAC,CAAC;gBACH,QAAQ,CAAC,OAAO,GAAG,KAAK,CAAC;gBACzB,QAAQ,CAAC,KAAK,CAAC,CAAC;YACpB,CAAC;YAAC,OAAO,KAAK,EAAE,CAAC;gBACb,IAAI,KAAK,YAAY,WAAW,EAAE,CAAC;oBAC/B,OAAO;gBACX,CAAC;gBACD,MAAM,KAAK,CAAC;YAChB,CAAC;QACL,CAAC,CAAC;QAEF,MAAM,SAAS,GAAG,uBAAuB,CACrC,EAAE,EACF,OAAO,EAAE,QAAQ,IAAI,IAAI,CAC5B,CAAC;QAEF,MAAM,YAAY,GAAG,GAAG,EAAE;YACtB,SAAS,EAAE,CAAC;QAChB,CAAC,CAAC;QAEF,SAAS,EAAE,CAAC;QACZ,EAAE,CAAC,MAAM,CAAC,gBAAgB,CAAC,QAAQ,EAAE,YAAY,CAAC,CAAC;QAEnD,OAAO,GAAG,EAAE;YACR,EAAE,CAAC,MAAM,CAAC,mBAAmB,CAAC,QAAQ,EAAE,YAAY,CAAC,CAAC;YACtD,SAAS,CAAC,MAAM,EAAE,CAAC;QACvB,CAAC,CAAC;IACN,CAAC,EAAE,CAAC,EAAE,EAAE,MAAM,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,EAAE,EAAE,WAAW,EAAE,OAAO,EAAE,EAAE,CAAC,CAAC,CAAC;IAE5D,OAAO,KAAK,CAAC;AACjB,CAAC,CAAC"}
1
+ {"version":3,"file":"useCount.js","sourceRoot":"","sources":["../../src/useCount.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAA0B,MAAM,mBAAmB,CAAC;AACxE,OAAO,EAAE,SAAS,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAC;AAEpD,OAAO,EAAE,uBAAuB,EAAE,MAAM,SAAS,CAAC;AAMlD,MAAM,CAAC,MAAM,QAAQ,GAAG,CACpB,EAA2B,EAC3B,OAGe,EACjB,EAAE;IACA,MAAM,CAAC,KAAK,EAAE,QAAQ,CAAC,GAAG,QAAQ,CAAS,CAAC,CAAC,CAAC;IAC9C,MAAM,QAAQ,GAAG,MAAM,CAAS,CAAC,CAAC,CAAC;IAEnC,SAAS,CAAC,GAAG,EAAE;QACX,IAAI,CAAC,EAAE,IAAI,EAAE,CAAC,MAAM,EAAE,CAAC;YACnB,OAAO;QACX,CAAC;QAED,MAAM,EAAE,GAAG,KAAK,EAAE,IAAU,EAAE,EAAE;YAC5B,IAAI,CAAC;gBACD,MAAM,KAAK,GAAG,MAAM,EAAE,CAAC,KAAK,CAAC;oBACzB,KAAK,EAAE,OAAO,EAAE,KAAK;oBACrB,WAAW,EAAE,IAAI;iBACpB,CAAC,CAAC;gBACH,QAAQ,CAAC,OAAO,GAAG,KAAK,CAAC;gBACzB,QAAQ,CAAC,KAAK,CAAC,CAAC;YACpB,CAAC;YAAC,OAAO,KAAK,EAAE,CAAC;gBACb,IAAI,KAAK,YAAY,WAAW,EAAE,CAAC;oBAC/B,OAAO;gBACX,CAAC;gBACD,MAAM,KAAK,CAAC;YAChB,CAAC;QACL,CAAC,CAAC;QAEF,MAAM,SAAS,GAAG,uBAAuB,CACrC,EAAE,EACF,OAAO,EAAE,QAAQ,IAAI,IAAI,CAC5B,CAAC;QAEF,MAAM,YAAY,GAAG,GAAG,EAAE;YACtB,SAAS,EAAE,CAAC;QAChB,CAAC,CAAC;QAEF,SAAS,EAAE,CAAC;QACZ,EAAE,CAAC,MAAM,CAAC,gBAAgB,CAAC,QAAQ,EAAE,YAAY,CAAC,CAAC;QAEnD,OAAO,GAAG,EAAE;YACR,EAAE,CAAC,MAAM,CAAC,mBAAmB,CAAC,QAAQ,EAAE,YAAY,CAAC,CAAC;YACtD,SAAS,CAAC,MAAM,EAAE,CAAC;QACvB,CAAC,CAAC;IACN,CAAC,EAAE;QACC,EAAE,EAAE,MAAM,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,EAAE,EAAE,WAAW;QACxC,OAAO,EAAE,EAAE,IAAI,OAAO,EAAE,KAAK;KAChC,CAAC,CAAC;IAEH,OAAO,KAAK,CAAC;AACjB,CAAC,CAAC"}
@@ -6,32 +6,33 @@ type QueryLike = {
6
6
  };
7
7
  type QueryOptions = {
8
8
  query: QueryLike;
9
- id: string;
9
+ id?: string;
10
+ };
11
+ type WaitForReplicatorsOption = {
12
+ warmup?: number;
13
+ eager?: boolean;
10
14
  };
11
15
  export declare const useQuery: <T extends Record<string, any>, I extends Record<string, any>, R extends boolean | undefined = true, RT = R extends false ? WithContext<I> : WithContext<T>>(db?: Documents<T, I>, options?: {
12
16
  resolve?: R;
13
- waitForReplicators?: boolean | {
14
- timeout?: number;
15
- };
16
- transform?: (result: WithContext<RT>) => Promise<WithContext<RT>>;
17
+ transform?: (r: RT) => Promise<RT>;
17
18
  debounce?: number;
18
19
  debug?: boolean | {
19
20
  id: string;
20
21
  };
21
22
  reverse?: boolean;
22
23
  batchSize?: number;
24
+ prefetch?: boolean;
23
25
  onChange?: {
24
- merge?: boolean | ((change: DocumentsChange<T>) => DocumentsChange<T> | Promise<DocumentsChange<T>> | undefined);
25
- update?: (prev: WithContext<RT>[], change: DocumentsChange<T>) => WithContext<RT>[];
26
+ merge?: boolean | ((c: DocumentsChange<T>) => DocumentsChange<T> | Promise<DocumentsChange<T>> | undefined);
27
+ update?: (prev: RT[], change: DocumentsChange<T>) => RT[];
26
28
  };
27
29
  local?: boolean;
28
- remote?: boolean | {
29
- eager?: boolean;
30
- };
30
+ remote?: boolean | WaitForReplicatorsOption;
31
31
  } & QueryOptions) => {
32
- items: WithContext<RT>[];
33
- loadMore: () => Promise<void>;
32
+ items: RT[];
33
+ loadMore: () => Promise<boolean | undefined>;
34
34
  isLoading: boolean;
35
- empty: boolean;
35
+ empty: () => boolean;
36
+ id: string | undefined;
36
37
  };
37
38
  export {};
@@ -1,17 +1,11 @@
1
- import { useState, useEffect, useRef } from "react";
1
+ import { useState, useEffect, useRef, useReducer } from "react";
2
2
  import { ClosedError, } from "@peerbit/document";
3
3
  import { AbortError } from "@peerbit/time";
4
- const logWithId = (options, ...args) => {
5
- if (!options?.debug)
6
- return;
7
- if (typeof options.debug === "boolean") {
8
- console.log(...args);
9
- }
10
- else if (typeof options.debug.id === "string") {
11
- console.log(options.debug.id, ...args);
12
- }
13
- };
4
+ import { NoPeersError } from "@peerbit/shared-log";
5
+ import { v4 as uuid } from "uuid";
6
+ /* ────────────── main hook ────────────── */
14
7
  export const useQuery = (db, options) => {
8
+ /* ────────────── state & refs ────────────── */
15
9
  const [all, setAll] = useState([]);
16
10
  const allRef = useRef([]);
17
11
  const [isLoading, setIsLoading] = useState(false);
@@ -19,103 +13,113 @@ export const useQuery = (db, options) => {
19
13
  const iteratorRef = useRef(null);
20
14
  const emptyResultsRef = useRef(false);
21
15
  const closeControllerRef = useRef(null);
22
- const updateAll = (combined, fromChange) => {
23
- logWithId(options, "Loading more items, new combined length", combined.length, "from change", fromChange);
16
+ const waitedOnceRef = useRef(false);
17
+ const resetResultsOnReset = useRef(true);
18
+ const [id, setId] = useState(undefined);
19
+ const [resetCounter, invokeReset] = useReducer((n) => n + 1, 0);
20
+ const reverseRef = useRef(options?.reverse);
21
+ useEffect(() => {
22
+ reverseRef.current = options?.reverse;
23
+ }, [options?.reverse]);
24
+ /* ────────────── util ────────────── */
25
+ const log = (...a) => {
26
+ if (!options?.debug)
27
+ return;
28
+ if (typeof options.debug === "boolean")
29
+ console.log(...a);
30
+ else
31
+ console.log(options.debug.id, ...a);
32
+ };
33
+ const updateAll = (combined) => {
24
34
  allRef.current = combined;
25
35
  setAll(combined);
26
36
  };
27
37
  const reset = (fromRef) => {
28
- if (iteratorRef.current != null && iteratorRef.current !== fromRef) {
38
+ const toClose = iteratorRef.current;
39
+ if (toClose && fromRef && toClose !== fromRef) {
29
40
  return;
30
41
  }
42
+ iteratorRef.current = null;
31
43
  closeControllerRef.current?.abort();
32
44
  closeControllerRef.current = new AbortController();
33
45
  emptyResultsRef.current = false;
34
- logWithId(options, "reset", {
35
- id: iteratorRef.current?.id,
36
- size: allRef.current.length,
37
- });
38
- !iteratorRef.current?.iterator.done() &&
39
- iteratorRef.current?.iterator?.close();
40
- iteratorRef.current = null;
41
- setAll([]);
46
+ toClose?.iterator.close();
47
+ if (resetResultsOnReset.current) {
48
+ allRef.current = [];
49
+ setAll([]);
50
+ }
42
51
  setIsLoading(false);
43
52
  loadingMoreRef.current = false;
44
- allRef.current = [];
53
+ log(options, "Iterator reset", toClose?.id, fromRef?.id);
54
+ setId(undefined);
45
55
  };
46
- // Initialize the iterator only once or when query changes
47
56
  useEffect(() => {
48
- if (!db || db.closed || options?.query === null) {
57
+ resetResultsOnReset.current = true;
58
+ waitedOnceRef.current = false;
59
+ }, [db, options?.id ?? options?.query, options?.resolve, options?.reverse]);
60
+ /* ────────────── effect: (re)create iterator ────────────── */
61
+ useEffect(() => {
62
+ if (!db || db.closed || options?.query == null) {
49
63
  reset(null);
50
64
  return;
51
65
  }
52
66
  const initIterator = () => {
53
- // Don't make this async, it will cause issues with the iterator refs
54
- try {
55
- // Initialize the iterator and load initial batch.
56
- const ref = {
57
- id: options?.id,
58
- iterator: db.index.iterate(options?.query ?? {}, {
59
- local: options?.local ?? true,
60
- remote: options?.remote ?? true,
61
- resolve: options?.resolve,
62
- }),
63
- };
64
- iteratorRef.current = ref;
65
- logWithId(options, "Initializing iterator", {
66
- id: options?.id,
67
- options: options,
68
- });
69
- loadMore(); // initial load
70
- return ref;
71
- }
72
- catch (error) {
73
- console.error("Error initializing iterator", error);
74
- return null;
67
+ let id = options?.id ?? uuid();
68
+ const ref = {
69
+ id,
70
+ iterator: db.index.iterate(options.query ?? {}, {
71
+ local: options?.local ?? true,
72
+ remote: options.remote == null || options.remote === false
73
+ ? false
74
+ : typeof options.remote === "object"
75
+ ? options.remote
76
+ : true,
77
+ resolve: options?.resolve,
78
+ }),
79
+ itemsConsumed: 0,
80
+ };
81
+ iteratorRef.current = ref;
82
+ if (options?.prefetch) {
83
+ loadMore();
75
84
  }
85
+ setId(id);
86
+ log("Iterator initialised", ref.id);
87
+ return ref;
76
88
  };
77
- // Reset state when the db or query changes.
78
89
  reset(iteratorRef.current);
79
90
  const newIteratorRef = initIterator();
80
- let handleChange = undefined;
81
- if (options?.onChange && options?.onChange?.merge !== false) {
82
- let mergeFunction = typeof options.onChange.merge === "function"
91
+ /* live-merge listener (optional) */
92
+ let handleChange;
93
+ if (options?.onChange && options.onChange.merge !== false) {
94
+ const mergeFn = typeof options.onChange.merge === "function"
83
95
  ? options.onChange.merge
84
- : (change) => change;
96
+ : (c) => c;
85
97
  handleChange = async (e) => {
86
- // while we are iterating, we might get new documents.. so this method inserts them where they should be
87
- let filteredChange = await mergeFunction(e.detail);
88
- if (!filteredChange ||
89
- (filteredChange.added.length === 0 &&
90
- filteredChange.removed.length === 0)) {
98
+ log(options, "Merge change", e.detail, "iterator", newIteratorRef.id);
99
+ const filtered = await mergeFn(e.detail);
100
+ if (!filtered ||
101
+ (filtered.added.length === 0 &&
102
+ filtered.removed.length === 0))
91
103
  return;
92
- }
93
- let merged = [];
104
+ let merged;
94
105
  if (options.onChange?.update) {
95
106
  merged = [
96
- ...options.onChange.update(allRef.current, filteredChange),
107
+ ...options.onChange?.update(allRef.current, filtered),
97
108
  ];
98
109
  }
99
110
  else {
100
- merged = await db.index.updateResults(allRef.current, filteredChange, options?.query || {}, options?.resolve ?? true);
101
- logWithId(options, "After update", allRef.current, merged);
102
- const expectedDiff = filteredChange.added.length -
103
- filteredChange.removed.length;
111
+ merged = await db.index.updateResults(allRef.current, filtered, options.query || {}, options.resolve ?? true);
112
+ log(options, "After update", allRef.current, merged);
113
+ const expectedDiff = filtered.added.length - filtered.removed.length;
104
114
  if (merged === allRef.current ||
105
115
  (expectedDiff !== 0 &&
106
116
  merged.length === allRef.current.length)) {
107
117
  // no change
108
- logWithId(options, "no change after merge");
118
+ log(options, "no change after merge");
109
119
  return;
110
120
  }
111
121
  }
112
- logWithId(options, "handleChange", {
113
- added: e.detail.added.length,
114
- removed: e.detail.removed.length,
115
- merged: merged.length,
116
- allRef: allRef.current.length,
117
- });
118
- updateAll(options?.reverse ? merged.reverse() : merged, e.detail);
122
+ updateAll(options?.reverse ? merged.reverse() : merged);
119
123
  };
120
124
  db.events.addEventListener("change", handleChange);
121
125
  }
@@ -126,99 +130,130 @@ export const useQuery = (db, options) => {
126
130
  };
127
131
  }, [
128
132
  db?.closed ? undefined : db?.address,
129
- options?.id != null ? options?.id : options?.query,
133
+ options?.id ?? options?.query,
130
134
  options?.resolve,
135
+ options?.reverse,
136
+ resetCounter,
131
137
  ]);
132
- // Define the loadMore function
138
+ /* ────────────── loadMore (once-wait aware) ────────────── */
133
139
  const batchSize = options?.batchSize ?? 10;
140
+ const shouldWait = () => {
141
+ if (waitedOnceRef.current) {
142
+ return false;
143
+ }
144
+ if (options?.remote === false)
145
+ return false;
146
+ if (options?.remote === true)
147
+ return true;
148
+ if (options?.remote == null)
149
+ return true;
150
+ if (typeof options?.remote === "object") {
151
+ return true;
152
+ }
153
+ return true;
154
+ };
155
+ const reloadAfterTime = () => {
156
+ if (typeof options?.remote === "object" && options.remote.eager) {
157
+ return options.remote.warmup ?? db?.log.timeUntilRoleMaturity;
158
+ }
159
+ return undefined;
160
+ };
161
+ const markWaited = () => {
162
+ waitedOnceRef.current = true;
163
+ };
134
164
  const loadMore = async () => {
135
- if (!iteratorRef.current ||
165
+ const iterator = iteratorRef.current;
166
+ if (!iterator ||
136
167
  emptyResultsRef.current ||
137
- iteratorRef.current.iterator.done() ||
168
+ iterator.iterator.done() ||
138
169
  loadingMoreRef.current) {
139
- logWithId(options, "loadMore: already loading or no more items", {
140
- isLoading,
141
- emptyResultsRef: emptyResultsRef.current,
142
- iteratorRef: !iteratorRef.current,
143
- });
144
- return;
170
+ return false;
145
171
  }
146
- const iterator = iteratorRef.current;
147
172
  setIsLoading(true);
148
173
  loadingMoreRef.current = true;
149
174
  try {
150
- // Fetch next batchSize number of items:
151
- logWithId(options, "wait for replicators for iterator " + iterator.id);
152
- if (options?.waitForReplicators !== false) {
153
- let timeout = 5e3;
154
- if (typeof options?.waitForReplicators === "object") {
155
- timeout = options.waitForReplicators.timeout ?? 1e4;
156
- }
157
- await db?.log
175
+ /* ── optional replicate-wait ── */
176
+ if (shouldWait()) {
177
+ log(options, "Wait for replicators", iterator.id);
178
+ let isEager = typeof options?.remote === "object" && options.remote.eager;
179
+ const waitTimeout = typeof options?.remote === "object" &&
180
+ typeof options?.remote.warmup === "number"
181
+ ? options?.remote.warmup
182
+ : 5_000;
183
+ let shouldResetAfterMaturity = isEager && !waitedOnceRef.current;
184
+ let promise = db?.log
158
185
  .waitForReplicators({
159
- timeout,
186
+ timeout: waitTimeout,
160
187
  signal: closeControllerRef.current?.signal,
161
188
  })
162
189
  .catch((e) => {
163
- if (e instanceof AbortError) {
164
- // Ignore abort error
190
+ if (e instanceof AbortError ||
191
+ e instanceof NoPeersError)
165
192
  return;
166
- }
167
193
  console.warn("Remote replicators not ready", e);
194
+ })
195
+ .finally(() => {
196
+ markWaited();
197
+ if (shouldResetAfterMaturity) {
198
+ resetResultsOnReset.current = false; // don't reset results, because we expect to get same or more results
199
+ invokeReset();
200
+ }
168
201
  });
202
+ if (!shouldResetAfterMaturity) {
203
+ await promise;
204
+ }
169
205
  }
170
- logWithId(options, "loadMore: loading more items for iterator " +
171
- iteratorRef.current?.id, "should resolve?: " + options?.resolve, "query local?: " + options?.local, "query remote?: " + options?.remote, "isReplicating: " + (await db?.log.isReplicating()));
206
+ else {
207
+ log(options, "Skip wait for replicators", iterator.id);
208
+ }
209
+ /* ── fetch next batch ── */
210
+ log(options, "Retrieve next batch", iterator.id);
172
211
  let newItems = await iterator.iterator.next(batchSize);
173
212
  if (options?.transform) {
174
- newItems = await Promise.all(newItems.map((item) => options.transform(item)));
213
+ log(options, "Transform start", iterator.id);
214
+ newItems = await Promise.all(newItems.map(options.transform));
215
+ log(options, "Transform end", iterator.id);
175
216
  }
217
+ /* iterator might have been reset while we were async… */
176
218
  if (iteratorRef.current !== iterator) {
177
- // If the iterator has changed, we should not update the state
178
- // This can happen if the iterator was closed and a new one was created
179
- logWithId(options, "Iterator ref changed, not updating state", {
180
- refBefore: iterator.id,
181
- currentRef: iteratorRef.current?.id,
182
- ignoredItems: newItems.length,
183
- });
184
- return;
219
+ log(options, "Iterator reset while loading more");
220
+ return false;
185
221
  }
186
- logWithId(options, "loadMore: loaded more items for iterator " +
187
- iteratorRef.current?.id, "new items length", newItems.length, "all items length", allRef.current.length);
222
+ iterator.itemsConsumed += newItems.length;
188
223
  emptyResultsRef.current = newItems.length === 0;
189
- if (newItems.length > 0) {
190
- let prev = allRef.current;
191
- let prevHash = new Set(prev.map((x) => x.__context.head));
192
- let newItemsNoHash = newItems.filter((x) => !prevHash.has(x.__context.head));
193
- if (newItemsNoHash.length === 0) {
194
- logWithId(options, "no new items after dedup, not updating state. Prev length", prev.length);
224
+ if (newItems.length) {
225
+ log("Loaded more items for iterator", iterator.id, "current id", iteratorRef.current?.id, "new items", newItems.length, "previous results", allRef.current.length, "batchSize", batchSize, "items consumed", iterator.itemsConsumed);
226
+ const prev = allRef.current;
227
+ const dedup = new Set(prev.map((x) => x.__context.head));
228
+ const unique = newItems.filter((x) => !dedup.has(x.__context.head));
229
+ if (!unique.length)
195
230
  return;
196
- }
197
- const combined = options?.reverse
198
- ? [...newItemsNoHash.reverse(), ...prev]
199
- : [...prev, ...newItemsNoHash];
200
- updateAll(combined, null);
231
+ const combined = reverseRef.current
232
+ ? [...unique.reverse(), ...prev]
233
+ : [...prev, ...unique];
234
+ updateAll(combined);
201
235
  }
202
236
  else {
203
- logWithId(options, "no new items, not updating state for iterator " +
204
- iteratorRef.current?.id +
205
- " existing results length", allRef.current.length);
237
+ log(options, "No new items", iterator.id);
206
238
  }
239
+ return !iterator.iterator.done();
207
240
  }
208
- catch (error) {
209
- if (error instanceof ClosedError) {
210
- // Handle closed database gracefully
211
- logWithId(options, "Database closed error");
212
- }
213
- else {
214
- throw error;
215
- }
241
+ catch (e) {
242
+ if (!(e instanceof ClosedError))
243
+ throw e;
216
244
  }
217
245
  finally {
218
246
  setIsLoading(false);
219
247
  loadingMoreRef.current = false;
220
248
  }
221
249
  };
222
- return { items: all, loadMore, isLoading, empty: emptyResultsRef.current };
250
+ /* ────────────── public API ────────────── */
251
+ return {
252
+ items: all,
253
+ loadMore,
254
+ isLoading,
255
+ empty: () => emptyResultsRef.current,
256
+ id: id,
257
+ };
223
258
  };
224
259
  //# sourceMappingURL=useQuery.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"useQuery.js","sourceRoot":"","sources":["../../src/useQuery.tsx"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"useQuery.js","sourceRoot":"","sources":["../../src/useQuery.tsx"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@peerbit/react",
3
- "version": "0.0.21",
3
+ "version": "0.0.23",
4
4
  "homepage": "https://dao-xyz.github.io/peerbit-examples",
5
5
  "type": "module",
6
6
  "module": "lib/esm/index.js",
@@ -70,5 +70,5 @@
70
70
  "last 1 safari version"
71
71
  ]
72
72
  },
73
- "gitHead": "6e2a023d63cd34b6058da13820562d64b8101253"
73
+ "gitHead": "22ca16d2f5cf13dbc538602dd6e9d0a00382b768"
74
74
  }
package/src/useCount.tsx CHANGED
@@ -5,7 +5,7 @@ import { debounceLeadingTrailing } from "./utils";
5
5
 
6
6
  type QueryOptons = {
7
7
  query: indexerTypes.Query[] | indexerTypes.QueryLike;
8
- id: string;
8
+ id?: string;
9
9
  };
10
10
  export const useCount = <T extends Record<string, any>>(
11
11
  db?: Documents<T, any, any>,
@@ -54,7 +54,10 @@ export const useCount = <T extends Record<string, any>>(
54
54
  db.events.removeEventListener("change", handleChange);
55
55
  debounced.cancel();
56
56
  };
57
- }, [db?.closed ? undefined : db?.rootAddress, options?.id]);
57
+ }, [
58
+ db?.closed ? undefined : db?.rootAddress,
59
+ options?.id ?? options?.query,
60
+ ]);
58
61
 
59
62
  return count;
60
63
  };
package/src/useQuery.tsx CHANGED
@@ -1,4 +1,4 @@
1
- import { useState, useEffect, useRef } from "react";
1
+ import { useState, useEffect, useRef, useReducer } from "react";
2
2
  import {
3
3
  ClosedError,
4
4
  Documents,
@@ -8,26 +8,18 @@ import {
8
8
  } from "@peerbit/document";
9
9
  import * as indexerTypes from "@peerbit/indexer-interface";
10
10
  import { AbortError } from "@peerbit/time";
11
-
11
+ import { NoPeersError } from "@peerbit/shared-log";
12
+ import { v4 as uuid } from "uuid";
13
+ /* ────────────── helper types ────────────── */
12
14
  type QueryLike = {
13
15
  query?: indexerTypes.Query[] | indexerTypes.QueryLike;
14
16
  sort?: indexerTypes.Sort[] | indexerTypes.Sort | indexerTypes.SortLike;
15
17
  };
16
- type QueryOptions = { query: QueryLike; id: string };
18
+ type QueryOptions = { query: QueryLike; id?: string };
17
19
 
18
- const logWithId = (
19
- options: { debug?: boolean | { id: string } } | undefined,
20
- ...args: any[]
21
- ) => {
22
- if (!options?.debug) return;
23
-
24
- if (typeof options.debug === "boolean") {
25
- console.log(...args);
26
- } else if (typeof options.debug.id === "string") {
27
- console.log(options.debug.id, ...args);
28
- }
29
- };
20
+ type WaitForReplicatorsOption = { warmup?: number; eager?: boolean };
30
21
 
22
+ /* ────────────── main hook ────────────── */
31
23
  export const useQuery = <
32
24
  T extends Record<string, any>,
33
25
  I extends Record<string, any>,
@@ -37,167 +29,179 @@ export const useQuery = <
37
29
  db?: Documents<T, I>,
38
30
  options?: {
39
31
  resolve?: R;
40
- waitForReplicators?: boolean | { timeout?: number };
41
- transform?: (result: WithContext<RT>) => Promise<WithContext<RT>>;
32
+ transform?: (r: RT) => Promise<RT>;
42
33
  debounce?: number;
43
34
  debug?: boolean | { id: string };
44
35
  reverse?: boolean;
45
- batchSize?: number; // You can set a default batch size here
36
+ batchSize?: number;
37
+ prefetch?: boolean;
46
38
  onChange?: {
47
39
  merge?:
48
40
  | boolean
49
41
  | ((
50
- change: DocumentsChange<T>
42
+ c: DocumentsChange<T>
51
43
  ) =>
52
44
  | DocumentsChange<T>
53
45
  | Promise<DocumentsChange<T>>
54
- | undefined); // if true, the iterator will be updated with new documents
55
- update?: (
56
- prev: WithContext<RT>[],
57
- change: DocumentsChange<T>
58
- ) => WithContext<RT>[];
46
+ | undefined);
47
+ update?: (prev: RT[], change: DocumentsChange<T>) => RT[];
59
48
  };
60
- local?: boolean; // if true, (default is true) the iterator will only return local documents
61
- remote?:
62
- | boolean
63
- | {
64
- eager?: boolean;
65
- };
49
+ local?: boolean;
50
+ remote?: boolean | WaitForReplicatorsOption;
66
51
  } & QueryOptions
67
52
  ) => {
68
- const [all, setAll] = useState<WithContext<RT>[]>([]);
69
- const allRef = useRef<WithContext<RT>[]>([]);
53
+ /* ── «Item» is the concrete element type flowing through the hook ── */
54
+ type Item = RT;
55
+
56
+ /* ────────────── state & refs ────────────── */
57
+ const [all, setAll] = useState<Item[]>([]);
58
+ const allRef = useRef<Item[]>([]);
70
59
  const [isLoading, setIsLoading] = useState(false);
71
- const loadingMoreRef = useRef(false);
60
+ const loadingMoreRef = useRef<boolean>(false);
72
61
  const iteratorRef = useRef<{
73
62
  id?: string;
74
- iterator: ResultsIterator<WithContext<RT>>;
63
+ iterator: ResultsIterator<Item>;
64
+ itemsConsumed: number;
75
65
  } | null>(null);
76
66
  const emptyResultsRef = useRef(false);
77
67
  const closeControllerRef = useRef<AbortController | null>(null);
68
+ const waitedOnceRef = useRef(false);
69
+ const resetResultsOnReset = useRef(true);
78
70
 
79
- const updateAll = (
80
- combined: WithContext<RT>[],
81
- fromChange?: DocumentsChange<any> | null
82
- ) => {
83
- logWithId(
84
- options,
85
- "Loading more items, new combined length",
86
- combined.length,
87
- "from change",
88
- fromChange
89
- );
71
+ const [id, setId] = useState<string | undefined>(undefined);
72
+ const [resetCounter, invokeReset] = useReducer((n) => n + 1, 0);
90
73
 
91
- allRef.current = combined;
74
+ const reverseRef = useRef(options?.reverse);
75
+ useEffect(() => {
76
+ reverseRef.current = options?.reverse;
77
+ }, [options?.reverse]);
78
+
79
+ /* ────────────── util ────────────── */
80
+ const log = (...a: any[]) => {
81
+ if (!options?.debug) return;
82
+ if (typeof options.debug === "boolean") console.log(...a);
83
+ else console.log(options.debug.id, ...a);
84
+ };
92
85
 
86
+ const updateAll = (combined: Item[]) => {
87
+ allRef.current = combined;
93
88
  setAll(combined);
94
89
  };
95
90
 
96
91
  const reset = (
97
92
  fromRef: {
98
93
  id?: string;
99
- iterator: ResultsIterator<WithContext<RT>>;
94
+ iterator: ResultsIterator<Item>;
100
95
  } | null
101
96
  ) => {
102
- if (iteratorRef.current != null && iteratorRef.current !== fromRef) {
97
+ const toClose = iteratorRef.current;
98
+ if (toClose && fromRef && toClose !== fromRef) {
103
99
  return;
104
100
  }
101
+
102
+ iteratorRef.current = null;
103
+
105
104
  closeControllerRef.current?.abort();
106
105
  closeControllerRef.current = new AbortController();
107
-
108
106
  emptyResultsRef.current = false;
109
- logWithId(options, "reset", {
110
- id: iteratorRef.current?.id,
111
- size: allRef.current.length,
112
- });
113
107
 
114
- !iteratorRef.current?.iterator.done() &&
115
- iteratorRef.current?.iterator?.close();
116
- iteratorRef.current = null;
117
- setAll([]);
108
+ toClose?.iterator.close();
109
+
110
+ if (resetResultsOnReset.current) {
111
+ allRef.current = [];
112
+ setAll([]);
113
+ }
114
+
118
115
  setIsLoading(false);
119
116
  loadingMoreRef.current = false;
120
- allRef.current = [];
117
+ log(options, "Iterator reset", toClose?.id, fromRef?.id);
118
+ setId(undefined);
121
119
  };
122
120
 
123
- // Initialize the iterator only once or when query changes
124
121
  useEffect(() => {
125
- if (!db || db.closed || options?.query === null) {
122
+ resetResultsOnReset.current = true;
123
+ waitedOnceRef.current = false;
124
+ }, [db, options?.id ?? options?.query, options?.resolve, options?.reverse]);
125
+
126
+ /* ────────────── effect: (re)create iterator ────────────── */
127
+ useEffect(() => {
128
+ if (!db || db.closed || options?.query == null) {
126
129
  reset(null);
127
130
  return;
128
131
  }
129
132
 
130
133
  const initIterator = () => {
131
- // Don't make this async, it will cause issues with the iterator refs
132
- try {
133
- // Initialize the iterator and load initial batch.
134
-
135
- const ref = {
136
- id: options?.id,
137
- iterator: db.index.iterate(options?.query ?? {}, {
138
- local: options?.local ?? true,
139
- remote: options?.remote ?? true,
140
- resolve: options?.resolve as any,
141
- }) as any as ResultsIterator<WithContext<RT>>,
142
- };
143
- iteratorRef.current = ref;
144
-
145
- logWithId(options, "Initializing iterator", {
146
- id: options?.id,
147
- options: options,
148
- });
149
-
150
- loadMore(); // initial load
151
- return ref;
152
- } catch (error) {
153
- console.error("Error initializing iterator", error);
154
- return null;
134
+ let id = options?.id ?? uuid();
135
+ const ref = {
136
+ id,
137
+ iterator: db.index.iterate(options.query ?? {}, {
138
+ local: options?.local ?? true,
139
+ remote:
140
+ options.remote == null || options.remote === false
141
+ ? false
142
+ : typeof options.remote === "object"
143
+ ? options.remote
144
+ : true,
145
+ resolve: options?.resolve,
146
+ }) as ResultsIterator<Item>,
147
+ itemsConsumed: 0,
148
+ };
149
+ iteratorRef.current = ref;
150
+ if (options?.prefetch) {
151
+ loadMore();
155
152
  }
153
+ setId(id);
154
+
155
+ log("Iterator initialised", ref.id);
156
+ return ref;
156
157
  };
157
158
 
158
- // Reset state when the db or query changes.
159
159
  reset(iteratorRef.current);
160
-
161
160
  const newIteratorRef = initIterator();
162
161
 
162
+ /* live-merge listener (optional) */
163
163
  let handleChange:
164
- | undefined
165
- | ((e: CustomEvent<DocumentsChange<T>>) => void | Promise<void>) =
166
- undefined;
167
- if (options?.onChange && options?.onChange?.merge !== false) {
168
- let mergeFunction =
164
+ | ((e: CustomEvent<DocumentsChange<T>>) => void | Promise<void>)
165
+ | undefined;
166
+
167
+ if (options?.onChange && options.onChange.merge !== false) {
168
+ const mergeFn =
169
169
  typeof options.onChange.merge === "function"
170
170
  ? options.onChange.merge
171
- : (change: DocumentsChange<T>) => change;
171
+ : (c: DocumentsChange<T>) => c;
172
+
172
173
  handleChange = async (e: CustomEvent<DocumentsChange<T>>) => {
173
- // while we are iterating, we might get new documents.. so this method inserts them where they should be
174
- let filteredChange = await mergeFunction(e.detail);
174
+ log(
175
+ options,
176
+ "Merge change",
177
+ e.detail,
178
+ "iterator",
179
+ newIteratorRef.id
180
+ );
181
+ const filtered = await mergeFn(e.detail);
175
182
  if (
176
- !filteredChange ||
177
- (filteredChange.added.length === 0 &&
178
- filteredChange.removed.length === 0)
179
- ) {
183
+ !filtered ||
184
+ (filtered.added.length === 0 &&
185
+ filtered.removed.length === 0)
186
+ )
180
187
  return;
181
- }
182
- let merged: WithContext<RT>[] = [];
188
+
189
+ let merged: Item[];
183
190
  if (options.onChange?.update) {
184
191
  merged = [
185
- ...options.onChange.update(
186
- allRef.current,
187
- filteredChange
188
- ),
192
+ ...options.onChange?.update(allRef.current, filtered),
189
193
  ];
190
194
  } else {
191
195
  merged = await db.index.updateResults(
192
- allRef.current,
193
- filteredChange,
194
- options?.query || {},
195
- options?.resolve ?? true
196
+ allRef.current as WithContext<RT>[],
197
+ filtered,
198
+ options.query || {},
199
+ options.resolve ?? true
196
200
  );
197
- logWithId(options, "After update", allRef.current, merged);
201
+
202
+ log(options, "After update", allRef.current, merged);
198
203
  const expectedDiff =
199
- filteredChange.added.length -
200
- filteredChange.removed.length;
204
+ filtered.added.length - filtered.removed.length;
201
205
 
202
206
  if (
203
207
  merged === allRef.current ||
@@ -205,23 +209,14 @@ export const useQuery = <
205
209
  merged.length === allRef.current.length)
206
210
  ) {
207
211
  // no change
208
- logWithId(options, "no change after merge");
212
+ log(options, "no change after merge");
209
213
  return;
210
214
  }
211
215
  }
212
216
 
213
- logWithId(options, "handleChange", {
214
- added: e.detail.added.length,
215
- removed: e.detail.removed.length,
216
- merged: merged.length,
217
- allRef: allRef.current.length,
218
- });
219
-
220
- updateAll(
221
- options?.reverse ? merged.reverse() : merged,
222
- e.detail
223
- );
217
+ updateAll(options?.reverse ? merged.reverse() : merged);
224
218
  };
219
+
225
220
  db.events.addEventListener("change", handleChange);
226
221
  }
227
222
 
@@ -232,137 +227,162 @@ export const useQuery = <
232
227
  };
233
228
  }, [
234
229
  db?.closed ? undefined : db?.address,
235
- options?.id != null ? options?.id : options?.query,
230
+ options?.id ?? options?.query,
236
231
  options?.resolve,
232
+ options?.reverse,
233
+ resetCounter,
237
234
  ]);
238
235
 
239
- // Define the loadMore function
236
+ /* ────────────── loadMore (once-wait aware) ────────────── */
240
237
  const batchSize = options?.batchSize ?? 10;
238
+
239
+ const shouldWait = (): boolean => {
240
+ if (waitedOnceRef.current) {
241
+ return false;
242
+ }
243
+ if (options?.remote === false) return false;
244
+ if (options?.remote === true) return true;
245
+ if (options?.remote == null) return true;
246
+ if (typeof options?.remote === "object") {
247
+ return true;
248
+ }
249
+ return true;
250
+ };
251
+
252
+ const reloadAfterTime = (): number | undefined => {
253
+ if (typeof options?.remote === "object" && options.remote.eager) {
254
+ return options.remote.warmup ?? db?.log.timeUntilRoleMaturity;
255
+ }
256
+ return undefined;
257
+ };
258
+
259
+ const markWaited = () => {
260
+ waitedOnceRef.current = true;
261
+ };
262
+
241
263
  const loadMore = async () => {
264
+ const iterator = iteratorRef.current;
242
265
  if (
243
- !iteratorRef.current ||
266
+ !iterator ||
244
267
  emptyResultsRef.current ||
245
- iteratorRef.current.iterator.done() ||
268
+ iterator.iterator.done() ||
246
269
  loadingMoreRef.current
247
270
  ) {
248
- logWithId(options, "loadMore: already loading or no more items", {
249
- isLoading,
250
- emptyResultsRef: emptyResultsRef.current,
251
- iteratorRef: !iteratorRef.current,
252
- });
253
- return;
271
+ return false;
254
272
  }
255
- const iterator = iteratorRef.current;
256
273
 
257
274
  setIsLoading(true);
258
275
  loadingMoreRef.current = true;
276
+
259
277
  try {
260
- // Fetch next batchSize number of items:
261
- logWithId(
262
- options,
263
- "wait for replicators for iterator " + iterator.id
264
- );
265
- if (options?.waitForReplicators !== false) {
266
- let timeout = 5e3;
267
- if (typeof options?.waitForReplicators === "object") {
268
- timeout = options.waitForReplicators.timeout ?? 1e4;
269
- }
270
- await db?.log
278
+ /* ── optional replicate-wait ── */
279
+ if (shouldWait()) {
280
+ log(options, "Wait for replicators", iterator.id);
281
+
282
+ let isEager =
283
+ typeof options?.remote === "object" && options.remote.eager;
284
+ const waitTimeout =
285
+ typeof options?.remote === "object" &&
286
+ typeof options?.remote.warmup === "number"
287
+ ? options?.remote.warmup
288
+ : 5_000;
289
+
290
+ let shouldResetAfterMaturity =
291
+ isEager && !waitedOnceRef.current;
292
+ let promise = db?.log
271
293
  .waitForReplicators({
272
- timeout,
294
+ timeout: waitTimeout,
273
295
  signal: closeControllerRef.current?.signal,
274
296
  })
275
297
  .catch((e) => {
276
- if (e instanceof AbortError) {
277
- // Ignore abort error
298
+ if (
299
+ e instanceof AbortError ||
300
+ e instanceof NoPeersError
301
+ )
278
302
  return;
279
- }
280
303
  console.warn("Remote replicators not ready", e);
304
+ })
305
+ .finally(() => {
306
+ markWaited();
307
+ if (shouldResetAfterMaturity) {
308
+ resetResultsOnReset.current = false; // don't reset results, because we expect to get same or more results
309
+ invokeReset();
310
+ }
281
311
  });
312
+ if (!shouldResetAfterMaturity) {
313
+ await promise;
314
+ }
315
+ } else {
316
+ log(options, "Skip wait for replicators", iterator.id);
282
317
  }
283
318
 
284
- logWithId(
285
- options,
286
- "loadMore: loading more items for iterator " +
287
- iteratorRef.current?.id,
288
- "should resolve?: " + options?.resolve,
289
- "query local?: " + options?.local,
290
- "query remote?: " + options?.remote,
291
- "isReplicating: " + (await db?.log.isReplicating())
292
- );
293
-
294
- let newItems: WithContext<RT>[] = await iterator.iterator.next(
295
- batchSize
296
- );
297
-
319
+ /* ── fetch next batch ── */
320
+ log(options, "Retrieve next batch", iterator.id);
321
+ let newItems = await iterator.iterator.next(batchSize);
298
322
  if (options?.transform) {
299
- newItems = await Promise.all(
300
- newItems.map((item) => options.transform!(item))
301
- );
323
+ log(options, "Transform start", iterator.id);
324
+
325
+ newItems = await Promise.all(newItems.map(options.transform));
326
+ log(options, "Transform end", iterator.id);
302
327
  }
303
328
 
329
+ /* iterator might have been reset while we were async… */
330
+
304
331
  if (iteratorRef.current !== iterator) {
305
- // If the iterator has changed, we should not update the state
306
- // This can happen if the iterator was closed and a new one was created
307
- logWithId(options, "Iterator ref changed, not updating state", {
308
- refBefore: iterator.id,
309
- currentRef: iteratorRef.current?.id,
310
- ignoredItems: newItems.length,
311
- });
312
- return;
332
+ log(options, "Iterator reset while loading more");
333
+ return false;
313
334
  }
314
335
 
315
- logWithId(
316
- options,
317
- "loadMore: loaded more items for iterator " +
318
- iteratorRef.current?.id,
319
- "new items length",
320
- newItems.length,
321
- "all items length",
322
- allRef.current.length
323
- );
336
+ iterator.itemsConsumed += newItems.length;
324
337
 
325
338
  emptyResultsRef.current = newItems.length === 0;
326
339
 
327
- if (newItems.length > 0) {
328
- let prev = allRef.current;
329
- let prevHash = new Set(prev.map((x) => x.__context.head));
330
- let newItemsNoHash = newItems.filter(
331
- (x) => !prevHash.has(x.__context.head)
340
+ if (newItems.length) {
341
+ log(
342
+ "Loaded more items for iterator",
343
+ iterator.id,
344
+ "current id",
345
+ iteratorRef.current?.id,
346
+ "new items",
347
+ newItems.length,
348
+ "previous results",
349
+ allRef.current.length,
350
+ "batchSize",
351
+ batchSize,
352
+ "items consumed",
353
+ iterator.itemsConsumed
332
354
  );
333
- if (newItemsNoHash.length === 0) {
334
- logWithId(
335
- options,
336
- "no new items after dedup, not updating state. Prev length",
337
- prev.length
338
- );
339
- return;
340
- }
341
- const combined = options?.reverse
342
- ? [...newItemsNoHash.reverse(), ...prev]
343
- : [...prev, ...newItemsNoHash];
344
- updateAll(combined, null);
345
- } else {
346
- logWithId(
347
- options,
348
- "no new items, not updating state for iterator " +
349
- iteratorRef.current?.id +
350
- " existing results length",
351
- allRef.current.length
355
+ const prev = allRef.current;
356
+ const dedup = new Set(
357
+ prev.map((x) => (x as any).__context.head)
352
358
  );
353
- }
354
- } catch (error) {
355
- if (error instanceof ClosedError) {
356
- // Handle closed database gracefully
357
- logWithId(options, "Database closed error");
359
+ const unique = newItems.filter(
360
+ (x) => !dedup.has((x as any).__context.head)
361
+ );
362
+ if (!unique.length) return;
363
+
364
+ const combined = reverseRef.current
365
+ ? [...unique.reverse(), ...prev]
366
+ : [...prev, ...unique];
367
+ updateAll(combined);
358
368
  } else {
359
- throw error;
369
+ log(options, "No new items", iterator.id);
360
370
  }
371
+ return !iterator.iterator.done();
372
+ } catch (e) {
373
+ if (!(e instanceof ClosedError)) throw e;
361
374
  } finally {
362
375
  setIsLoading(false);
363
376
  loadingMoreRef.current = false;
364
377
  }
365
378
  };
366
379
 
367
- return { items: all, loadMore, isLoading, empty: emptyResultsRef.current };
380
+ /* ────────────── public API ────────────── */
381
+ return {
382
+ items: all,
383
+ loadMore,
384
+ isLoading,
385
+ empty: () => emptyResultsRef.current,
386
+ id: id,
387
+ };
368
388
  };