@peerbit/document 13.1.6 → 13.1.8

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/src/search.ts CHANGED
@@ -47,7 +47,7 @@ import {
47
47
  } from "@peerbit/stream-interface";
48
48
  import { AbortError, TimeoutError, waitFor } from "@peerbit/time";
49
49
  import pDefer, { type DeferredPromise } from "p-defer";
50
- import { concat, fromString } from "uint8arrays";
50
+ import { concat, equals, fromString } from "uint8arrays";
51
51
  import { copySerialization } from "./borsh.js";
52
52
  import { MAX_BATCH_SIZE } from "./constants.js";
53
53
  import type { DocumentEvents, DocumentsChange } from "./events.js";
@@ -511,6 +511,7 @@ type QueryDetailedOptions<
511
511
  from: PublicSignKey,
512
512
  ) => void | Promise<void>;
513
513
  onMissingResponses?: (error: MissingResponsesError) => void | Promise<void>;
514
+ onRemoteTargets?: (targets: string[]) => void;
514
515
  remote?: {
515
516
  from?: string[]; // if specified, only query these peers
516
517
  };
@@ -742,6 +743,7 @@ function isSubclassOf(
742
743
  const DEFAULT_TIMEOUT = 1e4;
743
744
  const DEFAULT_KEEP_REMOTE_ITERATOR_TIMEOUT = 3e5;
744
745
  const DISCOVER_TIMEOUT_FALLBACK = 500;
746
+ const CLOSE_ITERATOR_REQUEST_TIMEOUT = 5e3;
745
747
 
746
748
  const DEFAULT_INDEX_BY = "id";
747
749
 
@@ -4283,6 +4285,7 @@ export class DocumentIndex<
4283
4285
  let extraPromises: Promise<void>[] | undefined = undefined;
4284
4286
 
4285
4287
  const seenRemoteHashes = new Set<string>();
4288
+ const selectedRemoteHashes: string[] = [];
4286
4289
  const groupHashes: string[][] = replicatorGroups
4287
4290
  .filter((hash) => {
4288
4291
  if (hash === this.node.identity.publicKey.hashcode()) {
@@ -4299,6 +4302,7 @@ export class DocumentIndex<
4299
4302
  return false;
4300
4303
  }
4301
4304
  fetchFirstForRemote?.add(hash);
4305
+ selectedRemoteHashes.push(hash);
4302
4306
 
4303
4307
  const resultAlready = this._prefetch?.accumulator.consume(
4304
4308
  queryRequest,
@@ -4324,6 +4328,7 @@ export class DocumentIndex<
4324
4328
  })
4325
4329
  .map((x) => [x]);
4326
4330
 
4331
+ options?.onRemoteTargets?.(selectedRemoteHashes);
4327
4332
  extraPromises && (await Promise.all(extraPromises));
4328
4333
  let tearDown: (() => void) | undefined = undefined;
4329
4334
  const search = this;
@@ -4482,23 +4487,25 @@ export class DocumentIndex<
4482
4487
 
4483
4488
  const allResults: ValueTypeFromRequest<Resolve, T, I>[] = [];
4484
4489
 
4485
- while (
4486
- iterator.done() !== true &&
4487
- coercedRequest.fetch > allResults.length
4488
- ) {
4489
- // We might need to pull .next multiple time due to data message size limitations
4490
+ try {
4491
+ while (
4492
+ iterator.done() !== true &&
4493
+ coercedRequest.fetch > allResults.length
4494
+ ) {
4495
+ // We might need to pull .next multiple time due to data message size limitations
4490
4496
 
4491
- for (const result of await iterator.next(
4492
- coercedRequest.fetch - allResults.length,
4493
- )) {
4494
- allResults.push(result as ValueTypeFromRequest<Resolve, T, I>);
4497
+ for (const result of await iterator.next(
4498
+ coercedRequest.fetch - allResults.length,
4499
+ )) {
4500
+ allResults.push(result as ValueTypeFromRequest<Resolve, T, I>);
4501
+ }
4495
4502
  }
4496
- }
4497
-
4498
- await iterator.close();
4499
4503
 
4500
- // Deduplicate and return values directly
4501
- return dedup(allResults, this.indexByResolver);
4504
+ // Deduplicate and return values directly
4505
+ return dedup(allResults, this.indexByResolver);
4506
+ } finally {
4507
+ await iterator.close();
4508
+ }
4502
4509
  }
4503
4510
 
4504
4511
  private resolveIndexed<R>(
@@ -4790,6 +4797,50 @@ export class DocumentIndex<
4790
4797
  buffer: BufferedResult<types.ResultTypeFromRequest<R, T, I> | I, I>[];
4791
4798
  }
4792
4799
  > = new Map();
4800
+ const remoteIteratorPeersToClose = new Set<string>();
4801
+ const staleRemoteIteratorIdsToClose = new Map<string, Uint8Array[]>();
4802
+ const retiredRemoteIteratorPeers = new Set<string>();
4803
+ const pendingMissingResponseRetryPeers = new Set<string>();
4804
+ const missingResponseRetryAttempts = new Map<string, number>();
4805
+ const maxMissingResponseRetryAttempts = 2;
4806
+ const retireRemoteIteratorPeer = (peer: string) => {
4807
+ remoteIteratorPeersToClose.add(peer);
4808
+ retiredRemoteIteratorPeers.add(peer);
4809
+ const peerBuffer = peerBufferMap.get(peer);
4810
+ if (!peerBuffer || peerBuffer.buffer.length === 0) {
4811
+ peerBufferMap.delete(peer);
4812
+ } else {
4813
+ // Keep already-received values available to the caller, but do not
4814
+ // issue another CollectNextRequest until a fresh iteration succeeds.
4815
+ peerBuffer.kept = 0;
4816
+ }
4817
+ };
4818
+ const recordMissingResponseGroups = (missingGroups: string[][]) => {
4819
+ const selfHash = this.node.identity.publicKey.hashcode();
4820
+ for (const group of missingGroups) {
4821
+ for (const hash of group) {
4822
+ if (hash && hash !== selfHash) {
4823
+ retireRemoteIteratorPeer(hash);
4824
+ }
4825
+ }
4826
+
4827
+ if (!retryMissingResponseGroups) {
4828
+ continue;
4829
+ }
4830
+
4831
+ const target = group.find((hash) => {
4832
+ if (!hash || hash === selfHash) return false;
4833
+ const attempts = missingResponseRetryAttempts.get(hash) ?? 0;
4834
+ return attempts < maxMissingResponseRetryAttempts;
4835
+ });
4836
+ if (!target) continue;
4837
+ pendingMissingResponseRetryPeers.add(target);
4838
+ missingResponseRetryAttempts.set(
4839
+ target,
4840
+ (missingResponseRetryAttempts.get(target) ?? 0) + 1,
4841
+ );
4842
+ }
4843
+ };
4793
4844
  const visited = new Set<indexerTypes.IdPrimitive>();
4794
4845
  let indexedPlaceholders:
4795
4846
  | Map<
@@ -5021,6 +5072,17 @@ export class DocumentIndex<
5021
5072
  n: number,
5022
5073
  fetchOptions?: { from?: string[]; fetchedFirstForRemote?: Set<string> },
5023
5074
  ): Promise<boolean> => {
5075
+ const remoteRequestOptions =
5076
+ typeof options?.remote === "object" ? options.remote : undefined;
5077
+ const fetchSignals = [
5078
+ options?.signal,
5079
+ remoteRequestOptions?.signal,
5080
+ ensureController().signal,
5081
+ ].filter((signal): signal is AbortSignal => signal != null);
5082
+ const fetchSignal =
5083
+ fetchSignals.length === 1
5084
+ ? fetchSignals[0]
5085
+ : AbortSignal.any(fetchSignals);
5024
5086
  await warmupPromise;
5025
5087
  let hasMore = false;
5026
5088
  let missingResponses = false;
@@ -5036,33 +5098,43 @@ export class DocumentIndex<
5036
5098
  typeof options?.remote === "object" &&
5037
5099
  options.remote.reach?.discover &&
5038
5100
  discoveredTargetHashes?.length === 0;
5101
+ const queryRemote =
5102
+ options?.remote !== false && !skipRemoteDueToDiscovery;
5103
+ const remoteFrom =
5104
+ fetchOptions?.from ??
5105
+ initialRemoteTargets ??
5106
+ remoteRequestOptions?.from;
5107
+ if (queryRemote) {
5108
+ const selfHash = this.node.identity.publicKey.hashcode();
5109
+ for (const peer of remoteFrom ?? []) {
5110
+ if (peer !== selfHash) {
5111
+ remoteIteratorPeersToClose.add(peer);
5112
+ }
5113
+ }
5114
+ }
5039
5115
 
5040
5116
  queryRequestCoerced.fetch = n;
5041
5117
  await this.queryCommence(
5042
5118
  queryRequestCoerced,
5043
5119
  {
5044
5120
  local: fetchOptions?.from != null ? false : options?.local,
5045
- remote:
5046
- options?.remote !== false && !skipRemoteDueToDiscovery
5047
- ? {
5048
- ...(typeof options?.remote === "object"
5049
- ? options.remote
5050
- : {}),
5051
- from:
5052
- fetchOptions?.from ??
5053
- initialRemoteTargets ??
5054
- (typeof options?.remote === "object"
5055
- ? options.remote.from
5056
- : undefined),
5057
- }
5058
- : false,
5121
+ remote: queryRemote
5122
+ ? {
5123
+ ...remoteRequestOptions,
5124
+ from: remoteFrom,
5125
+ signal: fetchSignal,
5126
+ }
5127
+ : false,
5059
5128
  resolve,
5060
- signal: options?.signal,
5129
+ signal: fetchSignal,
5061
5130
  onResponse: async (response, from) => {
5062
5131
  if (!from) {
5063
5132
  logger.error("Missing response from");
5064
5133
  return;
5065
5134
  }
5135
+ const fromHash = from.hashcode();
5136
+ remoteIteratorPeersToClose.add(fromHash);
5137
+ retiredRemoteIteratorPeers.delete(fromHash);
5066
5138
  if (response instanceof types.NoAccess) {
5067
5139
  logger.error("Dont have access");
5068
5140
  return;
@@ -5071,7 +5143,7 @@ export class DocumentIndex<
5071
5143
  types.ResultTypeFromRequest<R, T, I>
5072
5144
  >;
5073
5145
 
5074
- const existingBuffer = peerBufferMap.get(from.hashcode());
5146
+ const existingBuffer = peerBufferMap.get(fromHash);
5075
5147
  const buffer: BufferedResult<
5076
5148
  types.ResultTypeFromRequest<R, T, I> | I,
5077
5149
  I
@@ -5079,10 +5151,12 @@ export class DocumentIndex<
5079
5151
 
5080
5152
  if (results.kept === 0n && results.results.length === 0) {
5081
5153
  if (keepRemoteAlive) {
5082
- peerBufferMap.set(from.hashcode(), {
5154
+ peerBufferMap.set(fromHash, {
5083
5155
  buffer,
5084
5156
  kept: 0,
5085
5157
  });
5158
+ } else {
5159
+ remoteIteratorPeersToClose.delete(fromHash);
5086
5160
  }
5087
5161
  return;
5088
5162
  }
@@ -5097,6 +5171,8 @@ export class DocumentIndex<
5097
5171
 
5098
5172
  if (effectiveKept > 0) {
5099
5173
  hasMore = true;
5174
+ } else if (!keepRemoteAlive) {
5175
+ remoteIteratorPeersToClose.delete(fromHash);
5100
5176
  }
5101
5177
 
5102
5178
  for (const result of results.results) {
@@ -5160,7 +5236,7 @@ export class DocumentIndex<
5160
5236
  }
5161
5237
  }
5162
5238
 
5163
- peerBufferMap.set(from.hashcode(), {
5239
+ peerBufferMap.set(fromHash, {
5164
5240
  buffer,
5165
5241
  kept: effectiveKept,
5166
5242
  });
@@ -5172,9 +5248,6 @@ export class DocumentIndex<
5172
5248
  },
5173
5249
  onMissingResponses: (error) => {
5174
5250
  missingResponses = true;
5175
- if (!retryMissingResponseGroups) {
5176
- return;
5177
- }
5178
5251
  const missingGroups = (
5179
5252
  error as MissingResponsesError & {
5180
5253
  missingGroups?: string[][];
@@ -5183,27 +5256,22 @@ export class DocumentIndex<
5183
5256
  if (!missingGroups?.length) {
5184
5257
  return;
5185
5258
  }
5186
-
5187
- const selfHash = this.node.identity.publicKey.hashcode();
5188
- for (const group of missingGroups) {
5189
- const target = group.find((hash) => {
5190
- if (!hash || hash === selfHash) return false;
5191
- const attempts = missingResponseRetryAttempts.get(hash) ?? 0;
5192
- return attempts < maxMissingResponseRetryAttempts;
5193
- });
5194
- if (!target) continue;
5195
- pendingMissingResponseRetryPeers.add(target);
5196
- missingResponseRetryAttempts.set(
5197
- target,
5198
- (missingResponseRetryAttempts.get(target) ?? 0) + 1,
5199
- );
5259
+ recordMissingResponseGroups(missingGroups);
5260
+ },
5261
+ onRemoteTargets: (targets) => {
5262
+ for (const peer of targets) {
5263
+ remoteIteratorPeersToClose.add(peer);
5200
5264
  }
5201
5265
  },
5202
5266
  },
5203
5267
  fetchOptions?.fetchedFirstForRemote,
5204
5268
  );
5205
5269
 
5206
- if (missingResponses && retryMissingResponseGroups) {
5270
+ if (
5271
+ missingResponses &&
5272
+ retryMissingResponseGroups &&
5273
+ pendingMissingResponseRetryPeers.size > 0
5274
+ ) {
5207
5275
  hasMore = true;
5208
5276
  unsetDone();
5209
5277
  }
@@ -5235,6 +5303,19 @@ export class DocumentIndex<
5235
5303
  if (pendingMissingResponseRetryPeers.size > 0) {
5236
5304
  const retryTargets = [...pendingMissingResponseRetryPeers];
5237
5305
  pendingMissingResponseRetryPeers.clear();
5306
+ const idTranslation =
5307
+ this._prefetch?.accumulator.getTranslationMap(queryRequestCoerced);
5308
+ for (const peer of retryTargets) {
5309
+ const staleRemoteIteratorId = idTranslation?.get(peer);
5310
+ if (staleRemoteIteratorId) {
5311
+ const staleIds = staleRemoteIteratorIdsToClose.get(peer) ?? [];
5312
+ if (!staleIds.some((id) => equals(id, staleRemoteIteratorId))) {
5313
+ staleIds.push(staleRemoteIteratorId);
5314
+ staleRemoteIteratorIdsToClose.set(peer, staleIds);
5315
+ }
5316
+ idTranslation!.delete(peer);
5317
+ }
5318
+ }
5238
5319
  return setFetchPromise(
5239
5320
  fetchFirst(n, {
5240
5321
  from: retryTargets,
@@ -5248,6 +5329,12 @@ export class DocumentIndex<
5248
5329
  let resultsLeft = 0;
5249
5330
 
5250
5331
  for (const [peer, buffer] of peerBufferMap) {
5332
+ if (retiredRemoteIteratorPeers.has(peer)) {
5333
+ if (buffer.buffer.length === 0) {
5334
+ peerBufferMap.delete(peer);
5335
+ }
5336
+ continue;
5337
+ }
5251
5338
  if (buffer.buffer.length < n) {
5252
5339
  const hasExistingRemoteResults = buffer.kept > 0;
5253
5340
  if (!hasExistingRemoteResults && !keepRemoteAlive) {
@@ -5390,10 +5477,11 @@ export class DocumentIndex<
5390
5477
  "Failed to collect sorted results from self. " + e?.message,
5391
5478
  );
5392
5479
  peerBufferMap.delete(peer);
5393
- }),
5480
+ }),
5394
5481
  );
5395
5482
  } else {
5396
5483
  // Fetch remotely
5484
+ remoteIteratorPeersToClose.add(peer);
5397
5485
  const idTranslation =
5398
5486
  this._prefetch?.accumulator.getTranslationMap(
5399
5487
  queryRequestCoerced,
@@ -5405,22 +5493,45 @@ export class DocumentIndex<
5405
5493
  amount: collectRequest.amount,
5406
5494
  });
5407
5495
  }
5496
+ const remoteRequestOptions =
5497
+ typeof options?.remote === "object" ? options.remote : undefined;
5498
+ const collectSignals = [
5499
+ options?.signal,
5500
+ remoteRequestOptions?.signal,
5501
+ ensureController().signal,
5502
+ ].filter((signal): signal is AbortSignal => signal != null);
5503
+ const collectSignal =
5504
+ collectSignals.length === 1
5505
+ ? collectSignals[0]
5506
+ : AbortSignal.any(collectSignals);
5408
5507
 
5409
5508
  promises.push(
5410
5509
  this._query
5411
5510
  .request(remoteCollectRequest, {
5412
5511
  ...options,
5413
- signal: options?.signal
5414
- ? AbortSignal.any([
5415
- options.signal,
5416
- ensureController().signal,
5417
- ])
5418
- : ensureController().signal,
5512
+ ...remoteRequestOptions,
5513
+ signal: collectSignal,
5419
5514
  priority: getRemoteQueryPriority(options?.remote),
5420
5515
  mode: new SilentDelivery({ to: [peer], redundancy: 1 }),
5421
5516
  })
5422
- .then((response) =>
5423
- introduceEntries(
5517
+ .then((response) => {
5518
+ if (
5519
+ !response.some((result) => result.from?.hashcode() === peer)
5520
+ ) {
5521
+ const missingGroups = [[peer]];
5522
+ if (remoteRequestOptions?.throwOnMissing) {
5523
+ retireRemoteIteratorPeer(peer);
5524
+ throw new MissingResponsesError(
5525
+ "Did not receive responses from all shards: " +
5526
+ JSON.stringify(missingGroups),
5527
+ missingGroups,
5528
+ );
5529
+ }
5530
+ recordMissingResponseGroups(missingGroups);
5531
+ return;
5532
+ }
5533
+
5534
+ return introduceEntries(
5424
5535
  queryRequestCoerced,
5425
5536
  response,
5426
5537
  this.documentType,
@@ -5437,6 +5548,12 @@ export class DocumentIndex<
5437
5548
  logger.error("Missing from for sorted query");
5438
5549
  return;
5439
5550
  }
5551
+ if (
5552
+ !keepRemoteAlive &&
5553
+ response.response.kept === 0n
5554
+ ) {
5555
+ remoteIteratorPeersToClose.delete(peer);
5556
+ }
5440
5557
 
5441
5558
  if (response.response.results.length === 0) {
5442
5559
  if (
@@ -5557,8 +5674,8 @@ export class DocumentIndex<
5557
5674
  e?.message,
5558
5675
  );
5559
5676
  peerBufferMap.delete(peer);
5560
- }),
5561
- ),
5677
+ });
5678
+ }),
5562
5679
  );
5563
5680
  }
5564
5681
  } else {
@@ -5584,7 +5701,9 @@ export class DocumentIndex<
5584
5701
  }
5585
5702
  }
5586
5703
  }
5587
- return resultsLeft === 0; // 0 results left to fetch and 0 pending results
5704
+ return (
5705
+ resultsLeft === 0 && pendingMissingResponseRetryPeers.size === 0
5706
+ ); // 0 results left to fetch and 0 pending results
5588
5707
  }),
5589
5708
  );
5590
5709
  };
@@ -5705,32 +5824,89 @@ export class DocumentIndex<
5705
5824
  done = true;
5706
5825
  };
5707
5826
 
5708
- let close = async () => {
5827
+ const outerSignal = options?.signal;
5828
+ let outerAbortListener: (() => void) | undefined;
5829
+ let closeStarted = false;
5830
+ let closePromise: Promise<void> | undefined;
5831
+ const performClose = async () => {
5832
+ const idTranslation =
5833
+ this._prefetch?.accumulator.getTranslationMap(queryRequestCoerced);
5834
+ const remoteIteratorIds = idTranslation
5835
+ ? new Map(idTranslation)
5836
+ : undefined;
5709
5837
  cleanupAndDone();
5710
5838
 
5711
5839
  // Keep-open iterators can still have active remote state even when
5712
5840
  // their pending count has already drained to zero.
5713
- const closeRequest = new types.CloseIteratorRequest({
5714
- id: queryRequestCoerced.id,
5715
- });
5716
5841
  const selfHash = this.node.identity.publicKey.hashcode();
5717
- const remotePeers = keepRemoteAlive
5718
- ? [...peerBufferMap.keys()].filter((peer) => peer !== selfHash)
5719
- : [...peerBufferMap.entries()]
5720
- .filter(([peer, buffer]) => peer !== selfHash && buffer.kept > 0)
5721
- .map(([peer]) => peer);
5842
+ const activeRemotePeers = new Set(
5843
+ keepRemoteAlive
5844
+ ? [...peerBufferMap.keys()].filter((peer) => peer !== selfHash)
5845
+ : [...peerBufferMap.entries()]
5846
+ .filter(
5847
+ ([peer, buffer]) => peer !== selfHash && buffer.kept > 0,
5848
+ )
5849
+ .map(([peer]) => peer),
5850
+ );
5851
+ for (const peer of remoteIteratorPeersToClose) {
5852
+ if (peer !== selfHash) {
5853
+ activeRemotePeers.add(peer);
5854
+ }
5855
+ }
5856
+ const staleRemoteIteratorIds = new Map(
5857
+ [...staleRemoteIteratorIdsToClose].map(([peer, ids]) => [
5858
+ peer,
5859
+ [...ids],
5860
+ ]),
5861
+ );
5862
+ const remotePeers = new Set([
5863
+ ...activeRemotePeers,
5864
+ ...staleRemoteIteratorIds.keys(),
5865
+ ]);
5722
5866
  peerBufferMap.clear();
5867
+ retiredRemoteIteratorPeers.clear();
5868
+ remoteIteratorPeersToClose.clear();
5869
+ staleRemoteIteratorIdsToClose.clear();
5870
+ if (remotePeers.size === 0) {
5871
+ return;
5872
+ }
5873
+ const remoteCloseOptions =
5874
+ typeof options?.remote === "object" ? options.remote : undefined;
5875
+ const closeSignal = AbortSignal.timeout(CLOSE_ITERATOR_REQUEST_TIMEOUT);
5723
5876
  await Promise.allSettled(
5724
- remotePeers.map((peer) =>
5725
- this._query.send(closeRequest, {
5726
- ...options,
5727
- priority: getRemoteQueryPriority(options?.remote),
5728
- mode: new SilentDelivery({ to: [peer], redundancy: 1 }),
5729
- }),
5730
- ),
5877
+ [...remotePeers].flatMap((peer) => {
5878
+ const ids: Uint8Array[] = [];
5879
+ if (activeRemotePeers.has(peer)) {
5880
+ ids.push(remoteIteratorIds?.get(peer) ?? queryRequestCoerced.id);
5881
+ }
5882
+ for (const staleRemoteIteratorId of
5883
+ staleRemoteIteratorIds.get(peer) ?? []) {
5884
+ if (!ids.some((id) => equals(id, staleRemoteIteratorId))) {
5885
+ ids.push(staleRemoteIteratorId);
5886
+ }
5887
+ }
5888
+ return ids.map((id) => {
5889
+ const closeRequest = new types.CloseIteratorRequest({ id });
5890
+ return this._query.send(closeRequest, {
5891
+ ...remoteCloseOptions,
5892
+ signal: closeSignal,
5893
+ priority: getRemoteQueryPriority(options?.remote),
5894
+ mode: new SilentDelivery({ to: [peer], redundancy: 1 }),
5895
+ });
5896
+ });
5897
+ }),
5731
5898
  );
5732
5899
  };
5733
- options?.signal && options.signal.addEventListener("abort", close);
5900
+ const close = () => {
5901
+ if (closeStarted) return closePromise!;
5902
+ closeStarted = true;
5903
+ if (outerAbortListener) {
5904
+ outerSignal?.removeEventListener("abort", outerAbortListener);
5905
+ outerAbortListener = undefined;
5906
+ }
5907
+ closePromise = performClose();
5908
+ return closePromise;
5909
+ };
5734
5910
 
5735
5911
  let doneFn = () => {
5736
5912
  return done;
@@ -5739,9 +5915,6 @@ export class DocumentIndex<
5739
5915
  let joinListener: (() => void) | undefined;
5740
5916
 
5741
5917
  let fetchedFirstForRemote: Set<string> | undefined = undefined;
5742
- const pendingMissingResponseRetryPeers = new Set<string>();
5743
- const missingResponseRetryAttempts = new Map<string, number>();
5744
- const maxMissingResponseRetryAttempts = 2;
5745
5918
  let joinFetchesInFlight = 0;
5746
5919
 
5747
5920
  let updateDeferred: ReturnType<typeof pDefer> | undefined;
@@ -6581,6 +6754,16 @@ export class DocumentIndex<
6581
6754
  }
6582
6755
  };
6583
6756
 
6757
+ if (outerSignal) {
6758
+ outerAbortListener = () => {
6759
+ void close();
6760
+ };
6761
+ outerSignal.addEventListener("abort", outerAbortListener, { once: true });
6762
+ if (outerSignal.aborted) {
6763
+ void close();
6764
+ }
6765
+ }
6766
+
6584
6767
  return {
6585
6768
  close,
6586
6769
  next,
@@ -6654,53 +6837,62 @@ export class DocumentIndex<
6654
6837
  const drainBatchSize = replicate ? 1000 : 100;
6655
6838
  let result: ValueTypeFromRequest<Resolve, T, I>[] = [];
6656
6839
  let c = 0;
6657
- while (doneFn() !== true) {
6658
- let batch = await next(drainBatchSize);
6659
- c += batch.length;
6660
- if (c > WARNING_WHEN_ITERATING_FOR_MORE_THAN) {
6661
- warn(
6662
- "Iterating for more than " +
6663
- WARNING_WHEN_ITERATING_FOR_MORE_THAN +
6664
- " results",
6665
- );
6666
- }
6667
- if (batch.length > 0) {
6668
- result.push(...batch);
6669
- continue;
6840
+ try {
6841
+ while (doneFn() !== true) {
6842
+ let batch = await next(drainBatchSize);
6843
+ c += batch.length;
6844
+ if (c > WARNING_WHEN_ITERATING_FOR_MORE_THAN) {
6845
+ warn(
6846
+ "Iterating for more than " +
6847
+ WARNING_WHEN_ITERATING_FOR_MORE_THAN +
6848
+ " results",
6849
+ );
6850
+ }
6851
+ if (batch.length > 0) {
6852
+ result.push(...batch);
6853
+ continue;
6854
+ }
6855
+ await waitForUpdateAndResetDeferred();
6670
6856
  }
6671
- await waitForUpdateAndResetDeferred();
6857
+ return result;
6858
+ } finally {
6859
+ await close();
6672
6860
  }
6673
- cleanupAndDone();
6674
- return result;
6675
6861
  },
6676
6862
  first: async () => {
6677
- if (doneFn()) {
6678
- return undefined;
6863
+ try {
6864
+ if (doneFn()) {
6865
+ return undefined;
6866
+ }
6867
+ let batch = await next(1);
6868
+ return batch[0];
6869
+ } finally {
6870
+ await close();
6679
6871
  }
6680
- let batch = await next(1);
6681
- cleanupAndDone();
6682
- return batch[0];
6683
6872
  },
6684
6873
  [Symbol.asyncIterator]: async function* () {
6685
6874
  drain = true;
6686
6875
  const drainBatchSize = replicate ? 1000 : 100;
6687
6876
  let c = 0;
6688
- while (doneFn() !== true) {
6689
- const batch = await next(drainBatchSize);
6690
- c += batch.length;
6691
- if (c > WARNING_WHEN_ITERATING_FOR_MORE_THAN) {
6692
- warn(
6693
- "Iterating for more than " +
6694
- WARNING_WHEN_ITERATING_FOR_MORE_THAN +
6695
- " results",
6696
- );
6697
- }
6698
- for (const entry of batch) {
6699
- yield entry;
6877
+ try {
6878
+ while (doneFn() !== true) {
6879
+ const batch = await next(drainBatchSize);
6880
+ c += batch.length;
6881
+ if (c > WARNING_WHEN_ITERATING_FOR_MORE_THAN) {
6882
+ warn(
6883
+ "Iterating for more than " +
6884
+ WARNING_WHEN_ITERATING_FOR_MORE_THAN +
6885
+ " results",
6886
+ );
6887
+ }
6888
+ for (const entry of batch) {
6889
+ yield entry;
6890
+ }
6891
+ await waitForUpdateAndResetDeferred();
6700
6892
  }
6701
- await waitForUpdateAndResetDeferred();
6893
+ } finally {
6894
+ await close();
6702
6895
  }
6703
- cleanupAndDone();
6704
6896
  },
6705
6897
  };
6706
6898
  }