@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.
- package/dist/src/program.d.ts +2 -2
- package/dist/src/program.d.ts.map +1 -1
- package/dist/src/program.js +3 -3
- package/dist/src/program.js.map +1 -1
- package/dist/src/resumable-iterator.d.ts +1 -0
- package/dist/src/resumable-iterator.d.ts.map +1 -1
- package/dist/src/resumable-iterator.js +3 -0
- package/dist/src/resumable-iterator.js.map +1 -1
- package/dist/src/search.d.ts +94 -21
- package/dist/src/search.d.ts.map +1 -1
- package/dist/src/search.js +346 -89
- package/dist/src/search.js.map +1 -1
- package/package.json +75 -75
- package/src/program.ts +6 -5
- package/src/resumable-iterator.ts +4 -0
- package/src/search.ts +658 -216
package/dist/src/search.js
CHANGED
|
@@ -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,
|
|
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
|
|
468
|
-
if (!
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
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.
|
|
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
|
-
|
|
1118
|
-
|
|
1119
|
-
|
|
1120
|
-
|
|
1121
|
-
|
|
1122
|
-
|
|
1123
|
-
|
|
1124
|
-
|
|
1125
|
-
|
|
1126
|
-
|
|
1127
|
-
|
|
1128
|
-
|
|
1129
|
-
|
|
1130
|
-
|
|
1131
|
-
|
|
1132
|
-
|
|
1133
|
-
|
|
1134
|
-
|
|
1135
|
-
|
|
1136
|
-
|
|
1137
|
-
|
|
1138
|
-
|
|
1139
|
-
|
|
1140
|
-
|
|
1141
|
-
|
|
1142
|
-
|
|
1143
|
-
|
|
1144
|
-
|
|
1145
|
-
|
|
1146
|
-
|
|
1147
|
-
|
|
1148
|
-
|
|
1149
|
-
|
|
1150
|
-
|
|
1151
|
-
|
|
1152
|
-
|
|
1153
|
-
|
|
1154
|
-
|
|
1155
|
-
|
|
1156
|
-
|
|
1157
|
-
|
|
1158
|
-
|
|
1159
|
-
|
|
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"
|
|
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
|
|
1477
|
-
|
|
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?.
|
|
1482
|
-
typeof options?.remote.
|
|
1483
|
-
? options.remote.
|
|
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.
|
|
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
|
-
|
|
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
|
-
|
|
1555
|
-
|
|
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
|
-
|
|
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
|
|
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([
|