@peerbit/document 13.0.44 → 13.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/benchmark/document-put.js +1224 -63
- package/dist/benchmark/document-put.js.map +1 -1
- package/dist/src/index.d.ts +2 -0
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/index.js +2 -0
- package/dist/src/index.js.map +1 -1
- package/dist/src/native-rust.d.ts +50 -0
- package/dist/src/native-rust.d.ts.map +1 -0
- package/dist/src/native-rust.js +38 -0
- package/dist/src/native-rust.js.map +1 -0
- package/dist/src/policy.d.ts +51 -0
- package/dist/src/policy.d.ts.map +1 -0
- package/dist/src/policy.js +347 -0
- package/dist/src/policy.js.map +1 -0
- package/dist/src/program.d.ts +139 -22
- package/dist/src/program.d.ts.map +1 -1
- package/dist/src/program.js +3074 -342
- package/dist/src/program.js.map +1 -1
- package/dist/src/search.d.ts +80 -6
- package/dist/src/search.d.ts.map +1 -1
- package/dist/src/search.js +1155 -59
- package/dist/src/search.js.map +1 -1
- package/dist/src/transform.d.ts +42 -0
- package/dist/src/transform.d.ts.map +1 -0
- package/dist/src/transform.js +136 -0
- package/dist/src/transform.js.map +1 -0
- package/package.json +21 -16
- package/src/index.ts +2 -0
- package/src/native-rust.ts +152 -0
- package/src/policy.ts +592 -0
- package/src/program.ts +5020 -467
- package/src/search.ts +1766 -109
- package/src/transform.ts +277 -0
package/src/search.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import {
|
|
2
2
|
type AbstractType,
|
|
3
|
+
FixedArrayKind,
|
|
3
4
|
field,
|
|
4
5
|
getSchema,
|
|
5
6
|
serialize,
|
|
@@ -14,6 +15,12 @@ import {
|
|
|
14
15
|
sha256Base64Sync,
|
|
15
16
|
} from "@peerbit/crypto";
|
|
16
17
|
import * as types from "@peerbit/document-interface";
|
|
18
|
+
import {
|
|
19
|
+
type SimpleDocumentFieldExtractionPlan,
|
|
20
|
+
type SimpleDocumentProjectionContext,
|
|
21
|
+
type SimpleDocumentProjectionPlan,
|
|
22
|
+
tryProjectDocumentIndexSimple,
|
|
23
|
+
} from "./native-rust.js";
|
|
17
24
|
import { CachedIndex, type QueryCacheOptions } from "@peerbit/indexer-cache";
|
|
18
25
|
import * as indexerTypes from "@peerbit/indexer-interface";
|
|
19
26
|
import { HashmapIndex } from "@peerbit/indexer-simple";
|
|
@@ -58,6 +65,15 @@ import {
|
|
|
58
65
|
isResults,
|
|
59
66
|
} from "./result-shape.js";
|
|
60
67
|
import { ResumableIterators } from "./resumable-iterator.js";
|
|
68
|
+
import {
|
|
69
|
+
canPrepareDocumentTransformBeforeAppend,
|
|
70
|
+
canPrepareDocumentTransformWithAppendFacts,
|
|
71
|
+
documentTransformPreservesFieldPath,
|
|
72
|
+
getDocumentTransformDescriptor,
|
|
73
|
+
type DocumentTransformFacts,
|
|
74
|
+
type DocumentTransformDescriptor,
|
|
75
|
+
type DocumentTransformer,
|
|
76
|
+
} from "./transform.js";
|
|
61
77
|
|
|
62
78
|
const WARNING_WHEN_ITERATING_FOR_MORE_THAN = 1e5;
|
|
63
79
|
|
|
@@ -70,6 +86,191 @@ const indexCacheLogger = documentIndexLogger.newScope("cache");
|
|
|
70
86
|
const indexPrefetchLogger = documentIndexLogger.newScope("prefetch");
|
|
71
87
|
const indexIteratorLogger = documentIndexLogger.newScope("iterate");
|
|
72
88
|
|
|
89
|
+
const isPromiseLike = <T>(value: MaybePromise<T>): value is Promise<T> =>
|
|
90
|
+
!!value && typeof (value as Promise<T>).then === "function";
|
|
91
|
+
|
|
92
|
+
const schemaVariant = (
|
|
93
|
+
schema: ReturnType<typeof getSchema>,
|
|
94
|
+
): { type?: "u8" | "string"; value?: string } | undefined => {
|
|
95
|
+
const variant = schema?.variant;
|
|
96
|
+
if (variant == null) {
|
|
97
|
+
return {};
|
|
98
|
+
}
|
|
99
|
+
if (typeof variant === "number") {
|
|
100
|
+
return { type: "u8", value: String(variant) };
|
|
101
|
+
}
|
|
102
|
+
if (typeof variant === "string") {
|
|
103
|
+
return { type: "string", value: variant };
|
|
104
|
+
}
|
|
105
|
+
return;
|
|
106
|
+
};
|
|
107
|
+
|
|
108
|
+
const schemaTypeName = (type: unknown): string | undefined => {
|
|
109
|
+
if (typeof type === "string") {
|
|
110
|
+
switch (type) {
|
|
111
|
+
case "string":
|
|
112
|
+
case "u8":
|
|
113
|
+
case "u32":
|
|
114
|
+
case "u64":
|
|
115
|
+
case "bool":
|
|
116
|
+
return type;
|
|
117
|
+
default:
|
|
118
|
+
return;
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
if (type === Uint8Array) {
|
|
122
|
+
return "bytes";
|
|
123
|
+
}
|
|
124
|
+
if (type === PublicSignKey) {
|
|
125
|
+
return "publicsignkey";
|
|
126
|
+
}
|
|
127
|
+
if (type instanceof FixedArrayKind && type.elementType === "u8") {
|
|
128
|
+
return `fixedbytes:${type.length}`;
|
|
129
|
+
}
|
|
130
|
+
const kind = type as {
|
|
131
|
+
constructor?: { name?: string };
|
|
132
|
+
elementType?: unknown;
|
|
133
|
+
sizeEncoding?: string;
|
|
134
|
+
};
|
|
135
|
+
if (kind.constructor?.name === "OptionKind") {
|
|
136
|
+
const element = schemaTypeName(kind.elementType);
|
|
137
|
+
return element ? `option:${element}` : undefined;
|
|
138
|
+
}
|
|
139
|
+
if (kind.constructor?.name === "VecKind") {
|
|
140
|
+
const element = schemaTypeName(kind.elementType);
|
|
141
|
+
return kind.sizeEncoding === "u32" && element
|
|
142
|
+
? `vec:${element}`
|
|
143
|
+
: undefined;
|
|
144
|
+
}
|
|
145
|
+
return;
|
|
146
|
+
};
|
|
147
|
+
|
|
148
|
+
const schemaFieldPlan = (
|
|
149
|
+
schema: ReturnType<typeof getSchema>,
|
|
150
|
+
): { names: string[]; types: string[] } | undefined => {
|
|
151
|
+
if (!schema?.fields) {
|
|
152
|
+
return;
|
|
153
|
+
}
|
|
154
|
+
const names: string[] = [];
|
|
155
|
+
const types: string[] = [];
|
|
156
|
+
for (const field of schema.fields) {
|
|
157
|
+
const type = schemaTypeName(field.type);
|
|
158
|
+
if (!type) {
|
|
159
|
+
return;
|
|
160
|
+
}
|
|
161
|
+
names.push(field.key);
|
|
162
|
+
types.push(type);
|
|
163
|
+
}
|
|
164
|
+
return { names, types };
|
|
165
|
+
};
|
|
166
|
+
|
|
167
|
+
const asSingleFieldPath = (
|
|
168
|
+
path: string | readonly string[],
|
|
169
|
+
): string | undefined =>
|
|
170
|
+
typeof path === "string" ? path : path.length === 1 ? path[0] : undefined;
|
|
171
|
+
|
|
172
|
+
const createSimpleProjectionPlan = (
|
|
173
|
+
documentSchema: ReturnType<typeof getSchema>,
|
|
174
|
+
indexedSchema: ReturnType<typeof getSchema>,
|
|
175
|
+
descriptor: DocumentTransformDescriptor | undefined,
|
|
176
|
+
): SimpleDocumentProjectionPlan | undefined => {
|
|
177
|
+
if (!descriptor || descriptor.kind === "identity") {
|
|
178
|
+
return;
|
|
179
|
+
}
|
|
180
|
+
const documentVariant = schemaVariant(documentSchema);
|
|
181
|
+
const outputVariant = schemaVariant(indexedSchema);
|
|
182
|
+
const documentFields = schemaFieldPlan(documentSchema);
|
|
183
|
+
const outputFields = schemaFieldPlan(indexedSchema);
|
|
184
|
+
if (!documentVariant || !outputVariant || !documentFields || !outputFields) {
|
|
185
|
+
return;
|
|
186
|
+
}
|
|
187
|
+
const sources = new Map<string, { kind: string; value: string }>();
|
|
188
|
+
if (descriptor.kind === "pick") {
|
|
189
|
+
for (const field of descriptor.fields) {
|
|
190
|
+
const name = asSingleFieldPath(field);
|
|
191
|
+
if (!name) {
|
|
192
|
+
return;
|
|
193
|
+
}
|
|
194
|
+
sources.set(name, { kind: "field", value: name });
|
|
195
|
+
}
|
|
196
|
+
} else {
|
|
197
|
+
for (const field of descriptor.fields) {
|
|
198
|
+
const target = asSingleFieldPath(field.target);
|
|
199
|
+
if (!target) {
|
|
200
|
+
return;
|
|
201
|
+
}
|
|
202
|
+
switch (field.source.kind) {
|
|
203
|
+
case "field": {
|
|
204
|
+
const source = asSingleFieldPath(field.source.path);
|
|
205
|
+
if (!source) {
|
|
206
|
+
return;
|
|
207
|
+
}
|
|
208
|
+
sources.set(target, { kind: "field", value: source });
|
|
209
|
+
break;
|
|
210
|
+
}
|
|
211
|
+
case "context":
|
|
212
|
+
sources.set(target, {
|
|
213
|
+
kind: "context",
|
|
214
|
+
value: field.source.field,
|
|
215
|
+
});
|
|
216
|
+
break;
|
|
217
|
+
case "entryFirstSignerPublicKey":
|
|
218
|
+
sources.set(target, {
|
|
219
|
+
kind: "entryFirstSignerPublicKey",
|
|
220
|
+
value: "",
|
|
221
|
+
});
|
|
222
|
+
break;
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
const sourceKinds: string[] = [];
|
|
227
|
+
const sourceValues: string[] = [];
|
|
228
|
+
for (const field of outputFields.names) {
|
|
229
|
+
const source = sources.get(field);
|
|
230
|
+
if (!source) {
|
|
231
|
+
return;
|
|
232
|
+
}
|
|
233
|
+
sourceKinds.push(source.kind);
|
|
234
|
+
sourceValues.push(source.value);
|
|
235
|
+
}
|
|
236
|
+
return {
|
|
237
|
+
documentVariantType: documentVariant.type,
|
|
238
|
+
documentVariantValue: documentVariant.value,
|
|
239
|
+
documentFieldNames: documentFields.names,
|
|
240
|
+
documentFieldTypes: documentFields.types,
|
|
241
|
+
outputVariantType: outputVariant.type,
|
|
242
|
+
outputVariantValue: outputVariant.value,
|
|
243
|
+
outputFieldTypes: outputFields.types,
|
|
244
|
+
sourceKinds,
|
|
245
|
+
sourceValues,
|
|
246
|
+
};
|
|
247
|
+
};
|
|
248
|
+
|
|
249
|
+
const createSimpleFieldExtractionPlan = (
|
|
250
|
+
documentSchema: ReturnType<typeof getSchema>,
|
|
251
|
+
path: string | readonly string[],
|
|
252
|
+
): SimpleDocumentFieldExtractionPlan | undefined => {
|
|
253
|
+
const fieldName = asSingleFieldPath(path);
|
|
254
|
+
if (!fieldName) {
|
|
255
|
+
return;
|
|
256
|
+
}
|
|
257
|
+
const documentVariant = schemaVariant(documentSchema);
|
|
258
|
+
const documentFields = schemaFieldPlan(documentSchema);
|
|
259
|
+
if (!documentVariant || !documentFields) {
|
|
260
|
+
return;
|
|
261
|
+
}
|
|
262
|
+
if (!documentFields.names.includes(fieldName)) {
|
|
263
|
+
return;
|
|
264
|
+
}
|
|
265
|
+
return {
|
|
266
|
+
documentVariantType: documentVariant.type,
|
|
267
|
+
documentVariantValue: documentVariant.value,
|
|
268
|
+
documentFieldNames: documentFields.names,
|
|
269
|
+
documentFieldTypes: documentFields.types,
|
|
270
|
+
fieldName,
|
|
271
|
+
};
|
|
272
|
+
};
|
|
273
|
+
|
|
73
274
|
type BufferedResult<T, I extends Record<string, any>> = {
|
|
74
275
|
value: T;
|
|
75
276
|
indexed: WithContext<I>;
|
|
@@ -282,7 +483,11 @@ export type SearchOptions<
|
|
|
282
483
|
Resolve extends boolean | undefined,
|
|
283
484
|
> = QueryOptions<T, I, D, Resolve>;
|
|
284
485
|
|
|
285
|
-
type Transformer<T, I> = (
|
|
486
|
+
type Transformer<T, I> = (
|
|
487
|
+
obj: T,
|
|
488
|
+
context: types.Context,
|
|
489
|
+
facts?: DocumentTransformFacts,
|
|
490
|
+
) => MaybePromise<I>;
|
|
286
491
|
|
|
287
492
|
export type ResultsIterator<T> = {
|
|
288
493
|
close: () => Promise<void>;
|
|
@@ -593,7 +798,7 @@ export type TransformerAsConstructor<T, I> = {
|
|
|
593
798
|
|
|
594
799
|
export type TransformerAsFunction<T, I> = {
|
|
595
800
|
type: AbstractType<I>;
|
|
596
|
-
transform:
|
|
801
|
+
transform: DocumentTransformer<T, I>;
|
|
597
802
|
};
|
|
598
803
|
export type TransformOptions<T, I> =
|
|
599
804
|
| TransformerAsConstructor<T, I>
|
|
@@ -654,6 +859,7 @@ export type OpenOptions<
|
|
|
654
859
|
maybeOpen: (value: T & Program) => Promise<T & Program>;
|
|
655
860
|
prefetch?: boolean | Partial<PrefetchOptions>;
|
|
656
861
|
includeIndexed?: boolean; // if true, indexed representations will always be included in the search results
|
|
862
|
+
immutable?: boolean; // conflict rule of the owning store: oldest version wins when true (mirrors Documents "immutable")
|
|
657
863
|
};
|
|
658
864
|
|
|
659
865
|
type IndexableClass<I> = new (
|
|
@@ -661,6 +867,151 @@ type IndexableClass<I> = new (
|
|
|
661
867
|
context: types.Context,
|
|
662
868
|
) => WithContext<I>;
|
|
663
869
|
|
|
870
|
+
type ContextualPutOptions = {
|
|
871
|
+
replace?: boolean;
|
|
872
|
+
encodedValue?: Uint8Array;
|
|
873
|
+
encodedValueParts?: {
|
|
874
|
+
prefix: Uint8Array;
|
|
875
|
+
suffix: Uint8Array;
|
|
876
|
+
};
|
|
877
|
+
transformFacts?: DocumentTransformFacts;
|
|
878
|
+
};
|
|
879
|
+
|
|
880
|
+
const stripEncodedValue = (
|
|
881
|
+
options: ContextualPutOptions | undefined,
|
|
882
|
+
): { replace?: boolean } | undefined =>
|
|
883
|
+
options?.encodedValue || options?.encodedValueParts || options?.transformFacts
|
|
884
|
+
? { replace: options.replace }
|
|
885
|
+
: options;
|
|
886
|
+
|
|
887
|
+
const writeU32Le = (target: Uint8Array, offset: number, value: number) => {
|
|
888
|
+
target[offset] = value & 0xff;
|
|
889
|
+
target[offset + 1] = (value >>> 8) & 0xff;
|
|
890
|
+
target[offset + 2] = (value >>> 16) & 0xff;
|
|
891
|
+
target[offset + 3] = (value >>> 24) & 0xff;
|
|
892
|
+
return offset + 4;
|
|
893
|
+
};
|
|
894
|
+
|
|
895
|
+
const writeU64Le = (target: Uint8Array, offset: number, value: bigint) => {
|
|
896
|
+
let remaining = value;
|
|
897
|
+
for (let i = 0; i < 8; i++) {
|
|
898
|
+
target[offset + i] = Number(remaining & 0xffn);
|
|
899
|
+
remaining >>= 8n;
|
|
900
|
+
}
|
|
901
|
+
return offset + 8;
|
|
902
|
+
};
|
|
903
|
+
|
|
904
|
+
export const encodeContextSuffix = (context: types.Context): Uint8Array => {
|
|
905
|
+
const head = fromString(context.head);
|
|
906
|
+
const gid = fromString(context.gid);
|
|
907
|
+
const encoded = new Uint8Array(
|
|
908
|
+
1 + 8 + 8 + 4 + head.byteLength + 4 + gid.byteLength + 4,
|
|
909
|
+
);
|
|
910
|
+
let offset = 0;
|
|
911
|
+
// Context is @variant(0); keep this byte-for-byte aligned with Borsh.
|
|
912
|
+
encoded[offset++] = 0;
|
|
913
|
+
offset = writeU64Le(encoded, offset, context.created);
|
|
914
|
+
offset = writeU64Le(encoded, offset, context.modified);
|
|
915
|
+
offset = writeU32Le(encoded, offset, head.byteLength);
|
|
916
|
+
encoded.set(head, offset);
|
|
917
|
+
offset += head.byteLength;
|
|
918
|
+
offset = writeU32Le(encoded, offset, gid.byteLength);
|
|
919
|
+
encoded.set(gid, offset);
|
|
920
|
+
offset += gid.byteLength;
|
|
921
|
+
writeU32Le(encoded, offset, context.size);
|
|
922
|
+
return encoded;
|
|
923
|
+
};
|
|
924
|
+
|
|
925
|
+
type ContextualIndexPut<I> = {
|
|
926
|
+
putWithContext?: (
|
|
927
|
+
value: I,
|
|
928
|
+
id: indexerTypes.IdKey,
|
|
929
|
+
context: types.Context,
|
|
930
|
+
options?: ContextualPutOptions,
|
|
931
|
+
) => Promise<void> | void;
|
|
932
|
+
putStoredContextualEncodedValue?: (
|
|
933
|
+
id: indexerTypes.IdKey,
|
|
934
|
+
encodedValueParts: {
|
|
935
|
+
prefix: Uint8Array;
|
|
936
|
+
suffix: Uint8Array;
|
|
937
|
+
},
|
|
938
|
+
options?: { replace?: boolean },
|
|
939
|
+
) => Promise<void> | void | false;
|
|
940
|
+
persistStoredContextualEncodedValue?: (
|
|
941
|
+
id: indexerTypes.IdKey,
|
|
942
|
+
encodedValueParts: {
|
|
943
|
+
prefix: Uint8Array;
|
|
944
|
+
suffix: Uint8Array;
|
|
945
|
+
},
|
|
946
|
+
options?: { replace?: boolean },
|
|
947
|
+
) => Promise<void> | void | false;
|
|
948
|
+
putStoredContextualEncodedValueBatch?: (
|
|
949
|
+
values: Array<{
|
|
950
|
+
id: indexerTypes.IdKey;
|
|
951
|
+
encodedValueParts: {
|
|
952
|
+
prefix: Uint8Array;
|
|
953
|
+
suffix: Uint8Array;
|
|
954
|
+
};
|
|
955
|
+
options?: { replace?: boolean };
|
|
956
|
+
}>,
|
|
957
|
+
) => Promise<boolean> | boolean;
|
|
958
|
+
putWithContextBatch?: (
|
|
959
|
+
values: Array<{
|
|
960
|
+
value: I;
|
|
961
|
+
id: indexerTypes.IdKey;
|
|
962
|
+
context: types.Context;
|
|
963
|
+
options?: ContextualPutOptions;
|
|
964
|
+
}>,
|
|
965
|
+
) => Promise<void> | void;
|
|
966
|
+
};
|
|
967
|
+
|
|
968
|
+
type ContextHeadIndex<I> = {
|
|
969
|
+
getByContextHead?: (
|
|
970
|
+
head: string,
|
|
971
|
+
) => indexerTypes.IndexedResult<WithContext<I>> | undefined;
|
|
972
|
+
getByContextHeadBatch?: (
|
|
973
|
+
heads: string[],
|
|
974
|
+
) => Array<indexerTypes.IndexedResult<WithContext<I>> | undefined>;
|
|
975
|
+
getIdByContextHead?: (head: string) => indexerTypes.IdKey | undefined;
|
|
976
|
+
};
|
|
977
|
+
|
|
978
|
+
type ExactDeleteIndex = {
|
|
979
|
+
delIdsNoReturn?: (
|
|
980
|
+
deleteIds: Array<indexerTypes.IdKey | indexerTypes.Ideable>,
|
|
981
|
+
) => Promise<void> | void;
|
|
982
|
+
delIds?: (
|
|
983
|
+
deleteIds: Array<indexerTypes.IdKey | indexerTypes.Ideable>,
|
|
984
|
+
) => Promise<indexerTypes.IdKey[]> | indexerTypes.IdKey[];
|
|
985
|
+
};
|
|
986
|
+
|
|
987
|
+
type NativeBackboneDocumentIndex = {
|
|
988
|
+
attachNativeBackboneDocumentIndex?: (
|
|
989
|
+
backbone: unknown,
|
|
990
|
+
options?: { preserveExisting?: boolean },
|
|
991
|
+
) => boolean | void;
|
|
992
|
+
};
|
|
993
|
+
|
|
994
|
+
type NativeBackboneDocumentProjection = {
|
|
995
|
+
projectDocumentIndexSimple?: (
|
|
996
|
+
encodedDocument: Uint8Array,
|
|
997
|
+
plan: SimpleDocumentProjectionPlan,
|
|
998
|
+
context: SimpleDocumentProjectionContext,
|
|
999
|
+
) => Uint8Array | undefined;
|
|
1000
|
+
};
|
|
1001
|
+
|
|
1002
|
+
type NativeBackboneDocumentIndexCommit<I> = {
|
|
1003
|
+
valuePrefixBytes?: Uint8Array;
|
|
1004
|
+
usePlainPutPayload?: boolean;
|
|
1005
|
+
projection?: {
|
|
1006
|
+
encodedDocument: Uint8Array;
|
|
1007
|
+
plan: SimpleDocumentProjectionPlan;
|
|
1008
|
+
signer?: Uint8Array;
|
|
1009
|
+
};
|
|
1010
|
+
indexable?: I;
|
|
1011
|
+
getIndexable?: () => I;
|
|
1012
|
+
setContext?: (context: types.Context) => void;
|
|
1013
|
+
};
|
|
1014
|
+
|
|
664
1015
|
export const coerceWithContext = <T>(
|
|
665
1016
|
value: T | WithContext<T>,
|
|
666
1017
|
context: types.Context,
|
|
@@ -679,6 +1030,36 @@ export const coerceWithIndexed = <T, I>(
|
|
|
679
1030
|
return valueWithContext;
|
|
680
1031
|
};
|
|
681
1032
|
|
|
1033
|
+
export const coerceWithLazyIndexed = <T, I>(
|
|
1034
|
+
value: T | WithIndexedContext<T, I>,
|
|
1035
|
+
getIndexable: () => I,
|
|
1036
|
+
): WithIndexedContext<T, I> => {
|
|
1037
|
+
let cached: I | undefined;
|
|
1038
|
+
let hasCached = false;
|
|
1039
|
+
Object.defineProperty(value, "__indexed", {
|
|
1040
|
+
configurable: true,
|
|
1041
|
+
enumerable: true,
|
|
1042
|
+
get() {
|
|
1043
|
+
if (!hasCached) {
|
|
1044
|
+
cached = getIndexable();
|
|
1045
|
+
hasCached = true;
|
|
1046
|
+
Object.defineProperty(value, "__indexed", {
|
|
1047
|
+
configurable: true,
|
|
1048
|
+
enumerable: true,
|
|
1049
|
+
writable: true,
|
|
1050
|
+
value: cached,
|
|
1051
|
+
});
|
|
1052
|
+
}
|
|
1053
|
+
return cached;
|
|
1054
|
+
},
|
|
1055
|
+
set(indexed: I) {
|
|
1056
|
+
cached = indexed;
|
|
1057
|
+
hasCached = true;
|
|
1058
|
+
},
|
|
1059
|
+
});
|
|
1060
|
+
return value as WithIndexedContext<T, I>;
|
|
1061
|
+
};
|
|
1062
|
+
|
|
682
1063
|
@variant("documents_index")
|
|
683
1064
|
export class DocumentIndex<
|
|
684
1065
|
T,
|
|
@@ -693,6 +1074,10 @@ export class DocumentIndex<
|
|
|
693
1074
|
|
|
694
1075
|
// transform options
|
|
695
1076
|
transformer: Transformer<T, I>;
|
|
1077
|
+
private transformerIsIdentity = false;
|
|
1078
|
+
private nativeTransformDescriptor?: DocumentTransformDescriptor;
|
|
1079
|
+
private nativeTransformProjectionPlan?: SimpleDocumentProjectionPlan;
|
|
1080
|
+
private nativeBackboneDocumentProjection?: NativeBackboneDocumentProjection;
|
|
696
1081
|
|
|
697
1082
|
// The indexed document wrapped in a context
|
|
698
1083
|
wrappedIndexedType: IndexableClass<I>;
|
|
@@ -709,6 +1094,7 @@ export class DocumentIndex<
|
|
|
709
1094
|
private _resumableIterators: ResumableIterators<WithContext<I>>;
|
|
710
1095
|
private _prefetch?: PrefetchOptions | undefined;
|
|
711
1096
|
private includeIndexed: boolean | undefined = undefined;
|
|
1097
|
+
private immutable: boolean = false;
|
|
712
1098
|
|
|
713
1099
|
compatibility: 6 | 7 | 8 | 9 | undefined;
|
|
714
1100
|
|
|
@@ -757,11 +1143,21 @@ export class DocumentIndex<
|
|
|
757
1143
|
>;
|
|
758
1144
|
private iteratorKeepAliveTimers?: Map<string, ReturnType<typeof setTimeout>>;
|
|
759
1145
|
|
|
1146
|
+
private deleteResolvedCacheForKey(key: indexerTypes.IdKey): void {
|
|
1147
|
+
if (this.isProgramValued) {
|
|
1148
|
+
this._resolverProgramCache!.delete(key.primitive);
|
|
1149
|
+
indexCacheLogger("cache:del:program", { id: key.primitive });
|
|
1150
|
+
} else if (this._resolverCache?.del(key.primitive)) {
|
|
1151
|
+
indexCacheLogger("cache:del:value", { id: key.primitive });
|
|
1152
|
+
}
|
|
1153
|
+
}
|
|
1154
|
+
|
|
760
1155
|
constructor(properties?: {
|
|
761
1156
|
query?: RPC<types.AbstractSearchRequest, types.AbstractSearchResult>;
|
|
762
1157
|
}) {
|
|
763
1158
|
super();
|
|
764
1159
|
this._query = properties?.query || new RPC();
|
|
1160
|
+
this._resultQueue = new Map();
|
|
765
1161
|
this.iteratorKeepAliveTimers = new Map();
|
|
766
1162
|
}
|
|
767
1163
|
|
|
@@ -903,9 +1299,16 @@ export class DocumentIndex<
|
|
|
903
1299
|
return results;
|
|
904
1300
|
}
|
|
905
1301
|
|
|
906
|
-
|
|
1302
|
+
// Bound in open(). Deserialized instances skip constructor/field initializers
|
|
1303
|
+
// (borsh creates objects via Object.create), so this must not rely on a field
|
|
1304
|
+
// initializer to exist.
|
|
1305
|
+
private handleDocumentChange?: (
|
|
907
1306
|
event: CustomEvent<DocumentsChange<T, I>>,
|
|
908
|
-
) =>
|
|
1307
|
+
) => Promise<void>;
|
|
1308
|
+
|
|
1309
|
+
private async onDocumentChange(
|
|
1310
|
+
event: CustomEvent<DocumentsChange<T, I>>,
|
|
1311
|
+
): Promise<void> {
|
|
909
1312
|
const added = event.detail.added;
|
|
910
1313
|
if (!added.length) {
|
|
911
1314
|
return;
|
|
@@ -980,7 +1383,7 @@ export class DocumentIndex<
|
|
|
980
1383
|
queue.pushInFlight = false;
|
|
981
1384
|
}
|
|
982
1385
|
}
|
|
983
|
-
}
|
|
1386
|
+
}
|
|
984
1387
|
|
|
985
1388
|
private get nestedProperties() {
|
|
986
1389
|
return {
|
|
@@ -1026,6 +1429,7 @@ export class DocumentIndex<
|
|
|
1026
1429
|
this.canRead = properties.canRead;
|
|
1027
1430
|
this.canSearch = properties.canSearch;
|
|
1028
1431
|
this.includeIndexed = properties.includeIndexed;
|
|
1432
|
+
this.immutable = properties.immutable ?? false;
|
|
1029
1433
|
|
|
1030
1434
|
@variant(0)
|
|
1031
1435
|
class IndexedClassWithContext {
|
|
@@ -1093,9 +1497,25 @@ export class DocumentIndex<
|
|
|
1093
1497
|
};
|
|
1094
1498
|
|
|
1095
1499
|
const transformOptions = properties.transform;
|
|
1500
|
+
const hasTransformFunction =
|
|
1501
|
+
transformOptions != null && isTransformerWithFunction(transformOptions);
|
|
1502
|
+
this.nativeTransformDescriptor = hasTransformFunction
|
|
1503
|
+
? getDocumentTransformDescriptor(transformOptions.transform)
|
|
1504
|
+
: undefined;
|
|
1505
|
+
this.nativeTransformProjectionPlan = createSimpleProjectionPlan(
|
|
1506
|
+
getSchema(this.documentType),
|
|
1507
|
+
indexedSchema,
|
|
1508
|
+
this.nativeTransformDescriptor,
|
|
1509
|
+
);
|
|
1510
|
+
this.transformerIsIdentity =
|
|
1511
|
+
transformOptions == null ||
|
|
1512
|
+
(!hasTransformFunction && transformOptions.type == null) ||
|
|
1513
|
+
(this.nativeTransformDescriptor?.kind === "identity" &&
|
|
1514
|
+
this.indexedTypeIsDocumentType);
|
|
1096
1515
|
this.transformer = transformOptions
|
|
1097
|
-
?
|
|
1098
|
-
? (obj, context) =>
|
|
1516
|
+
? hasTransformFunction
|
|
1517
|
+
? (obj, context, facts) =>
|
|
1518
|
+
transformOptions.transform(obj, context, facts)
|
|
1099
1519
|
: transformOptions.type
|
|
1100
1520
|
? (obj, context) => new transformOptions.type!(obj, context)
|
|
1101
1521
|
: (obj) => obj as any as I
|
|
@@ -1210,9 +1630,307 @@ export class DocumentIndex<
|
|
|
1210
1630
|
responseType: types.AbstractSearchResult,
|
|
1211
1631
|
queryType: types.AbstractSearchRequest,
|
|
1212
1632
|
});
|
|
1213
|
-
|
|
1214
|
-
|
|
1633
|
+
this.handleDocumentChange ??= (event) => this.onDocumentChange(event);
|
|
1634
|
+
this.documentEvents.addEventListener("change", this.handleDocumentChange);
|
|
1635
|
+
}
|
|
1636
|
+
|
|
1637
|
+
private attachNativeBackboneDocumentIndex(
|
|
1638
|
+
backbone: unknown,
|
|
1639
|
+
options?: { preserveExisting?: boolean },
|
|
1640
|
+
): boolean {
|
|
1641
|
+
this.nativeBackboneDocumentProjection = undefined;
|
|
1642
|
+
if (
|
|
1643
|
+
!backbone ||
|
|
1644
|
+
this.isProgramValued ||
|
|
1645
|
+
!this.canUseNativeBackboneDocumentIndex()
|
|
1646
|
+
) {
|
|
1647
|
+
return false;
|
|
1648
|
+
}
|
|
1649
|
+
const attach = (this.index as NativeBackboneDocumentIndex)
|
|
1650
|
+
.attachNativeBackboneDocumentIndex;
|
|
1651
|
+
const attached =
|
|
1652
|
+
typeof attach === "function" &&
|
|
1653
|
+
attach.call(this.index, backbone, options) === true;
|
|
1654
|
+
if (attached) {
|
|
1655
|
+
const projection = backbone as NativeBackboneDocumentProjection;
|
|
1656
|
+
if (typeof projection.projectDocumentIndexSimple === "function") {
|
|
1657
|
+
this.nativeBackboneDocumentProjection = projection;
|
|
1658
|
+
}
|
|
1659
|
+
}
|
|
1660
|
+
return attached;
|
|
1661
|
+
}
|
|
1662
|
+
|
|
1663
|
+
private canUseNativeBackboneDocumentIndex(): boolean {
|
|
1664
|
+
return (
|
|
1665
|
+
(this.transformerIsIdentity && this.indexedTypeIsDocumentType) ||
|
|
1666
|
+
this.nativeTransformDescriptor != null
|
|
1667
|
+
);
|
|
1668
|
+
}
|
|
1669
|
+
|
|
1670
|
+
private getNativeDocumentFieldExtractionPlan(
|
|
1671
|
+
path: string | readonly string[],
|
|
1672
|
+
): SimpleDocumentFieldExtractionPlan | undefined {
|
|
1673
|
+
return createSimpleFieldExtractionPlan(getSchema(this.documentType), path);
|
|
1674
|
+
}
|
|
1675
|
+
|
|
1676
|
+
private canPrepareNativeBackboneDocumentIndexCommit(): boolean {
|
|
1677
|
+
return (
|
|
1678
|
+
(this.transformerIsIdentity && this.indexedTypeIsDocumentType) ||
|
|
1679
|
+
canPrepareDocumentTransformBeforeAppend(
|
|
1680
|
+
this.nativeTransformDescriptor,
|
|
1681
|
+
)
|
|
1682
|
+
);
|
|
1683
|
+
}
|
|
1684
|
+
|
|
1685
|
+
private canPrepareNativeBackboneDocumentIndexCommitWithAppendFacts(): boolean {
|
|
1686
|
+
return (
|
|
1687
|
+
this.canPrepareNativeBackboneDocumentIndexCommit() ||
|
|
1688
|
+
!!this.nativeTransformProjectionPlan ||
|
|
1689
|
+
canPrepareDocumentTransformWithAppendFacts(
|
|
1690
|
+
this.nativeTransformDescriptor,
|
|
1691
|
+
)
|
|
1692
|
+
);
|
|
1693
|
+
}
|
|
1694
|
+
|
|
1695
|
+
private canUseNativeBackboneContextualBatch(): boolean {
|
|
1696
|
+
return (
|
|
1697
|
+
!this.isProgramValued &&
|
|
1698
|
+
typeof (this.index as ContextualIndexPut<I>).putWithContextBatch ===
|
|
1699
|
+
"function" &&
|
|
1700
|
+
((this.transformerIsIdentity && this.indexedTypeIsDocumentType) ||
|
|
1701
|
+
this.canPrepareNativeBackboneDocumentIndexCommitWithAppendFacts())
|
|
1702
|
+
);
|
|
1703
|
+
}
|
|
1704
|
+
|
|
1705
|
+
private prepareNativeBackboneDocumentIndexCommit(
|
|
1706
|
+
value: T,
|
|
1707
|
+
encodedDocument: Uint8Array,
|
|
1708
|
+
transformFacts?: DocumentTransformFacts,
|
|
1709
|
+
): MaybePromise<NativeBackboneDocumentIndexCommit<I> | undefined> {
|
|
1710
|
+
if (this.transformerIsIdentity && this.indexedTypeIsDocumentType) {
|
|
1711
|
+
return {
|
|
1712
|
+
valuePrefixBytes: encodedDocument,
|
|
1713
|
+
usePlainPutPayload: true,
|
|
1714
|
+
indexable: value as any as I,
|
|
1715
|
+
};
|
|
1716
|
+
}
|
|
1717
|
+
if (this.nativeTransformProjectionPlan) {
|
|
1718
|
+
let projectionContext: types.Context | undefined;
|
|
1719
|
+
let cached: I | undefined;
|
|
1720
|
+
let hasCached = false;
|
|
1721
|
+
return {
|
|
1722
|
+
projection: {
|
|
1723
|
+
encodedDocument,
|
|
1724
|
+
plan: this.nativeTransformProjectionPlan,
|
|
1725
|
+
signer: transformFacts?.entryPublicKeys?.[0]?.bytes,
|
|
1726
|
+
},
|
|
1727
|
+
getIndexable: () => {
|
|
1728
|
+
if (!hasCached) {
|
|
1729
|
+
const transformed = this.transformer(
|
|
1730
|
+
value,
|
|
1731
|
+
projectionContext as types.Context,
|
|
1732
|
+
transformFacts,
|
|
1733
|
+
);
|
|
1734
|
+
if (isPromiseLike(transformed)) {
|
|
1735
|
+
throw new Error(
|
|
1736
|
+
"Native descriptor transform unexpectedly returned a promise",
|
|
1737
|
+
);
|
|
1738
|
+
}
|
|
1739
|
+
cached = transformed;
|
|
1740
|
+
hasCached = true;
|
|
1741
|
+
}
|
|
1742
|
+
return cached!;
|
|
1743
|
+
},
|
|
1744
|
+
setContext: (context) => {
|
|
1745
|
+
projectionContext = context;
|
|
1746
|
+
hasCached = false;
|
|
1747
|
+
cached = undefined;
|
|
1748
|
+
},
|
|
1749
|
+
};
|
|
1750
|
+
}
|
|
1751
|
+
if (
|
|
1752
|
+
!canPrepareDocumentTransformBeforeAppend(
|
|
1753
|
+
this.nativeTransformDescriptor,
|
|
1754
|
+
)
|
|
1755
|
+
) {
|
|
1756
|
+
return;
|
|
1757
|
+
}
|
|
1758
|
+
const transformed = this.transformer(
|
|
1759
|
+
value,
|
|
1760
|
+
undefined as unknown as types.Context,
|
|
1761
|
+
transformFacts,
|
|
1762
|
+
);
|
|
1763
|
+
const finish = (indexable: I): NativeBackboneDocumentIndexCommit<I> => ({
|
|
1764
|
+
valuePrefixBytes: serialize(this.asIndexedTypeValue(indexable)),
|
|
1765
|
+
indexable,
|
|
1766
|
+
});
|
|
1767
|
+
return isPromiseLike(transformed)
|
|
1768
|
+
? transformed.then(finish)
|
|
1769
|
+
: finish(transformed);
|
|
1770
|
+
}
|
|
1771
|
+
|
|
1772
|
+
private prepareNativeBackboneDocumentIndexCommitWithAppendFacts(
|
|
1773
|
+
value: T,
|
|
1774
|
+
encodedDocument: Uint8Array,
|
|
1775
|
+
context: types.Context,
|
|
1776
|
+
transformFacts?: DocumentTransformFacts,
|
|
1777
|
+
): NativeBackboneDocumentIndexCommit<I> | undefined {
|
|
1778
|
+
if (this.transformerIsIdentity && this.indexedTypeIsDocumentType) {
|
|
1779
|
+
return {
|
|
1780
|
+
valuePrefixBytes: encodedDocument,
|
|
1781
|
+
usePlainPutPayload: true,
|
|
1782
|
+
indexable: value as any as I,
|
|
1783
|
+
};
|
|
1784
|
+
}
|
|
1785
|
+
if (this.nativeTransformProjectionPlan) {
|
|
1786
|
+
let projectionContext = {
|
|
1787
|
+
created: context.created,
|
|
1788
|
+
modified: context.modified,
|
|
1789
|
+
head: context.head,
|
|
1790
|
+
gid: context.gid,
|
|
1791
|
+
size: context.size,
|
|
1792
|
+
signer: transformFacts?.entryPublicKeys?.[0]?.bytes,
|
|
1793
|
+
};
|
|
1794
|
+
let cached: I | undefined;
|
|
1795
|
+
let hasCached = false;
|
|
1796
|
+
return {
|
|
1797
|
+
projection: {
|
|
1798
|
+
encodedDocument,
|
|
1799
|
+
plan: this.nativeTransformProjectionPlan,
|
|
1800
|
+
signer: projectionContext.signer,
|
|
1801
|
+
},
|
|
1802
|
+
getIndexable: () => {
|
|
1803
|
+
if (!hasCached) {
|
|
1804
|
+
const transformed = this.transformer(
|
|
1805
|
+
value,
|
|
1806
|
+
projectionContext as types.Context,
|
|
1807
|
+
transformFacts,
|
|
1808
|
+
);
|
|
1809
|
+
if (isPromiseLike(transformed)) {
|
|
1810
|
+
throw new Error(
|
|
1811
|
+
"Native descriptor transform unexpectedly returned a promise",
|
|
1812
|
+
);
|
|
1813
|
+
}
|
|
1814
|
+
cached = transformed;
|
|
1815
|
+
hasCached = true;
|
|
1816
|
+
}
|
|
1817
|
+
return cached!;
|
|
1818
|
+
},
|
|
1819
|
+
setContext: (nextContext) => {
|
|
1820
|
+
projectionContext = {
|
|
1821
|
+
created: nextContext.created,
|
|
1822
|
+
modified: nextContext.modified,
|
|
1823
|
+
head: nextContext.head,
|
|
1824
|
+
gid: nextContext.gid,
|
|
1825
|
+
size: nextContext.size,
|
|
1826
|
+
signer: transformFacts?.entryPublicKeys?.[0]?.bytes,
|
|
1827
|
+
};
|
|
1828
|
+
hasCached = false;
|
|
1829
|
+
cached = undefined;
|
|
1830
|
+
},
|
|
1831
|
+
};
|
|
1215
1832
|
}
|
|
1833
|
+
if (
|
|
1834
|
+
!canPrepareDocumentTransformWithAppendFacts(
|
|
1835
|
+
this.nativeTransformDescriptor,
|
|
1836
|
+
) &&
|
|
1837
|
+
!canPrepareDocumentTransformBeforeAppend(
|
|
1838
|
+
this.nativeTransformDescriptor,
|
|
1839
|
+
)
|
|
1840
|
+
) {
|
|
1841
|
+
return;
|
|
1842
|
+
}
|
|
1843
|
+
const transformed = this.transformer(value, context, transformFacts);
|
|
1844
|
+
if (isPromiseLike(transformed)) {
|
|
1845
|
+
return;
|
|
1846
|
+
}
|
|
1847
|
+
const indexable = transformed;
|
|
1848
|
+
return {
|
|
1849
|
+
valuePrefixBytes: serialize(this.asIndexedTypeValue(indexable)),
|
|
1850
|
+
indexable,
|
|
1851
|
+
};
|
|
1852
|
+
}
|
|
1853
|
+
|
|
1854
|
+
private prepareNativeBackboneDocumentIndexStoredCommitWithAppendFacts(
|
|
1855
|
+
encodedDocument: Uint8Array,
|
|
1856
|
+
context: types.Context,
|
|
1857
|
+
transformFacts?: DocumentTransformFacts,
|
|
1858
|
+
): NativeBackboneDocumentIndexCommit<I> | undefined {
|
|
1859
|
+
if (this.transformerIsIdentity && this.indexedTypeIsDocumentType) {
|
|
1860
|
+
return {
|
|
1861
|
+
valuePrefixBytes: encodedDocument,
|
|
1862
|
+
usePlainPutPayload: true,
|
|
1863
|
+
};
|
|
1864
|
+
}
|
|
1865
|
+
if (this.nativeTransformProjectionPlan) {
|
|
1866
|
+
return {
|
|
1867
|
+
projection: {
|
|
1868
|
+
encodedDocument,
|
|
1869
|
+
plan: this.nativeTransformProjectionPlan,
|
|
1870
|
+
signer: transformFacts?.entryPublicKeys?.[0]?.bytes,
|
|
1871
|
+
},
|
|
1872
|
+
};
|
|
1873
|
+
}
|
|
1874
|
+
if (
|
|
1875
|
+
!canPrepareDocumentTransformWithAppendFacts(
|
|
1876
|
+
this.nativeTransformDescriptor,
|
|
1877
|
+
) &&
|
|
1878
|
+
!canPrepareDocumentTransformBeforeAppend(
|
|
1879
|
+
this.nativeTransformDescriptor,
|
|
1880
|
+
)
|
|
1881
|
+
) {
|
|
1882
|
+
return;
|
|
1883
|
+
}
|
|
1884
|
+
return;
|
|
1885
|
+
}
|
|
1886
|
+
|
|
1887
|
+
private nativeBackboneDocumentIndexValuePrefixBytes(
|
|
1888
|
+
nativeDocumentIndex: NativeBackboneDocumentIndexCommit<I>,
|
|
1889
|
+
context: types.Context,
|
|
1890
|
+
): Uint8Array | undefined {
|
|
1891
|
+
return (
|
|
1892
|
+
nativeDocumentIndex.valuePrefixBytes ??
|
|
1893
|
+
(nativeDocumentIndex.projection
|
|
1894
|
+
? (this.nativeBackboneDocumentProjection?.projectDocumentIndexSimple?.(
|
|
1895
|
+
nativeDocumentIndex.projection.encodedDocument,
|
|
1896
|
+
nativeDocumentIndex.projection.plan,
|
|
1897
|
+
{
|
|
1898
|
+
created: context.created,
|
|
1899
|
+
modified: context.modified,
|
|
1900
|
+
head: context.head,
|
|
1901
|
+
gid: context.gid,
|
|
1902
|
+
size: context.size,
|
|
1903
|
+
signer: nativeDocumentIndex.projection.signer,
|
|
1904
|
+
},
|
|
1905
|
+
) ??
|
|
1906
|
+
tryProjectDocumentIndexSimple(
|
|
1907
|
+
nativeDocumentIndex.projection.encodedDocument,
|
|
1908
|
+
nativeDocumentIndex.projection.plan,
|
|
1909
|
+
{
|
|
1910
|
+
created: context.created,
|
|
1911
|
+
modified: context.modified,
|
|
1912
|
+
head: context.head,
|
|
1913
|
+
gid: context.gid,
|
|
1914
|
+
size: context.size,
|
|
1915
|
+
signer: nativeDocumentIndex.projection.signer,
|
|
1916
|
+
},
|
|
1917
|
+
))
|
|
1918
|
+
: undefined)
|
|
1919
|
+
);
|
|
1920
|
+
}
|
|
1921
|
+
|
|
1922
|
+
private asIndexedTypeValue(value: I): I {
|
|
1923
|
+
if (
|
|
1924
|
+
value &&
|
|
1925
|
+
Object.getPrototypeOf(value) ===
|
|
1926
|
+
(this.indexedType as { prototype: object }).prototype
|
|
1927
|
+
) {
|
|
1928
|
+
return value;
|
|
1929
|
+
}
|
|
1930
|
+
return Object.assign(
|
|
1931
|
+
Object.create((this.indexedType as { prototype: object }).prototype),
|
|
1932
|
+
value,
|
|
1933
|
+
);
|
|
1216
1934
|
}
|
|
1217
1935
|
|
|
1218
1936
|
get prefetch() {
|
|
@@ -1435,14 +2153,14 @@ export class DocumentIndex<
|
|
|
1435
2153
|
if (this._joinListener) {
|
|
1436
2154
|
this._query.events.removeEventListener("join", this._joinListener);
|
|
1437
2155
|
}
|
|
1438
|
-
if (this.handleDocumentChange) {
|
|
2156
|
+
if (this.handleDocumentChange && this.documentEvents) {
|
|
1439
2157
|
this.documentEvents.removeEventListener(
|
|
1440
2158
|
"change",
|
|
1441
2159
|
this.handleDocumentChange,
|
|
1442
2160
|
);
|
|
1443
2161
|
}
|
|
1444
2162
|
this.clearAllResultQueues();
|
|
1445
|
-
await this._resumableIterators
|
|
2163
|
+
await this._resumableIterators?.clearAll();
|
|
1446
2164
|
if (this.iteratorKeepAliveTimers) {
|
|
1447
2165
|
for (const timer of this.iteratorKeepAliveTimers.values()) {
|
|
1448
2166
|
clearTimeout(timer);
|
|
@@ -1466,12 +2184,12 @@ export class DocumentIndex<
|
|
|
1466
2184
|
async drop(from?: Program): Promise<boolean> {
|
|
1467
2185
|
const dropped = await super.drop(from);
|
|
1468
2186
|
if (dropped) {
|
|
1469
|
-
this.documentEvents
|
|
2187
|
+
this.documentEvents?.removeEventListener(
|
|
1470
2188
|
"change",
|
|
1471
2189
|
this.handleDocumentChange,
|
|
1472
2190
|
);
|
|
1473
2191
|
this.clearAllResultQueues();
|
|
1474
|
-
await this._resumableIterators
|
|
2192
|
+
await this._resumableIterators?.clearAll();
|
|
1475
2193
|
if (this.iteratorKeepAliveTimers) {
|
|
1476
2194
|
for (const timer of this.iteratorKeepAliveTimers.values()) {
|
|
1477
2195
|
clearTimeout(timer);
|
|
@@ -1580,59 +2298,687 @@ export class DocumentIndex<
|
|
|
1580
2298
|
}
|
|
1581
2299
|
}
|
|
1582
2300
|
|
|
1583
|
-
// Re-query on peer joins (like iterate), scoped to the joining peer
|
|
1584
|
-
let joinListener: (() => void) | undefined;
|
|
1585
|
-
if (baseRemote) {
|
|
1586
|
-
joinListener = this.createReplicatorJoinListener({
|
|
1587
|
-
eager: baseRemote.reach?.eager,
|
|
1588
|
-
onPeer: async (pk) => {
|
|
1589
|
-
if (cleanedUp) return;
|
|
1590
|
-
const hash = pk.hashcode();
|
|
1591
|
-
const requeryOptions: QueryOptions<T, I, D, Resolve> = {
|
|
1592
|
-
...(options as any),
|
|
1593
|
-
remote: {
|
|
1594
|
-
...(baseRemote || {}),
|
|
1595
|
-
from: [hash],
|
|
1596
|
-
},
|
|
1597
|
-
};
|
|
1598
|
-
const re = await this.getDetailed(idKey, requeryOptions as any);
|
|
1599
|
-
const first = re?.[0]?.results[0];
|
|
1600
|
-
if (first) {
|
|
1601
|
-
deferred!.resolve(first.value as any);
|
|
1602
|
-
}
|
|
2301
|
+
// Re-query on peer joins (like iterate), scoped to the joining peer
|
|
2302
|
+
let joinListener: (() => void) | undefined;
|
|
2303
|
+
if (baseRemote) {
|
|
2304
|
+
joinListener = this.createReplicatorJoinListener({
|
|
2305
|
+
eager: baseRemote.reach?.eager,
|
|
2306
|
+
onPeer: async (pk) => {
|
|
2307
|
+
if (cleanedUp) return;
|
|
2308
|
+
const hash = pk.hashcode();
|
|
2309
|
+
const requeryOptions: QueryOptions<T, I, D, Resolve> = {
|
|
2310
|
+
...(options as any),
|
|
2311
|
+
remote: {
|
|
2312
|
+
...(baseRemote || {}),
|
|
2313
|
+
from: [hash],
|
|
2314
|
+
},
|
|
2315
|
+
};
|
|
2316
|
+
const re = await this.getDetailed(idKey, requeryOptions as any);
|
|
2317
|
+
const first = re?.[0]?.results[0];
|
|
2318
|
+
if (first) {
|
|
2319
|
+
deferred!.resolve(first.value as any);
|
|
2320
|
+
}
|
|
2321
|
+
},
|
|
2322
|
+
});
|
|
2323
|
+
}
|
|
2324
|
+
}
|
|
2325
|
+
|
|
2326
|
+
const initialOptions = baseRemote
|
|
2327
|
+
? ({ ...(options as any), remote: baseRemote } as Options)
|
|
2328
|
+
: options;
|
|
2329
|
+
const result = (await this.getDetailed(idKey, initialOptions))?.[0]
|
|
2330
|
+
?.results[0];
|
|
2331
|
+
|
|
2332
|
+
// if no results, and we have remote joining options, we wait for the timout and if there are joining peers we re-query
|
|
2333
|
+
if (!result) {
|
|
2334
|
+
return deferred?.promise;
|
|
2335
|
+
} else if (deferred) {
|
|
2336
|
+
deferred.resolve(undefined);
|
|
2337
|
+
}
|
|
2338
|
+
return result?.value;
|
|
2339
|
+
}
|
|
2340
|
+
|
|
2341
|
+
public async getFromGid(gid: string) {
|
|
2342
|
+
const iterator = this.index.iterate({ query: { gid } });
|
|
2343
|
+
const one = await iterator.next(1);
|
|
2344
|
+
await iterator.close();
|
|
2345
|
+
return one[0];
|
|
2346
|
+
}
|
|
2347
|
+
|
|
2348
|
+
public async getFromHash(hash: string) {
|
|
2349
|
+
const iterator = this.index.iterate({ query: { hash } });
|
|
2350
|
+
const one = await iterator.next(1);
|
|
2351
|
+
await iterator.close();
|
|
2352
|
+
return one[0];
|
|
2353
|
+
}
|
|
2354
|
+
|
|
2355
|
+
public async getIdentityIndexedByHead(
|
|
2356
|
+
head: string,
|
|
2357
|
+
): Promise<indexerTypes.IndexedResult<WithContext<I>> | undefined> {
|
|
2358
|
+
if (!this.canGetIdentityIndexedByHead()) {
|
|
2359
|
+
return;
|
|
2360
|
+
}
|
|
2361
|
+
return (this.index as ContextHeadIndex<I>).getByContextHead?.(head);
|
|
2362
|
+
}
|
|
2363
|
+
|
|
2364
|
+
public async getIdentityIndexedKeyByHead(
|
|
2365
|
+
head: string,
|
|
2366
|
+
): Promise<indexerTypes.IdKey | undefined> {
|
|
2367
|
+
const key = this.getIndexedKeyByHead(head);
|
|
2368
|
+
if (key) {
|
|
2369
|
+
return key;
|
|
2370
|
+
}
|
|
2371
|
+
const indexed = await this.getIdentityIndexedByHead(head);
|
|
2372
|
+
return indexed?.id;
|
|
2373
|
+
}
|
|
2374
|
+
|
|
2375
|
+
private getIndexedKeyByHead(
|
|
2376
|
+
head: string,
|
|
2377
|
+
): indexerTypes.IdKey | undefined {
|
|
2378
|
+
const getIdByHead = (this.index as ContextHeadIndex<I>).getIdByContextHead;
|
|
2379
|
+
return typeof getIdByHead === "function"
|
|
2380
|
+
? getIdByHead.call(this.index, head)
|
|
2381
|
+
: undefined;
|
|
2382
|
+
}
|
|
2383
|
+
|
|
2384
|
+
public getIndexedKeysByHeads(
|
|
2385
|
+
heads: string[],
|
|
2386
|
+
): Array<indexerTypes.IdKey | undefined> | undefined {
|
|
2387
|
+
const getIdByHead = (this.index as ContextHeadIndex<I>).getIdByContextHead;
|
|
2388
|
+
if (typeof getIdByHead !== "function") {
|
|
2389
|
+
return;
|
|
2390
|
+
}
|
|
2391
|
+
return heads.map((head) => getIdByHead.call(this.index, head));
|
|
2392
|
+
}
|
|
2393
|
+
|
|
2394
|
+
public tryGetIdentityIndexedKeyByHead(
|
|
2395
|
+
head: string,
|
|
2396
|
+
): { supported: boolean; key?: indexerTypes.IdKey } {
|
|
2397
|
+
const getIdByHead = (this.index as ContextHeadIndex<I>).getIdByContextHead;
|
|
2398
|
+
if (typeof getIdByHead !== "function") {
|
|
2399
|
+
return { supported: false };
|
|
2400
|
+
}
|
|
2401
|
+
return {
|
|
2402
|
+
supported: true,
|
|
2403
|
+
key: getIdByHead.call(this.index, head),
|
|
2404
|
+
};
|
|
2405
|
+
}
|
|
2406
|
+
|
|
2407
|
+
public async getIdentityIndexedByHeads(
|
|
2408
|
+
heads: string[],
|
|
2409
|
+
): Promise<
|
|
2410
|
+
Array<indexerTypes.IndexedResult<WithContext<I>> | undefined> | undefined
|
|
2411
|
+
> {
|
|
2412
|
+
if (!this.canGetIdentityIndexedByHead()) {
|
|
2413
|
+
return;
|
|
2414
|
+
}
|
|
2415
|
+
const batch = (this.index as ContextHeadIndex<I>).getByContextHeadBatch;
|
|
2416
|
+
if (batch) {
|
|
2417
|
+
return batch.call(this.index, heads);
|
|
2418
|
+
}
|
|
2419
|
+
return Promise.all(
|
|
2420
|
+
heads.map((head) => this.getIdentityIndexedByHead(head)),
|
|
2421
|
+
);
|
|
2422
|
+
}
|
|
2423
|
+
|
|
2424
|
+
public canGetIdentityIndexedByHead(): boolean {
|
|
2425
|
+
return (
|
|
2426
|
+
this.transformerIsIdentity &&
|
|
2427
|
+
this.indexedTypeIsDocumentType &&
|
|
2428
|
+
!this.isProgramValued &&
|
|
2429
|
+
typeof (this.index as ContextHeadIndex<I>).getByContextHead === "function"
|
|
2430
|
+
);
|
|
2431
|
+
}
|
|
2432
|
+
|
|
2433
|
+
public canGetIndexedKeyByHead(): boolean {
|
|
2434
|
+
return (
|
|
2435
|
+
!this.isProgramValued &&
|
|
2436
|
+
typeof (this.index as ContextHeadIndex<I>).getIdByContextHead ===
|
|
2437
|
+
"function"
|
|
2438
|
+
);
|
|
2439
|
+
}
|
|
2440
|
+
|
|
2441
|
+
public canReadOriginalFieldPathsFromIndexedValue(
|
|
2442
|
+
paths: readonly (string | readonly string[])[],
|
|
2443
|
+
): boolean {
|
|
2444
|
+
return (
|
|
2445
|
+
!this.isProgramValued &&
|
|
2446
|
+
paths.every((path) =>
|
|
2447
|
+
this.transformerIsIdentity && this.indexedTypeIsDocumentType
|
|
2448
|
+
? true
|
|
2449
|
+
: documentTransformPreservesFieldPath(
|
|
2450
|
+
this.nativeTransformDescriptor,
|
|
2451
|
+
path,
|
|
2452
|
+
),
|
|
2453
|
+
)
|
|
2454
|
+
);
|
|
2455
|
+
}
|
|
2456
|
+
|
|
2457
|
+
public canReadNativeIndexedFieldValues(
|
|
2458
|
+
paths: readonly (string | readonly string[])[],
|
|
2459
|
+
): boolean {
|
|
2460
|
+
return (
|
|
2461
|
+
this.canReadOriginalFieldPathsFromIndexedValue(paths) &&
|
|
2462
|
+
typeof (
|
|
2463
|
+
this.index as {
|
|
2464
|
+
getNativeIndexedFieldValue?: (
|
|
2465
|
+
id: indexerTypes.IdKey,
|
|
2466
|
+
path: readonly string[],
|
|
2467
|
+
) => unknown;
|
|
2468
|
+
}
|
|
2469
|
+
).getNativeIndexedFieldValue === "function"
|
|
2470
|
+
);
|
|
2471
|
+
}
|
|
2472
|
+
|
|
2473
|
+
public getNativeIndexedFieldValue(
|
|
2474
|
+
id: indexerTypes.IdKey,
|
|
2475
|
+
path: string | readonly string[],
|
|
2476
|
+
): unknown {
|
|
2477
|
+
const read = (
|
|
2478
|
+
this.index as {
|
|
2479
|
+
getNativeIndexedFieldValue?: (
|
|
2480
|
+
id: indexerTypes.IdKey,
|
|
2481
|
+
path: readonly string[],
|
|
2482
|
+
) => unknown;
|
|
2483
|
+
}
|
|
2484
|
+
).getNativeIndexedFieldValue;
|
|
2485
|
+
if (typeof read !== "function") {
|
|
2486
|
+
return undefined;
|
|
2487
|
+
}
|
|
2488
|
+
return read.call(this.index, id, typeof path === "string" ? [path] : path);
|
|
2489
|
+
}
|
|
2490
|
+
|
|
2491
|
+
public _putIdentityWithContext(
|
|
2492
|
+
value: T,
|
|
2493
|
+
id: indexerTypes.IdKey,
|
|
2494
|
+
context: types.Context,
|
|
2495
|
+
options?: ContextualPutOptions,
|
|
2496
|
+
): MaybePromise<WithIndexedContext<T, I> | undefined> {
|
|
2497
|
+
const contextualPut = this.transformerIsIdentity
|
|
2498
|
+
? (this.index as ContextualIndexPut<I>).putWithContext
|
|
2499
|
+
: undefined;
|
|
2500
|
+
if (!contextualPut || this.isProgramValued) {
|
|
2501
|
+
return;
|
|
2502
|
+
}
|
|
2503
|
+
const indexable = value as any as I;
|
|
2504
|
+
const indexedValue = coerceWithIndexed(
|
|
2505
|
+
coerceWithContext(value, context),
|
|
2506
|
+
indexable,
|
|
2507
|
+
);
|
|
2508
|
+
this.cacheResolvedValue(id.primitive, value);
|
|
2509
|
+
const handleError = (error: unknown) => {
|
|
2510
|
+
if (error instanceof indexerTypes.NotStartedError && this.closed) {
|
|
2511
|
+
return indexedValue;
|
|
2512
|
+
}
|
|
2513
|
+
throw error;
|
|
2514
|
+
};
|
|
2515
|
+
try {
|
|
2516
|
+
const putResult = contextualPut.call(
|
|
2517
|
+
this.index,
|
|
2518
|
+
indexable,
|
|
2519
|
+
id,
|
|
2520
|
+
context,
|
|
2521
|
+
this.withContextualEncodedValue(options, context),
|
|
2522
|
+
);
|
|
2523
|
+
return isPromiseLike(putResult)
|
|
2524
|
+
? putResult.then(() => indexedValue, handleError)
|
|
2525
|
+
: indexedValue;
|
|
2526
|
+
} catch (error) {
|
|
2527
|
+
return handleError(error);
|
|
2528
|
+
}
|
|
2529
|
+
}
|
|
2530
|
+
|
|
2531
|
+
public _putStoredIdentityWithContext(
|
|
2532
|
+
value: T,
|
|
2533
|
+
id: indexerTypes.IdKey,
|
|
2534
|
+
context: types.Context,
|
|
2535
|
+
encodedValueParts: NonNullable<ContextualPutOptions["encodedValueParts"]>,
|
|
2536
|
+
options?: { replace?: boolean },
|
|
2537
|
+
): MaybePromise<WithIndexedContext<T, I> | undefined> {
|
|
2538
|
+
const contextualStoredPut = this.transformerIsIdentity
|
|
2539
|
+
? (this.index as ContextualIndexPut<I>).putStoredContextualEncodedValue
|
|
2540
|
+
: undefined;
|
|
2541
|
+
if (
|
|
2542
|
+
!contextualStoredPut ||
|
|
2543
|
+
this.isProgramValued ||
|
|
2544
|
+
!this.indexedTypeIsDocumentType
|
|
2545
|
+
) {
|
|
2546
|
+
return;
|
|
2547
|
+
}
|
|
2548
|
+
const indexable = value as any as I;
|
|
2549
|
+
const indexedValue = coerceWithIndexed(
|
|
2550
|
+
coerceWithContext(value, context),
|
|
2551
|
+
indexable,
|
|
2552
|
+
);
|
|
2553
|
+
this.cacheResolvedValue(id.primitive, value);
|
|
2554
|
+
const handleError = (error: unknown) => {
|
|
2555
|
+
if (error instanceof indexerTypes.NotStartedError && this.closed) {
|
|
2556
|
+
return indexedValue;
|
|
2557
|
+
}
|
|
2558
|
+
throw error;
|
|
2559
|
+
};
|
|
2560
|
+
try {
|
|
2561
|
+
const putResult = contextualStoredPut.call(
|
|
2562
|
+
this.index,
|
|
2563
|
+
id,
|
|
2564
|
+
encodedValueParts,
|
|
2565
|
+
options,
|
|
2566
|
+
);
|
|
2567
|
+
if (putResult === false) {
|
|
2568
|
+
return;
|
|
2569
|
+
}
|
|
2570
|
+
return isPromiseLike(putResult)
|
|
2571
|
+
? putResult.then(() => indexedValue, handleError)
|
|
2572
|
+
: indexedValue;
|
|
2573
|
+
} catch (error) {
|
|
2574
|
+
return handleError(error);
|
|
2575
|
+
}
|
|
2576
|
+
}
|
|
2577
|
+
|
|
2578
|
+
private _putPreparedNativeBackboneDocumentIndexWithContext(
|
|
2579
|
+
value: T,
|
|
2580
|
+
id: indexerTypes.IdKey,
|
|
2581
|
+
context: types.Context,
|
|
2582
|
+
nativeDocumentIndex: NativeBackboneDocumentIndexCommit<I>,
|
|
2583
|
+
options?: { replace?: boolean },
|
|
2584
|
+
): MaybePromise<WithIndexedContext<T, I> | undefined> {
|
|
2585
|
+
const contextualStoredPut = (this.index as ContextualIndexPut<I>)
|
|
2586
|
+
.putStoredContextualEncodedValue;
|
|
2587
|
+
if (!contextualStoredPut || this.isProgramValued) {
|
|
2588
|
+
return;
|
|
2589
|
+
}
|
|
2590
|
+
nativeDocumentIndex.setContext?.(context);
|
|
2591
|
+
const valueWithContext = coerceWithContext(value, context);
|
|
2592
|
+
const indexedValue = nativeDocumentIndex.indexable
|
|
2593
|
+
? coerceWithIndexed(valueWithContext, nativeDocumentIndex.indexable)
|
|
2594
|
+
: nativeDocumentIndex.getIndexable
|
|
2595
|
+
? coerceWithLazyIndexed(
|
|
2596
|
+
valueWithContext,
|
|
2597
|
+
nativeDocumentIndex.getIndexable,
|
|
2598
|
+
)
|
|
2599
|
+
: undefined;
|
|
2600
|
+
if (!indexedValue) {
|
|
2601
|
+
return;
|
|
2602
|
+
}
|
|
2603
|
+
this.cacheResolvedValue(id.primitive, value);
|
|
2604
|
+
const valuePrefixBytes = this.nativeBackboneDocumentIndexValuePrefixBytes(
|
|
2605
|
+
nativeDocumentIndex,
|
|
2606
|
+
context,
|
|
2607
|
+
);
|
|
2608
|
+
if (!valuePrefixBytes) {
|
|
2609
|
+
return;
|
|
2610
|
+
}
|
|
2611
|
+
const encodedValueParts = {
|
|
2612
|
+
prefix: valuePrefixBytes,
|
|
2613
|
+
suffix: encodeContextSuffix(context),
|
|
2614
|
+
};
|
|
2615
|
+
const handleError = (error: unknown) => {
|
|
2616
|
+
if (error instanceof indexerTypes.NotStartedError && this.closed) {
|
|
2617
|
+
return indexedValue;
|
|
2618
|
+
}
|
|
2619
|
+
throw error;
|
|
2620
|
+
};
|
|
2621
|
+
try {
|
|
2622
|
+
const putResult = contextualStoredPut.call(
|
|
2623
|
+
this.index,
|
|
2624
|
+
id,
|
|
2625
|
+
encodedValueParts,
|
|
2626
|
+
options,
|
|
2627
|
+
);
|
|
2628
|
+
if (putResult === false) {
|
|
2629
|
+
return;
|
|
2630
|
+
}
|
|
2631
|
+
return isPromiseLike(putResult)
|
|
2632
|
+
? putResult.then(() => indexedValue, handleError)
|
|
2633
|
+
: indexedValue;
|
|
2634
|
+
} catch (error) {
|
|
2635
|
+
return handleError(error);
|
|
2636
|
+
}
|
|
2637
|
+
}
|
|
2638
|
+
|
|
2639
|
+
private _putPreparedNativeBackboneDocumentIndexStoredWithContext(
|
|
2640
|
+
id: indexerTypes.IdKey,
|
|
2641
|
+
context: types.Context,
|
|
2642
|
+
nativeDocumentIndex: NativeBackboneDocumentIndexCommit<I>,
|
|
2643
|
+
options?: { replace?: boolean },
|
|
2644
|
+
): MaybePromise<boolean | undefined> {
|
|
2645
|
+
const contextualStoredPut = (this.index as ContextualIndexPut<I>)
|
|
2646
|
+
.putStoredContextualEncodedValue;
|
|
2647
|
+
if (!contextualStoredPut || this.isProgramValued) {
|
|
2648
|
+
return;
|
|
2649
|
+
}
|
|
2650
|
+
nativeDocumentIndex.setContext?.(context);
|
|
2651
|
+
const valuePrefixBytes = this.nativeBackboneDocumentIndexValuePrefixBytes(
|
|
2652
|
+
nativeDocumentIndex,
|
|
2653
|
+
context,
|
|
2654
|
+
);
|
|
2655
|
+
if (!valuePrefixBytes) {
|
|
2656
|
+
return;
|
|
2657
|
+
}
|
|
2658
|
+
const encodedValueParts = {
|
|
2659
|
+
prefix: valuePrefixBytes,
|
|
2660
|
+
suffix: encodeContextSuffix(context),
|
|
2661
|
+
};
|
|
2662
|
+
const handleError = (error: unknown) => {
|
|
2663
|
+
if (error instanceof indexerTypes.NotStartedError && this.closed) {
|
|
2664
|
+
return true;
|
|
2665
|
+
}
|
|
2666
|
+
throw error;
|
|
2667
|
+
};
|
|
2668
|
+
try {
|
|
2669
|
+
const putResult = contextualStoredPut.call(
|
|
2670
|
+
this.index,
|
|
2671
|
+
id,
|
|
2672
|
+
encodedValueParts,
|
|
2673
|
+
options,
|
|
2674
|
+
);
|
|
2675
|
+
if (putResult === false) {
|
|
2676
|
+
return false;
|
|
2677
|
+
}
|
|
2678
|
+
return isPromiseLike(putResult)
|
|
2679
|
+
? putResult.then(() => true, handleError)
|
|
2680
|
+
: true;
|
|
2681
|
+
} catch (error) {
|
|
2682
|
+
return handleError(error);
|
|
2683
|
+
}
|
|
2684
|
+
}
|
|
2685
|
+
|
|
2686
|
+
private _persistPreparedNativeBackboneDocumentIndexStoredWithContext(
|
|
2687
|
+
id: indexerTypes.IdKey,
|
|
2688
|
+
context: types.Context,
|
|
2689
|
+
nativeDocumentIndex?: NativeBackboneDocumentIndexCommit<I>,
|
|
2690
|
+
encodedValueParts?: NonNullable<ContextualPutOptions["encodedValueParts"]>,
|
|
2691
|
+
options?: { replace?: boolean },
|
|
2692
|
+
): MaybePromise<boolean | undefined> {
|
|
2693
|
+
const persistStoredPut = (this.index as ContextualIndexPut<I>)
|
|
2694
|
+
.persistStoredContextualEncodedValue;
|
|
2695
|
+
if (!persistStoredPut || this.isProgramValued) {
|
|
2696
|
+
return;
|
|
2697
|
+
}
|
|
2698
|
+
let storedParts:
|
|
2699
|
+
| NonNullable<ContextualPutOptions["encodedValueParts"]>
|
|
2700
|
+
| undefined;
|
|
2701
|
+
if (
|
|
2702
|
+
this.transformerIsIdentity &&
|
|
2703
|
+
this.indexedTypeIsDocumentType &&
|
|
2704
|
+
encodedValueParts
|
|
2705
|
+
) {
|
|
2706
|
+
storedParts = encodedValueParts;
|
|
2707
|
+
} else if (nativeDocumentIndex) {
|
|
2708
|
+
nativeDocumentIndex.setContext?.(context);
|
|
2709
|
+
const valuePrefixBytes =
|
|
2710
|
+
nativeDocumentIndex.valuePrefixBytes ??
|
|
2711
|
+
this.nativeBackboneDocumentIndexValuePrefixBytes(
|
|
2712
|
+
nativeDocumentIndex,
|
|
2713
|
+
context,
|
|
2714
|
+
);
|
|
2715
|
+
if (!valuePrefixBytes) {
|
|
2716
|
+
return;
|
|
2717
|
+
}
|
|
2718
|
+
storedParts = {
|
|
2719
|
+
prefix: valuePrefixBytes,
|
|
2720
|
+
suffix: encodeContextSuffix(context),
|
|
2721
|
+
};
|
|
2722
|
+
} else {
|
|
2723
|
+
return;
|
|
2724
|
+
}
|
|
2725
|
+
try {
|
|
2726
|
+
const persistResult = persistStoredPut.call(
|
|
2727
|
+
this.index,
|
|
2728
|
+
id,
|
|
2729
|
+
storedParts,
|
|
2730
|
+
options,
|
|
2731
|
+
);
|
|
2732
|
+
if (persistResult === false) {
|
|
2733
|
+
return false;
|
|
2734
|
+
}
|
|
2735
|
+
return isPromiseLike(persistResult)
|
|
2736
|
+
? persistResult.then(() => true, (error: unknown) => {
|
|
2737
|
+
if (error instanceof indexerTypes.NotStartedError && this.closed) {
|
|
2738
|
+
return true;
|
|
2739
|
+
}
|
|
2740
|
+
throw error;
|
|
2741
|
+
})
|
|
2742
|
+
: true;
|
|
2743
|
+
} catch (error) {
|
|
2744
|
+
if (error instanceof indexerTypes.NotStartedError && this.closed) {
|
|
2745
|
+
return true;
|
|
2746
|
+
}
|
|
2747
|
+
throw error;
|
|
2748
|
+
}
|
|
2749
|
+
}
|
|
2750
|
+
|
|
2751
|
+
private async _putManyPreparedNativeBackboneDocumentIndexWithContext(
|
|
2752
|
+
values: Array<{
|
|
2753
|
+
value: T;
|
|
2754
|
+
id: indexerTypes.IdKey;
|
|
2755
|
+
context: types.Context;
|
|
2756
|
+
nativeDocumentIndex?: NativeBackboneDocumentIndexCommit<I>;
|
|
2757
|
+
options?: { replace?: boolean };
|
|
2758
|
+
}>,
|
|
2759
|
+
): Promise<WithIndexedContext<T, I>[] | undefined> {
|
|
2760
|
+
if (values.length === 0) {
|
|
2761
|
+
return [];
|
|
2762
|
+
}
|
|
2763
|
+
const contextualBatchPut = (this.index as ContextualIndexPut<I>)
|
|
2764
|
+
.putWithContextBatch;
|
|
2765
|
+
if (!contextualBatchPut || this.isProgramValued) {
|
|
2766
|
+
return;
|
|
2767
|
+
}
|
|
2768
|
+
const indexedValues: WithIndexedContext<T, I>[] = [];
|
|
2769
|
+
const batchValues: Array<{
|
|
2770
|
+
value: I;
|
|
2771
|
+
id: indexerTypes.IdKey;
|
|
2772
|
+
context: types.Context;
|
|
2773
|
+
options: ContextualPutOptions;
|
|
2774
|
+
}> = [];
|
|
2775
|
+
for (const item of values) {
|
|
2776
|
+
if (!item.nativeDocumentIndex) {
|
|
2777
|
+
return;
|
|
2778
|
+
}
|
|
2779
|
+
item.nativeDocumentIndex.setContext?.(item.context);
|
|
2780
|
+
const valueWithContext = coerceWithContext(item.value, item.context);
|
|
2781
|
+
const indexedValue = item.nativeDocumentIndex.indexable
|
|
2782
|
+
? coerceWithIndexed(
|
|
2783
|
+
valueWithContext,
|
|
2784
|
+
item.nativeDocumentIndex.indexable,
|
|
2785
|
+
)
|
|
2786
|
+
: item.nativeDocumentIndex.getIndexable
|
|
2787
|
+
? coerceWithLazyIndexed(
|
|
2788
|
+
valueWithContext,
|
|
2789
|
+
item.nativeDocumentIndex.getIndexable,
|
|
2790
|
+
)
|
|
2791
|
+
: undefined;
|
|
2792
|
+
if (!indexedValue) {
|
|
2793
|
+
return;
|
|
2794
|
+
}
|
|
2795
|
+
const valuePrefixBytes = this.nativeBackboneDocumentIndexValuePrefixBytes(
|
|
2796
|
+
item.nativeDocumentIndex,
|
|
2797
|
+
item.context,
|
|
2798
|
+
);
|
|
2799
|
+
if (!valuePrefixBytes) {
|
|
2800
|
+
return;
|
|
2801
|
+
}
|
|
2802
|
+
this.cacheResolvedValue(item.id.primitive, item.value);
|
|
2803
|
+
indexedValues.push(indexedValue);
|
|
2804
|
+
batchValues.push({
|
|
2805
|
+
// Encoded native batches store from encodedValueParts; descriptor
|
|
2806
|
+
// projections keep the JS indexable lazy for event consumers.
|
|
2807
|
+
value:
|
|
2808
|
+
item.nativeDocumentIndex.indexable ?? (undefined as unknown as I),
|
|
2809
|
+
id: item.id,
|
|
2810
|
+
context: item.context,
|
|
2811
|
+
options: {
|
|
2812
|
+
replace: item.options?.replace,
|
|
2813
|
+
encodedValueParts: {
|
|
2814
|
+
prefix: valuePrefixBytes,
|
|
2815
|
+
suffix: encodeContextSuffix(item.context),
|
|
1603
2816
|
},
|
|
1604
|
-
}
|
|
2817
|
+
},
|
|
2818
|
+
});
|
|
2819
|
+
}
|
|
2820
|
+
const handleError = (error: unknown) => {
|
|
2821
|
+
if (error instanceof indexerTypes.NotStartedError && this.closed) {
|
|
2822
|
+
return indexedValues;
|
|
1605
2823
|
}
|
|
2824
|
+
throw error;
|
|
2825
|
+
};
|
|
2826
|
+
try {
|
|
2827
|
+
const putResult = contextualBatchPut.call(this.index, batchValues);
|
|
2828
|
+
return isPromiseLike(putResult)
|
|
2829
|
+
? putResult.then(() => indexedValues, handleError)
|
|
2830
|
+
: indexedValues;
|
|
2831
|
+
} catch (error) {
|
|
2832
|
+
return handleError(error);
|
|
1606
2833
|
}
|
|
2834
|
+
}
|
|
1607
2835
|
|
|
1608
|
-
|
|
1609
|
-
|
|
1610
|
-
:
|
|
1611
|
-
|
|
1612
|
-
|
|
1613
|
-
|
|
1614
|
-
|
|
1615
|
-
|
|
1616
|
-
|
|
1617
|
-
|
|
1618
|
-
|
|
2836
|
+
private async _putManyPreparedNativeBackboneDocumentIndexStored(
|
|
2837
|
+
values: Array<{
|
|
2838
|
+
value: T;
|
|
2839
|
+
id: indexerTypes.IdKey;
|
|
2840
|
+
context: types.Context;
|
|
2841
|
+
encodedValueParts?: NonNullable<
|
|
2842
|
+
ContextualPutOptions["encodedValueParts"]
|
|
2843
|
+
>;
|
|
2844
|
+
nativeDocumentIndex?: NativeBackboneDocumentIndexCommit<I>;
|
|
2845
|
+
options?: { replace?: boolean };
|
|
2846
|
+
}>,
|
|
2847
|
+
): Promise<boolean | undefined> {
|
|
2848
|
+
if (values.length === 0) {
|
|
2849
|
+
return true;
|
|
2850
|
+
}
|
|
2851
|
+
if (this.isProgramValued) {
|
|
2852
|
+
return;
|
|
2853
|
+
}
|
|
2854
|
+
const storedBatchPut = (this.index as ContextualIndexPut<I>)
|
|
2855
|
+
.putStoredContextualEncodedValueBatch;
|
|
2856
|
+
if (!storedBatchPut) {
|
|
2857
|
+
return;
|
|
2858
|
+
}
|
|
2859
|
+
const batchValues: Array<{
|
|
2860
|
+
id: indexerTypes.IdKey;
|
|
2861
|
+
encodedValueParts: NonNullable<
|
|
2862
|
+
ContextualPutOptions["encodedValueParts"]
|
|
2863
|
+
>;
|
|
2864
|
+
options?: { replace?: boolean };
|
|
2865
|
+
}> = [];
|
|
2866
|
+
for (const item of values) {
|
|
2867
|
+
let encodedValueParts:
|
|
2868
|
+
| NonNullable<ContextualPutOptions["encodedValueParts"]>
|
|
2869
|
+
| undefined;
|
|
2870
|
+
if (
|
|
2871
|
+
this.transformerIsIdentity &&
|
|
2872
|
+
this.indexedTypeIsDocumentType &&
|
|
2873
|
+
item.encodedValueParts
|
|
2874
|
+
) {
|
|
2875
|
+
encodedValueParts = item.encodedValueParts;
|
|
2876
|
+
} else if (item.nativeDocumentIndex) {
|
|
2877
|
+
item.nativeDocumentIndex.setContext?.(item.context);
|
|
2878
|
+
const valuePrefixBytes =
|
|
2879
|
+
item.nativeDocumentIndex.valuePrefixBytes ??
|
|
2880
|
+
this.nativeBackboneDocumentIndexValuePrefixBytes(
|
|
2881
|
+
item.nativeDocumentIndex,
|
|
2882
|
+
item.context,
|
|
2883
|
+
);
|
|
2884
|
+
if (!valuePrefixBytes) {
|
|
2885
|
+
return;
|
|
2886
|
+
}
|
|
2887
|
+
encodedValueParts = {
|
|
2888
|
+
prefix: valuePrefixBytes,
|
|
2889
|
+
suffix: encodeContextSuffix(item.context),
|
|
2890
|
+
};
|
|
2891
|
+
} else {
|
|
2892
|
+
return;
|
|
2893
|
+
}
|
|
2894
|
+
this.cacheResolvedValue(item.id.primitive, item.value);
|
|
2895
|
+
batchValues.push({
|
|
2896
|
+
id: item.id,
|
|
2897
|
+
encodedValueParts,
|
|
2898
|
+
options: item.options,
|
|
2899
|
+
});
|
|
2900
|
+
}
|
|
2901
|
+
const handleError = (error: unknown) => {
|
|
2902
|
+
if (error instanceof indexerTypes.NotStartedError && this.closed) {
|
|
2903
|
+
return true;
|
|
2904
|
+
}
|
|
2905
|
+
throw error;
|
|
2906
|
+
};
|
|
2907
|
+
try {
|
|
2908
|
+
return await storedBatchPut.call(this.index, batchValues);
|
|
2909
|
+
} catch (error) {
|
|
2910
|
+
return handleError(error);
|
|
1619
2911
|
}
|
|
1620
|
-
return result?.value;
|
|
1621
2912
|
}
|
|
1622
2913
|
|
|
1623
|
-
public async
|
|
1624
|
-
|
|
1625
|
-
|
|
1626
|
-
|
|
1627
|
-
|
|
1628
|
-
|
|
2914
|
+
public async _putManyIdentityWithContext(
|
|
2915
|
+
values: Array<{
|
|
2916
|
+
value: T;
|
|
2917
|
+
id: indexerTypes.IdKey;
|
|
2918
|
+
context: types.Context;
|
|
2919
|
+
options?: ContextualPutOptions;
|
|
2920
|
+
}>,
|
|
2921
|
+
): Promise<WithIndexedContext<T, I>[] | undefined> {
|
|
2922
|
+
if (values.length === 0) {
|
|
2923
|
+
return [];
|
|
2924
|
+
}
|
|
2925
|
+
const contextualPut = this.transformerIsIdentity
|
|
2926
|
+
? (this.index as ContextualIndexPut<I>).putWithContext
|
|
2927
|
+
: undefined;
|
|
2928
|
+
const contextualBatchPut = this.transformerIsIdentity
|
|
2929
|
+
? (this.index as ContextualIndexPut<I>).putWithContextBatch
|
|
2930
|
+
: undefined;
|
|
2931
|
+
if ((!contextualBatchPut && !contextualPut) || this.isProgramValued) {
|
|
2932
|
+
return;
|
|
2933
|
+
}
|
|
1629
2934
|
|
|
1630
|
-
|
|
1631
|
-
|
|
1632
|
-
|
|
1633
|
-
|
|
1634
|
-
|
|
2935
|
+
const indexedValues = values.map((item) => {
|
|
2936
|
+
const indexable = item.value as any as I;
|
|
2937
|
+
this.cacheResolvedValue(item.id.primitive, item.value);
|
|
2938
|
+
return {
|
|
2939
|
+
indexable,
|
|
2940
|
+
value: coerceWithIndexed(
|
|
2941
|
+
coerceWithContext(item.value, item.context),
|
|
2942
|
+
indexable,
|
|
2943
|
+
),
|
|
2944
|
+
};
|
|
2945
|
+
});
|
|
2946
|
+
|
|
2947
|
+
try {
|
|
2948
|
+
if (contextualBatchPut) {
|
|
2949
|
+
await contextualBatchPut.call(
|
|
2950
|
+
this.index,
|
|
2951
|
+
values.map((item, index) => ({
|
|
2952
|
+
value: indexedValues[index]!.indexable,
|
|
2953
|
+
id: item.id,
|
|
2954
|
+
context: item.context,
|
|
2955
|
+
options: this.withContextualEncodedValue(
|
|
2956
|
+
item.options,
|
|
2957
|
+
item.context,
|
|
2958
|
+
),
|
|
2959
|
+
})),
|
|
2960
|
+
);
|
|
2961
|
+
} else {
|
|
2962
|
+
for (let i = 0; i < values.length; i++) {
|
|
2963
|
+
const item = values[i]!;
|
|
2964
|
+
await contextualPut!.call(
|
|
2965
|
+
this.index,
|
|
2966
|
+
indexedValues[i]!.indexable,
|
|
2967
|
+
item.id,
|
|
2968
|
+
item.context,
|
|
2969
|
+
this.withContextualEncodedValue(item.options, item.context),
|
|
2970
|
+
);
|
|
2971
|
+
}
|
|
2972
|
+
}
|
|
2973
|
+
} catch (error) {
|
|
2974
|
+
if (error instanceof indexerTypes.NotStartedError && this.closed) {
|
|
2975
|
+
return indexedValues.map((item) => item.value);
|
|
2976
|
+
}
|
|
2977
|
+
throw error;
|
|
2978
|
+
}
|
|
2979
|
+
return indexedValues.map((item) => item.value);
|
|
1635
2980
|
}
|
|
2981
|
+
|
|
1636
2982
|
public async put(
|
|
1637
2983
|
value: T,
|
|
1638
2984
|
id: indexerTypes.IdKey,
|
|
@@ -1660,6 +3006,7 @@ export class DocumentIndex<
|
|
|
1660
3006
|
});
|
|
1661
3007
|
return this.putWithContext(value, id, context, {
|
|
1662
3008
|
replace: existingDefined != null,
|
|
3009
|
+
transformFacts: { entryPublicKeys: entry.publicKeys },
|
|
1663
3010
|
});
|
|
1664
3011
|
}
|
|
1665
3012
|
|
|
@@ -1667,37 +3014,49 @@ export class DocumentIndex<
|
|
|
1667
3014
|
value: T,
|
|
1668
3015
|
id: indexerTypes.IdKey,
|
|
1669
3016
|
context: types.Context,
|
|
1670
|
-
options?:
|
|
3017
|
+
options?: ContextualPutOptions,
|
|
1671
3018
|
): Promise<{ context: types.Context; indexable: I }> {
|
|
1672
3019
|
const idString = id.primitive;
|
|
1673
|
-
|
|
1674
|
-
|
|
1675
|
-
|
|
1676
|
-
|
|
1677
|
-
(value as Program).closed === false &&
|
|
1678
|
-
(value as Program).parents.includes(this._log) */
|
|
1679
|
-
) {
|
|
1680
|
-
// TODO make last condition more efficient if there are many docs
|
|
1681
|
-
this._resolverProgramCache!.set(idString, value);
|
|
1682
|
-
indexCacheLogger("cache:set:program", { id: idString });
|
|
1683
|
-
} else {
|
|
1684
|
-
if (this._resolverCache) {
|
|
1685
|
-
this._resolverCache.add(idString, value);
|
|
1686
|
-
indexCacheLogger("cache:set:value", { id: idString });
|
|
1687
|
-
}
|
|
1688
|
-
}
|
|
1689
|
-
const valueToIndex = await this.transformer(value, context);
|
|
1690
|
-
const wrappedValueToIndex = new this.wrappedIndexedType(
|
|
1691
|
-
valueToIndex as I,
|
|
1692
|
-
context,
|
|
1693
|
-
);
|
|
3020
|
+
this.cacheResolvedValue(idString, value);
|
|
3021
|
+
const valueToIndex = this.transformerIsIdentity
|
|
3022
|
+
? (value as any as I)
|
|
3023
|
+
: await this.transformer(value, context, options?.transformFacts);
|
|
1694
3024
|
|
|
1695
3025
|
coerceWithIndexed(value, valueToIndex);
|
|
1696
3026
|
|
|
1697
3027
|
coerceWithContext(value, context);
|
|
1698
3028
|
|
|
1699
3029
|
try {
|
|
1700
|
-
|
|
3030
|
+
const contextualPut = this.transformerIsIdentity
|
|
3031
|
+
? (this.index as ContextualIndexPut<I>).putWithContext
|
|
3032
|
+
: undefined;
|
|
3033
|
+
if (contextualPut) {
|
|
3034
|
+
const encodedValueParts = this.encodeContextualIndexedValueParts(
|
|
3035
|
+
options?.encodedValue,
|
|
3036
|
+
context,
|
|
3037
|
+
);
|
|
3038
|
+
await contextualPut.call(
|
|
3039
|
+
this.index,
|
|
3040
|
+
valueToIndex,
|
|
3041
|
+
id,
|
|
3042
|
+
context,
|
|
3043
|
+
encodedValueParts
|
|
3044
|
+
? { ...options, encodedValue: undefined, encodedValueParts }
|
|
3045
|
+
: options?.encodedValue
|
|
3046
|
+
? { ...options, encodedValue: undefined }
|
|
3047
|
+
: options,
|
|
3048
|
+
);
|
|
3049
|
+
} else {
|
|
3050
|
+
const wrappedValueToIndex = new this.wrappedIndexedType(
|
|
3051
|
+
valueToIndex as I,
|
|
3052
|
+
context,
|
|
3053
|
+
);
|
|
3054
|
+
await this.index.put(
|
|
3055
|
+
wrappedValueToIndex,
|
|
3056
|
+
id,
|
|
3057
|
+
stripEncodedValue(options),
|
|
3058
|
+
);
|
|
3059
|
+
}
|
|
1701
3060
|
} catch (error) {
|
|
1702
3061
|
if (error instanceof indexerTypes.NotStartedError && this.closed) {
|
|
1703
3062
|
return { context, indexable: valueToIndex };
|
|
@@ -1707,20 +3066,211 @@ export class DocumentIndex<
|
|
|
1707
3066
|
return { context, indexable: valueToIndex };
|
|
1708
3067
|
}
|
|
1709
3068
|
|
|
1710
|
-
public
|
|
1711
|
-
|
|
1712
|
-
|
|
1713
|
-
|
|
3069
|
+
public async putManyWithContext(
|
|
3070
|
+
values: Array<{
|
|
3071
|
+
value: T;
|
|
3072
|
+
id: indexerTypes.IdKey;
|
|
3073
|
+
context: types.Context;
|
|
3074
|
+
options?: ContextualPutOptions;
|
|
3075
|
+
}>,
|
|
3076
|
+
): Promise<Array<{ context: types.Context; indexable: I }>> {
|
|
3077
|
+
if (values.length === 0) {
|
|
3078
|
+
return [];
|
|
3079
|
+
}
|
|
3080
|
+
let transformed: Array<
|
|
3081
|
+
(typeof values)[number] & {
|
|
3082
|
+
indexable: I;
|
|
3083
|
+
}
|
|
3084
|
+
>;
|
|
3085
|
+
if (this.transformerIsIdentity) {
|
|
3086
|
+
transformed = new Array(values.length);
|
|
3087
|
+
for (let i = 0; i < values.length; i++) {
|
|
3088
|
+
const item = values[i]!;
|
|
3089
|
+
this.cacheResolvedValue(item.id.primitive, item.value);
|
|
3090
|
+
const indexable = item.value as any as I;
|
|
3091
|
+
coerceWithIndexed(item.value, indexable);
|
|
3092
|
+
coerceWithContext(item.value, item.context);
|
|
3093
|
+
transformed[i] = { ...item, indexable };
|
|
3094
|
+
}
|
|
1714
3095
|
} else {
|
|
1715
|
-
|
|
1716
|
-
|
|
3096
|
+
transformed = await Promise.all(
|
|
3097
|
+
values.map(async (item) => {
|
|
3098
|
+
this.cacheResolvedValue(item.id.primitive, item.value);
|
|
3099
|
+
const indexable = await this.transformer(item.value, item.context);
|
|
3100
|
+
coerceWithIndexed(item.value, indexable);
|
|
3101
|
+
coerceWithContext(item.value, item.context);
|
|
3102
|
+
return { ...item, indexable };
|
|
3103
|
+
}),
|
|
3104
|
+
);
|
|
3105
|
+
}
|
|
3106
|
+
|
|
3107
|
+
try {
|
|
3108
|
+
const contextualBatchPut = this.transformerIsIdentity
|
|
3109
|
+
? (this.index as ContextualIndexPut<I>).putWithContextBatch
|
|
3110
|
+
: undefined;
|
|
3111
|
+
if (contextualBatchPut) {
|
|
3112
|
+
await contextualBatchPut.call(
|
|
3113
|
+
this.index,
|
|
3114
|
+
transformed.map((item) => ({
|
|
3115
|
+
value: item.indexable,
|
|
3116
|
+
id: item.id,
|
|
3117
|
+
context: item.context,
|
|
3118
|
+
options: this.withContextualEncodedValue(
|
|
3119
|
+
item.options,
|
|
3120
|
+
item.context,
|
|
3121
|
+
),
|
|
3122
|
+
})),
|
|
3123
|
+
);
|
|
3124
|
+
} else if (
|
|
3125
|
+
transformed.every((item) => item.options?.replace !== true) &&
|
|
3126
|
+
this.index.putBatch
|
|
3127
|
+
) {
|
|
3128
|
+
await this.index.putBatch(
|
|
3129
|
+
transformed.map(
|
|
3130
|
+
(item) => new this.wrappedIndexedType(item.indexable, item.context),
|
|
3131
|
+
),
|
|
3132
|
+
);
|
|
3133
|
+
} else {
|
|
3134
|
+
const contextualPut = this.transformerIsIdentity
|
|
3135
|
+
? (this.index as ContextualIndexPut<I>).putWithContext
|
|
3136
|
+
: undefined;
|
|
3137
|
+
for (const item of transformed) {
|
|
3138
|
+
if (contextualPut) {
|
|
3139
|
+
await contextualPut.call(
|
|
3140
|
+
this.index,
|
|
3141
|
+
item.indexable,
|
|
3142
|
+
item.id,
|
|
3143
|
+
item.context,
|
|
3144
|
+
this.withContextualEncodedValue(item.options, item.context),
|
|
3145
|
+
);
|
|
3146
|
+
} else {
|
|
3147
|
+
await this.index.put(
|
|
3148
|
+
new this.wrappedIndexedType(item.indexable, item.context),
|
|
3149
|
+
item.id,
|
|
3150
|
+
stripEncodedValue(item.options),
|
|
3151
|
+
);
|
|
3152
|
+
}
|
|
3153
|
+
}
|
|
3154
|
+
}
|
|
3155
|
+
} catch (error) {
|
|
3156
|
+
if (error instanceof indexerTypes.NotStartedError && this.closed) {
|
|
3157
|
+
return transformed.map((item) => ({
|
|
3158
|
+
context: item.context,
|
|
3159
|
+
indexable: item.indexable,
|
|
3160
|
+
}));
|
|
1717
3161
|
}
|
|
3162
|
+
throw error;
|
|
3163
|
+
}
|
|
3164
|
+
|
|
3165
|
+
return transformed.map((item) => ({
|
|
3166
|
+
context: item.context,
|
|
3167
|
+
indexable: item.indexable,
|
|
3168
|
+
}));
|
|
3169
|
+
}
|
|
3170
|
+
|
|
3171
|
+
public _cacheResolvedIdentityValue(
|
|
3172
|
+
id: string | number | bigint,
|
|
3173
|
+
value: T,
|
|
3174
|
+
): void {
|
|
3175
|
+
this.cacheResolvedValue(id, value);
|
|
3176
|
+
}
|
|
3177
|
+
|
|
3178
|
+
private cacheResolvedValue(id: string | number | bigint, value: T): void {
|
|
3179
|
+
if (this.isProgramValued) {
|
|
3180
|
+
this._resolverProgramCache!.set(id, value);
|
|
3181
|
+
indexCacheLogger("cache:set:program", { id });
|
|
3182
|
+
} else if (this._resolverCache) {
|
|
3183
|
+
this._resolverCache.add(id, value);
|
|
3184
|
+
indexCacheLogger("cache:set:value", { id });
|
|
3185
|
+
}
|
|
3186
|
+
}
|
|
3187
|
+
|
|
3188
|
+
private withContextualEncodedValue(
|
|
3189
|
+
options: ContextualPutOptions | undefined,
|
|
3190
|
+
context: types.Context,
|
|
3191
|
+
): ContextualPutOptions | undefined {
|
|
3192
|
+
if (!options?.encodedValue) {
|
|
3193
|
+
return options;
|
|
3194
|
+
}
|
|
3195
|
+
const encodedValueParts = this.encodeContextualIndexedValueParts(
|
|
3196
|
+
options.encodedValue,
|
|
3197
|
+
context,
|
|
3198
|
+
);
|
|
3199
|
+
return encodedValueParts
|
|
3200
|
+
? { ...options, encodedValue: undefined, encodedValueParts }
|
|
3201
|
+
: options;
|
|
3202
|
+
}
|
|
3203
|
+
|
|
3204
|
+
private encodeContextualIndexedValueParts(
|
|
3205
|
+
encodedValue: Uint8Array | undefined,
|
|
3206
|
+
context: types.Context,
|
|
3207
|
+
): ContextualPutOptions["encodedValueParts"] | undefined {
|
|
3208
|
+
if (
|
|
3209
|
+
!encodedValue ||
|
|
3210
|
+
!this.transformerIsIdentity ||
|
|
3211
|
+
!this.indexedTypeIsDocumentType
|
|
3212
|
+
) {
|
|
3213
|
+
return;
|
|
1718
3214
|
}
|
|
3215
|
+
return {
|
|
3216
|
+
prefix: encodedValue,
|
|
3217
|
+
suffix: encodeContextSuffix(context),
|
|
3218
|
+
};
|
|
3219
|
+
}
|
|
3220
|
+
|
|
3221
|
+
public del(key: indexerTypes.IdKey) {
|
|
3222
|
+
this.deleteResolvedCacheForKey(key);
|
|
1719
3223
|
return this.index.del({
|
|
1720
3224
|
query: [indexerTypes.getMatcher(this.indexBy, key.key)],
|
|
1721
3225
|
});
|
|
1722
3226
|
}
|
|
1723
3227
|
|
|
3228
|
+
public async delMany(keys: indexerTypes.IdKey[]): Promise<void> {
|
|
3229
|
+
if (keys.length === 0) {
|
|
3230
|
+
return;
|
|
3231
|
+
}
|
|
3232
|
+
for (const key of keys) {
|
|
3233
|
+
this.deleteResolvedCacheForKey(key);
|
|
3234
|
+
}
|
|
3235
|
+
const delIdsNoReturn = (this.index as ExactDeleteIndex).delIdsNoReturn;
|
|
3236
|
+
if (delIdsNoReturn) {
|
|
3237
|
+
await delIdsNoReturn.call(this.index, keys);
|
|
3238
|
+
return;
|
|
3239
|
+
}
|
|
3240
|
+
const delIds = (this.index as ExactDeleteIndex).delIds;
|
|
3241
|
+
if (delIds) {
|
|
3242
|
+
await delIds.call(this.index, keys);
|
|
3243
|
+
return;
|
|
3244
|
+
}
|
|
3245
|
+
await Promise.all(keys.map((key) => this.del(key)));
|
|
3246
|
+
}
|
|
3247
|
+
|
|
3248
|
+
public clearResolvedCacheForKeys(keys: indexerTypes.IdKey[]): void {
|
|
3249
|
+
for (const key of keys) {
|
|
3250
|
+
this.deleteResolvedCacheForKey(key);
|
|
3251
|
+
}
|
|
3252
|
+
}
|
|
3253
|
+
|
|
3254
|
+
public delManyMaybe(keys: indexerTypes.IdKey[]): MaybePromise<void> {
|
|
3255
|
+
if (keys.length === 0) {
|
|
3256
|
+
return;
|
|
3257
|
+
}
|
|
3258
|
+
for (const key of keys) {
|
|
3259
|
+
this.deleteResolvedCacheForKey(key);
|
|
3260
|
+
}
|
|
3261
|
+
const delIdsNoReturn = (this.index as ExactDeleteIndex).delIdsNoReturn;
|
|
3262
|
+
if (delIdsNoReturn) {
|
|
3263
|
+
const result = delIdsNoReturn.call(this.index, keys);
|
|
3264
|
+
return isPromiseLike(result) ? result.then(() => undefined) : undefined;
|
|
3265
|
+
}
|
|
3266
|
+
const delIds = (this.index as ExactDeleteIndex).delIds;
|
|
3267
|
+
if (delIds) {
|
|
3268
|
+
const result = delIds.call(this.index, keys);
|
|
3269
|
+
return isPromiseLike(result) ? result.then(() => undefined) : undefined;
|
|
3270
|
+
}
|
|
3271
|
+
return Promise.all(keys.map((key) => this.del(key))).then(() => undefined);
|
|
3272
|
+
}
|
|
3273
|
+
|
|
1724
3274
|
public async getDetailed<
|
|
1725
3275
|
Options extends QueryOptions<T, I, D, Resolve>,
|
|
1726
3276
|
Resolve extends boolean | undefined = ExtractResolveFromOptions<Options>,
|
|
@@ -2198,15 +3748,18 @@ export class DocumentIndex<
|
|
|
2198
3748
|
}
|
|
2199
3749
|
|
|
2200
3750
|
get countIteratorsInProgress() {
|
|
2201
|
-
return this._resumableIterators
|
|
3751
|
+
return this._resumableIterators?.queues.size ?? 0;
|
|
2202
3752
|
}
|
|
2203
3753
|
|
|
2204
3754
|
private clearAllResultQueues() {
|
|
3755
|
+
if (!this._resultQueue) {
|
|
3756
|
+
return;
|
|
3757
|
+
}
|
|
2205
3758
|
for (const [key, queue] of this._resultQueue) {
|
|
2206
3759
|
clearTimeout(queue.timeout);
|
|
2207
3760
|
this._resultQueue.delete(key);
|
|
2208
3761
|
this.cancelIteratorKeepAlive(key);
|
|
2209
|
-
this._resumableIterators
|
|
3762
|
+
this._resumableIterators?.close({ idString: key });
|
|
2210
3763
|
}
|
|
2211
3764
|
}
|
|
2212
3765
|
|
|
@@ -2501,9 +4054,10 @@ export class DocumentIndex<
|
|
|
2501
4054
|
signal: options?.signal,
|
|
2502
4055
|
});
|
|
2503
4056
|
|
|
2504
|
-
// Cold start: cover can be temporarily self-only
|
|
2505
|
-
// converges. For explicit remote searches,
|
|
2506
|
-
// instead of waiting for replicator
|
|
4057
|
+
// Cold start: cover can be temporarily self-only or empty while
|
|
4058
|
+
// replication metadata converges. For explicit bounded remote searches,
|
|
4059
|
+
// query bounded connected peers instead of waiting for replicator
|
|
4060
|
+
// metadata to catch up.
|
|
2507
4061
|
if (!options?.remote?.from && isDefaultDomainArgs && remoteWasExplicit) {
|
|
2508
4062
|
const selfHash = this.node.identity.publicKey.hashcode();
|
|
2509
4063
|
const remoteCount = replicatorGroups.filter(
|
|
@@ -2513,25 +4067,53 @@ export class DocumentIndex<
|
|
|
2513
4067
|
const waitEnabled = Boolean(remote.wait);
|
|
2514
4068
|
const coverIsSelfOnly =
|
|
2515
4069
|
replicatorGroups.length === 1 && replicatorGroups[0] === selfHash;
|
|
4070
|
+
const coverIsColdEmpty =
|
|
4071
|
+
replicatorGroups.length === 0 && remote.timeout != null;
|
|
4072
|
+
|
|
4073
|
+
// If the cover is explicitly empty (no shards), don't override it
|
|
4074
|
+
// unless the caller requested waiting for joins (e.g. get(waitFor))
|
|
4075
|
+
// or bounded the remote query with a timeout.
|
|
4076
|
+
if (waitEnabled || coverIsSelfOnly || coverIsColdEmpty) {
|
|
4077
|
+
const extra: string[] = [];
|
|
4078
|
+
const addExtra = (hash: string | undefined) => {
|
|
4079
|
+
if (!hash || hash === selfHash || extra.includes(hash)) {
|
|
4080
|
+
return;
|
|
4081
|
+
}
|
|
4082
|
+
extra.push(hash);
|
|
4083
|
+
};
|
|
2516
4084
|
|
|
2517
|
-
// If the cover is explicitly empty (no shards), don't override it unless
|
|
2518
|
-
// the caller requested waiting for joins (e.g. get(waitFor)).
|
|
2519
|
-
if (waitEnabled || coverIsSelfOnly) {
|
|
2520
4085
|
const peerMap: Map<string, unknown> | undefined = (
|
|
2521
4086
|
this.node.services.pubsub as any
|
|
2522
4087
|
)?.peers;
|
|
4088
|
+
|
|
4089
|
+
// Only consider replicators that are currently reachable.
|
|
4090
|
+
// The replicator index can contain stale (offline) peers, e.g.
|
|
4091
|
+
// persisted replicators after a restart; querying those would
|
|
4092
|
+
// block the first batch for the full wait timeout instead of
|
|
4093
|
+
// letting joining peers be merged as they arrive.
|
|
4094
|
+
if (peerMap?.has) {
|
|
4095
|
+
try {
|
|
4096
|
+
for (const hash of await this._log.getReplicators()) {
|
|
4097
|
+
if (peerMap.has(hash)) {
|
|
4098
|
+
addExtra(hash);
|
|
4099
|
+
}
|
|
4100
|
+
if (extra.length >= 8) break;
|
|
4101
|
+
}
|
|
4102
|
+
} catch {
|
|
4103
|
+
// Fall through to connected peers when the local replicator
|
|
4104
|
+
// index is not ready yet.
|
|
4105
|
+
}
|
|
4106
|
+
}
|
|
4107
|
+
|
|
2523
4108
|
if (peerMap?.keys) {
|
|
2524
|
-
const extra: string[] = [];
|
|
2525
4109
|
for (const hash of peerMap.keys()) {
|
|
2526
|
-
|
|
2527
|
-
extra.push(hash);
|
|
4110
|
+
addExtra(hash);
|
|
2528
4111
|
if (extra.length >= 8) break;
|
|
2529
4112
|
}
|
|
2530
|
-
|
|
2531
|
-
|
|
2532
|
-
|
|
2533
|
-
|
|
2534
|
-
}
|
|
4113
|
+
}
|
|
4114
|
+
|
|
4115
|
+
if (extra.length > 0) {
|
|
4116
|
+
replicatorGroups = [...new Set([...replicatorGroups, ...extra])];
|
|
2535
4117
|
}
|
|
2536
4118
|
}
|
|
2537
4119
|
}
|
|
@@ -3084,6 +4666,53 @@ export class DocumentIndex<
|
|
|
3084
4666
|
return indexedPlaceholders;
|
|
3085
4667
|
};
|
|
3086
4668
|
|
|
4669
|
+
// The `visited` set prevents re-emitting the same document across pages
|
|
4670
|
+
// and sources, but different sources can return different versions of the
|
|
4671
|
+
// same document (e.g. a stale local head merged before a newer remote
|
|
4672
|
+
// head arrives). Plain id-based dedupe would let the first-seen, stale
|
|
4673
|
+
// version permanently shadow the newer one. If the id is still buffered
|
|
4674
|
+
// (not yet handed to the consumer) and the incoming result is strictly
|
|
4675
|
+
// preferred by the store's conflict rule, evict the stale buffered entry
|
|
4676
|
+
// so the caller can buffer the preferred result in its place. Returns
|
|
4677
|
+
// true when the caller should proceed to buffer the incoming result.
|
|
4678
|
+
// Results that were already emitted to the consumer cannot be retracted
|
|
4679
|
+
// here; those are only corrected via live updates/replication.
|
|
4680
|
+
// The preference direction mirrors the index merge rule in program.ts:
|
|
4681
|
+
// newest wins for mutable stores, oldest wins for immutable stores.
|
|
4682
|
+
const isPreferredContext = (
|
|
4683
|
+
incoming: types.Context,
|
|
4684
|
+
existing: types.Context,
|
|
4685
|
+
): boolean =>
|
|
4686
|
+
incoming.head !== existing.head &&
|
|
4687
|
+
(this.immutable
|
|
4688
|
+
? incoming.modified < existing.modified
|
|
4689
|
+
: incoming.modified > existing.modified);
|
|
4690
|
+
|
|
4691
|
+
const evictStaleBuffered = (
|
|
4692
|
+
indexKey: indexerTypes.IdPrimitive,
|
|
4693
|
+
incomingContext: types.Context,
|
|
4694
|
+
): boolean => {
|
|
4695
|
+
for (const peerBuffer of peerBufferMap.values()) {
|
|
4696
|
+
for (let i = 0; i < peerBuffer.buffer.length; i++) {
|
|
4697
|
+
const existing = peerBuffer.buffer[i];
|
|
4698
|
+
const existingKey = indexerTypes.toId(
|
|
4699
|
+
this.indexByResolver(existing.indexed),
|
|
4700
|
+
).primitive;
|
|
4701
|
+
if (existingKey !== indexKey) {
|
|
4702
|
+
continue;
|
|
4703
|
+
}
|
|
4704
|
+
if (!isPreferredContext(incomingContext, existing.context)) {
|
|
4705
|
+
// same or non-preferred version: keep normal dedupe behavior
|
|
4706
|
+
return false;
|
|
4707
|
+
}
|
|
4708
|
+
peerBuffer.buffer.splice(i, 1);
|
|
4709
|
+
indexedPlaceholders?.delete(indexKey);
|
|
4710
|
+
return true;
|
|
4711
|
+
}
|
|
4712
|
+
}
|
|
4713
|
+
return false; // not buffered (already emitted): skip incoming
|
|
4714
|
+
};
|
|
4715
|
+
|
|
3087
4716
|
let done = false;
|
|
3088
4717
|
let drain = false; // if true, close on empty once (overrides manual)
|
|
3089
4718
|
let first = false;
|
|
@@ -3342,7 +4971,10 @@ export class DocumentIndex<
|
|
|
3342
4971
|
indexedPlaceholders?.delete(indexKey);
|
|
3343
4972
|
continue;
|
|
3344
4973
|
}
|
|
3345
|
-
if (
|
|
4974
|
+
if (
|
|
4975
|
+
visited.has(indexKey) &&
|
|
4976
|
+
!evictStaleBuffered(indexKey, result.context)
|
|
4977
|
+
) {
|
|
3346
4978
|
continue;
|
|
3347
4979
|
}
|
|
3348
4980
|
visited.add(indexKey);
|
|
@@ -3361,7 +4993,8 @@ export class DocumentIndex<
|
|
|
3361
4993
|
result as unknown as types.ResultIndexedValue<I>;
|
|
3362
4994
|
if (
|
|
3363
4995
|
visited.has(indexKey) &&
|
|
3364
|
-
!indexedPlaceholders?.has(indexKey)
|
|
4996
|
+
!indexedPlaceholders?.has(indexKey) &&
|
|
4997
|
+
!evictStaleBuffered(indexKey, indexedResult.context)
|
|
3365
4998
|
) {
|
|
3366
4999
|
continue;
|
|
3367
5000
|
}
|
|
@@ -3548,7 +5181,10 @@ export class DocumentIndex<
|
|
|
3548
5181
|
indexedPlaceholders?.delete(keyPrimitive);
|
|
3549
5182
|
continue;
|
|
3550
5183
|
}
|
|
3551
|
-
if (
|
|
5184
|
+
if (
|
|
5185
|
+
visited.has(keyPrimitive) &&
|
|
5186
|
+
!evictStaleBuffered(keyPrimitive, result.context)
|
|
5187
|
+
) {
|
|
3552
5188
|
continue;
|
|
3553
5189
|
}
|
|
3554
5190
|
visited.add(keyPrimitive);
|
|
@@ -3575,7 +5211,11 @@ export class DocumentIndex<
|
|
|
3575
5211
|
result as unknown as types.ResultIndexedValue<I>;
|
|
3576
5212
|
if (
|
|
3577
5213
|
visited.has(keyPrimitive) &&
|
|
3578
|
-
!indexedPlaceholders?.has(keyPrimitive)
|
|
5214
|
+
!indexedPlaceholders?.has(keyPrimitive) &&
|
|
5215
|
+
!evictStaleBuffered(
|
|
5216
|
+
keyPrimitive,
|
|
5217
|
+
indexedResult.context,
|
|
5218
|
+
)
|
|
3579
5219
|
) {
|
|
3580
5220
|
continue;
|
|
3581
5221
|
}
|
|
@@ -3700,7 +5340,10 @@ export class DocumentIndex<
|
|
|
3700
5340
|
indexedPlaceholders?.delete(indexKey);
|
|
3701
5341
|
continue;
|
|
3702
5342
|
}
|
|
3703
|
-
if (
|
|
5343
|
+
if (
|
|
5344
|
+
visited.has(indexKey) &&
|
|
5345
|
+
!evictStaleBuffered(indexKey, result.context)
|
|
5346
|
+
) {
|
|
3704
5347
|
continue;
|
|
3705
5348
|
}
|
|
3706
5349
|
visited.add(indexKey);
|
|
@@ -3730,7 +5373,11 @@ export class DocumentIndex<
|
|
|
3730
5373
|
result as unknown as types.ResultIndexedValue<I>;
|
|
3731
5374
|
if (
|
|
3732
5375
|
visited.has(indexKey) &&
|
|
3733
|
-
!indexedPlaceholders?.has(indexKey)
|
|
5376
|
+
!indexedPlaceholders?.has(indexKey) &&
|
|
5377
|
+
!evictStaleBuffered(
|
|
5378
|
+
indexKey,
|
|
5379
|
+
indexedResult.context,
|
|
5380
|
+
)
|
|
3734
5381
|
) {
|
|
3735
5382
|
continue;
|
|
3736
5383
|
}
|
|
@@ -3805,6 +5452,8 @@ export class DocumentIndex<
|
|
|
3805
5452
|
return [];
|
|
3806
5453
|
}
|
|
3807
5454
|
|
|
5455
|
+
await pendingUpdateProcessing;
|
|
5456
|
+
|
|
3808
5457
|
const bufferedBeforeFetch = peerBuffers().length;
|
|
3809
5458
|
const localHash = this.node.identity.publicKey.hashcode();
|
|
3810
5459
|
const hasBufferedRemoteResults = [...peerBufferMap.entries()].some(
|
|
@@ -4087,6 +5736,7 @@ export class DocumentIndex<
|
|
|
4087
5736
|
| Extract<UpdateReason, "join" | "change" | "push">
|
|
4088
5737
|
| undefined;
|
|
4089
5738
|
let hasDeliveredResults = false;
|
|
5739
|
+
let pendingUpdateProcessing: Promise<void> = Promise.resolve();
|
|
4090
5740
|
|
|
4091
5741
|
const emitOnBatch = async (
|
|
4092
5742
|
batch: ValueTypeFromRequest<Resolve, T, I>[],
|
|
@@ -4381,7 +6031,7 @@ export class DocumentIndex<
|
|
|
4381
6031
|
return value as WithContext<I>;
|
|
4382
6032
|
};
|
|
4383
6033
|
|
|
4384
|
-
const
|
|
6034
|
+
const processChange = async (evt: CustomEvent<DocumentsChange<T, I>>) => {
|
|
4385
6035
|
// Optional filter to mutate/suppress change events
|
|
4386
6036
|
indexIteratorLogger.trace(
|
|
4387
6037
|
"processing live update change event",
|
|
@@ -4523,6 +6173,13 @@ export class DocumentIndex<
|
|
|
4523
6173
|
}
|
|
4524
6174
|
signalUpdate();
|
|
4525
6175
|
};
|
|
6176
|
+
const onChange = (evt: CustomEvent<DocumentsChange<T, I>>) => {
|
|
6177
|
+
const task = pendingUpdateProcessing.then(() => processChange(evt));
|
|
6178
|
+
pendingUpdateProcessing = task.catch((error) => {
|
|
6179
|
+
warn("Failed to process iterator update", error);
|
|
6180
|
+
});
|
|
6181
|
+
return task;
|
|
6182
|
+
};
|
|
4526
6183
|
|
|
4527
6184
|
this.documentEvents.addEventListener("change", onChange);
|
|
4528
6185
|
updatesCleanup = () => {
|