@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.
@@ -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
- handleDocumentChange = async (event) => {
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
- ? isTransformerWithFunction(transformOptions)
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
- if (this.handleDocumentChange) {
663
- this.documentEvents.addEventListener("change", this.handleDocumentChange);
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.clearAll();
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.removeEventListener("change", this.handleDocumentChange);
1307
+ this.documentEvents?.removeEventListener("change", this.handleDocumentChange);
850
1308
  this.clearAllResultQueues();
851
- await this._resumableIterators.clearAll();
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
- 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);
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
- await this.index.put(wrappedValueToIndex, undefined, options);
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
- del(key) {
1031
- if (this.isProgramValued) {
1032
- this._resolverProgramCache.delete(key.primitive);
1033
- indexCacheLogger("cache:del:program", { id: key.primitive });
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
- if (this._resolverCache?.del(key.primitive)) {
1037
- indexCacheLogger("cache:del:value", { id: key.primitive });
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.queues.size;
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.close({ idString: key });
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 while replication metadata
1618
- // converges. For explicit remote searches, query bounded connected peers
1619
- // instead of waiting for replicator metadata to catch up.
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
- // 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) {
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
- if (!hash || hash === selfHash)
1634
- continue;
1635
- extra.push(hash);
2682
+ addExtra(hash);
1636
2683
  if (extra.length >= 8)
1637
2684
  break;
1638
2685
  }
1639
- if (extra.length > 0) {
1640
- replicatorGroups = [
1641
- ...new Set([...replicatorGroups, ...extra]),
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 onChange = async (evt) => {
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);