@peerbit/document 8.1.2 → 8.2.0-5fa75cc

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, sha256Base64Sync, } from "@peerbit/crypto";
12
+ import { PublicSignKey, getPublicKeyFromPeerId, sha256Base64Sync, } from "@peerbit/crypto";
13
13
  import * as types from "@peerbit/document-interface";
14
14
  import * as indexerTypes from "@peerbit/indexer-interface";
15
15
  import { HashmapIndex } from "@peerbit/indexer-simple";
@@ -19,21 +19,46 @@ import { ClosedError, Program } from "@peerbit/program";
19
19
  import { MissingResponsesError, RPC, queryAll, } from "@peerbit/rpc";
20
20
  import { SharedLog } from "@peerbit/shared-log";
21
21
  import { SilentDelivery } from "@peerbit/stream-interface";
22
- import { AbortError } from "@peerbit/time";
22
+ import { AbortError, waitFor } from "@peerbit/time";
23
23
  import { concat, fromString } from "uint8arrays";
24
24
  import { copySerialization } from "./borsh.js";
25
25
  import { MAX_BATCH_SIZE } from "./constants.js";
26
26
  import { isPutOperation } from "./operation.js";
27
27
  import { ResumableIterators } from "./resumable-iterator.js";
28
28
  const logger = loggerFn({ module: "document-index" });
29
- const introduceEntries = async (queryRequest, responses, type, sync, options) => {
29
+ const coerceQuery = (query, options) => {
30
+ let replicate = typeof options?.remote !== "boolean" ? options?.remote?.replicate : false;
31
+ if (query instanceof types.SearchRequestIndexed &&
32
+ query.replicate === false &&
33
+ replicate) {
34
+ query.replicate = true;
35
+ return query;
36
+ }
37
+ if (query instanceof types.SearchRequest) {
38
+ return query;
39
+ }
40
+ const queryObject = query;
41
+ return options?.resolve || options?.resolve == null
42
+ ? new types.SearchRequest({
43
+ query: indexerTypes.toQuery(queryObject.query),
44
+ sort: indexerTypes.toSort(query.sort),
45
+ })
46
+ : new types.SearchRequestIndexed({
47
+ query: indexerTypes.toQuery(queryObject.query),
48
+ sort: indexerTypes.toSort(query.sort),
49
+ replicate,
50
+ });
51
+ };
52
+ const introduceEntries = async (queryRequest, responses, documentType, indexedType, sync, options) => {
30
53
  const results = [];
31
54
  for (const response of responses) {
32
55
  if (!response.from) {
33
56
  logger.error("Missing from for response");
34
57
  }
35
58
  if (response.response instanceof types.Results) {
36
- response.response.results.forEach((r) => r.init(type));
59
+ response.response.results.forEach((r) => r instanceof types.ResultValue
60
+ ? r.init(documentType)
61
+ : r.init(indexedType));
37
62
  if (typeof options?.remote !== "boolean" && options?.remote?.replicate) {
38
63
  await sync(queryRequest, response.response);
39
64
  }
@@ -123,6 +148,7 @@ let DocumentIndex = class DocumentIndex extends Program {
123
148
  transformer;
124
149
  // The indexed document wrapped in a context
125
150
  wrappedIndexedType;
151
+ indexedType;
126
152
  // The database type, for recursive indexing
127
153
  dbType;
128
154
  indexedTypeIsDocumentType;
@@ -131,6 +157,7 @@ let DocumentIndex = class DocumentIndex extends Program {
131
157
  indexByResolver;
132
158
  index;
133
159
  _resumableIterators;
160
+ compatibility;
134
161
  // Transformation, indexer
135
162
  /* fields: IndexableFields<T, I>; */
136
163
  _valueEncoding;
@@ -153,6 +180,7 @@ let DocumentIndex = class DocumentIndex extends Program {
153
180
  this.indexedTypeIsDocumentType =
154
181
  !properties.transform?.type ||
155
182
  properties.transform?.type === properties.documentType;
183
+ this.compatibility = properties.compatibility;
156
184
  let IndexedClassWithContext = class IndexedClassWithContext {
157
185
  __context;
158
186
  constructor(value, context) {
@@ -169,13 +197,62 @@ let DocumentIndex = class DocumentIndex extends Program {
169
197
  __metadata("design:paramtypes", [Object, IndexableContext])
170
198
  ], IndexedClassWithContext);
171
199
  // copy all prototype values from indexedType to IndexedClassWithContext
172
- copySerialization((properties.transform?.type || properties.documentType), IndexedClassWithContext);
200
+ this.indexedType = (properties.transform?.type || properties.documentType);
201
+ copySerialization(this.indexedType, IndexedClassWithContext);
173
202
  this.wrappedIndexedType = IndexedClassWithContext;
174
203
  // if this.type is a class that extends Program we want to do special functionality
175
204
  this._isProgramValues = this.documentType instanceof Program;
176
205
  this.dbType = properties.dbType;
177
206
  this._resultQueue = new Map();
178
- this._sync = properties.sync;
207
+ this._sync = async (request, results) => {
208
+ /*
209
+ let allPromises: Promise<void> | undefined = undefined
210
+ if (waitForValue) {
211
+ let promises: Map<string, DeferredPromise<T>> = new Map();
212
+
213
+ for (const result of results) {
214
+ for (let i = 0; i < result.results.length; i++) {
215
+ let promise = defer<T>();
216
+ let r = result.results[i];
217
+ promises.set(r.context.head, promise);
218
+ const head = result.results[0].context.head;
219
+ let listeners = this.hashToValueListener.get(head);
220
+ if (!listeners) {
221
+ listeners = [];
222
+ this.hashToValueListener.set(head, listeners);
223
+ }
224
+ listeners.push(async (value) => {
225
+ promise.resolve(value);
226
+ result.results[i] = new types.ResultValue<T>({
227
+ context: r.context,
228
+ value,
229
+ source: serialize(value),
230
+ indexed: r.indexed,
231
+ }) as any;
232
+ });
233
+ promise.promise.finally(() => {
234
+ this.hashToValueListener.delete(head);
235
+ });
236
+ }
237
+ }
238
+
239
+ let timeout = setTimeout(() => {
240
+ for (const promise of promises!) {
241
+ promise[1].reject("Timed out resolving search result from value");
242
+ }
243
+ }, 1e4);
244
+
245
+ allPromises = Promise.all([...promises.values()].map((x) => x.promise)).then(
246
+ () => {
247
+ clearTimeout(timeout);
248
+ },
249
+ );
250
+ } */
251
+ await properties.replicate(request, results);
252
+ /* if (allPromises) {
253
+ await allPromises;
254
+ } */
255
+ };
179
256
  const transformOptions = properties.transform;
180
257
  this.transformer = transformOptions
181
258
  ? isTransformerWithFunction(transformOptions)
@@ -301,8 +378,12 @@ let DocumentIndex = class DocumentIndex extends Program {
301
378
  }
302
379
  async getDetailed(key, options) {
303
380
  let results;
381
+ const resolve = options?.resolve || options?.resolve == null;
382
+ let requestClazz = resolve
383
+ ? types.SearchRequest
384
+ : types.SearchRequestIndexed;
304
385
  if (key instanceof Uint8Array) {
305
- results = await this.queryDetailed(new types.SearchRequest({
386
+ results = await this.queryCommence(new requestClazz({
306
387
  query: [
307
388
  new indexerTypes.ByteMatchQuery({ key: this.indexBy, value: key }),
308
389
  ],
@@ -312,7 +393,7 @@ let DocumentIndex = class DocumentIndex extends Program {
312
393
  const indexableKey = indexerTypes.toIdeable(key);
313
394
  if (typeof indexableKey === "number" ||
314
395
  typeof indexableKey === "bigint") {
315
- results = await this.queryDetailed(new types.SearchRequest({
396
+ results = await this.queryCommence(new requestClazz({
316
397
  query: [
317
398
  new indexerTypes.IntegerCompare({
318
399
  key: this.indexBy,
@@ -323,7 +404,7 @@ let DocumentIndex = class DocumentIndex extends Program {
323
404
  }), options);
324
405
  }
325
406
  else if (typeof indexableKey === "string") {
326
- results = await this.queryDetailed(new types.SearchRequest({
407
+ results = await this.queryCommence(new requestClazz({
327
408
  query: [
328
409
  new indexerTypes.StringMatch({
329
410
  key: this.indexBy,
@@ -333,7 +414,7 @@ let DocumentIndex = class DocumentIndex extends Program {
333
414
  }), options);
334
415
  }
335
416
  else if (indexableKey instanceof Uint8Array) {
336
- results = await this.queryDetailed(new types.SearchRequest({
417
+ results = await this.queryCommence(new requestClazz({
337
418
  query: [
338
419
  new indexerTypes.ByteMatchQuery({
339
420
  key: this.indexBy,
@@ -343,24 +424,48 @@ let DocumentIndex = class DocumentIndex extends Program {
343
424
  }), options);
344
425
  }
345
426
  }
427
+ if (resolve &&
428
+ requestClazz === types.SearchRequestIndexed &&
429
+ !this.indexedTypeIsDocumentType &&
430
+ results) {
431
+ for (const set of results) {
432
+ let coercedResult = [];
433
+ for (const value of set.results) {
434
+ const resolved = value instanceof types.ResultIndexedValue
435
+ ? (await this.resolveDocument({
436
+ indexed: value.value,
437
+ head: value.context.head,
438
+ }))?.value
439
+ : value.value;
440
+ if (resolved) {
441
+ coercedResult.push(new types.ResultValue({
442
+ context: value.context,
443
+ value: resolved,
444
+ }));
445
+ }
446
+ }
447
+ set.results = coercedResult;
448
+ }
449
+ }
346
450
  return results;
347
451
  }
348
452
  getSize() {
349
453
  return this.index.getSize();
350
454
  }
351
455
  async resolveDocument(value) {
352
- const cached = this._resolverCache.get(value.id.primitive) ||
353
- this._resolverProgramCache?.get(value.id.primitive);
456
+ const id = value.id ??
457
+ indexerTypes.toId(this.indexByResolver(value.indexed)).primitive;
458
+ const cached = this._resolverCache.get(id) || this._resolverProgramCache?.get(id);
354
459
  if (cached != null) {
355
460
  return { value: cached };
356
461
  }
357
462
  if (this.indexedTypeIsDocumentType) {
358
463
  // cast value to T, i.e. convert the class but keep all properties except the __context
359
- const obj = Object.assign(Object.create(this.documentType.prototype), value.value);
464
+ const obj = Object.assign(Object.create(this.documentType.prototype), value.indexed);
360
465
  delete obj.__context;
361
466
  return { value: obj };
362
467
  }
363
- const head = await this._log.log.get(value.value.__context.head);
468
+ const head = await this._log.log.get(value.head);
364
469
  if (!head) {
365
470
  return undefined; // we could end up here if we recently pruned the document and other peers never persisted the entry
366
471
  // TODO update changes in index before removing entries from log entry storage
@@ -384,10 +489,14 @@ let DocumentIndex = class DocumentIndex extends Program {
384
489
  throw new Error("Different from in queued results");
385
490
  }
386
491
  let indexedResult = undefined;
387
- if (query instanceof types.SearchRequest) {
492
+ let fromQuery;
493
+ if (query instanceof types.SearchRequest ||
494
+ query instanceof types.SearchRequestIndexed) {
495
+ fromQuery = query;
388
496
  indexedResult = await this._resumableIterators.iterateAndFetch(query);
389
497
  }
390
498
  else if (query instanceof types.CollectNextRequest) {
499
+ fromQuery = this._resumableIterators.queues.get(query.idString)?.request;
391
500
  indexedResult =
392
501
  prevQueued?.keptInIndex === 0
393
502
  ? []
@@ -396,7 +505,6 @@ let DocumentIndex = class DocumentIndex extends Program {
396
505
  else {
397
506
  throw new Error("Unsupported");
398
507
  }
399
- const filteredResults = [];
400
508
  let resultSize = 0;
401
509
  let toIterate = prevQueued
402
510
  ? [...prevQueued.queue, ...indexedResult]
@@ -418,6 +526,7 @@ let DocumentIndex = class DocumentIndex extends Program {
418
526
  };
419
527
  this._resultQueue.set(query.idString, prevQueued);
420
528
  }
529
+ const filteredResults = [];
421
530
  for (const result of toIterate) {
422
531
  if (!isLocal) {
423
532
  resultSize += result.value.__context.size;
@@ -426,17 +535,40 @@ let DocumentIndex = class DocumentIndex extends Program {
426
535
  continue;
427
536
  }
428
537
  }
429
- const value = await this.resolveDocument(result);
430
- if (!value ||
431
- (options?.canRead && !(await options.canRead(value.value, from)))) {
538
+ const indexedUnwrapped = Object.assign(Object.create(this.indexedType.prototype), result.value);
539
+ if (options?.canRead &&
540
+ !(await options.canRead(indexedUnwrapped, from))) {
432
541
  continue;
433
542
  }
434
- filteredResults.push(new types.ResultWithSource({
435
- context: result.value.__context.toContext(),
436
- value: value.value,
437
- source: serialize(value.value),
438
- indexed: result.value,
439
- }));
543
+ if (fromQuery instanceof types.SearchRequest) {
544
+ const value = await this.resolveDocument({
545
+ indexed: result.value,
546
+ head: result.value.__context.head,
547
+ });
548
+ if (!value) {
549
+ continue;
550
+ }
551
+ filteredResults.push(new types.ResultValue({
552
+ context: result.value.__context.toContext(),
553
+ value: value.value,
554
+ source: serialize(value.value),
555
+ indexed: indexedUnwrapped,
556
+ }));
557
+ }
558
+ else if (fromQuery instanceof types.SearchRequestIndexed) {
559
+ const context = result.value.__context.toContext();
560
+ const head = await this._log.log.get(context.head);
561
+ // assume remote peer will start to replicate (TODO is this ideal?)
562
+ if (fromQuery.replicate) {
563
+ this._log.addPeersToGidPeerHistory(context.gid, [from.hashcode()]);
564
+ }
565
+ filteredResults.push(new types.ResultIndexedValue({
566
+ context,
567
+ source: serialize(indexedUnwrapped),
568
+ indexed: indexedUnwrapped,
569
+ entries: head ? [head] : [],
570
+ }));
571
+ }
440
572
  }
441
573
  const results = new types.Results({
442
574
  results: filteredResults,
@@ -476,7 +608,7 @@ let DocumentIndex = class DocumentIndex extends Program {
476
608
  * @param options
477
609
  * @returns
478
610
  */
479
- async queryDetailed(queryRequest, options) {
611
+ async queryCommence(queryRequest, options) {
480
612
  const local = typeof options?.local === "boolean" ? options?.local : true;
481
613
  let remote = undefined;
482
614
  if (typeof options?.remote === "boolean") {
@@ -517,7 +649,8 @@ let DocumentIndex = class DocumentIndex extends Program {
517
649
  if (replicatorGroups) {
518
650
  const groupHashes = replicatorGroups.map((x) => [x]);
519
651
  const responseHandler = async (results) => {
520
- for (const r of await introduceEntries(queryRequest, results, this.documentType, this._sync, options)) {
652
+ const resultInitialized = await introduceEntries(queryRequest, results, this.documentType, this.indexedType, this._sync, options);
653
+ for (const r of resultInitialized) {
521
654
  resolved.push(r.response);
522
655
  }
523
656
  };
@@ -564,7 +697,7 @@ let DocumentIndex = class DocumentIndex extends Program {
564
697
  }
565
698
  }
566
699
  }
567
- return allResults;
700
+ return allResults; // TODO types
568
701
  }
569
702
  /**
570
703
  * Query and retrieve results
@@ -574,19 +707,21 @@ let DocumentIndex = class DocumentIndex extends Program {
574
707
  */
575
708
  async search(queryRequest, options) {
576
709
  // Set fetch to search size, or max value (default to max u32 (4294967295))
577
- queryRequest.fetch = queryRequest.fetch ?? 0xffffffff;
710
+ const coercedRequest = coerceQuery(queryRequest, options);
711
+ coercedRequest.fetch = coercedRequest.fetch ?? 0xffffffff;
578
712
  // So that the iterator is pre-fetching the right amount of entries
579
- const iterator = this.iterate(queryRequest, options);
713
+ const iterator = this.iterate(coercedRequest, options);
580
714
  // So that this call will not do any remote requests
581
715
  const allResults = [];
582
- while (iterator.done() !== true && queryRequest.fetch > allResults.length) {
716
+ while (iterator.done() !== true &&
717
+ coercedRequest.fetch > allResults.length) {
583
718
  // We might need to pull .next multiple time due to data message size limitations
584
- for (const result of await iterator.next(queryRequest.fetch - allResults.length)) {
719
+ for (const result of await iterator.next(coercedRequest.fetch - allResults.length)) {
585
720
  allResults.push(result);
586
721
  }
587
722
  }
588
723
  await iterator.close();
589
- //s Deduplicate and return values directly
724
+ // Deduplicate and return values directly
590
725
  return dedup(allResults, this.indexByResolver);
591
726
  }
592
727
  /**
@@ -596,6 +731,30 @@ let DocumentIndex = class DocumentIndex extends Program {
596
731
  * @returns
597
732
  */
598
733
  iterate(queryRequest, options) {
734
+ let queryRequestCoerced = coerceQuery(queryRequest, options);
735
+ let resolve = false;
736
+ if (options?.remote &&
737
+ typeof options.remote !== "boolean" &&
738
+ options.remote.replicate &&
739
+ options?.resolve !== false) {
740
+ if ((queryRequest instanceof types.SearchRequestIndexed === false &&
741
+ this.compatibility == null) ||
742
+ (this.compatibility != null && this.compatibility > 8)) {
743
+ queryRequestCoerced = new types.SearchRequestIndexed({
744
+ query: queryRequestCoerced.query,
745
+ fetch: queryRequestCoerced.fetch,
746
+ sort: queryRequestCoerced.sort,
747
+ });
748
+ resolve = true;
749
+ }
750
+ }
751
+ let replicate = options?.remote &&
752
+ typeof options.remote !== "boolean" &&
753
+ options.remote.replicate;
754
+ if (replicate &&
755
+ queryRequestCoerced instanceof types.SearchRequestIndexed) {
756
+ queryRequestCoerced.replicate = true;
757
+ }
599
758
  let fetchPromise = undefined;
600
759
  const peerBufferMap = new Map();
601
760
  const visited = new Set();
@@ -608,8 +767,8 @@ let DocumentIndex = class DocumentIndex extends Program {
608
767
  };
609
768
  const fetchFirst = async (n) => {
610
769
  done = true; // Assume we are donne
611
- queryRequest.fetch = n;
612
- await this.queryDetailed(queryRequest, {
770
+ queryRequestCoerced.fetch = n;
771
+ await this.queryCommence(queryRequestCoerced, {
613
772
  ...options,
614
773
  onResponse: async (response, from) => {
615
774
  if (!from) {
@@ -630,18 +789,33 @@ let DocumentIndex = class DocumentIndex extends Program {
630
789
  }
631
790
  const buffer = [];
632
791
  for (const result of results.results) {
633
- const indexKey = indexerTypes.toId(this.indexByResolver(result.value)).primitive;
634
- if (visited.has(indexKey)) {
635
- continue;
792
+ if (result instanceof types.ResultValue) {
793
+ const indexKey = indexerTypes.toId(this.indexByResolver(result.value)).primitive;
794
+ if (visited.has(indexKey)) {
795
+ continue;
796
+ }
797
+ visited.add(indexKey);
798
+ buffer.push({
799
+ value: result.value,
800
+ context: result.context,
801
+ from,
802
+ indexed: result.indexed ||
803
+ (await this.transformer(result.value, result.context)),
804
+ });
805
+ }
806
+ else {
807
+ const indexKey = indexerTypes.toId(this.indexByResolver(result.value)).primitive;
808
+ if (visited.has(indexKey)) {
809
+ continue;
810
+ }
811
+ visited.add(indexKey);
812
+ buffer.push({
813
+ value: result.value,
814
+ context: result.context,
815
+ from,
816
+ indexed: result.indexed || result.value,
817
+ });
636
818
  }
637
- visited.add(indexKey);
638
- buffer.push({
639
- value: result.value,
640
- context: result.context,
641
- from,
642
- indexed: result.indexed ||
643
- (await this.transformer(result.value, result.context)),
644
- });
645
819
  }
646
820
  peerBufferMap.set(from.hashcode(), {
647
821
  buffer,
@@ -654,7 +828,7 @@ let DocumentIndex = class DocumentIndex extends Program {
654
828
  },
655
829
  });
656
830
  if (done) {
657
- this.clearResultsQueue(queryRequest);
831
+ this.clearResultsQueue(queryRequestCoerced);
658
832
  }
659
833
  return done;
660
834
  };
@@ -684,7 +858,7 @@ let DocumentIndex = class DocumentIndex extends Program {
684
858
  // TODO buffer more than deleted?
685
859
  // TODO batch to multiple 'to's
686
860
  const collectRequest = new types.CollectNextRequest({
687
- id: queryRequest.id,
861
+ id: queryRequestCoerced.id,
688
862
  amount: n - buffer.buffer.length,
689
863
  });
690
864
  // Fetch locally?
@@ -734,11 +908,12 @@ let DocumentIndex = class DocumentIndex extends Program {
734
908
  priority: 1,
735
909
  mode: new SilentDelivery({ to: [peer], redundancy: 1 }),
736
910
  })
737
- .then((response) => introduceEntries(queryRequest, response, this.documentType, this._sync, options)
738
- .then((responses) => {
739
- responses.map((response) => {
911
+ .then((response) => introduceEntries(queryRequestCoerced, response, this.documentType, this.indexedType, this._sync, options)
912
+ .then(async (responses) => {
913
+ return Promise.all(responses.map(async (response, i) => {
740
914
  resultsLeft += Number(response.response.kept);
741
- if (!response.from) {
915
+ const from = responses[i].from;
916
+ if (!from) {
742
917
  logger.error("Missing from for sorted query");
743
918
  return;
744
919
  }
@@ -754,19 +929,20 @@ let DocumentIndex = class DocumentIndex extends Program {
754
929
  }
755
930
  peerBuffer.kept = Number(response.response.kept);
756
931
  for (const result of response.response.results) {
757
- if (visited.has(indexerTypes.toId(this.indexByResolver(result.value)).primitive)) {
932
+ const idPrimitive = indexerTypes.toId(this.indexByResolver(result.value)).primitive;
933
+ if (visited.has(idPrimitive)) {
758
934
  continue;
759
935
  }
760
- visited.add(indexerTypes.toId(this.indexByResolver(result.value)).primitive);
936
+ visited.add(idPrimitive);
761
937
  peerBuffer.buffer.push({
762
938
  value: result.value,
763
939
  context: result.context,
764
- from: response.from,
765
- indexed: this.transformer(result.value, result.context),
940
+ from: from,
941
+ indexed: await this.transformer(result.value, result.context),
766
942
  });
767
943
  }
768
944
  }
769
- });
945
+ }));
770
946
  })
771
947
  .catch((e) => {
772
948
  logger.error("Failed to collect sorted results from: " +
@@ -796,7 +972,7 @@ let DocumentIndex = class DocumentIndex extends Program {
796
972
  const fetchedAll = await fetchAtLeast(n);
797
973
  // get n next top entries, shift and pull more results
798
974
  const peerBuffersArr = peerBuffers();
799
- const results = peerBuffersArr.sort((a, b) => indexerTypes.extractSortCompare(a.indexed, b.indexed, queryRequest.sort));
975
+ const results = peerBuffersArr.sort((a, b) => indexerTypes.extractSortCompare(a.indexed, b.indexed, queryRequestCoerced.sort));
800
976
  const pendingMoreResults = n < results.length;
801
977
  const batch = results.splice(0, n);
802
978
  for (const result of batch) {
@@ -811,12 +987,24 @@ let DocumentIndex = class DocumentIndex extends Program {
811
987
  }
812
988
  }
813
989
  done = fetchedAll && !pendingMoreResults;
814
- return dedup(batch.map((x) => x.value), this.indexByResolver);
990
+ let coercedBatch;
991
+ if (resolve) {
992
+ coercedBatch = (await Promise.all(batch.map(async (x) => x.value instanceof this.documentType
993
+ ? x.value
994
+ : (await this.resolveDocument({
995
+ head: x.context.head,
996
+ indexed: x.indexed,
997
+ }))?.value))).filter((x) => !!x);
998
+ }
999
+ else {
1000
+ coercedBatch = batch.map((x) => x.value);
1001
+ }
1002
+ return dedup(coercedBatch, this.indexByResolver);
815
1003
  };
816
1004
  const close = async () => {
817
1005
  controller.abort(new AbortError("Iterator closed"));
818
1006
  const closeRequest = new types.CloseIteratorRequest({
819
- id: queryRequest.id,
1007
+ id: queryRequestCoerced.id,
820
1008
  });
821
1009
  const promises = [];
822
1010
  for (const [peer, buffer] of peerBufferMap) {
@@ -853,6 +1041,19 @@ let DocumentIndex = class DocumentIndex extends Program {
853
1041
  },
854
1042
  };
855
1043
  }
1044
+ async waitFor(other, options) {
1045
+ await super.waitFor(other, options);
1046
+ const ids = Array.isArray(other) ? other : [other];
1047
+ const expectedHashes = new Set(ids.map((x) => typeof x === "string"
1048
+ ? x
1049
+ : x instanceof PublicSignKey
1050
+ ? x.hashcode()
1051
+ : getPublicKeyFromPeerId(x).hashcode()));
1052
+ for (const key of expectedHashes) {
1053
+ await waitFor(async () => (await this._log.replicationIndex.count({ query: { hash: key } })) >
1054
+ 0, options);
1055
+ }
1056
+ }
856
1057
  };
857
1058
  __decorate([
858
1059
  field({ type: RPC }),