@peerbit/document 9.13.7 → 9.13.8-40eedc0

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
@@ -2048,15 +2048,35 @@ export class DocumentIndex<
2048
2048
  buffer: BufferedResult<types.ResultTypeFromRequest<R, T, I> | I, I>[];
2049
2049
  }
2050
2050
  > = new Map();
2051
- const visited = new Set<string | number | bigint>();
2051
+ const visited = new Set<indexerTypes.IdPrimitive>();
2052
+ let indexedPlaceholders:
2053
+ | Map<
2054
+ indexerTypes.IdPrimitive,
2055
+ BufferedResult<types.ResultTypeFromRequest<R, T, I> | I, I>
2056
+ >
2057
+ | undefined;
2058
+ const ensureIndexedPlaceholders = () => {
2059
+ if (!indexedPlaceholders) {
2060
+ indexedPlaceholders = new Map<
2061
+ string | number | bigint,
2062
+ BufferedResult<types.ResultTypeFromRequest<R, T, I> | I, I>
2063
+ >();
2064
+ }
2065
+ return indexedPlaceholders;
2066
+ };
2052
2067
 
2053
2068
  let done = false;
2054
2069
  let drain = false; // if true, close on empty once (overrides manual)
2055
2070
  let first = false;
2056
2071
 
2057
2072
  // TODO handle join/leave while iterating
2058
- const controller = new AbortController();
2059
-
2073
+ const controller: AbortController | undefined = undefined;
2074
+ const ensureController = () => {
2075
+ if (!controller) {
2076
+ return new AbortController();
2077
+ }
2078
+ return controller;
2079
+ };
2060
2080
  let totalFetchedCounter = 0;
2061
2081
  let lastValueInOrder:
2062
2082
  | {
@@ -2081,7 +2101,6 @@ export class DocumentIndex<
2081
2101
  done = true;
2082
2102
  };
2083
2103
  let unsetDone = () => {
2084
- cleanup();
2085
2104
  done = false;
2086
2105
  };
2087
2106
  let cleanup = () => {
@@ -2127,7 +2146,7 @@ export class DocumentIndex<
2127
2146
 
2128
2147
  if (options.remote.reach?.discover) {
2129
2148
  warmupPromise = this.waitFor(options.remote.reach.discover, {
2130
- signal: controller.signal,
2149
+ signal: ensureController().signal,
2131
2150
  seek: "present",
2132
2151
  timeout: waitForTime ?? DEFAULT_TIMEOUT,
2133
2152
  });
@@ -2147,7 +2166,7 @@ export class DocumentIndex<
2147
2166
  eager: options.remote.reach?.eager,
2148
2167
  settle: "any",
2149
2168
  timeout: waitPolicy.timeout ?? DEFAULT_TIMEOUT,
2150
- signal: controller.signal,
2169
+ signal: ensureController().signal,
2151
2170
  onTimeout: waitPolicy.onTimeout,
2152
2171
  });
2153
2172
  warmupPromise = warmupPromise
@@ -2204,10 +2223,23 @@ export class DocumentIndex<
2204
2223
  >[] = peerBufferMap.get(from.hashcode())?.buffer || [];
2205
2224
 
2206
2225
  for (const result of results.results) {
2226
+ const indexKey = indexerTypes.toId(
2227
+ this.indexByResolver(result.value),
2228
+ ).primitive;
2207
2229
  if (result instanceof types.ResultValue) {
2208
- const indexKey = indexerTypes.toId(
2209
- this.indexByResolver(result.value),
2210
- ).primitive;
2230
+ const existingIndexed = indexedPlaceholders?.get(indexKey);
2231
+ if (existingIndexed) {
2232
+ existingIndexed.value =
2233
+ result.value as types.ResultTypeFromRequest<R, T, I>;
2234
+ existingIndexed.context = result.context;
2235
+ existingIndexed.from = from!;
2236
+ existingIndexed.indexed = await this.resolveIndexed<R>(
2237
+ result,
2238
+ results.results,
2239
+ );
2240
+ indexedPlaceholders?.delete(indexKey);
2241
+ continue;
2242
+ }
2211
2243
  if (visited.has(indexKey)) {
2212
2244
  continue;
2213
2245
  }
@@ -2223,23 +2255,25 @@ export class DocumentIndex<
2223
2255
  ),
2224
2256
  });
2225
2257
  } else {
2226
- const indexKey = indexerTypes.toId(
2227
- this.indexByResolver(result.value),
2228
- ).primitive;
2229
-
2230
- if (visited.has(indexKey)) {
2258
+ if (
2259
+ visited.has(indexKey) &&
2260
+ !indexedPlaceholders?.has(indexKey)
2261
+ ) {
2231
2262
  continue;
2232
2263
  }
2233
2264
  visited.add(indexKey);
2234
- buffer.push({
2265
+ const indexed = coerceWithContext(
2266
+ result.indexed || result.value,
2267
+ result.context,
2268
+ );
2269
+ const placeholder = {
2235
2270
  value: result.value,
2236
2271
  context: result.context,
2237
2272
  from,
2238
- indexed: coerceWithContext(
2239
- result.indexed || result.value,
2240
- result.context,
2241
- ),
2242
- });
2273
+ indexed,
2274
+ };
2275
+ buffer.push(placeholder);
2276
+ ensureIndexedPlaceholders().set(indexKey, placeholder);
2243
2277
  }
2244
2278
  }
2245
2279
 
@@ -2328,21 +2362,38 @@ export class DocumentIndex<
2328
2362
  peerBuffer.kept = Number(results.kept);
2329
2363
 
2330
2364
  for (const result of results.results) {
2331
- if (
2332
- visited.has(
2333
- indexerTypes.toId(this.indexByResolver(result.value))
2334
- .primitive,
2335
- )
2336
- ) {
2337
- continue;
2338
- }
2339
- visited.add(
2340
- indexerTypes.toId(this.indexByResolver(result.value))
2341
- .primitive,
2342
- );
2343
- let indexed: WithContext<I>;
2365
+ const keyPrimitive = indexerTypes.toId(
2366
+ this.indexByResolver(result.value),
2367
+ ).primitive;
2344
2368
  if (result instanceof types.ResultValue) {
2345
- indexed = await this.resolveIndexed<R>(
2369
+ const existingIndexed =
2370
+ indexedPlaceholders?.get(keyPrimitive);
2371
+ if (existingIndexed) {
2372
+ existingIndexed.value =
2373
+ result.value as types.ResultTypeFromRequest<
2374
+ R,
2375
+ T,
2376
+ I
2377
+ >;
2378
+ existingIndexed.context = result.context;
2379
+ existingIndexed.from = this.node.identity.publicKey;
2380
+ existingIndexed.indexed =
2381
+ await this.resolveIndexed<R>(
2382
+ result,
2383
+ results.results as types.ResultTypeFromRequest<
2384
+ R,
2385
+ T,
2386
+ I
2387
+ >[],
2388
+ );
2389
+ indexedPlaceholders?.delete(keyPrimitive);
2390
+ continue;
2391
+ }
2392
+ if (visited.has(keyPrimitive)) {
2393
+ continue;
2394
+ }
2395
+ visited.add(keyPrimitive);
2396
+ const indexed = await this.resolveIndexed<R>(
2346
2397
  result,
2347
2398
  results.results as types.ResultTypeFromRequest<
2348
2399
  R,
@@ -2350,22 +2401,40 @@ export class DocumentIndex<
2350
2401
  I
2351
2402
  >[],
2352
2403
  );
2404
+ peerBuffer.buffer.push({
2405
+ value: result.value as types.ResultTypeFromRequest<
2406
+ R,
2407
+ T,
2408
+ I
2409
+ >,
2410
+ context: result.context,
2411
+ from: this.node.identity.publicKey,
2412
+ indexed,
2413
+ });
2353
2414
  } else {
2354
- indexed = coerceWithContext(
2415
+ if (
2416
+ visited.has(keyPrimitive) &&
2417
+ !indexedPlaceholders?.has(keyPrimitive)
2418
+ ) {
2419
+ continue;
2420
+ }
2421
+ visited.add(keyPrimitive);
2422
+ const indexed = coerceWithContext(
2355
2423
  result.indexed || result.value,
2356
2424
  result.context,
2357
2425
  );
2426
+ const placeholder = {
2427
+ value: result.value,
2428
+ context: result.context,
2429
+ from: this.node.identity.publicKey,
2430
+ indexed,
2431
+ };
2432
+ peerBuffer.buffer.push(placeholder);
2433
+ ensureIndexedPlaceholders().set(
2434
+ keyPrimitive,
2435
+ placeholder,
2436
+ );
2358
2437
  }
2359
- peerBuffer.buffer.push({
2360
- value: result.value as types.ResultTypeFromRequest<
2361
- R,
2362
- T,
2363
- I
2364
- >,
2365
- context: result.context,
2366
- from: this.node.identity.publicKey,
2367
- indexed: indexed,
2368
- });
2369
2438
  }
2370
2439
  }
2371
2440
  })
@@ -2395,8 +2464,11 @@ export class DocumentIndex<
2395
2464
  .request(remoteCollectRequest, {
2396
2465
  ...options,
2397
2466
  signal: options?.signal
2398
- ? AbortSignal.any([options.signal, controller.signal])
2399
- : controller.signal,
2467
+ ? AbortSignal.any([
2468
+ options.signal,
2469
+ ensureController().signal,
2470
+ ])
2471
+ : ensureController().signal,
2400
2472
  priority: 1,
2401
2473
  mode: new SilentDelivery({ to: [peer], redundancy: 1 }),
2402
2474
  })
@@ -2436,17 +2508,40 @@ export class DocumentIndex<
2436
2508
  >
2437
2509
  >
2438
2510
  ).response.results) {
2439
- const idPrimitive = indexerTypes.toId(
2511
+ const indexKey = indexerTypes.toId(
2440
2512
  this.indexByResolver(result.value),
2441
2513
  ).primitive;
2442
- if (visited.has(idPrimitive)) {
2443
- continue;
2444
- }
2445
- visited.add(idPrimitive);
2446
-
2447
- let indexed: WithContext<I>;
2448
2514
  if (result instanceof types.ResultValue) {
2449
- indexed = await this.resolveIndexed(
2515
+ const existingIndexed =
2516
+ indexedPlaceholders?.get(indexKey);
2517
+ if (existingIndexed) {
2518
+ existingIndexed.value =
2519
+ result.value as types.ResultTypeFromRequest<
2520
+ R,
2521
+ T,
2522
+ I
2523
+ >;
2524
+ existingIndexed.context = result.context;
2525
+ existingIndexed.from = from!;
2526
+ existingIndexed.indexed =
2527
+ await this.resolveIndexed(
2528
+ result,
2529
+ response.response
2530
+ .results as types.ResultTypeFromRequest<
2531
+ R,
2532
+ T,
2533
+ I
2534
+ >[],
2535
+ );
2536
+ indexedPlaceholders?.delete(indexKey);
2537
+ continue;
2538
+ }
2539
+ if (visited.has(indexKey)) {
2540
+ continue;
2541
+ }
2542
+ visited.add(indexKey);
2543
+
2544
+ const indexed = await this.resolveIndexed(
2450
2545
  result,
2451
2546
  response.response
2452
2547
  .results as types.ResultTypeFromRequest<
@@ -2455,23 +2550,41 @@ export class DocumentIndex<
2455
2550
  I
2456
2551
  >[],
2457
2552
  );
2553
+ peerBuffer.buffer.push({
2554
+ value:
2555
+ result.value as types.ResultTypeFromRequest<
2556
+ R,
2557
+ T,
2558
+ I
2559
+ >,
2560
+ context: result.context,
2561
+ from: from!,
2562
+ indexed,
2563
+ });
2458
2564
  } else {
2459
- indexed = coerceWithContext(
2565
+ if (
2566
+ visited.has(indexKey) &&
2567
+ !indexedPlaceholders?.has(indexKey)
2568
+ ) {
2569
+ continue;
2570
+ }
2571
+ visited.add(indexKey);
2572
+ const indexed = coerceWithContext(
2460
2573
  result.value,
2461
2574
  result.context,
2462
2575
  );
2576
+ const placeholder = {
2577
+ value: result.value,
2578
+ context: result.context,
2579
+ from: from!,
2580
+ indexed,
2581
+ };
2582
+ peerBuffer.buffer.push(placeholder);
2583
+ ensureIndexedPlaceholders().set(
2584
+ indexKey,
2585
+ placeholder,
2586
+ );
2463
2587
  }
2464
- peerBuffer.buffer.push({
2465
- value:
2466
- result.value as types.ResultTypeFromRequest<
2467
- R,
2468
- T,
2469
- I
2470
- >,
2471
- context: result.context,
2472
- from: from!,
2473
- indexed,
2474
- });
2475
2588
  }
2476
2589
  }
2477
2590
  }),
@@ -2534,6 +2647,10 @@ export class DocumentIndex<
2534
2647
  const idx = arr.buffer.findIndex((x) => x.value === result.value);
2535
2648
  if (idx >= 0) {
2536
2649
  arr.buffer.splice(idx, 1);
2650
+ const consumedId = indexerTypes.toId(
2651
+ this.indexByResolver(result.indexed),
2652
+ ).primitive;
2653
+ indexedPlaceholders?.delete(consumedId);
2537
2654
  }
2538
2655
  }
2539
2656
 
@@ -2580,7 +2697,9 @@ export class DocumentIndex<
2580
2697
 
2581
2698
  let cleanupAndDone = () => {
2582
2699
  cleanup();
2583
- controller.abort(new AbortError("Iterator closed"));
2700
+ (controller as AbortController | undefined)?.abort(
2701
+ new AbortError("Iterator closed"),
2702
+ );
2584
2703
  this.prefetch?.accumulator.clear(queryRequestCoerced);
2585
2704
  this.processCloseIteratorRequest(
2586
2705
  queryRequestCoerced,
@@ -2751,27 +2870,65 @@ export class DocumentIndex<
2751
2870
  filtered = await mergePolicy.merge?.filter(evt.detail);
2752
2871
  }
2753
2872
  if (filtered) {
2873
+ const changeForCallback: DocumentsChange<T, I> = {
2874
+ added: [],
2875
+ removed: [],
2876
+ };
2877
+ let hasRelevantChange = false;
2878
+
2754
2879
  // Remove entries that were deleted from all pending structures
2755
2880
  if (filtered.removed?.length) {
2756
- // Remove from peer buffers
2881
+ const removedIds = new Set<string | number | bigint>();
2882
+ for (const removed of filtered.removed) {
2883
+ const id = indexerTypes.toId(
2884
+ this.indexByResolver(removed.__indexed),
2885
+ ).primitive;
2886
+ removedIds.add(id);
2887
+ }
2888
+ const matchedRemovedIds = new Set<string | number | bigint>();
2757
2889
  for (const [_peer, entry] of peerBufferMap) {
2890
+ if (entry.buffer.length === 0) {
2891
+ continue;
2892
+ }
2758
2893
  entry.buffer = entry.buffer.filter((x) => {
2759
2894
  const id = indexerTypes.toId(
2760
2895
  this.indexByResolver(x.indexed),
2761
2896
  ).primitive;
2762
- return !filtered!.removed!.some(
2763
- (r) =>
2764
- indexerTypes.toId(this.indexByResolver(r.__indexed))
2765
- .primitive === id,
2766
- );
2897
+ if (removedIds.has(id)) {
2898
+ matchedRemovedIds.add(id);
2899
+ indexedPlaceholders?.delete(id);
2900
+ return false;
2901
+ }
2902
+ return true;
2767
2903
  });
2768
2904
  }
2769
- // no non-sorted queues in simplified mode
2905
+ if (matchedRemovedIds.size > 0) {
2906
+ hasRelevantChange = true;
2907
+ for (const removed of filtered.removed) {
2908
+ const id = indexerTypes.toId(
2909
+ this.indexByResolver(removed.__indexed),
2910
+ ).primitive;
2911
+ if (matchedRemovedIds.has(id)) {
2912
+ changeForCallback.removed.push(removed);
2913
+ }
2914
+ }
2915
+ }
2770
2916
  }
2771
2917
 
2772
2918
  // Add new entries per strategy (sorted-only)
2773
2919
  if (filtered.added?.length) {
2774
- const buf = peerBufferMap.get(localHash)!;
2920
+ let buf = peerBufferMap.get(localHash);
2921
+ if (!buf) {
2922
+ const created: {
2923
+ kept: number;
2924
+ buffer: BufferedResult<
2925
+ types.ResultTypeFromRequest<R, T, I> | I,
2926
+ I
2927
+ >[];
2928
+ } = { kept: 0, buffer: [] };
2929
+ peerBufferMap.set(localHash, created);
2930
+ buf = created;
2931
+ }
2775
2932
  const filterIndex = hasQueryFiltersForUpdates
2776
2933
  ? await createUpdateFilterIndex()
2777
2934
  : undefined;
@@ -2801,31 +2958,49 @@ export class DocumentIndex<
2801
2958
  const id = indexerTypes.toId(
2802
2959
  this.indexByResolver(indexedCandidate),
2803
2960
  ).primitive;
2961
+ const existingIndexed = indexedPlaceholders?.get(id);
2962
+ if (existingIndexed) {
2963
+ if (resolve) {
2964
+ existingIndexed.value = added as any;
2965
+ existingIndexed.context = added.__context;
2966
+ existingIndexed.from = this.node.identity.publicKey;
2967
+ existingIndexed.indexed = indexedCandidate;
2968
+ indexedPlaceholders?.delete(id);
2969
+ }
2970
+ continue;
2971
+ }
2804
2972
  if (visited.has(id)) continue; // already presented
2805
2973
  visited.add(id);
2806
2974
  const valueForBuffer = resolve
2807
2975
  ? (added as any)
2808
2976
  : indexedCandidate;
2809
- buf.buffer.push({
2977
+ const placeholder = {
2810
2978
  value: valueForBuffer,
2811
2979
  context: added.__context,
2812
2980
  from: this.node.identity.publicKey,
2813
2981
  indexed: indexedCandidate,
2814
- });
2982
+ };
2983
+ buf.buffer.push(placeholder);
2984
+ if (!resolve) {
2985
+ ensureIndexedPlaceholders().set(id, placeholder);
2986
+ }
2987
+ hasRelevantChange = true;
2988
+ changeForCallback.added.push(added);
2815
2989
  }
2816
- buf.kept = buf.buffer.length;
2817
2990
  }
2818
2991
 
2819
- if (
2820
- (filtered.added?.length ?? 0) > 0 ||
2821
- (filtered.removed?.length ?? 0) > 0
2822
- ) {
2992
+ if (hasRelevantChange) {
2823
2993
  if (!pendingResultsReason) {
2824
2994
  pendingResultsReason = "change";
2825
2995
  }
2996
+ if (
2997
+ changeForCallback.added.length > 0 ||
2998
+ changeForCallback.removed.length > 0
2999
+ ) {
3000
+ updateCallbacks?.onChange?.(changeForCallback);
3001
+ }
2826
3002
  }
2827
3003
  }
2828
- updateCallbacks?.onChange?.(evt.detail); // TODO only emit if changes were relevant?
2829
3004
  signalUpdate();
2830
3005
  };
2831
3006
 
@@ -2866,10 +3041,10 @@ export class DocumentIndex<
2866
3041
  setTimeout(() => {
2867
3042
  signalUpdate();
2868
3043
  }, waitForTime);
2869
- controller.signal.addEventListener("abort", () => signalUpdate());
3044
+ ensureController().signal.addEventListener("abort", () => signalUpdate());
2870
3045
  fetchedFirstForRemote = new Set<string>();
2871
3046
  joinListener = this.attachJoinListener({
2872
- signal: controller.signal,
3047
+ signal: ensureController().signal,
2873
3048
  eager: options.remote.reach?.eager,
2874
3049
  onPeer: async (pk) => {
2875
3050
  if (done) return;
@@ -2952,11 +3127,11 @@ export class DocumentIndex<
2952
3127
  next,
2953
3128
  done: doneFn,
2954
3129
  pending: () => {
2955
- let kept = 0;
2956
- for (const [_, buffer] of peerBufferMap) {
2957
- kept += buffer.kept;
3130
+ let pendingCount = 0;
3131
+ for (const buffer of peerBufferMap.values()) {
3132
+ pendingCount += buffer.kept + buffer.buffer.length;
2958
3133
  }
2959
- return kept; // TODO this should be more accurate
3134
+ return pendingCount;
2960
3135
  },
2961
3136
  all: async () => {
2962
3137
  drain = true;