@peerbit/document 13.0.43 → 13.1.0

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.
@@ -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;
@@ -292,9 +520,19 @@ let DocumentIndex = (() => {
292
520
  _joinListener;
293
521
  _resultQueue;
294
522
  iteratorKeepAliveTimers;
523
+ deleteResolvedCacheForKey(key) {
524
+ if (this.isProgramValued) {
525
+ this._resolverProgramCache.delete(key.primitive);
526
+ indexCacheLogger("cache:del:program", { id: key.primitive });
527
+ }
528
+ else if (this._resolverCache?.del(key.primitive)) {
529
+ indexCacheLogger("cache:del:value", { id: key.primitive });
530
+ }
531
+ }
295
532
  constructor(properties) {
296
533
  super();
297
534
  this._query = properties?.query || new RPC();
535
+ this._resultQueue = new Map();
298
536
  this.iteratorKeepAliveTimers = new Map();
299
537
  }
300
538
  get valueEncoding() {
@@ -407,7 +645,11 @@ let DocumentIndex = (() => {
407
645
  }
408
646
  return results;
409
647
  }
410
- handleDocumentChange = async (event) => {
648
+ // Bound in open(). Deserialized instances skip constructor/field initializers
649
+ // (borsh creates objects via Object.create), so this must not rely on a field
650
+ // initializer to exist.
651
+ handleDocumentChange;
652
+ async onDocumentChange(event) {
411
653
  const added = event.detail.added;
412
654
  if (!added.length) {
413
655
  return;
@@ -474,7 +716,7 @@ let DocumentIndex = (() => {
474
716
  queue.pushInFlight = false;
475
717
  }
476
718
  }
477
- };
719
+ }
478
720
  get nestedProperties() {
479
721
  return {
480
722
  match: (obj) => obj instanceof this.dbType,
@@ -567,9 +809,19 @@ let DocumentIndex = (() => {
567
809
  return replicateFn(rq, rs);
568
810
  };
569
811
  const transformOptions = properties.transform;
812
+ const hasTransformFunction = transformOptions != null && isTransformerWithFunction(transformOptions);
813
+ this.nativeTransformDescriptor = hasTransformFunction
814
+ ? getDocumentTransformDescriptor(transformOptions.transform)
815
+ : undefined;
816
+ this.nativeTransformProjectionPlan = createSimpleProjectionPlan(getSchema(this.documentType), indexedSchema, this.nativeTransformDescriptor);
817
+ this.transformerIsIdentity =
818
+ transformOptions == null ||
819
+ (!hasTransformFunction && transformOptions.type == null) ||
820
+ (this.nativeTransformDescriptor?.kind === "identity" &&
821
+ this.indexedTypeIsDocumentType);
570
822
  this.transformer = transformOptions
571
- ? isTransformerWithFunction(transformOptions)
572
- ? (obj, context) => transformOptions.transform(obj, context)
823
+ ? hasTransformFunction
824
+ ? (obj, context, facts) => transformOptions.transform(obj, context, facts)
573
825
  : transformOptions.type
574
826
  ? (obj, context) => new transformOptions.type(obj, context)
575
827
  : (obj) => obj
@@ -659,9 +911,213 @@ let DocumentIndex = (() => {
659
911
  responseType: types.AbstractSearchResult,
660
912
  queryType: types.AbstractSearchRequest,
661
913
  });
662
- if (this.handleDocumentChange) {
663
- this.documentEvents.addEventListener("change", this.handleDocumentChange);
914
+ this.handleDocumentChange ??= (event) => this.onDocumentChange(event);
915
+ this.documentEvents.addEventListener("change", this.handleDocumentChange);
916
+ }
917
+ attachNativeBackboneDocumentIndex(backbone, options) {
918
+ this.nativeBackboneDocumentProjection = undefined;
919
+ if (!backbone ||
920
+ this.isProgramValued ||
921
+ !this.canUseNativeBackboneDocumentIndex()) {
922
+ return false;
664
923
  }
924
+ const attach = this.index
925
+ .attachNativeBackboneDocumentIndex;
926
+ const attached = typeof attach === "function" &&
927
+ attach.call(this.index, backbone, options) === true;
928
+ if (attached) {
929
+ const projection = backbone;
930
+ if (typeof projection.projectDocumentIndexSimple === "function") {
931
+ this.nativeBackboneDocumentProjection = projection;
932
+ }
933
+ }
934
+ return attached;
935
+ }
936
+ canUseNativeBackboneDocumentIndex() {
937
+ return ((this.transformerIsIdentity && this.indexedTypeIsDocumentType) ||
938
+ this.nativeTransformDescriptor != null);
939
+ }
940
+ getNativeDocumentFieldExtractionPlan(path) {
941
+ return createSimpleFieldExtractionPlan(getSchema(this.documentType), path);
942
+ }
943
+ canPrepareNativeBackboneDocumentIndexCommit() {
944
+ return ((this.transformerIsIdentity && this.indexedTypeIsDocumentType) ||
945
+ canPrepareDocumentTransformBeforeAppend(this.nativeTransformDescriptor));
946
+ }
947
+ canPrepareNativeBackboneDocumentIndexCommitWithAppendFacts() {
948
+ return (this.canPrepareNativeBackboneDocumentIndexCommit() ||
949
+ !!this.nativeTransformProjectionPlan ||
950
+ canPrepareDocumentTransformWithAppendFacts(this.nativeTransformDescriptor));
951
+ }
952
+ canUseNativeBackboneContextualBatch() {
953
+ return (!this.isProgramValued &&
954
+ typeof this.index.putWithContextBatch ===
955
+ "function" &&
956
+ ((this.transformerIsIdentity && this.indexedTypeIsDocumentType) ||
957
+ this.canPrepareNativeBackboneDocumentIndexCommitWithAppendFacts()));
958
+ }
959
+ prepareNativeBackboneDocumentIndexCommit(value, encodedDocument, transformFacts) {
960
+ if (this.transformerIsIdentity && this.indexedTypeIsDocumentType) {
961
+ return {
962
+ valuePrefixBytes: encodedDocument,
963
+ usePlainPutPayload: true,
964
+ indexable: value,
965
+ };
966
+ }
967
+ if (this.nativeTransformProjectionPlan) {
968
+ let projectionContext;
969
+ let cached;
970
+ let hasCached = false;
971
+ return {
972
+ projection: {
973
+ encodedDocument,
974
+ plan: this.nativeTransformProjectionPlan,
975
+ signer: transformFacts?.entryPublicKeys?.[0]?.bytes,
976
+ },
977
+ getIndexable: () => {
978
+ if (!hasCached) {
979
+ const transformed = this.transformer(value, projectionContext, transformFacts);
980
+ if (isPromiseLike(transformed)) {
981
+ throw new Error("Native descriptor transform unexpectedly returned a promise");
982
+ }
983
+ cached = transformed;
984
+ hasCached = true;
985
+ }
986
+ return cached;
987
+ },
988
+ setContext: (context) => {
989
+ projectionContext = context;
990
+ hasCached = false;
991
+ cached = undefined;
992
+ },
993
+ };
994
+ }
995
+ if (!canPrepareDocumentTransformBeforeAppend(this.nativeTransformDescriptor)) {
996
+ return;
997
+ }
998
+ const transformed = this.transformer(value, undefined, transformFacts);
999
+ const finish = (indexable) => ({
1000
+ valuePrefixBytes: serialize(this.asIndexedTypeValue(indexable)),
1001
+ indexable,
1002
+ });
1003
+ return isPromiseLike(transformed)
1004
+ ? transformed.then(finish)
1005
+ : finish(transformed);
1006
+ }
1007
+ prepareNativeBackboneDocumentIndexCommitWithAppendFacts(value, encodedDocument, context, transformFacts) {
1008
+ if (this.transformerIsIdentity && this.indexedTypeIsDocumentType) {
1009
+ return {
1010
+ valuePrefixBytes: encodedDocument,
1011
+ usePlainPutPayload: true,
1012
+ indexable: value,
1013
+ };
1014
+ }
1015
+ if (this.nativeTransformProjectionPlan) {
1016
+ let projectionContext = {
1017
+ created: context.created,
1018
+ modified: context.modified,
1019
+ head: context.head,
1020
+ gid: context.gid,
1021
+ size: context.size,
1022
+ signer: transformFacts?.entryPublicKeys?.[0]?.bytes,
1023
+ };
1024
+ let cached;
1025
+ let hasCached = false;
1026
+ return {
1027
+ projection: {
1028
+ encodedDocument,
1029
+ plan: this.nativeTransformProjectionPlan,
1030
+ signer: projectionContext.signer,
1031
+ },
1032
+ getIndexable: () => {
1033
+ if (!hasCached) {
1034
+ const transformed = this.transformer(value, projectionContext, transformFacts);
1035
+ if (isPromiseLike(transformed)) {
1036
+ throw new Error("Native descriptor transform unexpectedly returned a promise");
1037
+ }
1038
+ cached = transformed;
1039
+ hasCached = true;
1040
+ }
1041
+ return cached;
1042
+ },
1043
+ setContext: (nextContext) => {
1044
+ projectionContext = {
1045
+ created: nextContext.created,
1046
+ modified: nextContext.modified,
1047
+ head: nextContext.head,
1048
+ gid: nextContext.gid,
1049
+ size: nextContext.size,
1050
+ signer: transformFacts?.entryPublicKeys?.[0]?.bytes,
1051
+ };
1052
+ hasCached = false;
1053
+ cached = undefined;
1054
+ },
1055
+ };
1056
+ }
1057
+ if (!canPrepareDocumentTransformWithAppendFacts(this.nativeTransformDescriptor) &&
1058
+ !canPrepareDocumentTransformBeforeAppend(this.nativeTransformDescriptor)) {
1059
+ return;
1060
+ }
1061
+ const transformed = this.transformer(value, context, transformFacts);
1062
+ if (isPromiseLike(transformed)) {
1063
+ return;
1064
+ }
1065
+ const indexable = transformed;
1066
+ return {
1067
+ valuePrefixBytes: serialize(this.asIndexedTypeValue(indexable)),
1068
+ indexable,
1069
+ };
1070
+ }
1071
+ prepareNativeBackboneDocumentIndexStoredCommitWithAppendFacts(encodedDocument, context, transformFacts) {
1072
+ if (this.transformerIsIdentity && this.indexedTypeIsDocumentType) {
1073
+ return {
1074
+ valuePrefixBytes: encodedDocument,
1075
+ usePlainPutPayload: true,
1076
+ };
1077
+ }
1078
+ if (this.nativeTransformProjectionPlan) {
1079
+ return {
1080
+ projection: {
1081
+ encodedDocument,
1082
+ plan: this.nativeTransformProjectionPlan,
1083
+ signer: transformFacts?.entryPublicKeys?.[0]?.bytes,
1084
+ },
1085
+ };
1086
+ }
1087
+ if (!canPrepareDocumentTransformWithAppendFacts(this.nativeTransformDescriptor) &&
1088
+ !canPrepareDocumentTransformBeforeAppend(this.nativeTransformDescriptor)) {
1089
+ return;
1090
+ }
1091
+ return;
1092
+ }
1093
+ nativeBackboneDocumentIndexValuePrefixBytes(nativeDocumentIndex, context) {
1094
+ return (nativeDocumentIndex.valuePrefixBytes ??
1095
+ (nativeDocumentIndex.projection
1096
+ ? (this.nativeBackboneDocumentProjection?.projectDocumentIndexSimple?.(nativeDocumentIndex.projection.encodedDocument, nativeDocumentIndex.projection.plan, {
1097
+ created: context.created,
1098
+ modified: context.modified,
1099
+ head: context.head,
1100
+ gid: context.gid,
1101
+ size: context.size,
1102
+ signer: nativeDocumentIndex.projection.signer,
1103
+ }) ??
1104
+ tryProjectDocumentIndexSimple(nativeDocumentIndex.projection.encodedDocument, nativeDocumentIndex.projection.plan, {
1105
+ created: context.created,
1106
+ modified: context.modified,
1107
+ head: context.head,
1108
+ gid: context.gid,
1109
+ size: context.size,
1110
+ signer: nativeDocumentIndex.projection.signer,
1111
+ }))
1112
+ : undefined));
1113
+ }
1114
+ asIndexedTypeValue(value) {
1115
+ if (value &&
1116
+ Object.getPrototypeOf(value) ===
1117
+ this.indexedType.prototype) {
1118
+ return value;
1119
+ }
1120
+ return Object.assign(Object.create(this.indexedType.prototype), value);
665
1121
  }
666
1122
  get prefetch() {
667
1123
  return this._prefetch;
@@ -818,11 +1274,11 @@ let DocumentIndex = (() => {
818
1274
  if (this._joinListener) {
819
1275
  this._query.events.removeEventListener("join", this._joinListener);
820
1276
  }
821
- if (this.handleDocumentChange) {
1277
+ if (this.handleDocumentChange && this.documentEvents) {
822
1278
  this.documentEvents.removeEventListener("change", this.handleDocumentChange);
823
1279
  }
824
1280
  this.clearAllResultQueues();
825
- await this._resumableIterators.clearAll();
1281
+ await this._resumableIterators?.clearAll();
826
1282
  if (this.iteratorKeepAliveTimers) {
827
1283
  for (const timer of this.iteratorKeepAliveTimers.values()) {
828
1284
  clearTimeout(timer);
@@ -846,9 +1302,9 @@ let DocumentIndex = (() => {
846
1302
  async drop(from) {
847
1303
  const dropped = await super.drop(from);
848
1304
  if (dropped) {
849
- this.documentEvents.removeEventListener("change", this.handleDocumentChange);
1305
+ this.documentEvents?.removeEventListener("change", this.handleDocumentChange);
850
1306
  this.clearAllResultQueues();
851
- await this._resumableIterators.clearAll();
1307
+ await this._resumableIterators?.clearAll();
852
1308
  if (this.iteratorKeepAliveTimers) {
853
1309
  for (const timer of this.iteratorKeepAliveTimers.values()) {
854
1310
  clearTimeout(timer);
@@ -977,6 +1433,426 @@ let DocumentIndex = (() => {
977
1433
  await iterator.close();
978
1434
  return one[0];
979
1435
  }
1436
+ async getIdentityIndexedByHead(head) {
1437
+ if (!this.canGetIdentityIndexedByHead()) {
1438
+ return;
1439
+ }
1440
+ return this.index.getByContextHead?.(head);
1441
+ }
1442
+ async getIdentityIndexedKeyByHead(head) {
1443
+ const key = this.getIndexedKeyByHead(head);
1444
+ if (key) {
1445
+ return key;
1446
+ }
1447
+ const indexed = await this.getIdentityIndexedByHead(head);
1448
+ return indexed?.id;
1449
+ }
1450
+ getIndexedKeyByHead(head) {
1451
+ const getIdByHead = this.index.getIdByContextHead;
1452
+ return typeof getIdByHead === "function"
1453
+ ? getIdByHead.call(this.index, head)
1454
+ : undefined;
1455
+ }
1456
+ getIndexedKeysByHeads(heads) {
1457
+ const getIdByHead = this.index.getIdByContextHead;
1458
+ if (typeof getIdByHead !== "function") {
1459
+ return;
1460
+ }
1461
+ return heads.map((head) => getIdByHead.call(this.index, head));
1462
+ }
1463
+ tryGetIdentityIndexedKeyByHead(head) {
1464
+ const getIdByHead = this.index.getIdByContextHead;
1465
+ if (typeof getIdByHead !== "function") {
1466
+ return { supported: false };
1467
+ }
1468
+ return {
1469
+ supported: true,
1470
+ key: getIdByHead.call(this.index, head),
1471
+ };
1472
+ }
1473
+ async getIdentityIndexedByHeads(heads) {
1474
+ if (!this.canGetIdentityIndexedByHead()) {
1475
+ return;
1476
+ }
1477
+ const batch = this.index.getByContextHeadBatch;
1478
+ if (batch) {
1479
+ return batch.call(this.index, heads);
1480
+ }
1481
+ return Promise.all(heads.map((head) => this.getIdentityIndexedByHead(head)));
1482
+ }
1483
+ canGetIdentityIndexedByHead() {
1484
+ return (this.transformerIsIdentity &&
1485
+ this.indexedTypeIsDocumentType &&
1486
+ !this.isProgramValued &&
1487
+ typeof this.index.getByContextHead === "function");
1488
+ }
1489
+ canGetIndexedKeyByHead() {
1490
+ return (!this.isProgramValued &&
1491
+ typeof this.index.getIdByContextHead ===
1492
+ "function");
1493
+ }
1494
+ canReadOriginalFieldPathsFromIndexedValue(paths) {
1495
+ return (!this.isProgramValued &&
1496
+ paths.every((path) => this.transformerIsIdentity && this.indexedTypeIsDocumentType
1497
+ ? true
1498
+ : documentTransformPreservesFieldPath(this.nativeTransformDescriptor, path)));
1499
+ }
1500
+ canReadNativeIndexedFieldValues(paths) {
1501
+ return (this.canReadOriginalFieldPathsFromIndexedValue(paths) &&
1502
+ typeof this.index.getNativeIndexedFieldValue === "function");
1503
+ }
1504
+ getNativeIndexedFieldValue(id, path) {
1505
+ const read = this.index.getNativeIndexedFieldValue;
1506
+ if (typeof read !== "function") {
1507
+ return undefined;
1508
+ }
1509
+ return read.call(this.index, id, typeof path === "string" ? [path] : path);
1510
+ }
1511
+ _putIdentityWithContext(value, id, context, options) {
1512
+ const contextualPut = this.transformerIsIdentity
1513
+ ? this.index.putWithContext
1514
+ : undefined;
1515
+ if (!contextualPut || this.isProgramValued) {
1516
+ return;
1517
+ }
1518
+ const indexable = value;
1519
+ const indexedValue = coerceWithIndexed(coerceWithContext(value, context), indexable);
1520
+ this.cacheResolvedValue(id.primitive, value);
1521
+ const handleError = (error) => {
1522
+ if (error instanceof indexerTypes.NotStartedError && this.closed) {
1523
+ return indexedValue;
1524
+ }
1525
+ throw error;
1526
+ };
1527
+ try {
1528
+ const putResult = contextualPut.call(this.index, indexable, id, context, this.withContextualEncodedValue(options, context));
1529
+ return isPromiseLike(putResult)
1530
+ ? putResult.then(() => indexedValue, handleError)
1531
+ : indexedValue;
1532
+ }
1533
+ catch (error) {
1534
+ return handleError(error);
1535
+ }
1536
+ }
1537
+ _putStoredIdentityWithContext(value, id, context, encodedValueParts, options) {
1538
+ const contextualStoredPut = this.transformerIsIdentity
1539
+ ? this.index.putStoredContextualEncodedValue
1540
+ : undefined;
1541
+ if (!contextualStoredPut ||
1542
+ this.isProgramValued ||
1543
+ !this.indexedTypeIsDocumentType) {
1544
+ return;
1545
+ }
1546
+ const indexable = value;
1547
+ const indexedValue = coerceWithIndexed(coerceWithContext(value, context), indexable);
1548
+ this.cacheResolvedValue(id.primitive, value);
1549
+ const handleError = (error) => {
1550
+ if (error instanceof indexerTypes.NotStartedError && this.closed) {
1551
+ return indexedValue;
1552
+ }
1553
+ throw error;
1554
+ };
1555
+ try {
1556
+ const putResult = contextualStoredPut.call(this.index, id, encodedValueParts, options);
1557
+ if (putResult === false) {
1558
+ return;
1559
+ }
1560
+ return isPromiseLike(putResult)
1561
+ ? putResult.then(() => indexedValue, handleError)
1562
+ : indexedValue;
1563
+ }
1564
+ catch (error) {
1565
+ return handleError(error);
1566
+ }
1567
+ }
1568
+ _putPreparedNativeBackboneDocumentIndexWithContext(value, id, context, nativeDocumentIndex, options) {
1569
+ const contextualStoredPut = this.index
1570
+ .putStoredContextualEncodedValue;
1571
+ if (!contextualStoredPut || this.isProgramValued) {
1572
+ return;
1573
+ }
1574
+ nativeDocumentIndex.setContext?.(context);
1575
+ const valueWithContext = coerceWithContext(value, context);
1576
+ const indexedValue = nativeDocumentIndex.indexable
1577
+ ? coerceWithIndexed(valueWithContext, nativeDocumentIndex.indexable)
1578
+ : nativeDocumentIndex.getIndexable
1579
+ ? coerceWithLazyIndexed(valueWithContext, nativeDocumentIndex.getIndexable)
1580
+ : undefined;
1581
+ if (!indexedValue) {
1582
+ return;
1583
+ }
1584
+ this.cacheResolvedValue(id.primitive, value);
1585
+ const valuePrefixBytes = this.nativeBackboneDocumentIndexValuePrefixBytes(nativeDocumentIndex, context);
1586
+ if (!valuePrefixBytes) {
1587
+ return;
1588
+ }
1589
+ const encodedValueParts = {
1590
+ prefix: valuePrefixBytes,
1591
+ suffix: encodeContextSuffix(context),
1592
+ };
1593
+ const handleError = (error) => {
1594
+ if (error instanceof indexerTypes.NotStartedError && this.closed) {
1595
+ return indexedValue;
1596
+ }
1597
+ throw error;
1598
+ };
1599
+ try {
1600
+ const putResult = contextualStoredPut.call(this.index, id, encodedValueParts, options);
1601
+ if (putResult === false) {
1602
+ return;
1603
+ }
1604
+ return isPromiseLike(putResult)
1605
+ ? putResult.then(() => indexedValue, handleError)
1606
+ : indexedValue;
1607
+ }
1608
+ catch (error) {
1609
+ return handleError(error);
1610
+ }
1611
+ }
1612
+ _putPreparedNativeBackboneDocumentIndexStoredWithContext(id, context, nativeDocumentIndex, options) {
1613
+ const contextualStoredPut = this.index
1614
+ .putStoredContextualEncodedValue;
1615
+ if (!contextualStoredPut || this.isProgramValued) {
1616
+ return;
1617
+ }
1618
+ nativeDocumentIndex.setContext?.(context);
1619
+ const valuePrefixBytes = this.nativeBackboneDocumentIndexValuePrefixBytes(nativeDocumentIndex, context);
1620
+ if (!valuePrefixBytes) {
1621
+ return;
1622
+ }
1623
+ const encodedValueParts = {
1624
+ prefix: valuePrefixBytes,
1625
+ suffix: encodeContextSuffix(context),
1626
+ };
1627
+ const handleError = (error) => {
1628
+ if (error instanceof indexerTypes.NotStartedError && this.closed) {
1629
+ return true;
1630
+ }
1631
+ throw error;
1632
+ };
1633
+ try {
1634
+ const putResult = contextualStoredPut.call(this.index, id, encodedValueParts, options);
1635
+ if (putResult === false) {
1636
+ return false;
1637
+ }
1638
+ return isPromiseLike(putResult)
1639
+ ? putResult.then(() => true, handleError)
1640
+ : true;
1641
+ }
1642
+ catch (error) {
1643
+ return handleError(error);
1644
+ }
1645
+ }
1646
+ _persistPreparedNativeBackboneDocumentIndexStoredWithContext(id, context, nativeDocumentIndex, encodedValueParts, options) {
1647
+ const persistStoredPut = this.index
1648
+ .persistStoredContextualEncodedValue;
1649
+ if (!persistStoredPut || this.isProgramValued) {
1650
+ return;
1651
+ }
1652
+ let storedParts;
1653
+ if (this.transformerIsIdentity &&
1654
+ this.indexedTypeIsDocumentType &&
1655
+ encodedValueParts) {
1656
+ storedParts = encodedValueParts;
1657
+ }
1658
+ else if (nativeDocumentIndex) {
1659
+ nativeDocumentIndex.setContext?.(context);
1660
+ const valuePrefixBytes = nativeDocumentIndex.valuePrefixBytes ??
1661
+ this.nativeBackboneDocumentIndexValuePrefixBytes(nativeDocumentIndex, context);
1662
+ if (!valuePrefixBytes) {
1663
+ return;
1664
+ }
1665
+ storedParts = {
1666
+ prefix: valuePrefixBytes,
1667
+ suffix: encodeContextSuffix(context),
1668
+ };
1669
+ }
1670
+ else {
1671
+ return;
1672
+ }
1673
+ try {
1674
+ const persistResult = persistStoredPut.call(this.index, id, storedParts, options);
1675
+ if (persistResult === false) {
1676
+ return false;
1677
+ }
1678
+ return isPromiseLike(persistResult)
1679
+ ? persistResult.then(() => true, (error) => {
1680
+ if (error instanceof indexerTypes.NotStartedError && this.closed) {
1681
+ return true;
1682
+ }
1683
+ throw error;
1684
+ })
1685
+ : true;
1686
+ }
1687
+ catch (error) {
1688
+ if (error instanceof indexerTypes.NotStartedError && this.closed) {
1689
+ return true;
1690
+ }
1691
+ throw error;
1692
+ }
1693
+ }
1694
+ async _putManyPreparedNativeBackboneDocumentIndexWithContext(values) {
1695
+ if (values.length === 0) {
1696
+ return [];
1697
+ }
1698
+ const contextualBatchPut = this.index
1699
+ .putWithContextBatch;
1700
+ if (!contextualBatchPut || this.isProgramValued) {
1701
+ return;
1702
+ }
1703
+ const indexedValues = [];
1704
+ const batchValues = [];
1705
+ for (const item of values) {
1706
+ if (!item.nativeDocumentIndex) {
1707
+ return;
1708
+ }
1709
+ item.nativeDocumentIndex.setContext?.(item.context);
1710
+ const valueWithContext = coerceWithContext(item.value, item.context);
1711
+ const indexedValue = item.nativeDocumentIndex.indexable
1712
+ ? coerceWithIndexed(valueWithContext, item.nativeDocumentIndex.indexable)
1713
+ : item.nativeDocumentIndex.getIndexable
1714
+ ? coerceWithLazyIndexed(valueWithContext, item.nativeDocumentIndex.getIndexable)
1715
+ : undefined;
1716
+ if (!indexedValue) {
1717
+ return;
1718
+ }
1719
+ const valuePrefixBytes = this.nativeBackboneDocumentIndexValuePrefixBytes(item.nativeDocumentIndex, item.context);
1720
+ if (!valuePrefixBytes) {
1721
+ return;
1722
+ }
1723
+ this.cacheResolvedValue(item.id.primitive, item.value);
1724
+ indexedValues.push(indexedValue);
1725
+ batchValues.push({
1726
+ // Encoded native batches store from encodedValueParts; descriptor
1727
+ // projections keep the JS indexable lazy for event consumers.
1728
+ value: item.nativeDocumentIndex.indexable ?? undefined,
1729
+ id: item.id,
1730
+ context: item.context,
1731
+ options: {
1732
+ replace: item.options?.replace,
1733
+ encodedValueParts: {
1734
+ prefix: valuePrefixBytes,
1735
+ suffix: encodeContextSuffix(item.context),
1736
+ },
1737
+ },
1738
+ });
1739
+ }
1740
+ const handleError = (error) => {
1741
+ if (error instanceof indexerTypes.NotStartedError && this.closed) {
1742
+ return indexedValues;
1743
+ }
1744
+ throw error;
1745
+ };
1746
+ try {
1747
+ const putResult = contextualBatchPut.call(this.index, batchValues);
1748
+ return isPromiseLike(putResult)
1749
+ ? putResult.then(() => indexedValues, handleError)
1750
+ : indexedValues;
1751
+ }
1752
+ catch (error) {
1753
+ return handleError(error);
1754
+ }
1755
+ }
1756
+ async _putManyPreparedNativeBackboneDocumentIndexStored(values) {
1757
+ if (values.length === 0) {
1758
+ return true;
1759
+ }
1760
+ if (this.isProgramValued) {
1761
+ return;
1762
+ }
1763
+ const storedBatchPut = this.index
1764
+ .putStoredContextualEncodedValueBatch;
1765
+ if (!storedBatchPut) {
1766
+ return;
1767
+ }
1768
+ const batchValues = [];
1769
+ for (const item of values) {
1770
+ let encodedValueParts;
1771
+ if (this.transformerIsIdentity &&
1772
+ this.indexedTypeIsDocumentType &&
1773
+ item.encodedValueParts) {
1774
+ encodedValueParts = item.encodedValueParts;
1775
+ }
1776
+ else if (item.nativeDocumentIndex) {
1777
+ item.nativeDocumentIndex.setContext?.(item.context);
1778
+ const valuePrefixBytes = item.nativeDocumentIndex.valuePrefixBytes ??
1779
+ this.nativeBackboneDocumentIndexValuePrefixBytes(item.nativeDocumentIndex, item.context);
1780
+ if (!valuePrefixBytes) {
1781
+ return;
1782
+ }
1783
+ encodedValueParts = {
1784
+ prefix: valuePrefixBytes,
1785
+ suffix: encodeContextSuffix(item.context),
1786
+ };
1787
+ }
1788
+ else {
1789
+ return;
1790
+ }
1791
+ this.cacheResolvedValue(item.id.primitive, item.value);
1792
+ batchValues.push({
1793
+ id: item.id,
1794
+ encodedValueParts,
1795
+ options: item.options,
1796
+ });
1797
+ }
1798
+ const handleError = (error) => {
1799
+ if (error instanceof indexerTypes.NotStartedError && this.closed) {
1800
+ return true;
1801
+ }
1802
+ throw error;
1803
+ };
1804
+ try {
1805
+ return await storedBatchPut.call(this.index, batchValues);
1806
+ }
1807
+ catch (error) {
1808
+ return handleError(error);
1809
+ }
1810
+ }
1811
+ async _putManyIdentityWithContext(values) {
1812
+ if (values.length === 0) {
1813
+ return [];
1814
+ }
1815
+ const contextualPut = this.transformerIsIdentity
1816
+ ? this.index.putWithContext
1817
+ : undefined;
1818
+ const contextualBatchPut = this.transformerIsIdentity
1819
+ ? this.index.putWithContextBatch
1820
+ : undefined;
1821
+ if ((!contextualBatchPut && !contextualPut) || this.isProgramValued) {
1822
+ return;
1823
+ }
1824
+ const indexedValues = values.map((item) => {
1825
+ const indexable = item.value;
1826
+ this.cacheResolvedValue(item.id.primitive, item.value);
1827
+ return {
1828
+ indexable,
1829
+ value: coerceWithIndexed(coerceWithContext(item.value, item.context), indexable),
1830
+ };
1831
+ });
1832
+ try {
1833
+ if (contextualBatchPut) {
1834
+ await contextualBatchPut.call(this.index, values.map((item, index) => ({
1835
+ value: indexedValues[index].indexable,
1836
+ id: item.id,
1837
+ context: item.context,
1838
+ options: this.withContextualEncodedValue(item.options, item.context),
1839
+ })));
1840
+ }
1841
+ else {
1842
+ for (let i = 0; i < values.length; i++) {
1843
+ const item = values[i];
1844
+ await contextualPut.call(this.index, indexedValues[i].indexable, item.id, item.context, this.withContextualEncodedValue(item.options, item.context));
1845
+ }
1846
+ }
1847
+ }
1848
+ catch (error) {
1849
+ if (error instanceof indexerTypes.NotStartedError && this.closed) {
1850
+ return indexedValues.map((item) => item.value);
1851
+ }
1852
+ throw error;
1853
+ }
1854
+ return indexedValues.map((item) => item.value);
1855
+ }
980
1856
  async put(value, id, entry, existing) {
981
1857
  const existingDefined = existing === undefined
982
1858
  ? await this.index.get(id, {
@@ -993,31 +1869,33 @@ let DocumentIndex = (() => {
993
1869
  });
994
1870
  return this.putWithContext(value, id, context, {
995
1871
  replace: existingDefined != null,
1872
+ transformFacts: { entryPublicKeys: entry.publicKeys },
996
1873
  });
997
1874
  }
998
1875
  async putWithContext(value, id, context, options) {
999
1876
  const idString = id.primitive;
1000
- if (this.isProgramValued /*
1001
- TODO should we skip caching program value if they are not openend through this db?
1002
- &&
1003
- (value as Program).closed === false &&
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);
1877
+ this.cacheResolvedValue(idString, value);
1878
+ const valueToIndex = this.transformerIsIdentity
1879
+ ? value
1880
+ : await this.transformer(value, context, options?.transformFacts);
1017
1881
  coerceWithIndexed(value, valueToIndex);
1018
1882
  coerceWithContext(value, context);
1019
1883
  try {
1020
- await this.index.put(wrappedValueToIndex, undefined, options);
1884
+ const contextualPut = this.transformerIsIdentity
1885
+ ? this.index.putWithContext
1886
+ : undefined;
1887
+ if (contextualPut) {
1888
+ const encodedValueParts = this.encodeContextualIndexedValueParts(options?.encodedValue, context);
1889
+ await contextualPut.call(this.index, valueToIndex, id, context, encodedValueParts
1890
+ ? { ...options, encodedValue: undefined, encodedValueParts }
1891
+ : options?.encodedValue
1892
+ ? { ...options, encodedValue: undefined }
1893
+ : options);
1894
+ }
1895
+ else {
1896
+ const wrappedValueToIndex = new this.wrappedIndexedType(valueToIndex, context);
1897
+ await this.index.put(wrappedValueToIndex, id, stripEncodedValue(options));
1898
+ }
1021
1899
  }
1022
1900
  catch (error) {
1023
1901
  if (error instanceof indexerTypes.NotStartedError && this.closed) {
@@ -1027,20 +1905,157 @@ let DocumentIndex = (() => {
1027
1905
  }
1028
1906
  return { context, indexable: valueToIndex };
1029
1907
  }
1030
- del(key) {
1031
- if (this.isProgramValued) {
1032
- this._resolverProgramCache.delete(key.primitive);
1033
- indexCacheLogger("cache:del:program", { id: key.primitive });
1908
+ async putManyWithContext(values) {
1909
+ if (values.length === 0) {
1910
+ return [];
1911
+ }
1912
+ let transformed;
1913
+ if (this.transformerIsIdentity) {
1914
+ transformed = new Array(values.length);
1915
+ for (let i = 0; i < values.length; i++) {
1916
+ const item = values[i];
1917
+ this.cacheResolvedValue(item.id.primitive, item.value);
1918
+ const indexable = item.value;
1919
+ coerceWithIndexed(item.value, indexable);
1920
+ coerceWithContext(item.value, item.context);
1921
+ transformed[i] = { ...item, indexable };
1922
+ }
1034
1923
  }
1035
1924
  else {
1036
- if (this._resolverCache?.del(key.primitive)) {
1037
- indexCacheLogger("cache:del:value", { id: key.primitive });
1925
+ transformed = await Promise.all(values.map(async (item) => {
1926
+ this.cacheResolvedValue(item.id.primitive, item.value);
1927
+ const indexable = await this.transformer(item.value, item.context);
1928
+ coerceWithIndexed(item.value, indexable);
1929
+ coerceWithContext(item.value, item.context);
1930
+ return { ...item, indexable };
1931
+ }));
1932
+ }
1933
+ try {
1934
+ const contextualBatchPut = this.transformerIsIdentity
1935
+ ? this.index.putWithContextBatch
1936
+ : undefined;
1937
+ if (contextualBatchPut) {
1938
+ await contextualBatchPut.call(this.index, transformed.map((item) => ({
1939
+ value: item.indexable,
1940
+ id: item.id,
1941
+ context: item.context,
1942
+ options: this.withContextualEncodedValue(item.options, item.context),
1943
+ })));
1944
+ }
1945
+ else if (transformed.every((item) => item.options?.replace !== true) &&
1946
+ this.index.putBatch) {
1947
+ await this.index.putBatch(transformed.map((item) => new this.wrappedIndexedType(item.indexable, item.context)));
1948
+ }
1949
+ else {
1950
+ const contextualPut = this.transformerIsIdentity
1951
+ ? this.index.putWithContext
1952
+ : undefined;
1953
+ for (const item of transformed) {
1954
+ if (contextualPut) {
1955
+ await contextualPut.call(this.index, item.indexable, item.id, item.context, this.withContextualEncodedValue(item.options, item.context));
1956
+ }
1957
+ else {
1958
+ await this.index.put(new this.wrappedIndexedType(item.indexable, item.context), item.id, stripEncodedValue(item.options));
1959
+ }
1960
+ }
1961
+ }
1962
+ }
1963
+ catch (error) {
1964
+ if (error instanceof indexerTypes.NotStartedError && this.closed) {
1965
+ return transformed.map((item) => ({
1966
+ context: item.context,
1967
+ indexable: item.indexable,
1968
+ }));
1038
1969
  }
1970
+ throw error;
1971
+ }
1972
+ return transformed.map((item) => ({
1973
+ context: item.context,
1974
+ indexable: item.indexable,
1975
+ }));
1976
+ }
1977
+ _cacheResolvedIdentityValue(id, value) {
1978
+ this.cacheResolvedValue(id, value);
1979
+ }
1980
+ cacheResolvedValue(id, value) {
1981
+ if (this.isProgramValued) {
1982
+ this._resolverProgramCache.set(id, value);
1983
+ indexCacheLogger("cache:set:program", { id });
1984
+ }
1985
+ else if (this._resolverCache) {
1986
+ this._resolverCache.add(id, value);
1987
+ indexCacheLogger("cache:set:value", { id });
1988
+ }
1989
+ }
1990
+ withContextualEncodedValue(options, context) {
1991
+ if (!options?.encodedValue) {
1992
+ return options;
1993
+ }
1994
+ const encodedValueParts = this.encodeContextualIndexedValueParts(options.encodedValue, context);
1995
+ return encodedValueParts
1996
+ ? { ...options, encodedValue: undefined, encodedValueParts }
1997
+ : options;
1998
+ }
1999
+ encodeContextualIndexedValueParts(encodedValue, context) {
2000
+ if (!encodedValue ||
2001
+ !this.transformerIsIdentity ||
2002
+ !this.indexedTypeIsDocumentType) {
2003
+ return;
1039
2004
  }
2005
+ return {
2006
+ prefix: encodedValue,
2007
+ suffix: encodeContextSuffix(context),
2008
+ };
2009
+ }
2010
+ del(key) {
2011
+ this.deleteResolvedCacheForKey(key);
1040
2012
  return this.index.del({
1041
2013
  query: [indexerTypes.getMatcher(this.indexBy, key.key)],
1042
2014
  });
1043
2015
  }
2016
+ async delMany(keys) {
2017
+ if (keys.length === 0) {
2018
+ return;
2019
+ }
2020
+ for (const key of keys) {
2021
+ this.deleteResolvedCacheForKey(key);
2022
+ }
2023
+ const delIdsNoReturn = this.index.delIdsNoReturn;
2024
+ if (delIdsNoReturn) {
2025
+ await delIdsNoReturn.call(this.index, keys);
2026
+ return;
2027
+ }
2028
+ const delIds = this.index.delIds;
2029
+ if (delIds) {
2030
+ await delIds.call(this.index, keys);
2031
+ return;
2032
+ }
2033
+ await Promise.all(keys.map((key) => this.del(key)));
2034
+ }
2035
+ clearResolvedCacheForKeys(keys) {
2036
+ for (const key of keys) {
2037
+ this.deleteResolvedCacheForKey(key);
2038
+ }
2039
+ }
2040
+ delManyMaybe(keys) {
2041
+ if (keys.length === 0) {
2042
+ return;
2043
+ }
2044
+ for (const key of keys) {
2045
+ this.deleteResolvedCacheForKey(key);
2046
+ }
2047
+ const delIdsNoReturn = this.index.delIdsNoReturn;
2048
+ if (delIdsNoReturn) {
2049
+ const result = delIdsNoReturn.call(this.index, keys);
2050
+ return isPromiseLike(result) ? result.then(() => undefined) : undefined;
2051
+ }
2052
+ const delIds = this.index.delIds;
2053
+ if (delIds) {
2054
+ const result = delIds.call(this.index, keys);
2055
+ return isPromiseLike(result) ? result.then(() => undefined) : undefined;
2056
+ }
2057
+ return Promise.all(keys.map((key) => this.del(key))).then(() => undefined);
2058
+ }
1044
2059
  async getDetailed(key, options) {
1045
2060
  let coercedOptions = options;
1046
2061
  if (options?.remote && typeof options.remote !== "boolean") {
@@ -1393,14 +2408,17 @@ let DocumentIndex = (() => {
1393
2408
  }
1394
2409
  }
1395
2410
  get countIteratorsInProgress() {
1396
- return this._resumableIterators.queues.size;
2411
+ return this._resumableIterators?.queues.size ?? 0;
1397
2412
  }
1398
2413
  clearAllResultQueues() {
2414
+ if (!this._resultQueue) {
2415
+ return;
2416
+ }
1399
2417
  for (const [key, queue] of this._resultQueue) {
1400
2418
  clearTimeout(queue.timeout);
1401
2419
  this._resultQueue.delete(key);
1402
2420
  this.cancelIteratorKeepAlive(key);
1403
- this._resumableIterators.close({ idString: key });
2421
+ this._resumableIterators?.close({ idString: key });
1404
2422
  }
1405
2423
  }
1406
2424
  async waitForCoverReady(params) {
@@ -1614,33 +2632,58 @@ let DocumentIndex = (() => {
1614
2632
  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
2633
  signal: options?.signal,
1616
2634
  });
1617
- // Cold start: cover can be temporarily self-only while replication metadata
1618
- // converges. For explicit remote searches, query bounded connected peers
1619
- // instead of waiting for replicator metadata to catch up.
2635
+ // Cold start: cover can be temporarily self-only or empty while
2636
+ // replication metadata converges. For explicit bounded remote searches,
2637
+ // query bounded connected peers instead of waiting for replicator
2638
+ // metadata to catch up.
1620
2639
  if (!options?.remote?.from && isDefaultDomainArgs && remoteWasExplicit) {
1621
2640
  const selfHash = this.node.identity.publicKey.hashcode();
1622
2641
  const remoteCount = replicatorGroups.filter((h) => h !== selfHash).length;
1623
2642
  if (remoteCount === 0) {
1624
2643
  const waitEnabled = Boolean(remote.wait);
1625
2644
  const coverIsSelfOnly = replicatorGroups.length === 1 && replicatorGroups[0] === selfHash;
1626
- // If the cover is explicitly empty (no shards), don't override it unless
1627
- // the caller requested waiting for joins (e.g. get(waitFor)).
1628
- if (waitEnabled || coverIsSelfOnly) {
2645
+ const coverIsColdEmpty = replicatorGroups.length === 0 && remote.timeout != null;
2646
+ // If the cover is explicitly empty (no shards), don't override it
2647
+ // unless the caller requested waiting for joins (e.g. get(waitFor))
2648
+ // or bounded the remote query with a timeout.
2649
+ if (waitEnabled || coverIsSelfOnly || coverIsColdEmpty) {
2650
+ const extra = [];
2651
+ const addExtra = (hash) => {
2652
+ if (!hash || hash === selfHash || extra.includes(hash)) {
2653
+ return;
2654
+ }
2655
+ extra.push(hash);
2656
+ };
1629
2657
  const peerMap = this.node.services.pubsub?.peers;
2658
+ // Only consider replicators that are currently reachable.
2659
+ // The replicator index can contain stale (offline) peers, e.g.
2660
+ // persisted replicators after a restart; querying those would
2661
+ // block the first batch for the full wait timeout instead of
2662
+ // letting joining peers be merged as they arrive.
2663
+ if (peerMap?.has) {
2664
+ try {
2665
+ for (const hash of await this._log.getReplicators()) {
2666
+ if (peerMap.has(hash)) {
2667
+ addExtra(hash);
2668
+ }
2669
+ if (extra.length >= 8)
2670
+ break;
2671
+ }
2672
+ }
2673
+ catch {
2674
+ // Fall through to connected peers when the local replicator
2675
+ // index is not ready yet.
2676
+ }
2677
+ }
1630
2678
  if (peerMap?.keys) {
1631
- const extra = [];
1632
2679
  for (const hash of peerMap.keys()) {
1633
- if (!hash || hash === selfHash)
1634
- continue;
1635
- extra.push(hash);
2680
+ addExtra(hash);
1636
2681
  if (extra.length >= 8)
1637
2682
  break;
1638
2683
  }
1639
- if (extra.length > 0) {
1640
- replicatorGroups = [
1641
- ...new Set([...replicatorGroups, ...extra]),
1642
- ];
1643
- }
2684
+ }
2685
+ if (extra.length > 0) {
2686
+ replicatorGroups = [...new Set([...replicatorGroups, ...extra])];
1644
2687
  }
1645
2688
  }
1646
2689
  }
@@ -2521,6 +3564,7 @@ let DocumentIndex = (() => {
2521
3564
  if (n === 0) {
2522
3565
  return [];
2523
3566
  }
3567
+ await pendingUpdateProcessing;
2524
3568
  const bufferedBeforeFetch = peerBuffers().length;
2525
3569
  const localHash = this.node.identity.publicKey.hashcode();
2526
3570
  const hasBufferedRemoteResults = [...peerBufferMap.entries()].some(([peerHash, peerBuffer]) => peerHash !== localHash && peerBuffer.buffer.length > 0);
@@ -2710,6 +3754,7 @@ let DocumentIndex = (() => {
2710
3754
  const updateCallbacks = updateCallbacksRaw;
2711
3755
  let pendingBatchReason;
2712
3756
  let hasDeliveredResults = false;
3757
+ let pendingUpdateProcessing = Promise.resolve();
2713
3758
  const emitOnBatch = async (batch, defaultReason) => {
2714
3759
  if (!updateCallbacks?.onBatch || batch.length === 0) {
2715
3760
  return;
@@ -2929,7 +3974,7 @@ let DocumentIndex = (() => {
2929
3974
  }
2930
3975
  return value;
2931
3976
  };
2932
- const onChange = async (evt) => {
3977
+ const processChange = async (evt) => {
2933
3978
  // Optional filter to mutate/suppress change events
2934
3979
  indexIteratorLogger.trace("processing live update change event", evt.detail);
2935
3980
  let filtered = evt.detail;
@@ -3047,6 +4092,13 @@ let DocumentIndex = (() => {
3047
4092
  }
3048
4093
  signalUpdate();
3049
4094
  };
4095
+ const onChange = (evt) => {
4096
+ const task = pendingUpdateProcessing.then(() => processChange(evt));
4097
+ pendingUpdateProcessing = task.catch((error) => {
4098
+ warn("Failed to process iterator update", error);
4099
+ });
4100
+ return task;
4101
+ };
3050
4102
  this.documentEvents.addEventListener("change", onChange);
3051
4103
  updatesCleanup = () => {
3052
4104
  this.documentEvents.removeEventListener("change", onChange);