@peerbit/document 13.0.44 → 13.1.1
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/benchmark/document-put.js +1224 -63
- package/dist/benchmark/document-put.js.map +1 -1
- package/dist/src/index.d.ts +2 -0
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/index.js +2 -0
- package/dist/src/index.js.map +1 -1
- package/dist/src/native-rust.d.ts +50 -0
- package/dist/src/native-rust.d.ts.map +1 -0
- package/dist/src/native-rust.js +38 -0
- package/dist/src/native-rust.js.map +1 -0
- package/dist/src/policy.d.ts +51 -0
- package/dist/src/policy.d.ts.map +1 -0
- package/dist/src/policy.js +347 -0
- package/dist/src/policy.js.map +1 -0
- package/dist/src/program.d.ts +139 -22
- package/dist/src/program.d.ts.map +1 -1
- package/dist/src/program.js +3074 -342
- package/dist/src/program.js.map +1 -1
- package/dist/src/search.d.ts +80 -6
- package/dist/src/search.d.ts.map +1 -1
- package/dist/src/search.js +1155 -59
- package/dist/src/search.js.map +1 -1
- package/dist/src/transform.d.ts +42 -0
- package/dist/src/transform.d.ts.map +1 -0
- package/dist/src/transform.js +136 -0
- package/dist/src/transform.js.map +1 -0
- package/package.json +21 -16
- package/src/index.ts +2 -0
- package/src/native-rust.ts +152 -0
- package/src/policy.ts +592 -0
- package/src/program.ts +5020 -467
- package/src/search.ts +1766 -109
- package/src/transform.ts +277 -0
package/dist/src/search.js
CHANGED
|
@@ -32,10 +32,11 @@ var __runInitializers = (this && this.__runInitializers) || function (thisArg, i
|
|
|
32
32
|
}
|
|
33
33
|
return useValue ? value : void 0;
|
|
34
34
|
};
|
|
35
|
-
import { field, getSchema, serialize, variant, } from "@dao-xyz/borsh";
|
|
35
|
+
import { FixedArrayKind, field, getSchema, serialize, variant, } from "@dao-xyz/borsh";
|
|
36
36
|
import { Cache } from "@peerbit/cache";
|
|
37
37
|
import { PublicSignKey, sha256Base64Sync, } from "@peerbit/crypto";
|
|
38
38
|
import * as types from "@peerbit/document-interface";
|
|
39
|
+
import { tryProjectDocumentIndexSimple, } from "./native-rust.js";
|
|
39
40
|
import { CachedIndex } from "@peerbit/indexer-cache";
|
|
40
41
|
import * as indexerTypes from "@peerbit/indexer-interface";
|
|
41
42
|
import { HashmapIndex } from "@peerbit/indexer-simple";
|
|
@@ -55,6 +56,7 @@ import { isPutOperation } from "./operation.js";
|
|
|
55
56
|
import { Prefetch } from "./prefetch.js";
|
|
56
57
|
import { initializeResultType, isResultIndexedValue, isResultValue, isResults, } from "./result-shape.js";
|
|
57
58
|
import { ResumableIterators } from "./resumable-iterator.js";
|
|
59
|
+
import { canPrepareDocumentTransformBeforeAppend, canPrepareDocumentTransformWithAppendFacts, documentTransformPreservesFieldPath, getDocumentTransformDescriptor, } from "./transform.js";
|
|
58
60
|
const WARNING_WHEN_ITERATING_FOR_MORE_THAN = 1e5;
|
|
59
61
|
const logger = loggerFn("peerbit:program:document:search");
|
|
60
62
|
const warn = logger.newScope("warn");
|
|
@@ -64,6 +66,166 @@ const indexRpcLogger = documentIndexLogger.newScope("rpc");
|
|
|
64
66
|
const indexCacheLogger = documentIndexLogger.newScope("cache");
|
|
65
67
|
const indexPrefetchLogger = documentIndexLogger.newScope("prefetch");
|
|
66
68
|
const indexIteratorLogger = documentIndexLogger.newScope("iterate");
|
|
69
|
+
const isPromiseLike = (value) => !!value && typeof value.then === "function";
|
|
70
|
+
const schemaVariant = (schema) => {
|
|
71
|
+
const variant = schema?.variant;
|
|
72
|
+
if (variant == null) {
|
|
73
|
+
return {};
|
|
74
|
+
}
|
|
75
|
+
if (typeof variant === "number") {
|
|
76
|
+
return { type: "u8", value: String(variant) };
|
|
77
|
+
}
|
|
78
|
+
if (typeof variant === "string") {
|
|
79
|
+
return { type: "string", value: variant };
|
|
80
|
+
}
|
|
81
|
+
return;
|
|
82
|
+
};
|
|
83
|
+
const schemaTypeName = (type) => {
|
|
84
|
+
if (typeof type === "string") {
|
|
85
|
+
switch (type) {
|
|
86
|
+
case "string":
|
|
87
|
+
case "u8":
|
|
88
|
+
case "u32":
|
|
89
|
+
case "u64":
|
|
90
|
+
case "bool":
|
|
91
|
+
return type;
|
|
92
|
+
default:
|
|
93
|
+
return;
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
if (type === Uint8Array) {
|
|
97
|
+
return "bytes";
|
|
98
|
+
}
|
|
99
|
+
if (type === PublicSignKey) {
|
|
100
|
+
return "publicsignkey";
|
|
101
|
+
}
|
|
102
|
+
if (type instanceof FixedArrayKind && type.elementType === "u8") {
|
|
103
|
+
return `fixedbytes:${type.length}`;
|
|
104
|
+
}
|
|
105
|
+
const kind = type;
|
|
106
|
+
if (kind.constructor?.name === "OptionKind") {
|
|
107
|
+
const element = schemaTypeName(kind.elementType);
|
|
108
|
+
return element ? `option:${element}` : undefined;
|
|
109
|
+
}
|
|
110
|
+
if (kind.constructor?.name === "VecKind") {
|
|
111
|
+
const element = schemaTypeName(kind.elementType);
|
|
112
|
+
return kind.sizeEncoding === "u32" && element
|
|
113
|
+
? `vec:${element}`
|
|
114
|
+
: undefined;
|
|
115
|
+
}
|
|
116
|
+
return;
|
|
117
|
+
};
|
|
118
|
+
const schemaFieldPlan = (schema) => {
|
|
119
|
+
if (!schema?.fields) {
|
|
120
|
+
return;
|
|
121
|
+
}
|
|
122
|
+
const names = [];
|
|
123
|
+
const types = [];
|
|
124
|
+
for (const field of schema.fields) {
|
|
125
|
+
const type = schemaTypeName(field.type);
|
|
126
|
+
if (!type) {
|
|
127
|
+
return;
|
|
128
|
+
}
|
|
129
|
+
names.push(field.key);
|
|
130
|
+
types.push(type);
|
|
131
|
+
}
|
|
132
|
+
return { names, types };
|
|
133
|
+
};
|
|
134
|
+
const asSingleFieldPath = (path) => typeof path === "string" ? path : path.length === 1 ? path[0] : undefined;
|
|
135
|
+
const createSimpleProjectionPlan = (documentSchema, indexedSchema, descriptor) => {
|
|
136
|
+
if (!descriptor || descriptor.kind === "identity") {
|
|
137
|
+
return;
|
|
138
|
+
}
|
|
139
|
+
const documentVariant = schemaVariant(documentSchema);
|
|
140
|
+
const outputVariant = schemaVariant(indexedSchema);
|
|
141
|
+
const documentFields = schemaFieldPlan(documentSchema);
|
|
142
|
+
const outputFields = schemaFieldPlan(indexedSchema);
|
|
143
|
+
if (!documentVariant || !outputVariant || !documentFields || !outputFields) {
|
|
144
|
+
return;
|
|
145
|
+
}
|
|
146
|
+
const sources = new Map();
|
|
147
|
+
if (descriptor.kind === "pick") {
|
|
148
|
+
for (const field of descriptor.fields) {
|
|
149
|
+
const name = asSingleFieldPath(field);
|
|
150
|
+
if (!name) {
|
|
151
|
+
return;
|
|
152
|
+
}
|
|
153
|
+
sources.set(name, { kind: "field", value: name });
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
else {
|
|
157
|
+
for (const field of descriptor.fields) {
|
|
158
|
+
const target = asSingleFieldPath(field.target);
|
|
159
|
+
if (!target) {
|
|
160
|
+
return;
|
|
161
|
+
}
|
|
162
|
+
switch (field.source.kind) {
|
|
163
|
+
case "field": {
|
|
164
|
+
const source = asSingleFieldPath(field.source.path);
|
|
165
|
+
if (!source) {
|
|
166
|
+
return;
|
|
167
|
+
}
|
|
168
|
+
sources.set(target, { kind: "field", value: source });
|
|
169
|
+
break;
|
|
170
|
+
}
|
|
171
|
+
case "context":
|
|
172
|
+
sources.set(target, {
|
|
173
|
+
kind: "context",
|
|
174
|
+
value: field.source.field,
|
|
175
|
+
});
|
|
176
|
+
break;
|
|
177
|
+
case "entryFirstSignerPublicKey":
|
|
178
|
+
sources.set(target, {
|
|
179
|
+
kind: "entryFirstSignerPublicKey",
|
|
180
|
+
value: "",
|
|
181
|
+
});
|
|
182
|
+
break;
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
const sourceKinds = [];
|
|
187
|
+
const sourceValues = [];
|
|
188
|
+
for (const field of outputFields.names) {
|
|
189
|
+
const source = sources.get(field);
|
|
190
|
+
if (!source) {
|
|
191
|
+
return;
|
|
192
|
+
}
|
|
193
|
+
sourceKinds.push(source.kind);
|
|
194
|
+
sourceValues.push(source.value);
|
|
195
|
+
}
|
|
196
|
+
return {
|
|
197
|
+
documentVariantType: documentVariant.type,
|
|
198
|
+
documentVariantValue: documentVariant.value,
|
|
199
|
+
documentFieldNames: documentFields.names,
|
|
200
|
+
documentFieldTypes: documentFields.types,
|
|
201
|
+
outputVariantType: outputVariant.type,
|
|
202
|
+
outputVariantValue: outputVariant.value,
|
|
203
|
+
outputFieldTypes: outputFields.types,
|
|
204
|
+
sourceKinds,
|
|
205
|
+
sourceValues,
|
|
206
|
+
};
|
|
207
|
+
};
|
|
208
|
+
const createSimpleFieldExtractionPlan = (documentSchema, path) => {
|
|
209
|
+
const fieldName = asSingleFieldPath(path);
|
|
210
|
+
if (!fieldName) {
|
|
211
|
+
return;
|
|
212
|
+
}
|
|
213
|
+
const documentVariant = schemaVariant(documentSchema);
|
|
214
|
+
const documentFields = schemaFieldPlan(documentSchema);
|
|
215
|
+
if (!documentVariant || !documentFields) {
|
|
216
|
+
return;
|
|
217
|
+
}
|
|
218
|
+
if (!documentFields.names.includes(fieldName)) {
|
|
219
|
+
return;
|
|
220
|
+
}
|
|
221
|
+
return {
|
|
222
|
+
documentVariantType: documentVariant.type,
|
|
223
|
+
documentVariantValue: documentVariant.value,
|
|
224
|
+
documentFieldNames: documentFields.names,
|
|
225
|
+
documentFieldTypes: documentFields.types,
|
|
226
|
+
fieldName,
|
|
227
|
+
};
|
|
228
|
+
};
|
|
67
229
|
const getRemoteQueryPriority = (remote) => typeof remote === "object" && remote.priority != null
|
|
68
230
|
? remote.priority
|
|
69
231
|
: FOREGROUND_READ_MESSAGE_PRIORITY;
|
|
@@ -228,6 +390,42 @@ export const INDEX_CONTEXT_SHAPE = {
|
|
|
228
390
|
const isTransformerWithFunction = (options) => {
|
|
229
391
|
return options.transform != null;
|
|
230
392
|
};
|
|
393
|
+
const stripEncodedValue = (options) => options?.encodedValue || options?.encodedValueParts || options?.transformFacts
|
|
394
|
+
? { replace: options.replace }
|
|
395
|
+
: options;
|
|
396
|
+
const writeU32Le = (target, offset, value) => {
|
|
397
|
+
target[offset] = value & 0xff;
|
|
398
|
+
target[offset + 1] = (value >>> 8) & 0xff;
|
|
399
|
+
target[offset + 2] = (value >>> 16) & 0xff;
|
|
400
|
+
target[offset + 3] = (value >>> 24) & 0xff;
|
|
401
|
+
return offset + 4;
|
|
402
|
+
};
|
|
403
|
+
const writeU64Le = (target, offset, value) => {
|
|
404
|
+
let remaining = value;
|
|
405
|
+
for (let i = 0; i < 8; i++) {
|
|
406
|
+
target[offset + i] = Number(remaining & 0xffn);
|
|
407
|
+
remaining >>= 8n;
|
|
408
|
+
}
|
|
409
|
+
return offset + 8;
|
|
410
|
+
};
|
|
411
|
+
export const encodeContextSuffix = (context) => {
|
|
412
|
+
const head = fromString(context.head);
|
|
413
|
+
const gid = fromString(context.gid);
|
|
414
|
+
const encoded = new Uint8Array(1 + 8 + 8 + 4 + head.byteLength + 4 + gid.byteLength + 4);
|
|
415
|
+
let offset = 0;
|
|
416
|
+
// Context is @variant(0); keep this byte-for-byte aligned with Borsh.
|
|
417
|
+
encoded[offset++] = 0;
|
|
418
|
+
offset = writeU64Le(encoded, offset, context.created);
|
|
419
|
+
offset = writeU64Le(encoded, offset, context.modified);
|
|
420
|
+
offset = writeU32Le(encoded, offset, head.byteLength);
|
|
421
|
+
encoded.set(head, offset);
|
|
422
|
+
offset += head.byteLength;
|
|
423
|
+
offset = writeU32Le(encoded, offset, gid.byteLength);
|
|
424
|
+
encoded.set(gid, offset);
|
|
425
|
+
offset += gid.byteLength;
|
|
426
|
+
writeU32Le(encoded, offset, context.size);
|
|
427
|
+
return encoded;
|
|
428
|
+
};
|
|
231
429
|
export const coerceWithContext = (value, context) => {
|
|
232
430
|
let valueWithContext = value;
|
|
233
431
|
valueWithContext.__context = context;
|
|
@@ -238,6 +436,32 @@ export const coerceWithIndexed = (value, indexed) => {
|
|
|
238
436
|
valueWithContext.__indexed = indexed;
|
|
239
437
|
return valueWithContext;
|
|
240
438
|
};
|
|
439
|
+
export const coerceWithLazyIndexed = (value, getIndexable) => {
|
|
440
|
+
let cached;
|
|
441
|
+
let hasCached = false;
|
|
442
|
+
Object.defineProperty(value, "__indexed", {
|
|
443
|
+
configurable: true,
|
|
444
|
+
enumerable: true,
|
|
445
|
+
get() {
|
|
446
|
+
if (!hasCached) {
|
|
447
|
+
cached = getIndexable();
|
|
448
|
+
hasCached = true;
|
|
449
|
+
Object.defineProperty(value, "__indexed", {
|
|
450
|
+
configurable: true,
|
|
451
|
+
enumerable: true,
|
|
452
|
+
writable: true,
|
|
453
|
+
value: cached,
|
|
454
|
+
});
|
|
455
|
+
}
|
|
456
|
+
return cached;
|
|
457
|
+
},
|
|
458
|
+
set(indexed) {
|
|
459
|
+
cached = indexed;
|
|
460
|
+
hasCached = true;
|
|
461
|
+
},
|
|
462
|
+
});
|
|
463
|
+
return value;
|
|
464
|
+
};
|
|
241
465
|
let DocumentIndex = (() => {
|
|
242
466
|
let _classDecorators = [variant("documents_index")];
|
|
243
467
|
let _classDescriptor;
|
|
@@ -263,6 +487,10 @@ let DocumentIndex = (() => {
|
|
|
263
487
|
documentType = __runInitializers(this, __query_extraInitializers);
|
|
264
488
|
// transform options
|
|
265
489
|
transformer;
|
|
490
|
+
transformerIsIdentity = false;
|
|
491
|
+
nativeTransformDescriptor;
|
|
492
|
+
nativeTransformProjectionPlan;
|
|
493
|
+
nativeBackboneDocumentProjection;
|
|
266
494
|
// The indexed document wrapped in a context
|
|
267
495
|
wrappedIndexedType;
|
|
268
496
|
indexedType;
|
|
@@ -276,6 +504,7 @@ let DocumentIndex = (() => {
|
|
|
276
504
|
_resumableIterators;
|
|
277
505
|
_prefetch;
|
|
278
506
|
includeIndexed = undefined;
|
|
507
|
+
immutable = false;
|
|
279
508
|
compatibility;
|
|
280
509
|
// Transformation, indexer
|
|
281
510
|
/* fields: IndexableFields<T, I>; */
|
|
@@ -292,9 +521,19 @@ let DocumentIndex = (() => {
|
|
|
292
521
|
_joinListener;
|
|
293
522
|
_resultQueue;
|
|
294
523
|
iteratorKeepAliveTimers;
|
|
524
|
+
deleteResolvedCacheForKey(key) {
|
|
525
|
+
if (this.isProgramValued) {
|
|
526
|
+
this._resolverProgramCache.delete(key.primitive);
|
|
527
|
+
indexCacheLogger("cache:del:program", { id: key.primitive });
|
|
528
|
+
}
|
|
529
|
+
else if (this._resolverCache?.del(key.primitive)) {
|
|
530
|
+
indexCacheLogger("cache:del:value", { id: key.primitive });
|
|
531
|
+
}
|
|
532
|
+
}
|
|
295
533
|
constructor(properties) {
|
|
296
534
|
super();
|
|
297
535
|
this._query = properties?.query || new RPC();
|
|
536
|
+
this._resultQueue = new Map();
|
|
298
537
|
this.iteratorKeepAliveTimers = new Map();
|
|
299
538
|
}
|
|
300
539
|
get valueEncoding() {
|
|
@@ -407,7 +646,11 @@ let DocumentIndex = (() => {
|
|
|
407
646
|
}
|
|
408
647
|
return results;
|
|
409
648
|
}
|
|
410
|
-
|
|
649
|
+
// Bound in open(). Deserialized instances skip constructor/field initializers
|
|
650
|
+
// (borsh creates objects via Object.create), so this must not rely on a field
|
|
651
|
+
// initializer to exist.
|
|
652
|
+
handleDocumentChange;
|
|
653
|
+
async onDocumentChange(event) {
|
|
411
654
|
const added = event.detail.added;
|
|
412
655
|
if (!added.length) {
|
|
413
656
|
return;
|
|
@@ -474,7 +717,7 @@ let DocumentIndex = (() => {
|
|
|
474
717
|
queue.pushInFlight = false;
|
|
475
718
|
}
|
|
476
719
|
}
|
|
477
|
-
}
|
|
720
|
+
}
|
|
478
721
|
get nestedProperties() {
|
|
479
722
|
return {
|
|
480
723
|
match: (obj) => obj instanceof this.dbType,
|
|
@@ -511,6 +754,7 @@ let DocumentIndex = (() => {
|
|
|
511
754
|
this.canRead = properties.canRead;
|
|
512
755
|
this.canSearch = properties.canSearch;
|
|
513
756
|
this.includeIndexed = properties.includeIndexed;
|
|
757
|
+
this.immutable = properties.immutable ?? false;
|
|
514
758
|
let IndexedClassWithContext = (() => {
|
|
515
759
|
let _classDecorators = [variant(0)];
|
|
516
760
|
let _classDescriptor;
|
|
@@ -567,9 +811,19 @@ let DocumentIndex = (() => {
|
|
|
567
811
|
return replicateFn(rq, rs);
|
|
568
812
|
};
|
|
569
813
|
const transformOptions = properties.transform;
|
|
814
|
+
const hasTransformFunction = transformOptions != null && isTransformerWithFunction(transformOptions);
|
|
815
|
+
this.nativeTransformDescriptor = hasTransformFunction
|
|
816
|
+
? getDocumentTransformDescriptor(transformOptions.transform)
|
|
817
|
+
: undefined;
|
|
818
|
+
this.nativeTransformProjectionPlan = createSimpleProjectionPlan(getSchema(this.documentType), indexedSchema, this.nativeTransformDescriptor);
|
|
819
|
+
this.transformerIsIdentity =
|
|
820
|
+
transformOptions == null ||
|
|
821
|
+
(!hasTransformFunction && transformOptions.type == null) ||
|
|
822
|
+
(this.nativeTransformDescriptor?.kind === "identity" &&
|
|
823
|
+
this.indexedTypeIsDocumentType);
|
|
570
824
|
this.transformer = transformOptions
|
|
571
|
-
?
|
|
572
|
-
? (obj, context) => transformOptions.transform(obj, context)
|
|
825
|
+
? hasTransformFunction
|
|
826
|
+
? (obj, context, facts) => transformOptions.transform(obj, context, facts)
|
|
573
827
|
: transformOptions.type
|
|
574
828
|
? (obj, context) => new transformOptions.type(obj, context)
|
|
575
829
|
: (obj) => obj
|
|
@@ -659,9 +913,213 @@ let DocumentIndex = (() => {
|
|
|
659
913
|
responseType: types.AbstractSearchResult,
|
|
660
914
|
queryType: types.AbstractSearchRequest,
|
|
661
915
|
});
|
|
662
|
-
|
|
663
|
-
|
|
916
|
+
this.handleDocumentChange ??= (event) => this.onDocumentChange(event);
|
|
917
|
+
this.documentEvents.addEventListener("change", this.handleDocumentChange);
|
|
918
|
+
}
|
|
919
|
+
attachNativeBackboneDocumentIndex(backbone, options) {
|
|
920
|
+
this.nativeBackboneDocumentProjection = undefined;
|
|
921
|
+
if (!backbone ||
|
|
922
|
+
this.isProgramValued ||
|
|
923
|
+
!this.canUseNativeBackboneDocumentIndex()) {
|
|
924
|
+
return false;
|
|
925
|
+
}
|
|
926
|
+
const attach = this.index
|
|
927
|
+
.attachNativeBackboneDocumentIndex;
|
|
928
|
+
const attached = typeof attach === "function" &&
|
|
929
|
+
attach.call(this.index, backbone, options) === true;
|
|
930
|
+
if (attached) {
|
|
931
|
+
const projection = backbone;
|
|
932
|
+
if (typeof projection.projectDocumentIndexSimple === "function") {
|
|
933
|
+
this.nativeBackboneDocumentProjection = projection;
|
|
934
|
+
}
|
|
935
|
+
}
|
|
936
|
+
return attached;
|
|
937
|
+
}
|
|
938
|
+
canUseNativeBackboneDocumentIndex() {
|
|
939
|
+
return ((this.transformerIsIdentity && this.indexedTypeIsDocumentType) ||
|
|
940
|
+
this.nativeTransformDescriptor != null);
|
|
941
|
+
}
|
|
942
|
+
getNativeDocumentFieldExtractionPlan(path) {
|
|
943
|
+
return createSimpleFieldExtractionPlan(getSchema(this.documentType), path);
|
|
944
|
+
}
|
|
945
|
+
canPrepareNativeBackboneDocumentIndexCommit() {
|
|
946
|
+
return ((this.transformerIsIdentity && this.indexedTypeIsDocumentType) ||
|
|
947
|
+
canPrepareDocumentTransformBeforeAppend(this.nativeTransformDescriptor));
|
|
948
|
+
}
|
|
949
|
+
canPrepareNativeBackboneDocumentIndexCommitWithAppendFacts() {
|
|
950
|
+
return (this.canPrepareNativeBackboneDocumentIndexCommit() ||
|
|
951
|
+
!!this.nativeTransformProjectionPlan ||
|
|
952
|
+
canPrepareDocumentTransformWithAppendFacts(this.nativeTransformDescriptor));
|
|
953
|
+
}
|
|
954
|
+
canUseNativeBackboneContextualBatch() {
|
|
955
|
+
return (!this.isProgramValued &&
|
|
956
|
+
typeof this.index.putWithContextBatch ===
|
|
957
|
+
"function" &&
|
|
958
|
+
((this.transformerIsIdentity && this.indexedTypeIsDocumentType) ||
|
|
959
|
+
this.canPrepareNativeBackboneDocumentIndexCommitWithAppendFacts()));
|
|
960
|
+
}
|
|
961
|
+
prepareNativeBackboneDocumentIndexCommit(value, encodedDocument, transformFacts) {
|
|
962
|
+
if (this.transformerIsIdentity && this.indexedTypeIsDocumentType) {
|
|
963
|
+
return {
|
|
964
|
+
valuePrefixBytes: encodedDocument,
|
|
965
|
+
usePlainPutPayload: true,
|
|
966
|
+
indexable: value,
|
|
967
|
+
};
|
|
968
|
+
}
|
|
969
|
+
if (this.nativeTransformProjectionPlan) {
|
|
970
|
+
let projectionContext;
|
|
971
|
+
let cached;
|
|
972
|
+
let hasCached = false;
|
|
973
|
+
return {
|
|
974
|
+
projection: {
|
|
975
|
+
encodedDocument,
|
|
976
|
+
plan: this.nativeTransformProjectionPlan,
|
|
977
|
+
signer: transformFacts?.entryPublicKeys?.[0]?.bytes,
|
|
978
|
+
},
|
|
979
|
+
getIndexable: () => {
|
|
980
|
+
if (!hasCached) {
|
|
981
|
+
const transformed = this.transformer(value, projectionContext, transformFacts);
|
|
982
|
+
if (isPromiseLike(transformed)) {
|
|
983
|
+
throw new Error("Native descriptor transform unexpectedly returned a promise");
|
|
984
|
+
}
|
|
985
|
+
cached = transformed;
|
|
986
|
+
hasCached = true;
|
|
987
|
+
}
|
|
988
|
+
return cached;
|
|
989
|
+
},
|
|
990
|
+
setContext: (context) => {
|
|
991
|
+
projectionContext = context;
|
|
992
|
+
hasCached = false;
|
|
993
|
+
cached = undefined;
|
|
994
|
+
},
|
|
995
|
+
};
|
|
996
|
+
}
|
|
997
|
+
if (!canPrepareDocumentTransformBeforeAppend(this.nativeTransformDescriptor)) {
|
|
998
|
+
return;
|
|
999
|
+
}
|
|
1000
|
+
const transformed = this.transformer(value, undefined, transformFacts);
|
|
1001
|
+
const finish = (indexable) => ({
|
|
1002
|
+
valuePrefixBytes: serialize(this.asIndexedTypeValue(indexable)),
|
|
1003
|
+
indexable,
|
|
1004
|
+
});
|
|
1005
|
+
return isPromiseLike(transformed)
|
|
1006
|
+
? transformed.then(finish)
|
|
1007
|
+
: finish(transformed);
|
|
1008
|
+
}
|
|
1009
|
+
prepareNativeBackboneDocumentIndexCommitWithAppendFacts(value, encodedDocument, context, transformFacts) {
|
|
1010
|
+
if (this.transformerIsIdentity && this.indexedTypeIsDocumentType) {
|
|
1011
|
+
return {
|
|
1012
|
+
valuePrefixBytes: encodedDocument,
|
|
1013
|
+
usePlainPutPayload: true,
|
|
1014
|
+
indexable: value,
|
|
1015
|
+
};
|
|
1016
|
+
}
|
|
1017
|
+
if (this.nativeTransformProjectionPlan) {
|
|
1018
|
+
let projectionContext = {
|
|
1019
|
+
created: context.created,
|
|
1020
|
+
modified: context.modified,
|
|
1021
|
+
head: context.head,
|
|
1022
|
+
gid: context.gid,
|
|
1023
|
+
size: context.size,
|
|
1024
|
+
signer: transformFacts?.entryPublicKeys?.[0]?.bytes,
|
|
1025
|
+
};
|
|
1026
|
+
let cached;
|
|
1027
|
+
let hasCached = false;
|
|
1028
|
+
return {
|
|
1029
|
+
projection: {
|
|
1030
|
+
encodedDocument,
|
|
1031
|
+
plan: this.nativeTransformProjectionPlan,
|
|
1032
|
+
signer: projectionContext.signer,
|
|
1033
|
+
},
|
|
1034
|
+
getIndexable: () => {
|
|
1035
|
+
if (!hasCached) {
|
|
1036
|
+
const transformed = this.transformer(value, projectionContext, transformFacts);
|
|
1037
|
+
if (isPromiseLike(transformed)) {
|
|
1038
|
+
throw new Error("Native descriptor transform unexpectedly returned a promise");
|
|
1039
|
+
}
|
|
1040
|
+
cached = transformed;
|
|
1041
|
+
hasCached = true;
|
|
1042
|
+
}
|
|
1043
|
+
return cached;
|
|
1044
|
+
},
|
|
1045
|
+
setContext: (nextContext) => {
|
|
1046
|
+
projectionContext = {
|
|
1047
|
+
created: nextContext.created,
|
|
1048
|
+
modified: nextContext.modified,
|
|
1049
|
+
head: nextContext.head,
|
|
1050
|
+
gid: nextContext.gid,
|
|
1051
|
+
size: nextContext.size,
|
|
1052
|
+
signer: transformFacts?.entryPublicKeys?.[0]?.bytes,
|
|
1053
|
+
};
|
|
1054
|
+
hasCached = false;
|
|
1055
|
+
cached = undefined;
|
|
1056
|
+
},
|
|
1057
|
+
};
|
|
1058
|
+
}
|
|
1059
|
+
if (!canPrepareDocumentTransformWithAppendFacts(this.nativeTransformDescriptor) &&
|
|
1060
|
+
!canPrepareDocumentTransformBeforeAppend(this.nativeTransformDescriptor)) {
|
|
1061
|
+
return;
|
|
1062
|
+
}
|
|
1063
|
+
const transformed = this.transformer(value, context, transformFacts);
|
|
1064
|
+
if (isPromiseLike(transformed)) {
|
|
1065
|
+
return;
|
|
664
1066
|
}
|
|
1067
|
+
const indexable = transformed;
|
|
1068
|
+
return {
|
|
1069
|
+
valuePrefixBytes: serialize(this.asIndexedTypeValue(indexable)),
|
|
1070
|
+
indexable,
|
|
1071
|
+
};
|
|
1072
|
+
}
|
|
1073
|
+
prepareNativeBackboneDocumentIndexStoredCommitWithAppendFacts(encodedDocument, context, transformFacts) {
|
|
1074
|
+
if (this.transformerIsIdentity && this.indexedTypeIsDocumentType) {
|
|
1075
|
+
return {
|
|
1076
|
+
valuePrefixBytes: encodedDocument,
|
|
1077
|
+
usePlainPutPayload: true,
|
|
1078
|
+
};
|
|
1079
|
+
}
|
|
1080
|
+
if (this.nativeTransformProjectionPlan) {
|
|
1081
|
+
return {
|
|
1082
|
+
projection: {
|
|
1083
|
+
encodedDocument,
|
|
1084
|
+
plan: this.nativeTransformProjectionPlan,
|
|
1085
|
+
signer: transformFacts?.entryPublicKeys?.[0]?.bytes,
|
|
1086
|
+
},
|
|
1087
|
+
};
|
|
1088
|
+
}
|
|
1089
|
+
if (!canPrepareDocumentTransformWithAppendFacts(this.nativeTransformDescriptor) &&
|
|
1090
|
+
!canPrepareDocumentTransformBeforeAppend(this.nativeTransformDescriptor)) {
|
|
1091
|
+
return;
|
|
1092
|
+
}
|
|
1093
|
+
return;
|
|
1094
|
+
}
|
|
1095
|
+
nativeBackboneDocumentIndexValuePrefixBytes(nativeDocumentIndex, context) {
|
|
1096
|
+
return (nativeDocumentIndex.valuePrefixBytes ??
|
|
1097
|
+
(nativeDocumentIndex.projection
|
|
1098
|
+
? (this.nativeBackboneDocumentProjection?.projectDocumentIndexSimple?.(nativeDocumentIndex.projection.encodedDocument, nativeDocumentIndex.projection.plan, {
|
|
1099
|
+
created: context.created,
|
|
1100
|
+
modified: context.modified,
|
|
1101
|
+
head: context.head,
|
|
1102
|
+
gid: context.gid,
|
|
1103
|
+
size: context.size,
|
|
1104
|
+
signer: nativeDocumentIndex.projection.signer,
|
|
1105
|
+
}) ??
|
|
1106
|
+
tryProjectDocumentIndexSimple(nativeDocumentIndex.projection.encodedDocument, nativeDocumentIndex.projection.plan, {
|
|
1107
|
+
created: context.created,
|
|
1108
|
+
modified: context.modified,
|
|
1109
|
+
head: context.head,
|
|
1110
|
+
gid: context.gid,
|
|
1111
|
+
size: context.size,
|
|
1112
|
+
signer: nativeDocumentIndex.projection.signer,
|
|
1113
|
+
}))
|
|
1114
|
+
: undefined));
|
|
1115
|
+
}
|
|
1116
|
+
asIndexedTypeValue(value) {
|
|
1117
|
+
if (value &&
|
|
1118
|
+
Object.getPrototypeOf(value) ===
|
|
1119
|
+
this.indexedType.prototype) {
|
|
1120
|
+
return value;
|
|
1121
|
+
}
|
|
1122
|
+
return Object.assign(Object.create(this.indexedType.prototype), value);
|
|
665
1123
|
}
|
|
666
1124
|
get prefetch() {
|
|
667
1125
|
return this._prefetch;
|
|
@@ -818,11 +1276,11 @@ let DocumentIndex = (() => {
|
|
|
818
1276
|
if (this._joinListener) {
|
|
819
1277
|
this._query.events.removeEventListener("join", this._joinListener);
|
|
820
1278
|
}
|
|
821
|
-
if (this.handleDocumentChange) {
|
|
1279
|
+
if (this.handleDocumentChange && this.documentEvents) {
|
|
822
1280
|
this.documentEvents.removeEventListener("change", this.handleDocumentChange);
|
|
823
1281
|
}
|
|
824
1282
|
this.clearAllResultQueues();
|
|
825
|
-
await this._resumableIterators
|
|
1283
|
+
await this._resumableIterators?.clearAll();
|
|
826
1284
|
if (this.iteratorKeepAliveTimers) {
|
|
827
1285
|
for (const timer of this.iteratorKeepAliveTimers.values()) {
|
|
828
1286
|
clearTimeout(timer);
|
|
@@ -846,9 +1304,9 @@ let DocumentIndex = (() => {
|
|
|
846
1304
|
async drop(from) {
|
|
847
1305
|
const dropped = await super.drop(from);
|
|
848
1306
|
if (dropped) {
|
|
849
|
-
this.documentEvents
|
|
1307
|
+
this.documentEvents?.removeEventListener("change", this.handleDocumentChange);
|
|
850
1308
|
this.clearAllResultQueues();
|
|
851
|
-
await this._resumableIterators
|
|
1309
|
+
await this._resumableIterators?.clearAll();
|
|
852
1310
|
if (this.iteratorKeepAliveTimers) {
|
|
853
1311
|
for (const timer of this.iteratorKeepAliveTimers.values()) {
|
|
854
1312
|
clearTimeout(timer);
|
|
@@ -977,6 +1435,426 @@ let DocumentIndex = (() => {
|
|
|
977
1435
|
await iterator.close();
|
|
978
1436
|
return one[0];
|
|
979
1437
|
}
|
|
1438
|
+
async getIdentityIndexedByHead(head) {
|
|
1439
|
+
if (!this.canGetIdentityIndexedByHead()) {
|
|
1440
|
+
return;
|
|
1441
|
+
}
|
|
1442
|
+
return this.index.getByContextHead?.(head);
|
|
1443
|
+
}
|
|
1444
|
+
async getIdentityIndexedKeyByHead(head) {
|
|
1445
|
+
const key = this.getIndexedKeyByHead(head);
|
|
1446
|
+
if (key) {
|
|
1447
|
+
return key;
|
|
1448
|
+
}
|
|
1449
|
+
const indexed = await this.getIdentityIndexedByHead(head);
|
|
1450
|
+
return indexed?.id;
|
|
1451
|
+
}
|
|
1452
|
+
getIndexedKeyByHead(head) {
|
|
1453
|
+
const getIdByHead = this.index.getIdByContextHead;
|
|
1454
|
+
return typeof getIdByHead === "function"
|
|
1455
|
+
? getIdByHead.call(this.index, head)
|
|
1456
|
+
: undefined;
|
|
1457
|
+
}
|
|
1458
|
+
getIndexedKeysByHeads(heads) {
|
|
1459
|
+
const getIdByHead = this.index.getIdByContextHead;
|
|
1460
|
+
if (typeof getIdByHead !== "function") {
|
|
1461
|
+
return;
|
|
1462
|
+
}
|
|
1463
|
+
return heads.map((head) => getIdByHead.call(this.index, head));
|
|
1464
|
+
}
|
|
1465
|
+
tryGetIdentityIndexedKeyByHead(head) {
|
|
1466
|
+
const getIdByHead = this.index.getIdByContextHead;
|
|
1467
|
+
if (typeof getIdByHead !== "function") {
|
|
1468
|
+
return { supported: false };
|
|
1469
|
+
}
|
|
1470
|
+
return {
|
|
1471
|
+
supported: true,
|
|
1472
|
+
key: getIdByHead.call(this.index, head),
|
|
1473
|
+
};
|
|
1474
|
+
}
|
|
1475
|
+
async getIdentityIndexedByHeads(heads) {
|
|
1476
|
+
if (!this.canGetIdentityIndexedByHead()) {
|
|
1477
|
+
return;
|
|
1478
|
+
}
|
|
1479
|
+
const batch = this.index.getByContextHeadBatch;
|
|
1480
|
+
if (batch) {
|
|
1481
|
+
return batch.call(this.index, heads);
|
|
1482
|
+
}
|
|
1483
|
+
return Promise.all(heads.map((head) => this.getIdentityIndexedByHead(head)));
|
|
1484
|
+
}
|
|
1485
|
+
canGetIdentityIndexedByHead() {
|
|
1486
|
+
return (this.transformerIsIdentity &&
|
|
1487
|
+
this.indexedTypeIsDocumentType &&
|
|
1488
|
+
!this.isProgramValued &&
|
|
1489
|
+
typeof this.index.getByContextHead === "function");
|
|
1490
|
+
}
|
|
1491
|
+
canGetIndexedKeyByHead() {
|
|
1492
|
+
return (!this.isProgramValued &&
|
|
1493
|
+
typeof this.index.getIdByContextHead ===
|
|
1494
|
+
"function");
|
|
1495
|
+
}
|
|
1496
|
+
canReadOriginalFieldPathsFromIndexedValue(paths) {
|
|
1497
|
+
return (!this.isProgramValued &&
|
|
1498
|
+
paths.every((path) => this.transformerIsIdentity && this.indexedTypeIsDocumentType
|
|
1499
|
+
? true
|
|
1500
|
+
: documentTransformPreservesFieldPath(this.nativeTransformDescriptor, path)));
|
|
1501
|
+
}
|
|
1502
|
+
canReadNativeIndexedFieldValues(paths) {
|
|
1503
|
+
return (this.canReadOriginalFieldPathsFromIndexedValue(paths) &&
|
|
1504
|
+
typeof this.index.getNativeIndexedFieldValue === "function");
|
|
1505
|
+
}
|
|
1506
|
+
getNativeIndexedFieldValue(id, path) {
|
|
1507
|
+
const read = this.index.getNativeIndexedFieldValue;
|
|
1508
|
+
if (typeof read !== "function") {
|
|
1509
|
+
return undefined;
|
|
1510
|
+
}
|
|
1511
|
+
return read.call(this.index, id, typeof path === "string" ? [path] : path);
|
|
1512
|
+
}
|
|
1513
|
+
_putIdentityWithContext(value, id, context, options) {
|
|
1514
|
+
const contextualPut = this.transformerIsIdentity
|
|
1515
|
+
? this.index.putWithContext
|
|
1516
|
+
: undefined;
|
|
1517
|
+
if (!contextualPut || this.isProgramValued) {
|
|
1518
|
+
return;
|
|
1519
|
+
}
|
|
1520
|
+
const indexable = value;
|
|
1521
|
+
const indexedValue = coerceWithIndexed(coerceWithContext(value, context), indexable);
|
|
1522
|
+
this.cacheResolvedValue(id.primitive, value);
|
|
1523
|
+
const handleError = (error) => {
|
|
1524
|
+
if (error instanceof indexerTypes.NotStartedError && this.closed) {
|
|
1525
|
+
return indexedValue;
|
|
1526
|
+
}
|
|
1527
|
+
throw error;
|
|
1528
|
+
};
|
|
1529
|
+
try {
|
|
1530
|
+
const putResult = contextualPut.call(this.index, indexable, id, context, this.withContextualEncodedValue(options, context));
|
|
1531
|
+
return isPromiseLike(putResult)
|
|
1532
|
+
? putResult.then(() => indexedValue, handleError)
|
|
1533
|
+
: indexedValue;
|
|
1534
|
+
}
|
|
1535
|
+
catch (error) {
|
|
1536
|
+
return handleError(error);
|
|
1537
|
+
}
|
|
1538
|
+
}
|
|
1539
|
+
_putStoredIdentityWithContext(value, id, context, encodedValueParts, options) {
|
|
1540
|
+
const contextualStoredPut = this.transformerIsIdentity
|
|
1541
|
+
? this.index.putStoredContextualEncodedValue
|
|
1542
|
+
: undefined;
|
|
1543
|
+
if (!contextualStoredPut ||
|
|
1544
|
+
this.isProgramValued ||
|
|
1545
|
+
!this.indexedTypeIsDocumentType) {
|
|
1546
|
+
return;
|
|
1547
|
+
}
|
|
1548
|
+
const indexable = value;
|
|
1549
|
+
const indexedValue = coerceWithIndexed(coerceWithContext(value, context), indexable);
|
|
1550
|
+
this.cacheResolvedValue(id.primitive, value);
|
|
1551
|
+
const handleError = (error) => {
|
|
1552
|
+
if (error instanceof indexerTypes.NotStartedError && this.closed) {
|
|
1553
|
+
return indexedValue;
|
|
1554
|
+
}
|
|
1555
|
+
throw error;
|
|
1556
|
+
};
|
|
1557
|
+
try {
|
|
1558
|
+
const putResult = contextualStoredPut.call(this.index, id, encodedValueParts, options);
|
|
1559
|
+
if (putResult === false) {
|
|
1560
|
+
return;
|
|
1561
|
+
}
|
|
1562
|
+
return isPromiseLike(putResult)
|
|
1563
|
+
? putResult.then(() => indexedValue, handleError)
|
|
1564
|
+
: indexedValue;
|
|
1565
|
+
}
|
|
1566
|
+
catch (error) {
|
|
1567
|
+
return handleError(error);
|
|
1568
|
+
}
|
|
1569
|
+
}
|
|
1570
|
+
_putPreparedNativeBackboneDocumentIndexWithContext(value, id, context, nativeDocumentIndex, options) {
|
|
1571
|
+
const contextualStoredPut = this.index
|
|
1572
|
+
.putStoredContextualEncodedValue;
|
|
1573
|
+
if (!contextualStoredPut || this.isProgramValued) {
|
|
1574
|
+
return;
|
|
1575
|
+
}
|
|
1576
|
+
nativeDocumentIndex.setContext?.(context);
|
|
1577
|
+
const valueWithContext = coerceWithContext(value, context);
|
|
1578
|
+
const indexedValue = nativeDocumentIndex.indexable
|
|
1579
|
+
? coerceWithIndexed(valueWithContext, nativeDocumentIndex.indexable)
|
|
1580
|
+
: nativeDocumentIndex.getIndexable
|
|
1581
|
+
? coerceWithLazyIndexed(valueWithContext, nativeDocumentIndex.getIndexable)
|
|
1582
|
+
: undefined;
|
|
1583
|
+
if (!indexedValue) {
|
|
1584
|
+
return;
|
|
1585
|
+
}
|
|
1586
|
+
this.cacheResolvedValue(id.primitive, value);
|
|
1587
|
+
const valuePrefixBytes = this.nativeBackboneDocumentIndexValuePrefixBytes(nativeDocumentIndex, context);
|
|
1588
|
+
if (!valuePrefixBytes) {
|
|
1589
|
+
return;
|
|
1590
|
+
}
|
|
1591
|
+
const encodedValueParts = {
|
|
1592
|
+
prefix: valuePrefixBytes,
|
|
1593
|
+
suffix: encodeContextSuffix(context),
|
|
1594
|
+
};
|
|
1595
|
+
const handleError = (error) => {
|
|
1596
|
+
if (error instanceof indexerTypes.NotStartedError && this.closed) {
|
|
1597
|
+
return indexedValue;
|
|
1598
|
+
}
|
|
1599
|
+
throw error;
|
|
1600
|
+
};
|
|
1601
|
+
try {
|
|
1602
|
+
const putResult = contextualStoredPut.call(this.index, id, encodedValueParts, options);
|
|
1603
|
+
if (putResult === false) {
|
|
1604
|
+
return;
|
|
1605
|
+
}
|
|
1606
|
+
return isPromiseLike(putResult)
|
|
1607
|
+
? putResult.then(() => indexedValue, handleError)
|
|
1608
|
+
: indexedValue;
|
|
1609
|
+
}
|
|
1610
|
+
catch (error) {
|
|
1611
|
+
return handleError(error);
|
|
1612
|
+
}
|
|
1613
|
+
}
|
|
1614
|
+
_putPreparedNativeBackboneDocumentIndexStoredWithContext(id, context, nativeDocumentIndex, options) {
|
|
1615
|
+
const contextualStoredPut = this.index
|
|
1616
|
+
.putStoredContextualEncodedValue;
|
|
1617
|
+
if (!contextualStoredPut || this.isProgramValued) {
|
|
1618
|
+
return;
|
|
1619
|
+
}
|
|
1620
|
+
nativeDocumentIndex.setContext?.(context);
|
|
1621
|
+
const valuePrefixBytes = this.nativeBackboneDocumentIndexValuePrefixBytes(nativeDocumentIndex, context);
|
|
1622
|
+
if (!valuePrefixBytes) {
|
|
1623
|
+
return;
|
|
1624
|
+
}
|
|
1625
|
+
const encodedValueParts = {
|
|
1626
|
+
prefix: valuePrefixBytes,
|
|
1627
|
+
suffix: encodeContextSuffix(context),
|
|
1628
|
+
};
|
|
1629
|
+
const handleError = (error) => {
|
|
1630
|
+
if (error instanceof indexerTypes.NotStartedError && this.closed) {
|
|
1631
|
+
return true;
|
|
1632
|
+
}
|
|
1633
|
+
throw error;
|
|
1634
|
+
};
|
|
1635
|
+
try {
|
|
1636
|
+
const putResult = contextualStoredPut.call(this.index, id, encodedValueParts, options);
|
|
1637
|
+
if (putResult === false) {
|
|
1638
|
+
return false;
|
|
1639
|
+
}
|
|
1640
|
+
return isPromiseLike(putResult)
|
|
1641
|
+
? putResult.then(() => true, handleError)
|
|
1642
|
+
: true;
|
|
1643
|
+
}
|
|
1644
|
+
catch (error) {
|
|
1645
|
+
return handleError(error);
|
|
1646
|
+
}
|
|
1647
|
+
}
|
|
1648
|
+
_persistPreparedNativeBackboneDocumentIndexStoredWithContext(id, context, nativeDocumentIndex, encodedValueParts, options) {
|
|
1649
|
+
const persistStoredPut = this.index
|
|
1650
|
+
.persistStoredContextualEncodedValue;
|
|
1651
|
+
if (!persistStoredPut || this.isProgramValued) {
|
|
1652
|
+
return;
|
|
1653
|
+
}
|
|
1654
|
+
let storedParts;
|
|
1655
|
+
if (this.transformerIsIdentity &&
|
|
1656
|
+
this.indexedTypeIsDocumentType &&
|
|
1657
|
+
encodedValueParts) {
|
|
1658
|
+
storedParts = encodedValueParts;
|
|
1659
|
+
}
|
|
1660
|
+
else if (nativeDocumentIndex) {
|
|
1661
|
+
nativeDocumentIndex.setContext?.(context);
|
|
1662
|
+
const valuePrefixBytes = nativeDocumentIndex.valuePrefixBytes ??
|
|
1663
|
+
this.nativeBackboneDocumentIndexValuePrefixBytes(nativeDocumentIndex, context);
|
|
1664
|
+
if (!valuePrefixBytes) {
|
|
1665
|
+
return;
|
|
1666
|
+
}
|
|
1667
|
+
storedParts = {
|
|
1668
|
+
prefix: valuePrefixBytes,
|
|
1669
|
+
suffix: encodeContextSuffix(context),
|
|
1670
|
+
};
|
|
1671
|
+
}
|
|
1672
|
+
else {
|
|
1673
|
+
return;
|
|
1674
|
+
}
|
|
1675
|
+
try {
|
|
1676
|
+
const persistResult = persistStoredPut.call(this.index, id, storedParts, options);
|
|
1677
|
+
if (persistResult === false) {
|
|
1678
|
+
return false;
|
|
1679
|
+
}
|
|
1680
|
+
return isPromiseLike(persistResult)
|
|
1681
|
+
? persistResult.then(() => true, (error) => {
|
|
1682
|
+
if (error instanceof indexerTypes.NotStartedError && this.closed) {
|
|
1683
|
+
return true;
|
|
1684
|
+
}
|
|
1685
|
+
throw error;
|
|
1686
|
+
})
|
|
1687
|
+
: true;
|
|
1688
|
+
}
|
|
1689
|
+
catch (error) {
|
|
1690
|
+
if (error instanceof indexerTypes.NotStartedError && this.closed) {
|
|
1691
|
+
return true;
|
|
1692
|
+
}
|
|
1693
|
+
throw error;
|
|
1694
|
+
}
|
|
1695
|
+
}
|
|
1696
|
+
async _putManyPreparedNativeBackboneDocumentIndexWithContext(values) {
|
|
1697
|
+
if (values.length === 0) {
|
|
1698
|
+
return [];
|
|
1699
|
+
}
|
|
1700
|
+
const contextualBatchPut = this.index
|
|
1701
|
+
.putWithContextBatch;
|
|
1702
|
+
if (!contextualBatchPut || this.isProgramValued) {
|
|
1703
|
+
return;
|
|
1704
|
+
}
|
|
1705
|
+
const indexedValues = [];
|
|
1706
|
+
const batchValues = [];
|
|
1707
|
+
for (const item of values) {
|
|
1708
|
+
if (!item.nativeDocumentIndex) {
|
|
1709
|
+
return;
|
|
1710
|
+
}
|
|
1711
|
+
item.nativeDocumentIndex.setContext?.(item.context);
|
|
1712
|
+
const valueWithContext = coerceWithContext(item.value, item.context);
|
|
1713
|
+
const indexedValue = item.nativeDocumentIndex.indexable
|
|
1714
|
+
? coerceWithIndexed(valueWithContext, item.nativeDocumentIndex.indexable)
|
|
1715
|
+
: item.nativeDocumentIndex.getIndexable
|
|
1716
|
+
? coerceWithLazyIndexed(valueWithContext, item.nativeDocumentIndex.getIndexable)
|
|
1717
|
+
: undefined;
|
|
1718
|
+
if (!indexedValue) {
|
|
1719
|
+
return;
|
|
1720
|
+
}
|
|
1721
|
+
const valuePrefixBytes = this.nativeBackboneDocumentIndexValuePrefixBytes(item.nativeDocumentIndex, item.context);
|
|
1722
|
+
if (!valuePrefixBytes) {
|
|
1723
|
+
return;
|
|
1724
|
+
}
|
|
1725
|
+
this.cacheResolvedValue(item.id.primitive, item.value);
|
|
1726
|
+
indexedValues.push(indexedValue);
|
|
1727
|
+
batchValues.push({
|
|
1728
|
+
// Encoded native batches store from encodedValueParts; descriptor
|
|
1729
|
+
// projections keep the JS indexable lazy for event consumers.
|
|
1730
|
+
value: item.nativeDocumentIndex.indexable ?? undefined,
|
|
1731
|
+
id: item.id,
|
|
1732
|
+
context: item.context,
|
|
1733
|
+
options: {
|
|
1734
|
+
replace: item.options?.replace,
|
|
1735
|
+
encodedValueParts: {
|
|
1736
|
+
prefix: valuePrefixBytes,
|
|
1737
|
+
suffix: encodeContextSuffix(item.context),
|
|
1738
|
+
},
|
|
1739
|
+
},
|
|
1740
|
+
});
|
|
1741
|
+
}
|
|
1742
|
+
const handleError = (error) => {
|
|
1743
|
+
if (error instanceof indexerTypes.NotStartedError && this.closed) {
|
|
1744
|
+
return indexedValues;
|
|
1745
|
+
}
|
|
1746
|
+
throw error;
|
|
1747
|
+
};
|
|
1748
|
+
try {
|
|
1749
|
+
const putResult = contextualBatchPut.call(this.index, batchValues);
|
|
1750
|
+
return isPromiseLike(putResult)
|
|
1751
|
+
? putResult.then(() => indexedValues, handleError)
|
|
1752
|
+
: indexedValues;
|
|
1753
|
+
}
|
|
1754
|
+
catch (error) {
|
|
1755
|
+
return handleError(error);
|
|
1756
|
+
}
|
|
1757
|
+
}
|
|
1758
|
+
async _putManyPreparedNativeBackboneDocumentIndexStored(values) {
|
|
1759
|
+
if (values.length === 0) {
|
|
1760
|
+
return true;
|
|
1761
|
+
}
|
|
1762
|
+
if (this.isProgramValued) {
|
|
1763
|
+
return;
|
|
1764
|
+
}
|
|
1765
|
+
const storedBatchPut = this.index
|
|
1766
|
+
.putStoredContextualEncodedValueBatch;
|
|
1767
|
+
if (!storedBatchPut) {
|
|
1768
|
+
return;
|
|
1769
|
+
}
|
|
1770
|
+
const batchValues = [];
|
|
1771
|
+
for (const item of values) {
|
|
1772
|
+
let encodedValueParts;
|
|
1773
|
+
if (this.transformerIsIdentity &&
|
|
1774
|
+
this.indexedTypeIsDocumentType &&
|
|
1775
|
+
item.encodedValueParts) {
|
|
1776
|
+
encodedValueParts = item.encodedValueParts;
|
|
1777
|
+
}
|
|
1778
|
+
else if (item.nativeDocumentIndex) {
|
|
1779
|
+
item.nativeDocumentIndex.setContext?.(item.context);
|
|
1780
|
+
const valuePrefixBytes = item.nativeDocumentIndex.valuePrefixBytes ??
|
|
1781
|
+
this.nativeBackboneDocumentIndexValuePrefixBytes(item.nativeDocumentIndex, item.context);
|
|
1782
|
+
if (!valuePrefixBytes) {
|
|
1783
|
+
return;
|
|
1784
|
+
}
|
|
1785
|
+
encodedValueParts = {
|
|
1786
|
+
prefix: valuePrefixBytes,
|
|
1787
|
+
suffix: encodeContextSuffix(item.context),
|
|
1788
|
+
};
|
|
1789
|
+
}
|
|
1790
|
+
else {
|
|
1791
|
+
return;
|
|
1792
|
+
}
|
|
1793
|
+
this.cacheResolvedValue(item.id.primitive, item.value);
|
|
1794
|
+
batchValues.push({
|
|
1795
|
+
id: item.id,
|
|
1796
|
+
encodedValueParts,
|
|
1797
|
+
options: item.options,
|
|
1798
|
+
});
|
|
1799
|
+
}
|
|
1800
|
+
const handleError = (error) => {
|
|
1801
|
+
if (error instanceof indexerTypes.NotStartedError && this.closed) {
|
|
1802
|
+
return true;
|
|
1803
|
+
}
|
|
1804
|
+
throw error;
|
|
1805
|
+
};
|
|
1806
|
+
try {
|
|
1807
|
+
return await storedBatchPut.call(this.index, batchValues);
|
|
1808
|
+
}
|
|
1809
|
+
catch (error) {
|
|
1810
|
+
return handleError(error);
|
|
1811
|
+
}
|
|
1812
|
+
}
|
|
1813
|
+
async _putManyIdentityWithContext(values) {
|
|
1814
|
+
if (values.length === 0) {
|
|
1815
|
+
return [];
|
|
1816
|
+
}
|
|
1817
|
+
const contextualPut = this.transformerIsIdentity
|
|
1818
|
+
? this.index.putWithContext
|
|
1819
|
+
: undefined;
|
|
1820
|
+
const contextualBatchPut = this.transformerIsIdentity
|
|
1821
|
+
? this.index.putWithContextBatch
|
|
1822
|
+
: undefined;
|
|
1823
|
+
if ((!contextualBatchPut && !contextualPut) || this.isProgramValued) {
|
|
1824
|
+
return;
|
|
1825
|
+
}
|
|
1826
|
+
const indexedValues = values.map((item) => {
|
|
1827
|
+
const indexable = item.value;
|
|
1828
|
+
this.cacheResolvedValue(item.id.primitive, item.value);
|
|
1829
|
+
return {
|
|
1830
|
+
indexable,
|
|
1831
|
+
value: coerceWithIndexed(coerceWithContext(item.value, item.context), indexable),
|
|
1832
|
+
};
|
|
1833
|
+
});
|
|
1834
|
+
try {
|
|
1835
|
+
if (contextualBatchPut) {
|
|
1836
|
+
await contextualBatchPut.call(this.index, values.map((item, index) => ({
|
|
1837
|
+
value: indexedValues[index].indexable,
|
|
1838
|
+
id: item.id,
|
|
1839
|
+
context: item.context,
|
|
1840
|
+
options: this.withContextualEncodedValue(item.options, item.context),
|
|
1841
|
+
})));
|
|
1842
|
+
}
|
|
1843
|
+
else {
|
|
1844
|
+
for (let i = 0; i < values.length; i++) {
|
|
1845
|
+
const item = values[i];
|
|
1846
|
+
await contextualPut.call(this.index, indexedValues[i].indexable, item.id, item.context, this.withContextualEncodedValue(item.options, item.context));
|
|
1847
|
+
}
|
|
1848
|
+
}
|
|
1849
|
+
}
|
|
1850
|
+
catch (error) {
|
|
1851
|
+
if (error instanceof indexerTypes.NotStartedError && this.closed) {
|
|
1852
|
+
return indexedValues.map((item) => item.value);
|
|
1853
|
+
}
|
|
1854
|
+
throw error;
|
|
1855
|
+
}
|
|
1856
|
+
return indexedValues.map((item) => item.value);
|
|
1857
|
+
}
|
|
980
1858
|
async put(value, id, entry, existing) {
|
|
981
1859
|
const existingDefined = existing === undefined
|
|
982
1860
|
? await this.index.get(id, {
|
|
@@ -993,31 +1871,33 @@ let DocumentIndex = (() => {
|
|
|
993
1871
|
});
|
|
994
1872
|
return this.putWithContext(value, id, context, {
|
|
995
1873
|
replace: existingDefined != null,
|
|
1874
|
+
transformFacts: { entryPublicKeys: entry.publicKeys },
|
|
996
1875
|
});
|
|
997
1876
|
}
|
|
998
1877
|
async putWithContext(value, id, context, options) {
|
|
999
1878
|
const idString = id.primitive;
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
(value as Program).parents.includes(this._log) */) {
|
|
1005
|
-
// TODO make last condition more efficient if there are many docs
|
|
1006
|
-
this._resolverProgramCache.set(idString, value);
|
|
1007
|
-
indexCacheLogger("cache:set:program", { id: idString });
|
|
1008
|
-
}
|
|
1009
|
-
else {
|
|
1010
|
-
if (this._resolverCache) {
|
|
1011
|
-
this._resolverCache.add(idString, value);
|
|
1012
|
-
indexCacheLogger("cache:set:value", { id: idString });
|
|
1013
|
-
}
|
|
1014
|
-
}
|
|
1015
|
-
const valueToIndex = await this.transformer(value, context);
|
|
1016
|
-
const wrappedValueToIndex = new this.wrappedIndexedType(valueToIndex, context);
|
|
1879
|
+
this.cacheResolvedValue(idString, value);
|
|
1880
|
+
const valueToIndex = this.transformerIsIdentity
|
|
1881
|
+
? value
|
|
1882
|
+
: await this.transformer(value, context, options?.transformFacts);
|
|
1017
1883
|
coerceWithIndexed(value, valueToIndex);
|
|
1018
1884
|
coerceWithContext(value, context);
|
|
1019
1885
|
try {
|
|
1020
|
-
|
|
1886
|
+
const contextualPut = this.transformerIsIdentity
|
|
1887
|
+
? this.index.putWithContext
|
|
1888
|
+
: undefined;
|
|
1889
|
+
if (contextualPut) {
|
|
1890
|
+
const encodedValueParts = this.encodeContextualIndexedValueParts(options?.encodedValue, context);
|
|
1891
|
+
await contextualPut.call(this.index, valueToIndex, id, context, encodedValueParts
|
|
1892
|
+
? { ...options, encodedValue: undefined, encodedValueParts }
|
|
1893
|
+
: options?.encodedValue
|
|
1894
|
+
? { ...options, encodedValue: undefined }
|
|
1895
|
+
: options);
|
|
1896
|
+
}
|
|
1897
|
+
else {
|
|
1898
|
+
const wrappedValueToIndex = new this.wrappedIndexedType(valueToIndex, context);
|
|
1899
|
+
await this.index.put(wrappedValueToIndex, id, stripEncodedValue(options));
|
|
1900
|
+
}
|
|
1021
1901
|
}
|
|
1022
1902
|
catch (error) {
|
|
1023
1903
|
if (error instanceof indexerTypes.NotStartedError && this.closed) {
|
|
@@ -1027,20 +1907,157 @@ let DocumentIndex = (() => {
|
|
|
1027
1907
|
}
|
|
1028
1908
|
return { context, indexable: valueToIndex };
|
|
1029
1909
|
}
|
|
1030
|
-
|
|
1031
|
-
if (
|
|
1032
|
-
|
|
1033
|
-
|
|
1910
|
+
async putManyWithContext(values) {
|
|
1911
|
+
if (values.length === 0) {
|
|
1912
|
+
return [];
|
|
1913
|
+
}
|
|
1914
|
+
let transformed;
|
|
1915
|
+
if (this.transformerIsIdentity) {
|
|
1916
|
+
transformed = new Array(values.length);
|
|
1917
|
+
for (let i = 0; i < values.length; i++) {
|
|
1918
|
+
const item = values[i];
|
|
1919
|
+
this.cacheResolvedValue(item.id.primitive, item.value);
|
|
1920
|
+
const indexable = item.value;
|
|
1921
|
+
coerceWithIndexed(item.value, indexable);
|
|
1922
|
+
coerceWithContext(item.value, item.context);
|
|
1923
|
+
transformed[i] = { ...item, indexable };
|
|
1924
|
+
}
|
|
1034
1925
|
}
|
|
1035
1926
|
else {
|
|
1036
|
-
|
|
1037
|
-
|
|
1927
|
+
transformed = await Promise.all(values.map(async (item) => {
|
|
1928
|
+
this.cacheResolvedValue(item.id.primitive, item.value);
|
|
1929
|
+
const indexable = await this.transformer(item.value, item.context);
|
|
1930
|
+
coerceWithIndexed(item.value, indexable);
|
|
1931
|
+
coerceWithContext(item.value, item.context);
|
|
1932
|
+
return { ...item, indexable };
|
|
1933
|
+
}));
|
|
1934
|
+
}
|
|
1935
|
+
try {
|
|
1936
|
+
const contextualBatchPut = this.transformerIsIdentity
|
|
1937
|
+
? this.index.putWithContextBatch
|
|
1938
|
+
: undefined;
|
|
1939
|
+
if (contextualBatchPut) {
|
|
1940
|
+
await contextualBatchPut.call(this.index, transformed.map((item) => ({
|
|
1941
|
+
value: item.indexable,
|
|
1942
|
+
id: item.id,
|
|
1943
|
+
context: item.context,
|
|
1944
|
+
options: this.withContextualEncodedValue(item.options, item.context),
|
|
1945
|
+
})));
|
|
1946
|
+
}
|
|
1947
|
+
else if (transformed.every((item) => item.options?.replace !== true) &&
|
|
1948
|
+
this.index.putBatch) {
|
|
1949
|
+
await this.index.putBatch(transformed.map((item) => new this.wrappedIndexedType(item.indexable, item.context)));
|
|
1950
|
+
}
|
|
1951
|
+
else {
|
|
1952
|
+
const contextualPut = this.transformerIsIdentity
|
|
1953
|
+
? this.index.putWithContext
|
|
1954
|
+
: undefined;
|
|
1955
|
+
for (const item of transformed) {
|
|
1956
|
+
if (contextualPut) {
|
|
1957
|
+
await contextualPut.call(this.index, item.indexable, item.id, item.context, this.withContextualEncodedValue(item.options, item.context));
|
|
1958
|
+
}
|
|
1959
|
+
else {
|
|
1960
|
+
await this.index.put(new this.wrappedIndexedType(item.indexable, item.context), item.id, stripEncodedValue(item.options));
|
|
1961
|
+
}
|
|
1962
|
+
}
|
|
1963
|
+
}
|
|
1964
|
+
}
|
|
1965
|
+
catch (error) {
|
|
1966
|
+
if (error instanceof indexerTypes.NotStartedError && this.closed) {
|
|
1967
|
+
return transformed.map((item) => ({
|
|
1968
|
+
context: item.context,
|
|
1969
|
+
indexable: item.indexable,
|
|
1970
|
+
}));
|
|
1038
1971
|
}
|
|
1972
|
+
throw error;
|
|
1973
|
+
}
|
|
1974
|
+
return transformed.map((item) => ({
|
|
1975
|
+
context: item.context,
|
|
1976
|
+
indexable: item.indexable,
|
|
1977
|
+
}));
|
|
1978
|
+
}
|
|
1979
|
+
_cacheResolvedIdentityValue(id, value) {
|
|
1980
|
+
this.cacheResolvedValue(id, value);
|
|
1981
|
+
}
|
|
1982
|
+
cacheResolvedValue(id, value) {
|
|
1983
|
+
if (this.isProgramValued) {
|
|
1984
|
+
this._resolverProgramCache.set(id, value);
|
|
1985
|
+
indexCacheLogger("cache:set:program", { id });
|
|
1986
|
+
}
|
|
1987
|
+
else if (this._resolverCache) {
|
|
1988
|
+
this._resolverCache.add(id, value);
|
|
1989
|
+
indexCacheLogger("cache:set:value", { id });
|
|
1039
1990
|
}
|
|
1991
|
+
}
|
|
1992
|
+
withContextualEncodedValue(options, context) {
|
|
1993
|
+
if (!options?.encodedValue) {
|
|
1994
|
+
return options;
|
|
1995
|
+
}
|
|
1996
|
+
const encodedValueParts = this.encodeContextualIndexedValueParts(options.encodedValue, context);
|
|
1997
|
+
return encodedValueParts
|
|
1998
|
+
? { ...options, encodedValue: undefined, encodedValueParts }
|
|
1999
|
+
: options;
|
|
2000
|
+
}
|
|
2001
|
+
encodeContextualIndexedValueParts(encodedValue, context) {
|
|
2002
|
+
if (!encodedValue ||
|
|
2003
|
+
!this.transformerIsIdentity ||
|
|
2004
|
+
!this.indexedTypeIsDocumentType) {
|
|
2005
|
+
return;
|
|
2006
|
+
}
|
|
2007
|
+
return {
|
|
2008
|
+
prefix: encodedValue,
|
|
2009
|
+
suffix: encodeContextSuffix(context),
|
|
2010
|
+
};
|
|
2011
|
+
}
|
|
2012
|
+
del(key) {
|
|
2013
|
+
this.deleteResolvedCacheForKey(key);
|
|
1040
2014
|
return this.index.del({
|
|
1041
2015
|
query: [indexerTypes.getMatcher(this.indexBy, key.key)],
|
|
1042
2016
|
});
|
|
1043
2017
|
}
|
|
2018
|
+
async delMany(keys) {
|
|
2019
|
+
if (keys.length === 0) {
|
|
2020
|
+
return;
|
|
2021
|
+
}
|
|
2022
|
+
for (const key of keys) {
|
|
2023
|
+
this.deleteResolvedCacheForKey(key);
|
|
2024
|
+
}
|
|
2025
|
+
const delIdsNoReturn = this.index.delIdsNoReturn;
|
|
2026
|
+
if (delIdsNoReturn) {
|
|
2027
|
+
await delIdsNoReturn.call(this.index, keys);
|
|
2028
|
+
return;
|
|
2029
|
+
}
|
|
2030
|
+
const delIds = this.index.delIds;
|
|
2031
|
+
if (delIds) {
|
|
2032
|
+
await delIds.call(this.index, keys);
|
|
2033
|
+
return;
|
|
2034
|
+
}
|
|
2035
|
+
await Promise.all(keys.map((key) => this.del(key)));
|
|
2036
|
+
}
|
|
2037
|
+
clearResolvedCacheForKeys(keys) {
|
|
2038
|
+
for (const key of keys) {
|
|
2039
|
+
this.deleteResolvedCacheForKey(key);
|
|
2040
|
+
}
|
|
2041
|
+
}
|
|
2042
|
+
delManyMaybe(keys) {
|
|
2043
|
+
if (keys.length === 0) {
|
|
2044
|
+
return;
|
|
2045
|
+
}
|
|
2046
|
+
for (const key of keys) {
|
|
2047
|
+
this.deleteResolvedCacheForKey(key);
|
|
2048
|
+
}
|
|
2049
|
+
const delIdsNoReturn = this.index.delIdsNoReturn;
|
|
2050
|
+
if (delIdsNoReturn) {
|
|
2051
|
+
const result = delIdsNoReturn.call(this.index, keys);
|
|
2052
|
+
return isPromiseLike(result) ? result.then(() => undefined) : undefined;
|
|
2053
|
+
}
|
|
2054
|
+
const delIds = this.index.delIds;
|
|
2055
|
+
if (delIds) {
|
|
2056
|
+
const result = delIds.call(this.index, keys);
|
|
2057
|
+
return isPromiseLike(result) ? result.then(() => undefined) : undefined;
|
|
2058
|
+
}
|
|
2059
|
+
return Promise.all(keys.map((key) => this.del(key))).then(() => undefined);
|
|
2060
|
+
}
|
|
1044
2061
|
async getDetailed(key, options) {
|
|
1045
2062
|
let coercedOptions = options;
|
|
1046
2063
|
if (options?.remote && typeof options.remote !== "boolean") {
|
|
@@ -1393,14 +2410,17 @@ let DocumentIndex = (() => {
|
|
|
1393
2410
|
}
|
|
1394
2411
|
}
|
|
1395
2412
|
get countIteratorsInProgress() {
|
|
1396
|
-
return this._resumableIterators
|
|
2413
|
+
return this._resumableIterators?.queues.size ?? 0;
|
|
1397
2414
|
}
|
|
1398
2415
|
clearAllResultQueues() {
|
|
2416
|
+
if (!this._resultQueue) {
|
|
2417
|
+
return;
|
|
2418
|
+
}
|
|
1399
2419
|
for (const [key, queue] of this._resultQueue) {
|
|
1400
2420
|
clearTimeout(queue.timeout);
|
|
1401
2421
|
this._resultQueue.delete(key);
|
|
1402
2422
|
this.cancelIteratorKeepAlive(key);
|
|
1403
|
-
this._resumableIterators
|
|
2423
|
+
this._resumableIterators?.close({ idString: key });
|
|
1404
2424
|
}
|
|
1405
2425
|
}
|
|
1406
2426
|
async waitForCoverReady(params) {
|
|
@@ -1614,33 +2634,58 @@ let DocumentIndex = (() => {
|
|
|
1614
2634
|
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
|
|
1615
2635
|
signal: options?.signal,
|
|
1616
2636
|
});
|
|
1617
|
-
// Cold start: cover can be temporarily self-only
|
|
1618
|
-
// converges. For explicit remote searches,
|
|
1619
|
-
// instead of waiting for replicator
|
|
2637
|
+
// Cold start: cover can be temporarily self-only or empty while
|
|
2638
|
+
// replication metadata converges. For explicit bounded remote searches,
|
|
2639
|
+
// query bounded connected peers instead of waiting for replicator
|
|
2640
|
+
// metadata to catch up.
|
|
1620
2641
|
if (!options?.remote?.from && isDefaultDomainArgs && remoteWasExplicit) {
|
|
1621
2642
|
const selfHash = this.node.identity.publicKey.hashcode();
|
|
1622
2643
|
const remoteCount = replicatorGroups.filter((h) => h !== selfHash).length;
|
|
1623
2644
|
if (remoteCount === 0) {
|
|
1624
2645
|
const waitEnabled = Boolean(remote.wait);
|
|
1625
2646
|
const coverIsSelfOnly = replicatorGroups.length === 1 && replicatorGroups[0] === selfHash;
|
|
1626
|
-
|
|
1627
|
-
// the
|
|
1628
|
-
|
|
2647
|
+
const coverIsColdEmpty = replicatorGroups.length === 0 && remote.timeout != null;
|
|
2648
|
+
// If the cover is explicitly empty (no shards), don't override it
|
|
2649
|
+
// unless the caller requested waiting for joins (e.g. get(waitFor))
|
|
2650
|
+
// or bounded the remote query with a timeout.
|
|
2651
|
+
if (waitEnabled || coverIsSelfOnly || coverIsColdEmpty) {
|
|
2652
|
+
const extra = [];
|
|
2653
|
+
const addExtra = (hash) => {
|
|
2654
|
+
if (!hash || hash === selfHash || extra.includes(hash)) {
|
|
2655
|
+
return;
|
|
2656
|
+
}
|
|
2657
|
+
extra.push(hash);
|
|
2658
|
+
};
|
|
1629
2659
|
const peerMap = this.node.services.pubsub?.peers;
|
|
2660
|
+
// Only consider replicators that are currently reachable.
|
|
2661
|
+
// The replicator index can contain stale (offline) peers, e.g.
|
|
2662
|
+
// persisted replicators after a restart; querying those would
|
|
2663
|
+
// block the first batch for the full wait timeout instead of
|
|
2664
|
+
// letting joining peers be merged as they arrive.
|
|
2665
|
+
if (peerMap?.has) {
|
|
2666
|
+
try {
|
|
2667
|
+
for (const hash of await this._log.getReplicators()) {
|
|
2668
|
+
if (peerMap.has(hash)) {
|
|
2669
|
+
addExtra(hash);
|
|
2670
|
+
}
|
|
2671
|
+
if (extra.length >= 8)
|
|
2672
|
+
break;
|
|
2673
|
+
}
|
|
2674
|
+
}
|
|
2675
|
+
catch {
|
|
2676
|
+
// Fall through to connected peers when the local replicator
|
|
2677
|
+
// index is not ready yet.
|
|
2678
|
+
}
|
|
2679
|
+
}
|
|
1630
2680
|
if (peerMap?.keys) {
|
|
1631
|
-
const extra = [];
|
|
1632
2681
|
for (const hash of peerMap.keys()) {
|
|
1633
|
-
|
|
1634
|
-
continue;
|
|
1635
|
-
extra.push(hash);
|
|
2682
|
+
addExtra(hash);
|
|
1636
2683
|
if (extra.length >= 8)
|
|
1637
2684
|
break;
|
|
1638
2685
|
}
|
|
1639
|
-
|
|
1640
|
-
|
|
1641
|
-
|
|
1642
|
-
];
|
|
1643
|
-
}
|
|
2686
|
+
}
|
|
2687
|
+
if (extra.length > 0) {
|
|
2688
|
+
replicatorGroups = [...new Set([...replicatorGroups, ...extra])];
|
|
1644
2689
|
}
|
|
1645
2690
|
}
|
|
1646
2691
|
}
|
|
@@ -2003,6 +3048,42 @@ let DocumentIndex = (() => {
|
|
|
2003
3048
|
}
|
|
2004
3049
|
return indexedPlaceholders;
|
|
2005
3050
|
};
|
|
3051
|
+
// The `visited` set prevents re-emitting the same document across pages
|
|
3052
|
+
// and sources, but different sources can return different versions of the
|
|
3053
|
+
// same document (e.g. a stale local head merged before a newer remote
|
|
3054
|
+
// head arrives). Plain id-based dedupe would let the first-seen, stale
|
|
3055
|
+
// version permanently shadow the newer one. If the id is still buffered
|
|
3056
|
+
// (not yet handed to the consumer) and the incoming result is strictly
|
|
3057
|
+
// preferred by the store's conflict rule, evict the stale buffered entry
|
|
3058
|
+
// so the caller can buffer the preferred result in its place. Returns
|
|
3059
|
+
// true when the caller should proceed to buffer the incoming result.
|
|
3060
|
+
// Results that were already emitted to the consumer cannot be retracted
|
|
3061
|
+
// here; those are only corrected via live updates/replication.
|
|
3062
|
+
// The preference direction mirrors the index merge rule in program.ts:
|
|
3063
|
+
// newest wins for mutable stores, oldest wins for immutable stores.
|
|
3064
|
+
const isPreferredContext = (incoming, existing) => incoming.head !== existing.head &&
|
|
3065
|
+
(this.immutable
|
|
3066
|
+
? incoming.modified < existing.modified
|
|
3067
|
+
: incoming.modified > existing.modified);
|
|
3068
|
+
const evictStaleBuffered = (indexKey, incomingContext) => {
|
|
3069
|
+
for (const peerBuffer of peerBufferMap.values()) {
|
|
3070
|
+
for (let i = 0; i < peerBuffer.buffer.length; i++) {
|
|
3071
|
+
const existing = peerBuffer.buffer[i];
|
|
3072
|
+
const existingKey = indexerTypes.toId(this.indexByResolver(existing.indexed)).primitive;
|
|
3073
|
+
if (existingKey !== indexKey) {
|
|
3074
|
+
continue;
|
|
3075
|
+
}
|
|
3076
|
+
if (!isPreferredContext(incomingContext, existing.context)) {
|
|
3077
|
+
// same or non-preferred version: keep normal dedupe behavior
|
|
3078
|
+
return false;
|
|
3079
|
+
}
|
|
3080
|
+
peerBuffer.buffer.splice(i, 1);
|
|
3081
|
+
indexedPlaceholders?.delete(indexKey);
|
|
3082
|
+
return true;
|
|
3083
|
+
}
|
|
3084
|
+
}
|
|
3085
|
+
return false; // not buffered (already emitted): skip incoming
|
|
3086
|
+
};
|
|
2006
3087
|
let done = false;
|
|
2007
3088
|
let drain = false; // if true, close on empty once (overrides manual)
|
|
2008
3089
|
let first = false;
|
|
@@ -2204,7 +3285,8 @@ let DocumentIndex = (() => {
|
|
|
2204
3285
|
indexedPlaceholders?.delete(indexKey);
|
|
2205
3286
|
continue;
|
|
2206
3287
|
}
|
|
2207
|
-
if (visited.has(indexKey)
|
|
3288
|
+
if (visited.has(indexKey) &&
|
|
3289
|
+
!evictStaleBuffered(indexKey, result.context)) {
|
|
2208
3290
|
continue;
|
|
2209
3291
|
}
|
|
2210
3292
|
visited.add(indexKey);
|
|
@@ -2218,7 +3300,8 @@ let DocumentIndex = (() => {
|
|
|
2218
3300
|
else {
|
|
2219
3301
|
const indexedResult = result;
|
|
2220
3302
|
if (visited.has(indexKey) &&
|
|
2221
|
-
!indexedPlaceholders?.has(indexKey)
|
|
3303
|
+
!indexedPlaceholders?.has(indexKey) &&
|
|
3304
|
+
!evictStaleBuffered(indexKey, indexedResult.context)) {
|
|
2222
3305
|
continue;
|
|
2223
3306
|
}
|
|
2224
3307
|
visited.add(indexKey);
|
|
@@ -2353,7 +3436,8 @@ let DocumentIndex = (() => {
|
|
|
2353
3436
|
indexedPlaceholders?.delete(keyPrimitive);
|
|
2354
3437
|
continue;
|
|
2355
3438
|
}
|
|
2356
|
-
if (visited.has(keyPrimitive)
|
|
3439
|
+
if (visited.has(keyPrimitive) &&
|
|
3440
|
+
!evictStaleBuffered(keyPrimitive, result.context)) {
|
|
2357
3441
|
continue;
|
|
2358
3442
|
}
|
|
2359
3443
|
visited.add(keyPrimitive);
|
|
@@ -2368,7 +3452,8 @@ let DocumentIndex = (() => {
|
|
|
2368
3452
|
else {
|
|
2369
3453
|
const indexedResult = result;
|
|
2370
3454
|
if (visited.has(keyPrimitive) &&
|
|
2371
|
-
!indexedPlaceholders?.has(keyPrimitive)
|
|
3455
|
+
!indexedPlaceholders?.has(keyPrimitive) &&
|
|
3456
|
+
!evictStaleBuffered(keyPrimitive, indexedResult.context)) {
|
|
2372
3457
|
continue;
|
|
2373
3458
|
}
|
|
2374
3459
|
visited.add(keyPrimitive);
|
|
@@ -2448,7 +3533,8 @@ let DocumentIndex = (() => {
|
|
|
2448
3533
|
indexedPlaceholders?.delete(indexKey);
|
|
2449
3534
|
continue;
|
|
2450
3535
|
}
|
|
2451
|
-
if (visited.has(indexKey)
|
|
3536
|
+
if (visited.has(indexKey) &&
|
|
3537
|
+
!evictStaleBuffered(indexKey, result.context)) {
|
|
2452
3538
|
continue;
|
|
2453
3539
|
}
|
|
2454
3540
|
visited.add(indexKey);
|
|
@@ -2464,7 +3550,8 @@ let DocumentIndex = (() => {
|
|
|
2464
3550
|
else {
|
|
2465
3551
|
const indexedResult = result;
|
|
2466
3552
|
if (visited.has(indexKey) &&
|
|
2467
|
-
!indexedPlaceholders?.has(indexKey)
|
|
3553
|
+
!indexedPlaceholders?.has(indexKey) &&
|
|
3554
|
+
!evictStaleBuffered(indexKey, indexedResult.context)) {
|
|
2468
3555
|
continue;
|
|
2469
3556
|
}
|
|
2470
3557
|
visited.add(indexKey);
|
|
@@ -2521,6 +3608,7 @@ let DocumentIndex = (() => {
|
|
|
2521
3608
|
if (n === 0) {
|
|
2522
3609
|
return [];
|
|
2523
3610
|
}
|
|
3611
|
+
await pendingUpdateProcessing;
|
|
2524
3612
|
const bufferedBeforeFetch = peerBuffers().length;
|
|
2525
3613
|
const localHash = this.node.identity.publicKey.hashcode();
|
|
2526
3614
|
const hasBufferedRemoteResults = [...peerBufferMap.entries()].some(([peerHash, peerBuffer]) => peerHash !== localHash && peerBuffer.buffer.length > 0);
|
|
@@ -2710,6 +3798,7 @@ let DocumentIndex = (() => {
|
|
|
2710
3798
|
const updateCallbacks = updateCallbacksRaw;
|
|
2711
3799
|
let pendingBatchReason;
|
|
2712
3800
|
let hasDeliveredResults = false;
|
|
3801
|
+
let pendingUpdateProcessing = Promise.resolve();
|
|
2713
3802
|
const emitOnBatch = async (batch, defaultReason) => {
|
|
2714
3803
|
if (!updateCallbacks?.onBatch || batch.length === 0) {
|
|
2715
3804
|
return;
|
|
@@ -2929,7 +4018,7 @@ let DocumentIndex = (() => {
|
|
|
2929
4018
|
}
|
|
2930
4019
|
return value;
|
|
2931
4020
|
};
|
|
2932
|
-
const
|
|
4021
|
+
const processChange = async (evt) => {
|
|
2933
4022
|
// Optional filter to mutate/suppress change events
|
|
2934
4023
|
indexIteratorLogger.trace("processing live update change event", evt.detail);
|
|
2935
4024
|
let filtered = evt.detail;
|
|
@@ -3047,6 +4136,13 @@ let DocumentIndex = (() => {
|
|
|
3047
4136
|
}
|
|
3048
4137
|
signalUpdate();
|
|
3049
4138
|
};
|
|
4139
|
+
const onChange = (evt) => {
|
|
4140
|
+
const task = pendingUpdateProcessing.then(() => processChange(evt));
|
|
4141
|
+
pendingUpdateProcessing = task.catch((error) => {
|
|
4142
|
+
warn("Failed to process iterator update", error);
|
|
4143
|
+
});
|
|
4144
|
+
return task;
|
|
4145
|
+
};
|
|
3050
4146
|
this.documentEvents.addEventListener("change", onChange);
|
|
3051
4147
|
updatesCleanup = () => {
|
|
3052
4148
|
this.documentEvents.removeEventListener("change", onChange);
|