@peerbit/document 9.12.17 → 9.13.0-5da22a7

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.
@@ -9,7 +9,7 @@ var __metadata = (this && this.__metadata) || function (k, v) {
9
9
  };
10
10
  import { field, serialize, variant } from "@dao-xyz/borsh";
11
11
  import { Cache } from "@peerbit/cache";
12
- import { PublicSignKey, getPublicKeyFromPeerId, sha256Base64Sync, } from "@peerbit/crypto";
12
+ import { PublicSignKey, sha256Base64Sync, } from "@peerbit/crypto";
13
13
  import * as types from "@peerbit/document-interface";
14
14
  import { CachedIndex } from "@peerbit/indexer-cache";
15
15
  import * as indexerTypes from "@peerbit/indexer-interface";
@@ -19,8 +19,8 @@ import { logger as loggerFn } from "@peerbit/logger";
19
19
  import { ClosedError, Program } from "@peerbit/program";
20
20
  import { MissingResponsesError, RPC, queryAll, } from "@peerbit/rpc";
21
21
  import { SharedLog, } from "@peerbit/shared-log";
22
- import { DataMessage, SilentDelivery } from "@peerbit/stream-interface";
23
- import { AbortError, waitFor } from "@peerbit/time";
22
+ import { DataMessage, SilentDelivery, } from "@peerbit/stream-interface";
23
+ import { AbortError, TimeoutError, waitFor } from "@peerbit/time";
24
24
  import pDefer, {} from "p-defer";
25
25
  import { concat, fromString } from "uint8arrays";
26
26
  import { copySerialization } from "./borsh.js";
@@ -29,6 +29,7 @@ import MostCommonQueryPredictor from "./most-common-query-predictor.js";
29
29
  import { isPutOperation } from "./operation.js";
30
30
  import { Prefetch } from "./prefetch.js";
31
31
  import { ResumableIterators } from "./resumable-iterator.js";
32
+ const WARNING_WHEN_ITERATING_FOR_MORE_THAN = 1e5;
32
33
  const logger = loggerFn({ module: "document-index" });
33
34
  const coerceQuery = (query, options) => {
34
35
  let replicate = typeof options?.remote !== "boolean" ? options?.remote?.replicate : false;
@@ -464,14 +465,13 @@ let DocumentIndex = class DocumentIndex extends Program {
464
465
  ? { ...options.remote }
465
466
  : {};
466
467
  if (baseRemote) {
467
- const prevJoining = baseRemote.joining;
468
- if (!prevJoining ||
469
- prevJoining === true ||
470
- (typeof prevJoining === "object" &&
471
- (prevJoining.waitFor || 0) < options.waitFor)) {
472
- baseRemote.joining = {
473
- ...(typeof prevJoining === "object" ? prevJoining : {}),
474
- waitFor: options.waitFor,
468
+ const waitPolicy = baseRemote.wait;
469
+ if (!waitPolicy ||
470
+ (typeof waitPolicy === "object" &&
471
+ (waitPolicy.timeout || 0) < options.waitFor)) {
472
+ baseRemote.wait = {
473
+ ...(typeof waitPolicy === "object" ? waitPolicy : {}),
474
+ timeout: options.waitFor,
475
475
  };
476
476
  }
477
477
  }
@@ -479,6 +479,7 @@ let DocumentIndex = class DocumentIndex extends Program {
479
479
  let joinListener;
480
480
  if (baseRemote) {
481
481
  joinListener = this.attachJoinListener({
482
+ eager: baseRemote.reach?.eager,
482
483
  onPeer: async (pk) => {
483
484
  if (cleanedUp)
484
485
  return;
@@ -819,6 +820,98 @@ let DocumentIndex = class DocumentIndex extends Program {
819
820
  this._resumableIterators.close({ idString: key });
820
821
  }
821
822
  }
823
+ async waitForCoverReady(params) {
824
+ const { domain, eager, settle, timeout, signal, onTimeout = "proceed", } = params;
825
+ if (settle !== "any") {
826
+ return;
827
+ }
828
+ const properties = domain && "range" in domain
829
+ ? { range: domain.range }
830
+ : { args: domain?.args };
831
+ const selfHash = this.node.identity.publicKey.hashcode();
832
+ const ready = async () => {
833
+ const cover = await this._log.getCover(properties, { eager });
834
+ return cover.some((hash) => hash !== selfHash);
835
+ };
836
+ if (await ready()) {
837
+ return;
838
+ }
839
+ const deferred = pDefer();
840
+ let settled = false;
841
+ let cleaned = false;
842
+ let timer;
843
+ let checking = false;
844
+ const cleanup = () => {
845
+ if (cleaned) {
846
+ return;
847
+ }
848
+ cleaned = true;
849
+ this._log.events.removeEventListener("replicator:join", onEvent);
850
+ this._log.events.removeEventListener("replication:change", onEvent);
851
+ this._log.events.removeEventListener("replicator:mature", onEvent);
852
+ signal?.removeEventListener("abort", onAbort);
853
+ if (timer != null) {
854
+ clearTimeout(timer);
855
+ timer = undefined;
856
+ }
857
+ };
858
+ const resolve = () => {
859
+ if (settled) {
860
+ return;
861
+ }
862
+ settled = true;
863
+ cleanup();
864
+ deferred.resolve();
865
+ };
866
+ const reject = (error) => {
867
+ if (settled) {
868
+ return;
869
+ }
870
+ settled = true;
871
+ cleanup();
872
+ deferred.reject(error);
873
+ };
874
+ const onAbort = () => reject(new AbortError());
875
+ const onEvent = async () => {
876
+ if (checking) {
877
+ return;
878
+ }
879
+ checking = true;
880
+ try {
881
+ if (await ready()) {
882
+ resolve();
883
+ }
884
+ }
885
+ catch (error) {
886
+ reject(error instanceof Error ? error : new Error(String(error)));
887
+ }
888
+ finally {
889
+ checking = false;
890
+ }
891
+ };
892
+ if (signal) {
893
+ signal.addEventListener("abort", onAbort);
894
+ }
895
+ if (timeout > 0) {
896
+ timer = setTimeout(() => {
897
+ if (onTimeout === "error") {
898
+ reject(new TimeoutError("Timeout waiting for participating replicator"));
899
+ }
900
+ else {
901
+ resolve();
902
+ }
903
+ }, timeout);
904
+ }
905
+ this._log.events.addEventListener("replicator:join", onEvent);
906
+ this._log.events.addEventListener("replication:change", onEvent);
907
+ this._log.events.addEventListener("replicator:mature", onEvent);
908
+ try {
909
+ await deferred.promise;
910
+ }
911
+ finally {
912
+ cleanup();
913
+ }
914
+ }
822
915
  // Utility: attach a join listener that waits until a peer is a replicator,
823
916
  // then invokes the provided callback. Returns a detach function.
824
917
  attachJoinListener(params) {
@@ -837,6 +930,7 @@ let DocumentIndex = class DocumentIndex extends Program {
837
930
  await this._log
838
931
  .waitForReplicator(pk, {
839
932
  signal: params.signal,
933
+ eager: params.eager,
840
934
  })
841
935
  .catch(() => undefined);
842
936
  if (params.signal?.aborted)
@@ -865,7 +959,7 @@ let DocumentIndex = class DocumentIndex extends Program {
865
959
  * @param options
866
960
  * @returns
867
961
  */
868
- async queryCommence(queryRequest, options) {
962
+ async queryCommence(queryRequest, options, fetchFirstForRemote) {
869
963
  const local = typeof options?.local === "boolean" ? options?.local : true;
870
964
  let remote = undefined;
871
965
  if (typeof options?.remote === "boolean") {
@@ -907,8 +1001,8 @@ let DocumentIndex = class DocumentIndex extends Program {
907
1001
  ? options?.remote?.from
908
1002
  : await this._log.getCover(remote.domain ?? { args: undefined }, {
909
1003
  roleAge: remote.minAge,
910
- eager: remote.eager,
911
- reachableOnly: !!remote.joining, // when we want to merge joining we can ignore pending to be online peers and instead consider them once they become online
1004
+ eager: remote.reach?.eager,
1005
+ reachableOnly: !!remote.wait, // when we want to merge joining we can ignore pending to be online peers and instead consider them once they become online
912
1006
  });
913
1007
  if (replicatorGroups) {
914
1008
  const responseHandler = async (results) => {
@@ -923,6 +1017,11 @@ let DocumentIndex = class DocumentIndex extends Program {
923
1017
  if (hash === this.node.identity.publicKey.hashcode()) {
924
1018
  return false;
925
1019
  }
1020
+ if (fetchFirstForRemote?.has(hash)) {
1021
+ // we already fetched this one for remote, no need to do it again
1022
+ return false;
1023
+ }
1024
+ fetchFirstForRemote?.add(hash);
926
1025
  const resultAlready = this._prefetch?.accumulator.consume(queryRequest, hash);
927
1026
  if (resultAlready) {
928
1027
  (extraPromises || (extraPromises = [])).push((async () => {
@@ -1105,6 +1204,7 @@ let DocumentIndex = class DocumentIndex extends Program {
1105
1204
  const peerBufferMap = new Map();
1106
1205
  const visited = new Set();
1107
1206
  let done = false;
1207
+ let drain = false; // if true, close on empty once (overrides manual)
1108
1208
  let first = false;
1109
1209
  // TODO handle join/leave while iterating
1110
1210
  const controller = new AbortController();
@@ -1113,60 +1213,88 @@ let DocumentIndex = class DocumentIndex extends Program {
1113
1213
  const peerBuffers = () => {
1114
1214
  return [...peerBufferMap.values()].map((x) => x.buffer).flat();
1115
1215
  };
1116
- let maybeSetDone;
1117
- let unsetDone;
1118
- let cleanup = () => { };
1119
- if (typeof options?.remote === "object" && options.remote.joining) {
1120
- let t0 = +new Date();
1121
- let waitForTime = typeof options.remote.joining === "boolean"
1122
- ? DEFAULT_TIMEOUT
1123
- : (options.remote.joining.waitFor ?? DEFAULT_TIMEOUT);
1124
- let setDoneIfTimeout = false;
1125
- maybeSetDone = () => {
1126
- if (t0 + waitForTime < +new Date()) {
1127
- cleanup();
1128
- done = true;
1129
- }
1130
- else {
1131
- setDoneIfTimeout = true;
1132
- }
1133
- };
1134
- unsetDone = () => {
1135
- setDoneIfTimeout = false;
1136
- done = false;
1137
- };
1138
- let timeout = setTimeout(() => {
1139
- if (setDoneIfTimeout) {
1140
- cleanup();
1141
- done = true;
1142
- }
1143
- }, waitForTime);
1144
- cleanup = () => {
1145
- this.clearResultsQueue(queryRequestCoerced);
1146
- clearTimeout(timeout);
1147
- };
1148
- }
1149
- else {
1150
- maybeSetDone = () => {
1151
- cleanup();
1152
- done = true;
1153
- };
1154
- unsetDone = () => {
1155
- cleanup();
1156
- done = false;
1157
- };
1158
- cleanup = () => {
1159
- this.clearResultsQueue(queryRequestCoerced);
1160
- };
1216
+ let maybeSetDone = () => {
1217
+ cleanup();
1218
+ done = true;
1219
+ };
1220
+ let unsetDone = () => {
1221
+ cleanup();
1222
+ done = false;
1223
+ };
1224
+ let cleanup = () => {
1225
+ this.clearResultsQueue(queryRequestCoerced);
1226
+ };
1227
+ let warmupPromise = undefined;
1228
+ if (typeof options?.remote === "object") {
1229
+ let waitForTime = undefined;
1230
+ if (options.remote.wait) {
1231
+ let t0 = +new Date();
1232
+ waitForTime =
1233
+ typeof options.remote.wait === "boolean"
1234
+ ? DEFAULT_TIMEOUT
1235
+ : (options.remote.wait.timeout ?? DEFAULT_TIMEOUT);
1236
+ let setDoneIfTimeout = false;
1237
+ maybeSetDone = () => {
1238
+ if (t0 + waitForTime < +new Date()) {
1239
+ cleanup();
1240
+ done = true;
1241
+ }
1242
+ else {
1243
+ setDoneIfTimeout = true;
1244
+ }
1245
+ };
1246
+ unsetDone = () => {
1247
+ setDoneIfTimeout = false;
1248
+ done = false;
1249
+ };
1250
+ let timeout = setTimeout(() => {
1251
+ if (setDoneIfTimeout) {
1252
+ cleanup();
1253
+ done = true;
1254
+ }
1255
+ }, waitForTime);
1256
+ cleanup = () => {
1257
+ this.clearResultsQueue(queryRequestCoerced);
1258
+ clearTimeout(timeout);
1259
+ };
1260
+ }
1261
+ if (options.remote.reach?.discover) {
1262
+ warmupPromise = this.waitFor(options.remote.reach.discover, {
1263
+ signal: controller.signal,
1264
+ seek: "present",
1265
+ timeout: waitForTime ?? DEFAULT_TIMEOUT,
1266
+ });
1267
+ options.remote.reach.eager = true; // include the results from the discovered peer even if it is not mature
1268
+ }
1269
+ const waitPolicy = typeof options.remote.wait === "object"
1270
+ ? options.remote.wait
1271
+ : undefined;
1272
+ if (waitPolicy?.behavior === "block" &&
1273
+ (waitPolicy.until ?? "any") === "any") {
1274
+ const blockPromise = this.waitForCoverReady({
1275
+ domain: options.remote.domain,
1276
+ eager: options.remote.reach?.eager,
1277
+ settle: "any",
1278
+ timeout: waitPolicy.timeout ?? DEFAULT_TIMEOUT,
1279
+ signal: controller.signal,
1280
+ onTimeout: waitPolicy.onTimeout,
1281
+ });
1282
+ warmupPromise = warmupPromise
1283
+ ? Promise.all([warmupPromise, blockPromise]).then(() => undefined)
1284
+ : blockPromise;
1285
+ }
1161
1286
  }
1162
1287
  const fetchFirst = async (n, fetchOptions) => {
1288
+ await warmupPromise;
1163
1289
  let hasMore = false;
1164
1290
  queryRequestCoerced.fetch = n;
1165
1291
  await this.queryCommence(queryRequestCoerced, {
1166
1292
  local: fetchOptions?.from != null ? false : options?.local,
1167
1293
  remote: options?.remote !== false
1168
1294
  ? {
1169
- ...(typeof options?.remote === "object" ? options.remote : {}),
1295
+ ...(typeof options?.remote === "object"
1296
+ ? options.remote
1297
+ : {}),
1170
1298
  from: fetchOptions?.from,
1171
1299
  }
1172
1300
  : false,
@@ -1226,7 +1354,7 @@ let DocumentIndex = class DocumentIndex extends Program {
1226
1354
  throw new Error("Unsupported result type: " + response?.constructor?.name);
1227
1355
  }
1228
1356
  },
1229
- });
1357
+ }, fetchOptions?.fetchedFirstForRemote);
1230
1358
  if (!hasMore) {
1231
1359
  maybeSetDone();
1232
1360
  }
@@ -1263,7 +1391,8 @@ let DocumentIndex = class DocumentIndex extends Program {
1263
1391
  amount: n - buffer.buffer.length,
1264
1392
  });
1265
1393
  // Fetch locally?
1266
- if (peer === this.node.identity.publicKey.hashcode()) {
1394
+ if (peer === this.node.identity.publicKey.hashcode() &&
1395
+ this._resumableIterators.has(queryRequestCoerced.idString)) {
1267
1396
  promises.push(this.processQuery(collectRequest, this.node.identity.publicKey, true)
1268
1397
  .then(async (results) => {
1269
1398
  resultsLeft += Number(results.kept);
@@ -1398,9 +1527,10 @@ let DocumentIndex = class DocumentIndex extends Program {
1398
1527
  // get n next top entries, shift and pull more results
1399
1528
  const peerBuffersArr = peerBuffers();
1400
1529
  const results = peerBuffersArr.sort((a, b) => indexerTypes.extractSortCompare(a.indexed, b.indexed, queryRequestCoerced.sort));
1401
- const pendingMoreResults = n < results.length;
1402
1530
  lastValueInOrder = results[0] || lastValueInOrder;
1531
+ const pendingMoreResults = n < results.length; // check if there are more results to fetch, before splicing
1403
1532
  const batch = results.splice(0, n);
1533
+ const hasMore = !fetchedAll || pendingMoreResults;
1404
1534
  for (const result of batch) {
1405
1535
  const arr = peerBufferMap.get(result.from.hashcode());
1406
1536
  if (!arr) {
@@ -1412,7 +1542,6 @@ let DocumentIndex = class DocumentIndex extends Program {
1412
1542
  arr.buffer.splice(idx, 1);
1413
1543
  }
1414
1544
  }
1415
- const hasMore = !fetchedAll || pendingMoreResults;
1416
1545
  if (hasMore) {
1417
1546
  unsetDone();
1418
1547
  }
@@ -1435,6 +1564,7 @@ let DocumentIndex = class DocumentIndex extends Program {
1435
1564
  else {
1436
1565
  coercedBatch = batch.map((x) => coerceWithContext(coerceWithIndexed(x.value, x.indexed), x.context));
1437
1566
  }
1567
+ // no extra queued-first/last in simplified API
1438
1568
  return dedup(coercedBatch, this.indexByResolver);
1439
1569
  };
1440
1570
  let cleanupAndDone = () => {
@@ -1471,19 +1601,107 @@ let DocumentIndex = class DocumentIndex extends Program {
1471
1601
  return done;
1472
1602
  };
1473
1603
  let joinListener;
1604
+ let fetchedFirstForRemote = undefined;
1474
1605
  let updateDeferred;
1475
1606
  const signalUpdate = () => updateDeferred?.resolve();
1476
- const waitForUpdate = () => updateDeferred ? updateDeferred.promise : Promise.resolve();
1477
- if (typeof options?.remote === "object" && options?.remote.joining) {
1607
+ const _waitForUpdate = () => updateDeferred ? updateDeferred.promise : Promise.resolve();
1608
+ // ---------------- Live updates wiring (sorted-only with optional filter) ----------------
1609
+ const normalizeUpdatesOption = (u) => {
1610
+ if (u == null || u === false)
1611
+ return undefined;
1612
+ if (u === true)
1613
+ return {
1614
+ merge: {
1615
+ filter: (evt) => evt,
1616
+ },
1617
+ };
1618
+ if (typeof u === "object") {
1619
+ return {
1620
+ merge: u.merge
1621
+ ? {
1622
+ filter: typeof u.merge === "object" ? u.merge.filter : (evt) => evt,
1623
+ }
1624
+ : {},
1625
+ };
1626
+ }
1627
+ return undefined;
1628
+ };
1629
+ const mergePolicy = normalizeUpdatesOption(options?.updates);
1630
+ const hasLiveUpdates = mergePolicy !== undefined;
1631
+ // sorted-only mode: no per-queue handling
1632
+ // If live updates enabled, ensure deferred exists so awaiting paths can block until changes
1633
+ if (hasLiveUpdates && !updateDeferred) {
1634
+ updateDeferred = pDefer();
1635
+ }
1636
+ let updatesCleanup;
1637
+ if (hasLiveUpdates) {
1638
+ const localHash = this.node.identity.publicKey.hashcode();
1639
+ if (mergePolicy?.merge) {
1640
+ // Ensure local buffer exists for sorted merging
1641
+ if (!peerBufferMap.has(localHash)) {
1642
+ peerBufferMap.set(localHash, { kept: 0, buffer: [] });
1643
+ }
1644
+ }
1645
+ const onChange = async (evt) => {
1646
+ // Optional filter to mutate/suppress change events
1647
+ let filtered = evt.detail;
1648
+ if (mergePolicy?.merge?.filter) {
1649
+ filtered = await mergePolicy.merge?.filter(evt.detail);
1650
+ }
1651
+ if (filtered) {
1652
+ // Remove entries that were deleted from all pending structures
1653
+ if (filtered.removed?.length) {
1654
+ // Remove from peer buffers
1655
+ for (const [_peer, entry] of peerBufferMap) {
1656
+ entry.buffer = entry.buffer.filter((x) => {
1657
+ const id = indexerTypes.toId(this.indexByResolver(x.indexed)).primitive;
1658
+ return !filtered.removed.some((r) => indexerTypes.toId(this.indexByResolver(r.__indexed))
1659
+ .primitive === id);
1660
+ });
1661
+ }
1662
+ // no non-sorted queues in simplified mode
1663
+ }
1664
+ // Add new entries per strategy (sorted-only)
1665
+ if (filtered.added?.length) {
1666
+ const buf = peerBufferMap.get(localHash);
1667
+ for (const added of filtered.added) {
1668
+ const id = indexerTypes.toId(this.indexByResolver(added.__indexed)).primitive;
1669
+ if (visited.has(id))
1670
+ continue; // already presented
1671
+ visited.add(id);
1672
+ buf.buffer.push({
1673
+ value: (resolve ? added : added.__indexed),
1674
+ context: added.__context,
1675
+ from: this.node.identity.publicKey,
1676
+ indexed: coerceWithContext(added.__indexed, added.__context),
1677
+ });
1678
+ }
1679
+ buf.kept = buf.buffer.length;
1680
+ }
1681
+ }
1682
+ typeof options?.updates === "object" &&
1683
+ options?.updates?.onChange?.(evt.detail);
1684
+ signalUpdate();
1685
+ };
1686
+ this.documentEvents.addEventListener("change", onChange);
1687
+ updatesCleanup = () => {
1688
+ this.documentEvents.removeEventListener("change", onChange);
1689
+ };
1690
+ const cleanupDefaultUpdates = cleanup;
1691
+ cleanup = () => {
1692
+ updatesCleanup?.();
1693
+ return cleanupDefaultUpdates();
1694
+ };
1695
+ }
1696
+ if (typeof options?.remote === "object" && options?.remote.wait) {
1478
1697
  // was used to account for missed results when a peer joins; omitted in this minimal handler
1479
1698
  updateDeferred = pDefer();
1480
1699
  // derive optional onMissedResults callback if provided
1481
- let onMissedResults = typeof options?.remote?.joining === "object" &&
1482
- typeof options?.remote.joining.onMissedResults === "function"
1483
- ? options.remote.joining.onMissedResults
1700
+ let onMissedResults = typeof options?.remote?.wait === "object" &&
1701
+ typeof options?.remote.onLateResults === "function"
1702
+ ? options.remote.onLateResults
1484
1703
  : undefined;
1485
- const waitForTime = typeof options.remote.joining === "object" &&
1486
- options.remote.joining.waitFor;
1704
+ const waitForTime = typeof options.remote.wait === "object" && options.remote.wait.timeout;
1487
1705
  const prevMaybeSetDone = maybeSetDone;
1488
1706
  maybeSetDone = () => {
1489
1707
  prevMaybeSetDone();
@@ -1495,16 +1713,25 @@ let DocumentIndex = class DocumentIndex extends Program {
1495
1713
  signalUpdate();
1496
1714
  }, waitForTime);
1497
1715
  controller.signal.addEventListener("abort", () => signalUpdate());
1716
+ fetchedFirstForRemote = new Set();
1498
1717
  joinListener = this.attachJoinListener({
1499
1718
  signal: controller.signal,
1719
+ eager: options.remote.reach?.eager,
1500
1720
  onPeer: async (pk) => {
1501
1721
  if (done)
1502
1722
  return;
1503
1723
  const hash = pk.hashcode();
1724
+ await fetchPromise; // ensure fetches in flight are done
1504
1725
  if (peerBufferMap.has(hash))
1505
1726
  return;
1727
+ if (fetchedFirstForRemote.has(hash))
1728
+ return;
1506
1729
  if (totalFetchedCounter > 0) {
1507
- await fetchFirst(totalFetchedCounter, { from: [hash] });
1730
+ fetchPromise = fetchFirst(totalFetchedCounter, {
1731
+ from: [hash],
1732
+ fetchedFirstForRemote,
1733
+ });
1734
+ await fetchPromise;
1508
1735
  if (onMissedResults) {
1509
1736
  const pending = peerBufferMap.get(hash)?.buffer;
1510
1737
  if (pending && pending.length > 0) {
@@ -1534,6 +1761,25 @@ let DocumentIndex = class DocumentIndex extends Program {
1534
1761
  return cleanupDefault();
1535
1762
  };
1536
1763
  }
1764
+ if (options?.closePolicy === "manual") {
1765
+ let prevMaybeSetDone = maybeSetDone;
1766
+ maybeSetDone = () => {
1767
+ if (drain) {
1768
+ prevMaybeSetDone();
1769
+ }
1770
+ };
1771
+ }
1772
+ const remoteWaitActive = typeof options?.remote === "object" && !!options.remote.wait;
1773
+ const waitForUpdateAndResetDeferred = async () => {
1774
+ if (remoteWaitActive) {
1775
+ // wait until: join fetch adds results, cleanup runs, or the join-wait times out
1776
+ await _waitForUpdate();
1777
+ // re-arm the deferred for the next cycle (only if joining is enabled and we're not done)
1778
+ if (updateDeferred && !doneFn()) {
1779
+ updateDeferred = pDefer();
1780
+ }
1781
+ }
1782
+ };
1537
1783
  return {
1538
1784
  close,
1539
1785
  next,
@@ -1546,24 +1792,22 @@ let DocumentIndex = class DocumentIndex extends Program {
1546
1792
  return kept; // TODO this should be more accurate
1547
1793
  },
1548
1794
  all: async () => {
1795
+ drain = true;
1549
1796
  let result = [];
1550
1797
  let c = 0;
1551
1798
  while (doneFn() !== true) {
1552
- c++;
1553
1799
  let batch = await next(100);
1554
- if (c > 100) {
1555
- break;
1800
+ c += batch.length;
1801
+ if (c > WARNING_WHEN_ITERATING_FOR_MORE_THAN) {
1802
+ logger.warn("Iterating for more than " +
1803
+ WARNING_WHEN_ITERATING_FOR_MORE_THAN +
1804
+ " results");
1556
1805
  }
1557
1806
  if (batch.length > 0) {
1558
1807
  result.push(...batch);
1559
1808
  continue;
1560
1809
  }
1561
- // wait until: join fetch adds results, cleanup runs, or the join-wait times out
1562
- await waitForUpdate();
1563
- // re-arm the deferred for the next cycle (only if joining is enabled and we’re not done)
1564
- if (updateDeferred && !doneFn()) {
1565
- updateDeferred = pDefer();
1566
- }
1810
+ await waitForUpdateAndResetDeferred();
1567
1811
  }
1568
1812
  cleanupAndDone();
1569
1813
  return result;
@@ -1576,6 +1820,24 @@ let DocumentIndex = class DocumentIndex extends Program {
1576
1820
  cleanupAndDone();
1577
1821
  return batch[0];
1578
1822
  },
1823
+ [Symbol.asyncIterator]: async function* () {
1824
+ drain = true;
1825
+ let c = 0;
1826
+ while (doneFn() !== true) {
1827
+ const batch = await next(100);
1828
+ c += batch.length;
1829
+ if (c > WARNING_WHEN_ITERATING_FOR_MORE_THAN) {
1830
+ logger.warn("Iterating for more than " +
1831
+ WARNING_WHEN_ITERATING_FOR_MORE_THAN +
1832
+ " results");
1833
+ }
1834
+ for (const entry of batch) {
1835
+ yield entry;
1836
+ }
1837
+ await waitForUpdateAndResetDeferred();
1838
+ }
1839
+ cleanupAndDone();
1840
+ },
1579
1841
  };
1580
1842
  }
1581
1843
  async updateResults(into, change, query, resolve) {
@@ -1659,17 +1921,12 @@ let DocumentIndex = class DocumentIndex extends Program {
1659
1921
  return all.map((x) => x.value);
1660
1922
  }
1661
1923
  async waitFor(other, options) {
1662
- await super.waitFor(other, options);
1663
- const ids = Array.isArray(other) ? other : [other];
1664
- const expectedHashes = new Set(ids.map((x) => typeof x === "string"
1665
- ? x
1666
- : x instanceof PublicSignKey
1667
- ? x.hashcode()
1668
- : getPublicKeyFromPeerId(x).hashcode()));
1669
- for (const key of expectedHashes) {
1924
+ const hashes = await super.waitFor(other, options);
1925
+ for (const key of hashes) {
1670
1926
  await waitFor(async () => (await this._log.replicationIndex.count({ query: { hash: key } })) >
1671
1927
  0, options);
1672
1928
  }
1929
+ return hashes;
1673
1930
  }
1674
1931
  };
1675
1932
  __decorate([