@peerbit/document 13.0.43 → 13.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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 +3073 -342
- package/dist/src/program.js.map +1 -1
- package/dist/src/search.d.ts +78 -6
- package/dist/src/search.d.ts.map +1 -1
- package/dist/src/search.js +1105 -53
- 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 +5019 -467
- package/src/search.ts +1777 -188
- 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>
|
|
@@ -661,6 +866,151 @@ type IndexableClass<I> = new (
|
|
|
661
866
|
context: types.Context,
|
|
662
867
|
) => WithContext<I>;
|
|
663
868
|
|
|
869
|
+
type ContextualPutOptions = {
|
|
870
|
+
replace?: boolean;
|
|
871
|
+
encodedValue?: Uint8Array;
|
|
872
|
+
encodedValueParts?: {
|
|
873
|
+
prefix: Uint8Array;
|
|
874
|
+
suffix: Uint8Array;
|
|
875
|
+
};
|
|
876
|
+
transformFacts?: DocumentTransformFacts;
|
|
877
|
+
};
|
|
878
|
+
|
|
879
|
+
const stripEncodedValue = (
|
|
880
|
+
options: ContextualPutOptions | undefined,
|
|
881
|
+
): { replace?: boolean } | undefined =>
|
|
882
|
+
options?.encodedValue || options?.encodedValueParts || options?.transformFacts
|
|
883
|
+
? { replace: options.replace }
|
|
884
|
+
: options;
|
|
885
|
+
|
|
886
|
+
const writeU32Le = (target: Uint8Array, offset: number, value: number) => {
|
|
887
|
+
target[offset] = value & 0xff;
|
|
888
|
+
target[offset + 1] = (value >>> 8) & 0xff;
|
|
889
|
+
target[offset + 2] = (value >>> 16) & 0xff;
|
|
890
|
+
target[offset + 3] = (value >>> 24) & 0xff;
|
|
891
|
+
return offset + 4;
|
|
892
|
+
};
|
|
893
|
+
|
|
894
|
+
const writeU64Le = (target: Uint8Array, offset: number, value: bigint) => {
|
|
895
|
+
let remaining = value;
|
|
896
|
+
for (let i = 0; i < 8; i++) {
|
|
897
|
+
target[offset + i] = Number(remaining & 0xffn);
|
|
898
|
+
remaining >>= 8n;
|
|
899
|
+
}
|
|
900
|
+
return offset + 8;
|
|
901
|
+
};
|
|
902
|
+
|
|
903
|
+
export const encodeContextSuffix = (context: types.Context): Uint8Array => {
|
|
904
|
+
const head = fromString(context.head);
|
|
905
|
+
const gid = fromString(context.gid);
|
|
906
|
+
const encoded = new Uint8Array(
|
|
907
|
+
1 + 8 + 8 + 4 + head.byteLength + 4 + gid.byteLength + 4,
|
|
908
|
+
);
|
|
909
|
+
let offset = 0;
|
|
910
|
+
// Context is @variant(0); keep this byte-for-byte aligned with Borsh.
|
|
911
|
+
encoded[offset++] = 0;
|
|
912
|
+
offset = writeU64Le(encoded, offset, context.created);
|
|
913
|
+
offset = writeU64Le(encoded, offset, context.modified);
|
|
914
|
+
offset = writeU32Le(encoded, offset, head.byteLength);
|
|
915
|
+
encoded.set(head, offset);
|
|
916
|
+
offset += head.byteLength;
|
|
917
|
+
offset = writeU32Le(encoded, offset, gid.byteLength);
|
|
918
|
+
encoded.set(gid, offset);
|
|
919
|
+
offset += gid.byteLength;
|
|
920
|
+
writeU32Le(encoded, offset, context.size);
|
|
921
|
+
return encoded;
|
|
922
|
+
};
|
|
923
|
+
|
|
924
|
+
type ContextualIndexPut<I> = {
|
|
925
|
+
putWithContext?: (
|
|
926
|
+
value: I,
|
|
927
|
+
id: indexerTypes.IdKey,
|
|
928
|
+
context: types.Context,
|
|
929
|
+
options?: ContextualPutOptions,
|
|
930
|
+
) => Promise<void> | void;
|
|
931
|
+
putStoredContextualEncodedValue?: (
|
|
932
|
+
id: indexerTypes.IdKey,
|
|
933
|
+
encodedValueParts: {
|
|
934
|
+
prefix: Uint8Array;
|
|
935
|
+
suffix: Uint8Array;
|
|
936
|
+
},
|
|
937
|
+
options?: { replace?: boolean },
|
|
938
|
+
) => Promise<void> | void | false;
|
|
939
|
+
persistStoredContextualEncodedValue?: (
|
|
940
|
+
id: indexerTypes.IdKey,
|
|
941
|
+
encodedValueParts: {
|
|
942
|
+
prefix: Uint8Array;
|
|
943
|
+
suffix: Uint8Array;
|
|
944
|
+
},
|
|
945
|
+
options?: { replace?: boolean },
|
|
946
|
+
) => Promise<void> | void | false;
|
|
947
|
+
putStoredContextualEncodedValueBatch?: (
|
|
948
|
+
values: Array<{
|
|
949
|
+
id: indexerTypes.IdKey;
|
|
950
|
+
encodedValueParts: {
|
|
951
|
+
prefix: Uint8Array;
|
|
952
|
+
suffix: Uint8Array;
|
|
953
|
+
};
|
|
954
|
+
options?: { replace?: boolean };
|
|
955
|
+
}>,
|
|
956
|
+
) => Promise<boolean> | boolean;
|
|
957
|
+
putWithContextBatch?: (
|
|
958
|
+
values: Array<{
|
|
959
|
+
value: I;
|
|
960
|
+
id: indexerTypes.IdKey;
|
|
961
|
+
context: types.Context;
|
|
962
|
+
options?: ContextualPutOptions;
|
|
963
|
+
}>,
|
|
964
|
+
) => Promise<void> | void;
|
|
965
|
+
};
|
|
966
|
+
|
|
967
|
+
type ContextHeadIndex<I> = {
|
|
968
|
+
getByContextHead?: (
|
|
969
|
+
head: string,
|
|
970
|
+
) => indexerTypes.IndexedResult<WithContext<I>> | undefined;
|
|
971
|
+
getByContextHeadBatch?: (
|
|
972
|
+
heads: string[],
|
|
973
|
+
) => Array<indexerTypes.IndexedResult<WithContext<I>> | undefined>;
|
|
974
|
+
getIdByContextHead?: (head: string) => indexerTypes.IdKey | undefined;
|
|
975
|
+
};
|
|
976
|
+
|
|
977
|
+
type ExactDeleteIndex = {
|
|
978
|
+
delIdsNoReturn?: (
|
|
979
|
+
deleteIds: Array<indexerTypes.IdKey | indexerTypes.Ideable>,
|
|
980
|
+
) => Promise<void> | void;
|
|
981
|
+
delIds?: (
|
|
982
|
+
deleteIds: Array<indexerTypes.IdKey | indexerTypes.Ideable>,
|
|
983
|
+
) => Promise<indexerTypes.IdKey[]> | indexerTypes.IdKey[];
|
|
984
|
+
};
|
|
985
|
+
|
|
986
|
+
type NativeBackboneDocumentIndex = {
|
|
987
|
+
attachNativeBackboneDocumentIndex?: (
|
|
988
|
+
backbone: unknown,
|
|
989
|
+
options?: { preserveExisting?: boolean },
|
|
990
|
+
) => boolean | void;
|
|
991
|
+
};
|
|
992
|
+
|
|
993
|
+
type NativeBackboneDocumentProjection = {
|
|
994
|
+
projectDocumentIndexSimple?: (
|
|
995
|
+
encodedDocument: Uint8Array,
|
|
996
|
+
plan: SimpleDocumentProjectionPlan,
|
|
997
|
+
context: SimpleDocumentProjectionContext,
|
|
998
|
+
) => Uint8Array | undefined;
|
|
999
|
+
};
|
|
1000
|
+
|
|
1001
|
+
type NativeBackboneDocumentIndexCommit<I> = {
|
|
1002
|
+
valuePrefixBytes?: Uint8Array;
|
|
1003
|
+
usePlainPutPayload?: boolean;
|
|
1004
|
+
projection?: {
|
|
1005
|
+
encodedDocument: Uint8Array;
|
|
1006
|
+
plan: SimpleDocumentProjectionPlan;
|
|
1007
|
+
signer?: Uint8Array;
|
|
1008
|
+
};
|
|
1009
|
+
indexable?: I;
|
|
1010
|
+
getIndexable?: () => I;
|
|
1011
|
+
setContext?: (context: types.Context) => void;
|
|
1012
|
+
};
|
|
1013
|
+
|
|
664
1014
|
export const coerceWithContext = <T>(
|
|
665
1015
|
value: T | WithContext<T>,
|
|
666
1016
|
context: types.Context,
|
|
@@ -679,6 +1029,36 @@ export const coerceWithIndexed = <T, I>(
|
|
|
679
1029
|
return valueWithContext;
|
|
680
1030
|
};
|
|
681
1031
|
|
|
1032
|
+
export const coerceWithLazyIndexed = <T, I>(
|
|
1033
|
+
value: T | WithIndexedContext<T, I>,
|
|
1034
|
+
getIndexable: () => I,
|
|
1035
|
+
): WithIndexedContext<T, I> => {
|
|
1036
|
+
let cached: I | undefined;
|
|
1037
|
+
let hasCached = false;
|
|
1038
|
+
Object.defineProperty(value, "__indexed", {
|
|
1039
|
+
configurable: true,
|
|
1040
|
+
enumerable: true,
|
|
1041
|
+
get() {
|
|
1042
|
+
if (!hasCached) {
|
|
1043
|
+
cached = getIndexable();
|
|
1044
|
+
hasCached = true;
|
|
1045
|
+
Object.defineProperty(value, "__indexed", {
|
|
1046
|
+
configurable: true,
|
|
1047
|
+
enumerable: true,
|
|
1048
|
+
writable: true,
|
|
1049
|
+
value: cached,
|
|
1050
|
+
});
|
|
1051
|
+
}
|
|
1052
|
+
return cached;
|
|
1053
|
+
},
|
|
1054
|
+
set(indexed: I) {
|
|
1055
|
+
cached = indexed;
|
|
1056
|
+
hasCached = true;
|
|
1057
|
+
},
|
|
1058
|
+
});
|
|
1059
|
+
return value as WithIndexedContext<T, I>;
|
|
1060
|
+
};
|
|
1061
|
+
|
|
682
1062
|
@variant("documents_index")
|
|
683
1063
|
export class DocumentIndex<
|
|
684
1064
|
T,
|
|
@@ -693,6 +1073,10 @@ export class DocumentIndex<
|
|
|
693
1073
|
|
|
694
1074
|
// transform options
|
|
695
1075
|
transformer: Transformer<T, I>;
|
|
1076
|
+
private transformerIsIdentity = false;
|
|
1077
|
+
private nativeTransformDescriptor?: DocumentTransformDescriptor;
|
|
1078
|
+
private nativeTransformProjectionPlan?: SimpleDocumentProjectionPlan;
|
|
1079
|
+
private nativeBackboneDocumentProjection?: NativeBackboneDocumentProjection;
|
|
696
1080
|
|
|
697
1081
|
// The indexed document wrapped in a context
|
|
698
1082
|
wrappedIndexedType: IndexableClass<I>;
|
|
@@ -757,11 +1141,21 @@ export class DocumentIndex<
|
|
|
757
1141
|
>;
|
|
758
1142
|
private iteratorKeepAliveTimers?: Map<string, ReturnType<typeof setTimeout>>;
|
|
759
1143
|
|
|
1144
|
+
private deleteResolvedCacheForKey(key: indexerTypes.IdKey): void {
|
|
1145
|
+
if (this.isProgramValued) {
|
|
1146
|
+
this._resolverProgramCache!.delete(key.primitive);
|
|
1147
|
+
indexCacheLogger("cache:del:program", { id: key.primitive });
|
|
1148
|
+
} else if (this._resolverCache?.del(key.primitive)) {
|
|
1149
|
+
indexCacheLogger("cache:del:value", { id: key.primitive });
|
|
1150
|
+
}
|
|
1151
|
+
}
|
|
1152
|
+
|
|
760
1153
|
constructor(properties?: {
|
|
761
1154
|
query?: RPC<types.AbstractSearchRequest, types.AbstractSearchResult>;
|
|
762
1155
|
}) {
|
|
763
1156
|
super();
|
|
764
1157
|
this._query = properties?.query || new RPC();
|
|
1158
|
+
this._resultQueue = new Map();
|
|
765
1159
|
this.iteratorKeepAliveTimers = new Map();
|
|
766
1160
|
}
|
|
767
1161
|
|
|
@@ -903,9 +1297,16 @@ export class DocumentIndex<
|
|
|
903
1297
|
return results;
|
|
904
1298
|
}
|
|
905
1299
|
|
|
906
|
-
|
|
1300
|
+
// Bound in open(). Deserialized instances skip constructor/field initializers
|
|
1301
|
+
// (borsh creates objects via Object.create), so this must not rely on a field
|
|
1302
|
+
// initializer to exist.
|
|
1303
|
+
private handleDocumentChange?: (
|
|
907
1304
|
event: CustomEvent<DocumentsChange<T, I>>,
|
|
908
|
-
) =>
|
|
1305
|
+
) => Promise<void>;
|
|
1306
|
+
|
|
1307
|
+
private async onDocumentChange(
|
|
1308
|
+
event: CustomEvent<DocumentsChange<T, I>>,
|
|
1309
|
+
): Promise<void> {
|
|
909
1310
|
const added = event.detail.added;
|
|
910
1311
|
if (!added.length) {
|
|
911
1312
|
return;
|
|
@@ -980,7 +1381,7 @@ export class DocumentIndex<
|
|
|
980
1381
|
queue.pushInFlight = false;
|
|
981
1382
|
}
|
|
982
1383
|
}
|
|
983
|
-
}
|
|
1384
|
+
}
|
|
984
1385
|
|
|
985
1386
|
private get nestedProperties() {
|
|
986
1387
|
return {
|
|
@@ -1093,9 +1494,25 @@ export class DocumentIndex<
|
|
|
1093
1494
|
};
|
|
1094
1495
|
|
|
1095
1496
|
const transformOptions = properties.transform;
|
|
1497
|
+
const hasTransformFunction =
|
|
1498
|
+
transformOptions != null && isTransformerWithFunction(transformOptions);
|
|
1499
|
+
this.nativeTransformDescriptor = hasTransformFunction
|
|
1500
|
+
? getDocumentTransformDescriptor(transformOptions.transform)
|
|
1501
|
+
: undefined;
|
|
1502
|
+
this.nativeTransformProjectionPlan = createSimpleProjectionPlan(
|
|
1503
|
+
getSchema(this.documentType),
|
|
1504
|
+
indexedSchema,
|
|
1505
|
+
this.nativeTransformDescriptor,
|
|
1506
|
+
);
|
|
1507
|
+
this.transformerIsIdentity =
|
|
1508
|
+
transformOptions == null ||
|
|
1509
|
+
(!hasTransformFunction && transformOptions.type == null) ||
|
|
1510
|
+
(this.nativeTransformDescriptor?.kind === "identity" &&
|
|
1511
|
+
this.indexedTypeIsDocumentType);
|
|
1096
1512
|
this.transformer = transformOptions
|
|
1097
|
-
?
|
|
1098
|
-
? (obj, context) =>
|
|
1513
|
+
? hasTransformFunction
|
|
1514
|
+
? (obj, context, facts) =>
|
|
1515
|
+
transformOptions.transform(obj, context, facts)
|
|
1099
1516
|
: transformOptions.type
|
|
1100
1517
|
? (obj, context) => new transformOptions.type!(obj, context)
|
|
1101
1518
|
: (obj) => obj as any as I
|
|
@@ -1210,9 +1627,307 @@ export class DocumentIndex<
|
|
|
1210
1627
|
responseType: types.AbstractSearchResult,
|
|
1211
1628
|
queryType: types.AbstractSearchRequest,
|
|
1212
1629
|
});
|
|
1213
|
-
|
|
1214
|
-
|
|
1630
|
+
this.handleDocumentChange ??= (event) => this.onDocumentChange(event);
|
|
1631
|
+
this.documentEvents.addEventListener("change", this.handleDocumentChange);
|
|
1632
|
+
}
|
|
1633
|
+
|
|
1634
|
+
private attachNativeBackboneDocumentIndex(
|
|
1635
|
+
backbone: unknown,
|
|
1636
|
+
options?: { preserveExisting?: boolean },
|
|
1637
|
+
): boolean {
|
|
1638
|
+
this.nativeBackboneDocumentProjection = undefined;
|
|
1639
|
+
if (
|
|
1640
|
+
!backbone ||
|
|
1641
|
+
this.isProgramValued ||
|
|
1642
|
+
!this.canUseNativeBackboneDocumentIndex()
|
|
1643
|
+
) {
|
|
1644
|
+
return false;
|
|
1645
|
+
}
|
|
1646
|
+
const attach = (this.index as NativeBackboneDocumentIndex)
|
|
1647
|
+
.attachNativeBackboneDocumentIndex;
|
|
1648
|
+
const attached =
|
|
1649
|
+
typeof attach === "function" &&
|
|
1650
|
+
attach.call(this.index, backbone, options) === true;
|
|
1651
|
+
if (attached) {
|
|
1652
|
+
const projection = backbone as NativeBackboneDocumentProjection;
|
|
1653
|
+
if (typeof projection.projectDocumentIndexSimple === "function") {
|
|
1654
|
+
this.nativeBackboneDocumentProjection = projection;
|
|
1655
|
+
}
|
|
1656
|
+
}
|
|
1657
|
+
return attached;
|
|
1658
|
+
}
|
|
1659
|
+
|
|
1660
|
+
private canUseNativeBackboneDocumentIndex(): boolean {
|
|
1661
|
+
return (
|
|
1662
|
+
(this.transformerIsIdentity && this.indexedTypeIsDocumentType) ||
|
|
1663
|
+
this.nativeTransformDescriptor != null
|
|
1664
|
+
);
|
|
1665
|
+
}
|
|
1666
|
+
|
|
1667
|
+
private getNativeDocumentFieldExtractionPlan(
|
|
1668
|
+
path: string | readonly string[],
|
|
1669
|
+
): SimpleDocumentFieldExtractionPlan | undefined {
|
|
1670
|
+
return createSimpleFieldExtractionPlan(getSchema(this.documentType), path);
|
|
1671
|
+
}
|
|
1672
|
+
|
|
1673
|
+
private canPrepareNativeBackboneDocumentIndexCommit(): boolean {
|
|
1674
|
+
return (
|
|
1675
|
+
(this.transformerIsIdentity && this.indexedTypeIsDocumentType) ||
|
|
1676
|
+
canPrepareDocumentTransformBeforeAppend(
|
|
1677
|
+
this.nativeTransformDescriptor,
|
|
1678
|
+
)
|
|
1679
|
+
);
|
|
1680
|
+
}
|
|
1681
|
+
|
|
1682
|
+
private canPrepareNativeBackboneDocumentIndexCommitWithAppendFacts(): boolean {
|
|
1683
|
+
return (
|
|
1684
|
+
this.canPrepareNativeBackboneDocumentIndexCommit() ||
|
|
1685
|
+
!!this.nativeTransformProjectionPlan ||
|
|
1686
|
+
canPrepareDocumentTransformWithAppendFacts(
|
|
1687
|
+
this.nativeTransformDescriptor,
|
|
1688
|
+
)
|
|
1689
|
+
);
|
|
1690
|
+
}
|
|
1691
|
+
|
|
1692
|
+
private canUseNativeBackboneContextualBatch(): boolean {
|
|
1693
|
+
return (
|
|
1694
|
+
!this.isProgramValued &&
|
|
1695
|
+
typeof (this.index as ContextualIndexPut<I>).putWithContextBatch ===
|
|
1696
|
+
"function" &&
|
|
1697
|
+
((this.transformerIsIdentity && this.indexedTypeIsDocumentType) ||
|
|
1698
|
+
this.canPrepareNativeBackboneDocumentIndexCommitWithAppendFacts())
|
|
1699
|
+
);
|
|
1700
|
+
}
|
|
1701
|
+
|
|
1702
|
+
private prepareNativeBackboneDocumentIndexCommit(
|
|
1703
|
+
value: T,
|
|
1704
|
+
encodedDocument: Uint8Array,
|
|
1705
|
+
transformFacts?: DocumentTransformFacts,
|
|
1706
|
+
): MaybePromise<NativeBackboneDocumentIndexCommit<I> | undefined> {
|
|
1707
|
+
if (this.transformerIsIdentity && this.indexedTypeIsDocumentType) {
|
|
1708
|
+
return {
|
|
1709
|
+
valuePrefixBytes: encodedDocument,
|
|
1710
|
+
usePlainPutPayload: true,
|
|
1711
|
+
indexable: value as any as I,
|
|
1712
|
+
};
|
|
1713
|
+
}
|
|
1714
|
+
if (this.nativeTransformProjectionPlan) {
|
|
1715
|
+
let projectionContext: types.Context | undefined;
|
|
1716
|
+
let cached: I | undefined;
|
|
1717
|
+
let hasCached = false;
|
|
1718
|
+
return {
|
|
1719
|
+
projection: {
|
|
1720
|
+
encodedDocument,
|
|
1721
|
+
plan: this.nativeTransformProjectionPlan,
|
|
1722
|
+
signer: transformFacts?.entryPublicKeys?.[0]?.bytes,
|
|
1723
|
+
},
|
|
1724
|
+
getIndexable: () => {
|
|
1725
|
+
if (!hasCached) {
|
|
1726
|
+
const transformed = this.transformer(
|
|
1727
|
+
value,
|
|
1728
|
+
projectionContext as types.Context,
|
|
1729
|
+
transformFacts,
|
|
1730
|
+
);
|
|
1731
|
+
if (isPromiseLike(transformed)) {
|
|
1732
|
+
throw new Error(
|
|
1733
|
+
"Native descriptor transform unexpectedly returned a promise",
|
|
1734
|
+
);
|
|
1735
|
+
}
|
|
1736
|
+
cached = transformed;
|
|
1737
|
+
hasCached = true;
|
|
1738
|
+
}
|
|
1739
|
+
return cached!;
|
|
1740
|
+
},
|
|
1741
|
+
setContext: (context) => {
|
|
1742
|
+
projectionContext = context;
|
|
1743
|
+
hasCached = false;
|
|
1744
|
+
cached = undefined;
|
|
1745
|
+
},
|
|
1746
|
+
};
|
|
1747
|
+
}
|
|
1748
|
+
if (
|
|
1749
|
+
!canPrepareDocumentTransformBeforeAppend(
|
|
1750
|
+
this.nativeTransformDescriptor,
|
|
1751
|
+
)
|
|
1752
|
+
) {
|
|
1753
|
+
return;
|
|
1754
|
+
}
|
|
1755
|
+
const transformed = this.transformer(
|
|
1756
|
+
value,
|
|
1757
|
+
undefined as unknown as types.Context,
|
|
1758
|
+
transformFacts,
|
|
1759
|
+
);
|
|
1760
|
+
const finish = (indexable: I): NativeBackboneDocumentIndexCommit<I> => ({
|
|
1761
|
+
valuePrefixBytes: serialize(this.asIndexedTypeValue(indexable)),
|
|
1762
|
+
indexable,
|
|
1763
|
+
});
|
|
1764
|
+
return isPromiseLike(transformed)
|
|
1765
|
+
? transformed.then(finish)
|
|
1766
|
+
: finish(transformed);
|
|
1767
|
+
}
|
|
1768
|
+
|
|
1769
|
+
private prepareNativeBackboneDocumentIndexCommitWithAppendFacts(
|
|
1770
|
+
value: T,
|
|
1771
|
+
encodedDocument: Uint8Array,
|
|
1772
|
+
context: types.Context,
|
|
1773
|
+
transformFacts?: DocumentTransformFacts,
|
|
1774
|
+
): NativeBackboneDocumentIndexCommit<I> | undefined {
|
|
1775
|
+
if (this.transformerIsIdentity && this.indexedTypeIsDocumentType) {
|
|
1776
|
+
return {
|
|
1777
|
+
valuePrefixBytes: encodedDocument,
|
|
1778
|
+
usePlainPutPayload: true,
|
|
1779
|
+
indexable: value as any as I,
|
|
1780
|
+
};
|
|
1781
|
+
}
|
|
1782
|
+
if (this.nativeTransformProjectionPlan) {
|
|
1783
|
+
let projectionContext = {
|
|
1784
|
+
created: context.created,
|
|
1785
|
+
modified: context.modified,
|
|
1786
|
+
head: context.head,
|
|
1787
|
+
gid: context.gid,
|
|
1788
|
+
size: context.size,
|
|
1789
|
+
signer: transformFacts?.entryPublicKeys?.[0]?.bytes,
|
|
1790
|
+
};
|
|
1791
|
+
let cached: I | undefined;
|
|
1792
|
+
let hasCached = false;
|
|
1793
|
+
return {
|
|
1794
|
+
projection: {
|
|
1795
|
+
encodedDocument,
|
|
1796
|
+
plan: this.nativeTransformProjectionPlan,
|
|
1797
|
+
signer: projectionContext.signer,
|
|
1798
|
+
},
|
|
1799
|
+
getIndexable: () => {
|
|
1800
|
+
if (!hasCached) {
|
|
1801
|
+
const transformed = this.transformer(
|
|
1802
|
+
value,
|
|
1803
|
+
projectionContext as types.Context,
|
|
1804
|
+
transformFacts,
|
|
1805
|
+
);
|
|
1806
|
+
if (isPromiseLike(transformed)) {
|
|
1807
|
+
throw new Error(
|
|
1808
|
+
"Native descriptor transform unexpectedly returned a promise",
|
|
1809
|
+
);
|
|
1810
|
+
}
|
|
1811
|
+
cached = transformed;
|
|
1812
|
+
hasCached = true;
|
|
1813
|
+
}
|
|
1814
|
+
return cached!;
|
|
1815
|
+
},
|
|
1816
|
+
setContext: (nextContext) => {
|
|
1817
|
+
projectionContext = {
|
|
1818
|
+
created: nextContext.created,
|
|
1819
|
+
modified: nextContext.modified,
|
|
1820
|
+
head: nextContext.head,
|
|
1821
|
+
gid: nextContext.gid,
|
|
1822
|
+
size: nextContext.size,
|
|
1823
|
+
signer: transformFacts?.entryPublicKeys?.[0]?.bytes,
|
|
1824
|
+
};
|
|
1825
|
+
hasCached = false;
|
|
1826
|
+
cached = undefined;
|
|
1827
|
+
},
|
|
1828
|
+
};
|
|
1829
|
+
}
|
|
1830
|
+
if (
|
|
1831
|
+
!canPrepareDocumentTransformWithAppendFacts(
|
|
1832
|
+
this.nativeTransformDescriptor,
|
|
1833
|
+
) &&
|
|
1834
|
+
!canPrepareDocumentTransformBeforeAppend(
|
|
1835
|
+
this.nativeTransformDescriptor,
|
|
1836
|
+
)
|
|
1837
|
+
) {
|
|
1838
|
+
return;
|
|
1839
|
+
}
|
|
1840
|
+
const transformed = this.transformer(value, context, transformFacts);
|
|
1841
|
+
if (isPromiseLike(transformed)) {
|
|
1842
|
+
return;
|
|
1843
|
+
}
|
|
1844
|
+
const indexable = transformed;
|
|
1845
|
+
return {
|
|
1846
|
+
valuePrefixBytes: serialize(this.asIndexedTypeValue(indexable)),
|
|
1847
|
+
indexable,
|
|
1848
|
+
};
|
|
1849
|
+
}
|
|
1850
|
+
|
|
1851
|
+
private prepareNativeBackboneDocumentIndexStoredCommitWithAppendFacts(
|
|
1852
|
+
encodedDocument: Uint8Array,
|
|
1853
|
+
context: types.Context,
|
|
1854
|
+
transformFacts?: DocumentTransformFacts,
|
|
1855
|
+
): NativeBackboneDocumentIndexCommit<I> | undefined {
|
|
1856
|
+
if (this.transformerIsIdentity && this.indexedTypeIsDocumentType) {
|
|
1857
|
+
return {
|
|
1858
|
+
valuePrefixBytes: encodedDocument,
|
|
1859
|
+
usePlainPutPayload: true,
|
|
1860
|
+
};
|
|
1861
|
+
}
|
|
1862
|
+
if (this.nativeTransformProjectionPlan) {
|
|
1863
|
+
return {
|
|
1864
|
+
projection: {
|
|
1865
|
+
encodedDocument,
|
|
1866
|
+
plan: this.nativeTransformProjectionPlan,
|
|
1867
|
+
signer: transformFacts?.entryPublicKeys?.[0]?.bytes,
|
|
1868
|
+
},
|
|
1869
|
+
};
|
|
1215
1870
|
}
|
|
1871
|
+
if (
|
|
1872
|
+
!canPrepareDocumentTransformWithAppendFacts(
|
|
1873
|
+
this.nativeTransformDescriptor,
|
|
1874
|
+
) &&
|
|
1875
|
+
!canPrepareDocumentTransformBeforeAppend(
|
|
1876
|
+
this.nativeTransformDescriptor,
|
|
1877
|
+
)
|
|
1878
|
+
) {
|
|
1879
|
+
return;
|
|
1880
|
+
}
|
|
1881
|
+
return;
|
|
1882
|
+
}
|
|
1883
|
+
|
|
1884
|
+
private nativeBackboneDocumentIndexValuePrefixBytes(
|
|
1885
|
+
nativeDocumentIndex: NativeBackboneDocumentIndexCommit<I>,
|
|
1886
|
+
context: types.Context,
|
|
1887
|
+
): Uint8Array | undefined {
|
|
1888
|
+
return (
|
|
1889
|
+
nativeDocumentIndex.valuePrefixBytes ??
|
|
1890
|
+
(nativeDocumentIndex.projection
|
|
1891
|
+
? (this.nativeBackboneDocumentProjection?.projectDocumentIndexSimple?.(
|
|
1892
|
+
nativeDocumentIndex.projection.encodedDocument,
|
|
1893
|
+
nativeDocumentIndex.projection.plan,
|
|
1894
|
+
{
|
|
1895
|
+
created: context.created,
|
|
1896
|
+
modified: context.modified,
|
|
1897
|
+
head: context.head,
|
|
1898
|
+
gid: context.gid,
|
|
1899
|
+
size: context.size,
|
|
1900
|
+
signer: nativeDocumentIndex.projection.signer,
|
|
1901
|
+
},
|
|
1902
|
+
) ??
|
|
1903
|
+
tryProjectDocumentIndexSimple(
|
|
1904
|
+
nativeDocumentIndex.projection.encodedDocument,
|
|
1905
|
+
nativeDocumentIndex.projection.plan,
|
|
1906
|
+
{
|
|
1907
|
+
created: context.created,
|
|
1908
|
+
modified: context.modified,
|
|
1909
|
+
head: context.head,
|
|
1910
|
+
gid: context.gid,
|
|
1911
|
+
size: context.size,
|
|
1912
|
+
signer: nativeDocumentIndex.projection.signer,
|
|
1913
|
+
},
|
|
1914
|
+
))
|
|
1915
|
+
: undefined)
|
|
1916
|
+
);
|
|
1917
|
+
}
|
|
1918
|
+
|
|
1919
|
+
private asIndexedTypeValue(value: I): I {
|
|
1920
|
+
if (
|
|
1921
|
+
value &&
|
|
1922
|
+
Object.getPrototypeOf(value) ===
|
|
1923
|
+
(this.indexedType as { prototype: object }).prototype
|
|
1924
|
+
) {
|
|
1925
|
+
return value;
|
|
1926
|
+
}
|
|
1927
|
+
return Object.assign(
|
|
1928
|
+
Object.create((this.indexedType as { prototype: object }).prototype),
|
|
1929
|
+
value,
|
|
1930
|
+
);
|
|
1216
1931
|
}
|
|
1217
1932
|
|
|
1218
1933
|
get prefetch() {
|
|
@@ -1435,14 +2150,14 @@ export class DocumentIndex<
|
|
|
1435
2150
|
if (this._joinListener) {
|
|
1436
2151
|
this._query.events.removeEventListener("join", this._joinListener);
|
|
1437
2152
|
}
|
|
1438
|
-
if (this.handleDocumentChange) {
|
|
2153
|
+
if (this.handleDocumentChange && this.documentEvents) {
|
|
1439
2154
|
this.documentEvents.removeEventListener(
|
|
1440
2155
|
"change",
|
|
1441
2156
|
this.handleDocumentChange,
|
|
1442
2157
|
);
|
|
1443
2158
|
}
|
|
1444
2159
|
this.clearAllResultQueues();
|
|
1445
|
-
await this._resumableIterators
|
|
2160
|
+
await this._resumableIterators?.clearAll();
|
|
1446
2161
|
if (this.iteratorKeepAliveTimers) {
|
|
1447
2162
|
for (const timer of this.iteratorKeepAliveTimers.values()) {
|
|
1448
2163
|
clearTimeout(timer);
|
|
@@ -1466,12 +2181,12 @@ export class DocumentIndex<
|
|
|
1466
2181
|
async drop(from?: Program): Promise<boolean> {
|
|
1467
2182
|
const dropped = await super.drop(from);
|
|
1468
2183
|
if (dropped) {
|
|
1469
|
-
this.documentEvents
|
|
2184
|
+
this.documentEvents?.removeEventListener(
|
|
1470
2185
|
"change",
|
|
1471
2186
|
this.handleDocumentChange,
|
|
1472
2187
|
);
|
|
1473
2188
|
this.clearAllResultQueues();
|
|
1474
|
-
await this._resumableIterators
|
|
2189
|
+
await this._resumableIterators?.clearAll();
|
|
1475
2190
|
if (this.iteratorKeepAliveTimers) {
|
|
1476
2191
|
for (const timer of this.iteratorKeepAliveTimers.values()) {
|
|
1477
2192
|
clearTimeout(timer);
|
|
@@ -1493,146 +2208,774 @@ export class DocumentIndex<
|
|
|
1493
2208
|
}
|
|
1494
2209
|
}
|
|
1495
2210
|
}
|
|
1496
|
-
return dropped;
|
|
2211
|
+
return dropped;
|
|
2212
|
+
}
|
|
2213
|
+
|
|
2214
|
+
public async get<Options extends GetOptions<T, I, D, true | undefined>>(
|
|
2215
|
+
key: indexerTypes.Ideable | indexerTypes.IdKey,
|
|
2216
|
+
options?: Options,
|
|
2217
|
+
): Promise<WithIndexedContext<T, I>>;
|
|
2218
|
+
|
|
2219
|
+
public async get<Options extends GetOptions<T, I, D, false>>(
|
|
2220
|
+
key: indexerTypes.Ideable | indexerTypes.IdKey,
|
|
2221
|
+
options?: Options,
|
|
2222
|
+
): Promise<WithContext<I>>;
|
|
2223
|
+
|
|
2224
|
+
public async get<
|
|
2225
|
+
Options extends GetOptions<T, I, D, Resolve>,
|
|
2226
|
+
Resolve extends boolean | undefined = ExtractResolveFromOptions<Options>,
|
|
2227
|
+
>(key: indexerTypes.Ideable | indexerTypes.IdKey, options?: Options) {
|
|
2228
|
+
let deferred:
|
|
2229
|
+
| DeferredPromise<WithIndexedContext<T, I> | WithContext<I>>
|
|
2230
|
+
| undefined;
|
|
2231
|
+
let baseRemote:
|
|
2232
|
+
| RemoteQueryOptions<
|
|
2233
|
+
types.AbstractSearchRequest,
|
|
2234
|
+
types.AbstractSearchResult,
|
|
2235
|
+
D
|
|
2236
|
+
>
|
|
2237
|
+
| undefined;
|
|
2238
|
+
|
|
2239
|
+
// Normalize the id key early so listeners can use it
|
|
2240
|
+
let idKey =
|
|
2241
|
+
key instanceof indexerTypes.IdKey ? key : indexerTypes.toId(key);
|
|
2242
|
+
|
|
2243
|
+
if (options?.waitFor) {
|
|
2244
|
+
// add change listener before query because we might get a concurrent change that matches the query,
|
|
2245
|
+
// that will not be included in the query result
|
|
2246
|
+
deferred = pDefer<WithIndexedContext<T, I> | WithContext<I>>();
|
|
2247
|
+
|
|
2248
|
+
const listener = (evt: CustomEvent<DocumentsChange<T, I>>) => {
|
|
2249
|
+
for (const added of evt.detail.added) {
|
|
2250
|
+
const id = indexerTypes.toId(
|
|
2251
|
+
this.indexByResolver(added.__indexed),
|
|
2252
|
+
).primitive;
|
|
2253
|
+
if (id === idKey.primitive) {
|
|
2254
|
+
deferred!.resolve(added);
|
|
2255
|
+
}
|
|
2256
|
+
}
|
|
2257
|
+
};
|
|
2258
|
+
let cleanedUp = false;
|
|
2259
|
+
let cleanup = () => {
|
|
2260
|
+
if (cleanedUp) return;
|
|
2261
|
+
cleanedUp = true;
|
|
2262
|
+
this.documentEvents.removeEventListener("change", listener);
|
|
2263
|
+
clearTimeout(timeout);
|
|
2264
|
+
this.events.removeEventListener("close", resolveUndefined);
|
|
2265
|
+
joinListener?.();
|
|
2266
|
+
};
|
|
2267
|
+
|
|
2268
|
+
let resolveUndefined = () => {
|
|
2269
|
+
deferred!.resolve(undefined);
|
|
2270
|
+
};
|
|
2271
|
+
|
|
2272
|
+
let timeout = setTimeout(resolveUndefined, options.waitFor);
|
|
2273
|
+
this.events.addEventListener("close", resolveUndefined);
|
|
2274
|
+
this.documentEvents.addEventListener("change", listener);
|
|
2275
|
+
deferred.promise.then(cleanup);
|
|
2276
|
+
|
|
2277
|
+
// Prepare remote options without mutating caller options
|
|
2278
|
+
baseRemote =
|
|
2279
|
+
options?.remote === false
|
|
2280
|
+
? undefined
|
|
2281
|
+
: typeof options?.remote === "object"
|
|
2282
|
+
? { ...options.remote }
|
|
2283
|
+
: {};
|
|
2284
|
+
if (baseRemote) {
|
|
2285
|
+
const waitPolicy = baseRemote.wait;
|
|
2286
|
+
if (
|
|
2287
|
+
!waitPolicy ||
|
|
2288
|
+
(typeof waitPolicy === "object" &&
|
|
2289
|
+
(waitPolicy.timeout || 0) < options.waitFor)
|
|
2290
|
+
) {
|
|
2291
|
+
baseRemote.wait = {
|
|
2292
|
+
...(typeof waitPolicy === "object" ? waitPolicy : {}),
|
|
2293
|
+
timeout: options.waitFor,
|
|
2294
|
+
};
|
|
2295
|
+
}
|
|
2296
|
+
}
|
|
2297
|
+
|
|
2298
|
+
// Re-query on peer joins (like iterate), scoped to the joining peer
|
|
2299
|
+
let joinListener: (() => void) | undefined;
|
|
2300
|
+
if (baseRemote) {
|
|
2301
|
+
joinListener = this.createReplicatorJoinListener({
|
|
2302
|
+
eager: baseRemote.reach?.eager,
|
|
2303
|
+
onPeer: async (pk) => {
|
|
2304
|
+
if (cleanedUp) return;
|
|
2305
|
+
const hash = pk.hashcode();
|
|
2306
|
+
const requeryOptions: QueryOptions<T, I, D, Resolve> = {
|
|
2307
|
+
...(options as any),
|
|
2308
|
+
remote: {
|
|
2309
|
+
...(baseRemote || {}),
|
|
2310
|
+
from: [hash],
|
|
2311
|
+
},
|
|
2312
|
+
};
|
|
2313
|
+
const re = await this.getDetailed(idKey, requeryOptions as any);
|
|
2314
|
+
const first = re?.[0]?.results[0];
|
|
2315
|
+
if (first) {
|
|
2316
|
+
deferred!.resolve(first.value as any);
|
|
2317
|
+
}
|
|
2318
|
+
},
|
|
2319
|
+
});
|
|
2320
|
+
}
|
|
2321
|
+
}
|
|
2322
|
+
|
|
2323
|
+
const initialOptions = baseRemote
|
|
2324
|
+
? ({ ...(options as any), remote: baseRemote } as Options)
|
|
2325
|
+
: options;
|
|
2326
|
+
const result = (await this.getDetailed(idKey, initialOptions))?.[0]
|
|
2327
|
+
?.results[0];
|
|
2328
|
+
|
|
2329
|
+
// if no results, and we have remote joining options, we wait for the timout and if there are joining peers we re-query
|
|
2330
|
+
if (!result) {
|
|
2331
|
+
return deferred?.promise;
|
|
2332
|
+
} else if (deferred) {
|
|
2333
|
+
deferred.resolve(undefined);
|
|
2334
|
+
}
|
|
2335
|
+
return result?.value;
|
|
2336
|
+
}
|
|
2337
|
+
|
|
2338
|
+
public async getFromGid(gid: string) {
|
|
2339
|
+
const iterator = this.index.iterate({ query: { gid } });
|
|
2340
|
+
const one = await iterator.next(1);
|
|
2341
|
+
await iterator.close();
|
|
2342
|
+
return one[0];
|
|
2343
|
+
}
|
|
2344
|
+
|
|
2345
|
+
public async getFromHash(hash: string) {
|
|
2346
|
+
const iterator = this.index.iterate({ query: { hash } });
|
|
2347
|
+
const one = await iterator.next(1);
|
|
2348
|
+
await iterator.close();
|
|
2349
|
+
return one[0];
|
|
2350
|
+
}
|
|
2351
|
+
|
|
2352
|
+
public async getIdentityIndexedByHead(
|
|
2353
|
+
head: string,
|
|
2354
|
+
): Promise<indexerTypes.IndexedResult<WithContext<I>> | undefined> {
|
|
2355
|
+
if (!this.canGetIdentityIndexedByHead()) {
|
|
2356
|
+
return;
|
|
2357
|
+
}
|
|
2358
|
+
return (this.index as ContextHeadIndex<I>).getByContextHead?.(head);
|
|
2359
|
+
}
|
|
2360
|
+
|
|
2361
|
+
public async getIdentityIndexedKeyByHead(
|
|
2362
|
+
head: string,
|
|
2363
|
+
): Promise<indexerTypes.IdKey | undefined> {
|
|
2364
|
+
const key = this.getIndexedKeyByHead(head);
|
|
2365
|
+
if (key) {
|
|
2366
|
+
return key;
|
|
2367
|
+
}
|
|
2368
|
+
const indexed = await this.getIdentityIndexedByHead(head);
|
|
2369
|
+
return indexed?.id;
|
|
2370
|
+
}
|
|
2371
|
+
|
|
2372
|
+
private getIndexedKeyByHead(
|
|
2373
|
+
head: string,
|
|
2374
|
+
): indexerTypes.IdKey | undefined {
|
|
2375
|
+
const getIdByHead = (this.index as ContextHeadIndex<I>).getIdByContextHead;
|
|
2376
|
+
return typeof getIdByHead === "function"
|
|
2377
|
+
? getIdByHead.call(this.index, head)
|
|
2378
|
+
: undefined;
|
|
2379
|
+
}
|
|
2380
|
+
|
|
2381
|
+
public getIndexedKeysByHeads(
|
|
2382
|
+
heads: string[],
|
|
2383
|
+
): Array<indexerTypes.IdKey | undefined> | undefined {
|
|
2384
|
+
const getIdByHead = (this.index as ContextHeadIndex<I>).getIdByContextHead;
|
|
2385
|
+
if (typeof getIdByHead !== "function") {
|
|
2386
|
+
return;
|
|
2387
|
+
}
|
|
2388
|
+
return heads.map((head) => getIdByHead.call(this.index, head));
|
|
2389
|
+
}
|
|
2390
|
+
|
|
2391
|
+
public tryGetIdentityIndexedKeyByHead(
|
|
2392
|
+
head: string,
|
|
2393
|
+
): { supported: boolean; key?: indexerTypes.IdKey } {
|
|
2394
|
+
const getIdByHead = (this.index as ContextHeadIndex<I>).getIdByContextHead;
|
|
2395
|
+
if (typeof getIdByHead !== "function") {
|
|
2396
|
+
return { supported: false };
|
|
2397
|
+
}
|
|
2398
|
+
return {
|
|
2399
|
+
supported: true,
|
|
2400
|
+
key: getIdByHead.call(this.index, head),
|
|
2401
|
+
};
|
|
2402
|
+
}
|
|
2403
|
+
|
|
2404
|
+
public async getIdentityIndexedByHeads(
|
|
2405
|
+
heads: string[],
|
|
2406
|
+
): Promise<
|
|
2407
|
+
Array<indexerTypes.IndexedResult<WithContext<I>> | undefined> | undefined
|
|
2408
|
+
> {
|
|
2409
|
+
if (!this.canGetIdentityIndexedByHead()) {
|
|
2410
|
+
return;
|
|
2411
|
+
}
|
|
2412
|
+
const batch = (this.index as ContextHeadIndex<I>).getByContextHeadBatch;
|
|
2413
|
+
if (batch) {
|
|
2414
|
+
return batch.call(this.index, heads);
|
|
2415
|
+
}
|
|
2416
|
+
return Promise.all(
|
|
2417
|
+
heads.map((head) => this.getIdentityIndexedByHead(head)),
|
|
2418
|
+
);
|
|
2419
|
+
}
|
|
2420
|
+
|
|
2421
|
+
public canGetIdentityIndexedByHead(): boolean {
|
|
2422
|
+
return (
|
|
2423
|
+
this.transformerIsIdentity &&
|
|
2424
|
+
this.indexedTypeIsDocumentType &&
|
|
2425
|
+
!this.isProgramValued &&
|
|
2426
|
+
typeof (this.index as ContextHeadIndex<I>).getByContextHead === "function"
|
|
2427
|
+
);
|
|
2428
|
+
}
|
|
2429
|
+
|
|
2430
|
+
public canGetIndexedKeyByHead(): boolean {
|
|
2431
|
+
return (
|
|
2432
|
+
!this.isProgramValued &&
|
|
2433
|
+
typeof (this.index as ContextHeadIndex<I>).getIdByContextHead ===
|
|
2434
|
+
"function"
|
|
2435
|
+
);
|
|
2436
|
+
}
|
|
2437
|
+
|
|
2438
|
+
public canReadOriginalFieldPathsFromIndexedValue(
|
|
2439
|
+
paths: readonly (string | readonly string[])[],
|
|
2440
|
+
): boolean {
|
|
2441
|
+
return (
|
|
2442
|
+
!this.isProgramValued &&
|
|
2443
|
+
paths.every((path) =>
|
|
2444
|
+
this.transformerIsIdentity && this.indexedTypeIsDocumentType
|
|
2445
|
+
? true
|
|
2446
|
+
: documentTransformPreservesFieldPath(
|
|
2447
|
+
this.nativeTransformDescriptor,
|
|
2448
|
+
path,
|
|
2449
|
+
),
|
|
2450
|
+
)
|
|
2451
|
+
);
|
|
2452
|
+
}
|
|
2453
|
+
|
|
2454
|
+
public canReadNativeIndexedFieldValues(
|
|
2455
|
+
paths: readonly (string | readonly string[])[],
|
|
2456
|
+
): boolean {
|
|
2457
|
+
return (
|
|
2458
|
+
this.canReadOriginalFieldPathsFromIndexedValue(paths) &&
|
|
2459
|
+
typeof (
|
|
2460
|
+
this.index as {
|
|
2461
|
+
getNativeIndexedFieldValue?: (
|
|
2462
|
+
id: indexerTypes.IdKey,
|
|
2463
|
+
path: readonly string[],
|
|
2464
|
+
) => unknown;
|
|
2465
|
+
}
|
|
2466
|
+
).getNativeIndexedFieldValue === "function"
|
|
2467
|
+
);
|
|
2468
|
+
}
|
|
2469
|
+
|
|
2470
|
+
public getNativeIndexedFieldValue(
|
|
2471
|
+
id: indexerTypes.IdKey,
|
|
2472
|
+
path: string | readonly string[],
|
|
2473
|
+
): unknown {
|
|
2474
|
+
const read = (
|
|
2475
|
+
this.index as {
|
|
2476
|
+
getNativeIndexedFieldValue?: (
|
|
2477
|
+
id: indexerTypes.IdKey,
|
|
2478
|
+
path: readonly string[],
|
|
2479
|
+
) => unknown;
|
|
2480
|
+
}
|
|
2481
|
+
).getNativeIndexedFieldValue;
|
|
2482
|
+
if (typeof read !== "function") {
|
|
2483
|
+
return undefined;
|
|
2484
|
+
}
|
|
2485
|
+
return read.call(this.index, id, typeof path === "string" ? [path] : path);
|
|
2486
|
+
}
|
|
2487
|
+
|
|
2488
|
+
public _putIdentityWithContext(
|
|
2489
|
+
value: T,
|
|
2490
|
+
id: indexerTypes.IdKey,
|
|
2491
|
+
context: types.Context,
|
|
2492
|
+
options?: ContextualPutOptions,
|
|
2493
|
+
): MaybePromise<WithIndexedContext<T, I> | undefined> {
|
|
2494
|
+
const contextualPut = this.transformerIsIdentity
|
|
2495
|
+
? (this.index as ContextualIndexPut<I>).putWithContext
|
|
2496
|
+
: undefined;
|
|
2497
|
+
if (!contextualPut || this.isProgramValued) {
|
|
2498
|
+
return;
|
|
2499
|
+
}
|
|
2500
|
+
const indexable = value as any as I;
|
|
2501
|
+
const indexedValue = coerceWithIndexed(
|
|
2502
|
+
coerceWithContext(value, context),
|
|
2503
|
+
indexable,
|
|
2504
|
+
);
|
|
2505
|
+
this.cacheResolvedValue(id.primitive, value);
|
|
2506
|
+
const handleError = (error: unknown) => {
|
|
2507
|
+
if (error instanceof indexerTypes.NotStartedError && this.closed) {
|
|
2508
|
+
return indexedValue;
|
|
2509
|
+
}
|
|
2510
|
+
throw error;
|
|
2511
|
+
};
|
|
2512
|
+
try {
|
|
2513
|
+
const putResult = contextualPut.call(
|
|
2514
|
+
this.index,
|
|
2515
|
+
indexable,
|
|
2516
|
+
id,
|
|
2517
|
+
context,
|
|
2518
|
+
this.withContextualEncodedValue(options, context),
|
|
2519
|
+
);
|
|
2520
|
+
return isPromiseLike(putResult)
|
|
2521
|
+
? putResult.then(() => indexedValue, handleError)
|
|
2522
|
+
: indexedValue;
|
|
2523
|
+
} catch (error) {
|
|
2524
|
+
return handleError(error);
|
|
2525
|
+
}
|
|
2526
|
+
}
|
|
2527
|
+
|
|
2528
|
+
public _putStoredIdentityWithContext(
|
|
2529
|
+
value: T,
|
|
2530
|
+
id: indexerTypes.IdKey,
|
|
2531
|
+
context: types.Context,
|
|
2532
|
+
encodedValueParts: NonNullable<ContextualPutOptions["encodedValueParts"]>,
|
|
2533
|
+
options?: { replace?: boolean },
|
|
2534
|
+
): MaybePromise<WithIndexedContext<T, I> | undefined> {
|
|
2535
|
+
const contextualStoredPut = this.transformerIsIdentity
|
|
2536
|
+
? (this.index as ContextualIndexPut<I>).putStoredContextualEncodedValue
|
|
2537
|
+
: undefined;
|
|
2538
|
+
if (
|
|
2539
|
+
!contextualStoredPut ||
|
|
2540
|
+
this.isProgramValued ||
|
|
2541
|
+
!this.indexedTypeIsDocumentType
|
|
2542
|
+
) {
|
|
2543
|
+
return;
|
|
2544
|
+
}
|
|
2545
|
+
const indexable = value as any as I;
|
|
2546
|
+
const indexedValue = coerceWithIndexed(
|
|
2547
|
+
coerceWithContext(value, context),
|
|
2548
|
+
indexable,
|
|
2549
|
+
);
|
|
2550
|
+
this.cacheResolvedValue(id.primitive, value);
|
|
2551
|
+
const handleError = (error: unknown) => {
|
|
2552
|
+
if (error instanceof indexerTypes.NotStartedError && this.closed) {
|
|
2553
|
+
return indexedValue;
|
|
2554
|
+
}
|
|
2555
|
+
throw error;
|
|
2556
|
+
};
|
|
2557
|
+
try {
|
|
2558
|
+
const putResult = contextualStoredPut.call(
|
|
2559
|
+
this.index,
|
|
2560
|
+
id,
|
|
2561
|
+
encodedValueParts,
|
|
2562
|
+
options,
|
|
2563
|
+
);
|
|
2564
|
+
if (putResult === false) {
|
|
2565
|
+
return;
|
|
2566
|
+
}
|
|
2567
|
+
return isPromiseLike(putResult)
|
|
2568
|
+
? putResult.then(() => indexedValue, handleError)
|
|
2569
|
+
: indexedValue;
|
|
2570
|
+
} catch (error) {
|
|
2571
|
+
return handleError(error);
|
|
2572
|
+
}
|
|
2573
|
+
}
|
|
2574
|
+
|
|
2575
|
+
private _putPreparedNativeBackboneDocumentIndexWithContext(
|
|
2576
|
+
value: T,
|
|
2577
|
+
id: indexerTypes.IdKey,
|
|
2578
|
+
context: types.Context,
|
|
2579
|
+
nativeDocumentIndex: NativeBackboneDocumentIndexCommit<I>,
|
|
2580
|
+
options?: { replace?: boolean },
|
|
2581
|
+
): MaybePromise<WithIndexedContext<T, I> | undefined> {
|
|
2582
|
+
const contextualStoredPut = (this.index as ContextualIndexPut<I>)
|
|
2583
|
+
.putStoredContextualEncodedValue;
|
|
2584
|
+
if (!contextualStoredPut || this.isProgramValued) {
|
|
2585
|
+
return;
|
|
2586
|
+
}
|
|
2587
|
+
nativeDocumentIndex.setContext?.(context);
|
|
2588
|
+
const valueWithContext = coerceWithContext(value, context);
|
|
2589
|
+
const indexedValue = nativeDocumentIndex.indexable
|
|
2590
|
+
? coerceWithIndexed(valueWithContext, nativeDocumentIndex.indexable)
|
|
2591
|
+
: nativeDocumentIndex.getIndexable
|
|
2592
|
+
? coerceWithLazyIndexed(
|
|
2593
|
+
valueWithContext,
|
|
2594
|
+
nativeDocumentIndex.getIndexable,
|
|
2595
|
+
)
|
|
2596
|
+
: undefined;
|
|
2597
|
+
if (!indexedValue) {
|
|
2598
|
+
return;
|
|
2599
|
+
}
|
|
2600
|
+
this.cacheResolvedValue(id.primitive, value);
|
|
2601
|
+
const valuePrefixBytes = this.nativeBackboneDocumentIndexValuePrefixBytes(
|
|
2602
|
+
nativeDocumentIndex,
|
|
2603
|
+
context,
|
|
2604
|
+
);
|
|
2605
|
+
if (!valuePrefixBytes) {
|
|
2606
|
+
return;
|
|
2607
|
+
}
|
|
2608
|
+
const encodedValueParts = {
|
|
2609
|
+
prefix: valuePrefixBytes,
|
|
2610
|
+
suffix: encodeContextSuffix(context),
|
|
2611
|
+
};
|
|
2612
|
+
const handleError = (error: unknown) => {
|
|
2613
|
+
if (error instanceof indexerTypes.NotStartedError && this.closed) {
|
|
2614
|
+
return indexedValue;
|
|
2615
|
+
}
|
|
2616
|
+
throw error;
|
|
2617
|
+
};
|
|
2618
|
+
try {
|
|
2619
|
+
const putResult = contextualStoredPut.call(
|
|
2620
|
+
this.index,
|
|
2621
|
+
id,
|
|
2622
|
+
encodedValueParts,
|
|
2623
|
+
options,
|
|
2624
|
+
);
|
|
2625
|
+
if (putResult === false) {
|
|
2626
|
+
return;
|
|
2627
|
+
}
|
|
2628
|
+
return isPromiseLike(putResult)
|
|
2629
|
+
? putResult.then(() => indexedValue, handleError)
|
|
2630
|
+
: indexedValue;
|
|
2631
|
+
} catch (error) {
|
|
2632
|
+
return handleError(error);
|
|
2633
|
+
}
|
|
2634
|
+
}
|
|
2635
|
+
|
|
2636
|
+
private _putPreparedNativeBackboneDocumentIndexStoredWithContext(
|
|
2637
|
+
id: indexerTypes.IdKey,
|
|
2638
|
+
context: types.Context,
|
|
2639
|
+
nativeDocumentIndex: NativeBackboneDocumentIndexCommit<I>,
|
|
2640
|
+
options?: { replace?: boolean },
|
|
2641
|
+
): MaybePromise<boolean | undefined> {
|
|
2642
|
+
const contextualStoredPut = (this.index as ContextualIndexPut<I>)
|
|
2643
|
+
.putStoredContextualEncodedValue;
|
|
2644
|
+
if (!contextualStoredPut || this.isProgramValued) {
|
|
2645
|
+
return;
|
|
2646
|
+
}
|
|
2647
|
+
nativeDocumentIndex.setContext?.(context);
|
|
2648
|
+
const valuePrefixBytes = this.nativeBackboneDocumentIndexValuePrefixBytes(
|
|
2649
|
+
nativeDocumentIndex,
|
|
2650
|
+
context,
|
|
2651
|
+
);
|
|
2652
|
+
if (!valuePrefixBytes) {
|
|
2653
|
+
return;
|
|
2654
|
+
}
|
|
2655
|
+
const encodedValueParts = {
|
|
2656
|
+
prefix: valuePrefixBytes,
|
|
2657
|
+
suffix: encodeContextSuffix(context),
|
|
2658
|
+
};
|
|
2659
|
+
const handleError = (error: unknown) => {
|
|
2660
|
+
if (error instanceof indexerTypes.NotStartedError && this.closed) {
|
|
2661
|
+
return true;
|
|
2662
|
+
}
|
|
2663
|
+
throw error;
|
|
2664
|
+
};
|
|
2665
|
+
try {
|
|
2666
|
+
const putResult = contextualStoredPut.call(
|
|
2667
|
+
this.index,
|
|
2668
|
+
id,
|
|
2669
|
+
encodedValueParts,
|
|
2670
|
+
options,
|
|
2671
|
+
);
|
|
2672
|
+
if (putResult === false) {
|
|
2673
|
+
return false;
|
|
2674
|
+
}
|
|
2675
|
+
return isPromiseLike(putResult)
|
|
2676
|
+
? putResult.then(() => true, handleError)
|
|
2677
|
+
: true;
|
|
2678
|
+
} catch (error) {
|
|
2679
|
+
return handleError(error);
|
|
2680
|
+
}
|
|
2681
|
+
}
|
|
2682
|
+
|
|
2683
|
+
private _persistPreparedNativeBackboneDocumentIndexStoredWithContext(
|
|
2684
|
+
id: indexerTypes.IdKey,
|
|
2685
|
+
context: types.Context,
|
|
2686
|
+
nativeDocumentIndex?: NativeBackboneDocumentIndexCommit<I>,
|
|
2687
|
+
encodedValueParts?: NonNullable<ContextualPutOptions["encodedValueParts"]>,
|
|
2688
|
+
options?: { replace?: boolean },
|
|
2689
|
+
): MaybePromise<boolean | undefined> {
|
|
2690
|
+
const persistStoredPut = (this.index as ContextualIndexPut<I>)
|
|
2691
|
+
.persistStoredContextualEncodedValue;
|
|
2692
|
+
if (!persistStoredPut || this.isProgramValued) {
|
|
2693
|
+
return;
|
|
2694
|
+
}
|
|
2695
|
+
let storedParts:
|
|
2696
|
+
| NonNullable<ContextualPutOptions["encodedValueParts"]>
|
|
2697
|
+
| undefined;
|
|
2698
|
+
if (
|
|
2699
|
+
this.transformerIsIdentity &&
|
|
2700
|
+
this.indexedTypeIsDocumentType &&
|
|
2701
|
+
encodedValueParts
|
|
2702
|
+
) {
|
|
2703
|
+
storedParts = encodedValueParts;
|
|
2704
|
+
} else if (nativeDocumentIndex) {
|
|
2705
|
+
nativeDocumentIndex.setContext?.(context);
|
|
2706
|
+
const valuePrefixBytes =
|
|
2707
|
+
nativeDocumentIndex.valuePrefixBytes ??
|
|
2708
|
+
this.nativeBackboneDocumentIndexValuePrefixBytes(
|
|
2709
|
+
nativeDocumentIndex,
|
|
2710
|
+
context,
|
|
2711
|
+
);
|
|
2712
|
+
if (!valuePrefixBytes) {
|
|
2713
|
+
return;
|
|
2714
|
+
}
|
|
2715
|
+
storedParts = {
|
|
2716
|
+
prefix: valuePrefixBytes,
|
|
2717
|
+
suffix: encodeContextSuffix(context),
|
|
2718
|
+
};
|
|
2719
|
+
} else {
|
|
2720
|
+
return;
|
|
2721
|
+
}
|
|
2722
|
+
try {
|
|
2723
|
+
const persistResult = persistStoredPut.call(
|
|
2724
|
+
this.index,
|
|
2725
|
+
id,
|
|
2726
|
+
storedParts,
|
|
2727
|
+
options,
|
|
2728
|
+
);
|
|
2729
|
+
if (persistResult === false) {
|
|
2730
|
+
return false;
|
|
2731
|
+
}
|
|
2732
|
+
return isPromiseLike(persistResult)
|
|
2733
|
+
? persistResult.then(() => true, (error: unknown) => {
|
|
2734
|
+
if (error instanceof indexerTypes.NotStartedError && this.closed) {
|
|
2735
|
+
return true;
|
|
2736
|
+
}
|
|
2737
|
+
throw error;
|
|
2738
|
+
})
|
|
2739
|
+
: true;
|
|
2740
|
+
} catch (error) {
|
|
2741
|
+
if (error instanceof indexerTypes.NotStartedError && this.closed) {
|
|
2742
|
+
return true;
|
|
2743
|
+
}
|
|
2744
|
+
throw error;
|
|
2745
|
+
}
|
|
2746
|
+
}
|
|
2747
|
+
|
|
2748
|
+
private async _putManyPreparedNativeBackboneDocumentIndexWithContext(
|
|
2749
|
+
values: Array<{
|
|
2750
|
+
value: T;
|
|
2751
|
+
id: indexerTypes.IdKey;
|
|
2752
|
+
context: types.Context;
|
|
2753
|
+
nativeDocumentIndex?: NativeBackboneDocumentIndexCommit<I>;
|
|
2754
|
+
options?: { replace?: boolean };
|
|
2755
|
+
}>,
|
|
2756
|
+
): Promise<WithIndexedContext<T, I>[] | undefined> {
|
|
2757
|
+
if (values.length === 0) {
|
|
2758
|
+
return [];
|
|
2759
|
+
}
|
|
2760
|
+
const contextualBatchPut = (this.index as ContextualIndexPut<I>)
|
|
2761
|
+
.putWithContextBatch;
|
|
2762
|
+
if (!contextualBatchPut || this.isProgramValued) {
|
|
2763
|
+
return;
|
|
2764
|
+
}
|
|
2765
|
+
const indexedValues: WithIndexedContext<T, I>[] = [];
|
|
2766
|
+
const batchValues: Array<{
|
|
2767
|
+
value: I;
|
|
2768
|
+
id: indexerTypes.IdKey;
|
|
2769
|
+
context: types.Context;
|
|
2770
|
+
options: ContextualPutOptions;
|
|
2771
|
+
}> = [];
|
|
2772
|
+
for (const item of values) {
|
|
2773
|
+
if (!item.nativeDocumentIndex) {
|
|
2774
|
+
return;
|
|
2775
|
+
}
|
|
2776
|
+
item.nativeDocumentIndex.setContext?.(item.context);
|
|
2777
|
+
const valueWithContext = coerceWithContext(item.value, item.context);
|
|
2778
|
+
const indexedValue = item.nativeDocumentIndex.indexable
|
|
2779
|
+
? coerceWithIndexed(
|
|
2780
|
+
valueWithContext,
|
|
2781
|
+
item.nativeDocumentIndex.indexable,
|
|
2782
|
+
)
|
|
2783
|
+
: item.nativeDocumentIndex.getIndexable
|
|
2784
|
+
? coerceWithLazyIndexed(
|
|
2785
|
+
valueWithContext,
|
|
2786
|
+
item.nativeDocumentIndex.getIndexable,
|
|
2787
|
+
)
|
|
2788
|
+
: undefined;
|
|
2789
|
+
if (!indexedValue) {
|
|
2790
|
+
return;
|
|
2791
|
+
}
|
|
2792
|
+
const valuePrefixBytes = this.nativeBackboneDocumentIndexValuePrefixBytes(
|
|
2793
|
+
item.nativeDocumentIndex,
|
|
2794
|
+
item.context,
|
|
2795
|
+
);
|
|
2796
|
+
if (!valuePrefixBytes) {
|
|
2797
|
+
return;
|
|
2798
|
+
}
|
|
2799
|
+
this.cacheResolvedValue(item.id.primitive, item.value);
|
|
2800
|
+
indexedValues.push(indexedValue);
|
|
2801
|
+
batchValues.push({
|
|
2802
|
+
// Encoded native batches store from encodedValueParts; descriptor
|
|
2803
|
+
// projections keep the JS indexable lazy for event consumers.
|
|
2804
|
+
value:
|
|
2805
|
+
item.nativeDocumentIndex.indexable ?? (undefined as unknown as I),
|
|
2806
|
+
id: item.id,
|
|
2807
|
+
context: item.context,
|
|
2808
|
+
options: {
|
|
2809
|
+
replace: item.options?.replace,
|
|
2810
|
+
encodedValueParts: {
|
|
2811
|
+
prefix: valuePrefixBytes,
|
|
2812
|
+
suffix: encodeContextSuffix(item.context),
|
|
2813
|
+
},
|
|
2814
|
+
},
|
|
2815
|
+
});
|
|
2816
|
+
}
|
|
2817
|
+
const handleError = (error: unknown) => {
|
|
2818
|
+
if (error instanceof indexerTypes.NotStartedError && this.closed) {
|
|
2819
|
+
return indexedValues;
|
|
2820
|
+
}
|
|
2821
|
+
throw error;
|
|
2822
|
+
};
|
|
2823
|
+
try {
|
|
2824
|
+
const putResult = contextualBatchPut.call(this.index, batchValues);
|
|
2825
|
+
return isPromiseLike(putResult)
|
|
2826
|
+
? putResult.then(() => indexedValues, handleError)
|
|
2827
|
+
: indexedValues;
|
|
2828
|
+
} catch (error) {
|
|
2829
|
+
return handleError(error);
|
|
2830
|
+
}
|
|
2831
|
+
}
|
|
2832
|
+
|
|
2833
|
+
private async _putManyPreparedNativeBackboneDocumentIndexStored(
|
|
2834
|
+
values: Array<{
|
|
2835
|
+
value: T;
|
|
2836
|
+
id: indexerTypes.IdKey;
|
|
2837
|
+
context: types.Context;
|
|
2838
|
+
encodedValueParts?: NonNullable<
|
|
2839
|
+
ContextualPutOptions["encodedValueParts"]
|
|
2840
|
+
>;
|
|
2841
|
+
nativeDocumentIndex?: NativeBackboneDocumentIndexCommit<I>;
|
|
2842
|
+
options?: { replace?: boolean };
|
|
2843
|
+
}>,
|
|
2844
|
+
): Promise<boolean | undefined> {
|
|
2845
|
+
if (values.length === 0) {
|
|
2846
|
+
return true;
|
|
2847
|
+
}
|
|
2848
|
+
if (this.isProgramValued) {
|
|
2849
|
+
return;
|
|
2850
|
+
}
|
|
2851
|
+
const storedBatchPut = (this.index as ContextualIndexPut<I>)
|
|
2852
|
+
.putStoredContextualEncodedValueBatch;
|
|
2853
|
+
if (!storedBatchPut) {
|
|
2854
|
+
return;
|
|
2855
|
+
}
|
|
2856
|
+
const batchValues: Array<{
|
|
2857
|
+
id: indexerTypes.IdKey;
|
|
2858
|
+
encodedValueParts: NonNullable<
|
|
2859
|
+
ContextualPutOptions["encodedValueParts"]
|
|
2860
|
+
>;
|
|
2861
|
+
options?: { replace?: boolean };
|
|
2862
|
+
}> = [];
|
|
2863
|
+
for (const item of values) {
|
|
2864
|
+
let encodedValueParts:
|
|
2865
|
+
| NonNullable<ContextualPutOptions["encodedValueParts"]>
|
|
2866
|
+
| undefined;
|
|
2867
|
+
if (
|
|
2868
|
+
this.transformerIsIdentity &&
|
|
2869
|
+
this.indexedTypeIsDocumentType &&
|
|
2870
|
+
item.encodedValueParts
|
|
2871
|
+
) {
|
|
2872
|
+
encodedValueParts = item.encodedValueParts;
|
|
2873
|
+
} else if (item.nativeDocumentIndex) {
|
|
2874
|
+
item.nativeDocumentIndex.setContext?.(item.context);
|
|
2875
|
+
const valuePrefixBytes =
|
|
2876
|
+
item.nativeDocumentIndex.valuePrefixBytes ??
|
|
2877
|
+
this.nativeBackboneDocumentIndexValuePrefixBytes(
|
|
2878
|
+
item.nativeDocumentIndex,
|
|
2879
|
+
item.context,
|
|
2880
|
+
);
|
|
2881
|
+
if (!valuePrefixBytes) {
|
|
2882
|
+
return;
|
|
2883
|
+
}
|
|
2884
|
+
encodedValueParts = {
|
|
2885
|
+
prefix: valuePrefixBytes,
|
|
2886
|
+
suffix: encodeContextSuffix(item.context),
|
|
2887
|
+
};
|
|
2888
|
+
} else {
|
|
2889
|
+
return;
|
|
2890
|
+
}
|
|
2891
|
+
this.cacheResolvedValue(item.id.primitive, item.value);
|
|
2892
|
+
batchValues.push({
|
|
2893
|
+
id: item.id,
|
|
2894
|
+
encodedValueParts,
|
|
2895
|
+
options: item.options,
|
|
2896
|
+
});
|
|
2897
|
+
}
|
|
2898
|
+
const handleError = (error: unknown) => {
|
|
2899
|
+
if (error instanceof indexerTypes.NotStartedError && this.closed) {
|
|
2900
|
+
return true;
|
|
2901
|
+
}
|
|
2902
|
+
throw error;
|
|
2903
|
+
};
|
|
2904
|
+
try {
|
|
2905
|
+
return await storedBatchPut.call(this.index, batchValues);
|
|
2906
|
+
} catch (error) {
|
|
2907
|
+
return handleError(error);
|
|
2908
|
+
}
|
|
1497
2909
|
}
|
|
1498
2910
|
|
|
1499
|
-
public async
|
|
1500
|
-
|
|
1501
|
-
|
|
1502
|
-
|
|
1503
|
-
|
|
1504
|
-
|
|
1505
|
-
|
|
1506
|
-
|
|
1507
|
-
|
|
1508
|
-
|
|
1509
|
-
|
|
1510
|
-
|
|
1511
|
-
|
|
1512
|
-
|
|
1513
|
-
|
|
1514
|
-
|
|
1515
|
-
|
|
1516
|
-
|
|
1517
|
-
|
|
1518
|
-
|
|
1519
|
-
types.AbstractSearchResult,
|
|
1520
|
-
D
|
|
1521
|
-
>
|
|
1522
|
-
| undefined;
|
|
1523
|
-
|
|
1524
|
-
// Normalize the id key early so listeners can use it
|
|
1525
|
-
let idKey =
|
|
1526
|
-
key instanceof indexerTypes.IdKey ? key : indexerTypes.toId(key);
|
|
1527
|
-
|
|
1528
|
-
if (options?.waitFor) {
|
|
1529
|
-
// add change listener before query because we might get a concurrent change that matches the query,
|
|
1530
|
-
// that will not be included in the query result
|
|
1531
|
-
deferred = pDefer<WithIndexedContext<T, I> | WithContext<I>>();
|
|
1532
|
-
|
|
1533
|
-
const listener = (evt: CustomEvent<DocumentsChange<T, I>>) => {
|
|
1534
|
-
for (const added of evt.detail.added) {
|
|
1535
|
-
const id = indexerTypes.toId(
|
|
1536
|
-
this.indexByResolver(added.__indexed),
|
|
1537
|
-
).primitive;
|
|
1538
|
-
if (id === idKey.primitive) {
|
|
1539
|
-
deferred!.resolve(added);
|
|
1540
|
-
}
|
|
1541
|
-
}
|
|
1542
|
-
};
|
|
1543
|
-
let cleanedUp = false;
|
|
1544
|
-
let cleanup = () => {
|
|
1545
|
-
if (cleanedUp) return;
|
|
1546
|
-
cleanedUp = true;
|
|
1547
|
-
this.documentEvents.removeEventListener("change", listener);
|
|
1548
|
-
clearTimeout(timeout);
|
|
1549
|
-
this.events.removeEventListener("close", resolveUndefined);
|
|
1550
|
-
joinListener?.();
|
|
1551
|
-
};
|
|
2911
|
+
public async _putManyIdentityWithContext(
|
|
2912
|
+
values: Array<{
|
|
2913
|
+
value: T;
|
|
2914
|
+
id: indexerTypes.IdKey;
|
|
2915
|
+
context: types.Context;
|
|
2916
|
+
options?: ContextualPutOptions;
|
|
2917
|
+
}>,
|
|
2918
|
+
): Promise<WithIndexedContext<T, I>[] | undefined> {
|
|
2919
|
+
if (values.length === 0) {
|
|
2920
|
+
return [];
|
|
2921
|
+
}
|
|
2922
|
+
const contextualPut = this.transformerIsIdentity
|
|
2923
|
+
? (this.index as ContextualIndexPut<I>).putWithContext
|
|
2924
|
+
: undefined;
|
|
2925
|
+
const contextualBatchPut = this.transformerIsIdentity
|
|
2926
|
+
? (this.index as ContextualIndexPut<I>).putWithContextBatch
|
|
2927
|
+
: undefined;
|
|
2928
|
+
if ((!contextualBatchPut && !contextualPut) || this.isProgramValued) {
|
|
2929
|
+
return;
|
|
2930
|
+
}
|
|
1552
2931
|
|
|
1553
|
-
|
|
1554
|
-
|
|
2932
|
+
const indexedValues = values.map((item) => {
|
|
2933
|
+
const indexable = item.value as any as I;
|
|
2934
|
+
this.cacheResolvedValue(item.id.primitive, item.value);
|
|
2935
|
+
return {
|
|
2936
|
+
indexable,
|
|
2937
|
+
value: coerceWithIndexed(
|
|
2938
|
+
coerceWithContext(item.value, item.context),
|
|
2939
|
+
indexable,
|
|
2940
|
+
),
|
|
1555
2941
|
};
|
|
2942
|
+
});
|
|
1556
2943
|
|
|
1557
|
-
|
|
1558
|
-
|
|
1559
|
-
|
|
1560
|
-
|
|
1561
|
-
|
|
1562
|
-
|
|
1563
|
-
|
|
1564
|
-
|
|
1565
|
-
|
|
1566
|
-
|
|
1567
|
-
|
|
1568
|
-
|
|
1569
|
-
|
|
1570
|
-
|
|
1571
|
-
|
|
1572
|
-
|
|
1573
|
-
|
|
1574
|
-
|
|
1575
|
-
|
|
1576
|
-
|
|
1577
|
-
|
|
1578
|
-
|
|
1579
|
-
|
|
2944
|
+
try {
|
|
2945
|
+
if (contextualBatchPut) {
|
|
2946
|
+
await contextualBatchPut.call(
|
|
2947
|
+
this.index,
|
|
2948
|
+
values.map((item, index) => ({
|
|
2949
|
+
value: indexedValues[index]!.indexable,
|
|
2950
|
+
id: item.id,
|
|
2951
|
+
context: item.context,
|
|
2952
|
+
options: this.withContextualEncodedValue(
|
|
2953
|
+
item.options,
|
|
2954
|
+
item.context,
|
|
2955
|
+
),
|
|
2956
|
+
})),
|
|
2957
|
+
);
|
|
2958
|
+
} else {
|
|
2959
|
+
for (let i = 0; i < values.length; i++) {
|
|
2960
|
+
const item = values[i]!;
|
|
2961
|
+
await contextualPut!.call(
|
|
2962
|
+
this.index,
|
|
2963
|
+
indexedValues[i]!.indexable,
|
|
2964
|
+
item.id,
|
|
2965
|
+
item.context,
|
|
2966
|
+
this.withContextualEncodedValue(item.options, item.context),
|
|
2967
|
+
);
|
|
1580
2968
|
}
|
|
1581
2969
|
}
|
|
1582
|
-
|
|
1583
|
-
|
|
1584
|
-
|
|
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
|
-
}
|
|
1603
|
-
},
|
|
1604
|
-
});
|
|
2970
|
+
} catch (error) {
|
|
2971
|
+
if (error instanceof indexerTypes.NotStartedError && this.closed) {
|
|
2972
|
+
return indexedValues.map((item) => item.value);
|
|
1605
2973
|
}
|
|
2974
|
+
throw error;
|
|
1606
2975
|
}
|
|
1607
|
-
|
|
1608
|
-
const initialOptions = baseRemote
|
|
1609
|
-
? ({ ...(options as any), remote: baseRemote } as Options)
|
|
1610
|
-
: options;
|
|
1611
|
-
const result = (await this.getDetailed(idKey, initialOptions))?.[0]
|
|
1612
|
-
?.results[0];
|
|
1613
|
-
|
|
1614
|
-
// if no results, and we have remote joining options, we wait for the timout and if there are joining peers we re-query
|
|
1615
|
-
if (!result) {
|
|
1616
|
-
return deferred?.promise;
|
|
1617
|
-
} else if (deferred) {
|
|
1618
|
-
deferred.resolve(undefined);
|
|
1619
|
-
}
|
|
1620
|
-
return result?.value;
|
|
1621
|
-
}
|
|
1622
|
-
|
|
1623
|
-
public async getFromGid(gid: string) {
|
|
1624
|
-
const iterator = this.index.iterate({ query: { gid } });
|
|
1625
|
-
const one = await iterator.next(1);
|
|
1626
|
-
await iterator.close();
|
|
1627
|
-
return one[0];
|
|
2976
|
+
return indexedValues.map((item) => item.value);
|
|
1628
2977
|
}
|
|
1629
2978
|
|
|
1630
|
-
public async getFromHash(hash: string) {
|
|
1631
|
-
const iterator = this.index.iterate({ query: { hash } });
|
|
1632
|
-
const one = await iterator.next(1);
|
|
1633
|
-
await iterator.close();
|
|
1634
|
-
return one[0];
|
|
1635
|
-
}
|
|
1636
2979
|
public async put(
|
|
1637
2980
|
value: T,
|
|
1638
2981
|
id: indexerTypes.IdKey,
|
|
@@ -1660,6 +3003,7 @@ export class DocumentIndex<
|
|
|
1660
3003
|
});
|
|
1661
3004
|
return this.putWithContext(value, id, context, {
|
|
1662
3005
|
replace: existingDefined != null,
|
|
3006
|
+
transformFacts: { entryPublicKeys: entry.publicKeys },
|
|
1663
3007
|
});
|
|
1664
3008
|
}
|
|
1665
3009
|
|
|
@@ -1667,37 +3011,49 @@ export class DocumentIndex<
|
|
|
1667
3011
|
value: T,
|
|
1668
3012
|
id: indexerTypes.IdKey,
|
|
1669
3013
|
context: types.Context,
|
|
1670
|
-
options?:
|
|
3014
|
+
options?: ContextualPutOptions,
|
|
1671
3015
|
): Promise<{ context: types.Context; indexable: I }> {
|
|
1672
3016
|
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
|
-
);
|
|
3017
|
+
this.cacheResolvedValue(idString, value);
|
|
3018
|
+
const valueToIndex = this.transformerIsIdentity
|
|
3019
|
+
? (value as any as I)
|
|
3020
|
+
: await this.transformer(value, context, options?.transformFacts);
|
|
1694
3021
|
|
|
1695
3022
|
coerceWithIndexed(value, valueToIndex);
|
|
1696
3023
|
|
|
1697
3024
|
coerceWithContext(value, context);
|
|
1698
3025
|
|
|
1699
3026
|
try {
|
|
1700
|
-
|
|
3027
|
+
const contextualPut = this.transformerIsIdentity
|
|
3028
|
+
? (this.index as ContextualIndexPut<I>).putWithContext
|
|
3029
|
+
: undefined;
|
|
3030
|
+
if (contextualPut) {
|
|
3031
|
+
const encodedValueParts = this.encodeContextualIndexedValueParts(
|
|
3032
|
+
options?.encodedValue,
|
|
3033
|
+
context,
|
|
3034
|
+
);
|
|
3035
|
+
await contextualPut.call(
|
|
3036
|
+
this.index,
|
|
3037
|
+
valueToIndex,
|
|
3038
|
+
id,
|
|
3039
|
+
context,
|
|
3040
|
+
encodedValueParts
|
|
3041
|
+
? { ...options, encodedValue: undefined, encodedValueParts }
|
|
3042
|
+
: options?.encodedValue
|
|
3043
|
+
? { ...options, encodedValue: undefined }
|
|
3044
|
+
: options,
|
|
3045
|
+
);
|
|
3046
|
+
} else {
|
|
3047
|
+
const wrappedValueToIndex = new this.wrappedIndexedType(
|
|
3048
|
+
valueToIndex as I,
|
|
3049
|
+
context,
|
|
3050
|
+
);
|
|
3051
|
+
await this.index.put(
|
|
3052
|
+
wrappedValueToIndex,
|
|
3053
|
+
id,
|
|
3054
|
+
stripEncodedValue(options),
|
|
3055
|
+
);
|
|
3056
|
+
}
|
|
1701
3057
|
} catch (error) {
|
|
1702
3058
|
if (error instanceof indexerTypes.NotStartedError && this.closed) {
|
|
1703
3059
|
return { context, indexable: valueToIndex };
|
|
@@ -1707,20 +3063,211 @@ export class DocumentIndex<
|
|
|
1707
3063
|
return { context, indexable: valueToIndex };
|
|
1708
3064
|
}
|
|
1709
3065
|
|
|
1710
|
-
public
|
|
1711
|
-
|
|
1712
|
-
|
|
1713
|
-
|
|
3066
|
+
public async putManyWithContext(
|
|
3067
|
+
values: Array<{
|
|
3068
|
+
value: T;
|
|
3069
|
+
id: indexerTypes.IdKey;
|
|
3070
|
+
context: types.Context;
|
|
3071
|
+
options?: ContextualPutOptions;
|
|
3072
|
+
}>,
|
|
3073
|
+
): Promise<Array<{ context: types.Context; indexable: I }>> {
|
|
3074
|
+
if (values.length === 0) {
|
|
3075
|
+
return [];
|
|
3076
|
+
}
|
|
3077
|
+
let transformed: Array<
|
|
3078
|
+
(typeof values)[number] & {
|
|
3079
|
+
indexable: I;
|
|
3080
|
+
}
|
|
3081
|
+
>;
|
|
3082
|
+
if (this.transformerIsIdentity) {
|
|
3083
|
+
transformed = new Array(values.length);
|
|
3084
|
+
for (let i = 0; i < values.length; i++) {
|
|
3085
|
+
const item = values[i]!;
|
|
3086
|
+
this.cacheResolvedValue(item.id.primitive, item.value);
|
|
3087
|
+
const indexable = item.value as any as I;
|
|
3088
|
+
coerceWithIndexed(item.value, indexable);
|
|
3089
|
+
coerceWithContext(item.value, item.context);
|
|
3090
|
+
transformed[i] = { ...item, indexable };
|
|
3091
|
+
}
|
|
1714
3092
|
} else {
|
|
1715
|
-
|
|
1716
|
-
|
|
3093
|
+
transformed = await Promise.all(
|
|
3094
|
+
values.map(async (item) => {
|
|
3095
|
+
this.cacheResolvedValue(item.id.primitive, item.value);
|
|
3096
|
+
const indexable = await this.transformer(item.value, item.context);
|
|
3097
|
+
coerceWithIndexed(item.value, indexable);
|
|
3098
|
+
coerceWithContext(item.value, item.context);
|
|
3099
|
+
return { ...item, indexable };
|
|
3100
|
+
}),
|
|
3101
|
+
);
|
|
3102
|
+
}
|
|
3103
|
+
|
|
3104
|
+
try {
|
|
3105
|
+
const contextualBatchPut = this.transformerIsIdentity
|
|
3106
|
+
? (this.index as ContextualIndexPut<I>).putWithContextBatch
|
|
3107
|
+
: undefined;
|
|
3108
|
+
if (contextualBatchPut) {
|
|
3109
|
+
await contextualBatchPut.call(
|
|
3110
|
+
this.index,
|
|
3111
|
+
transformed.map((item) => ({
|
|
3112
|
+
value: item.indexable,
|
|
3113
|
+
id: item.id,
|
|
3114
|
+
context: item.context,
|
|
3115
|
+
options: this.withContextualEncodedValue(
|
|
3116
|
+
item.options,
|
|
3117
|
+
item.context,
|
|
3118
|
+
),
|
|
3119
|
+
})),
|
|
3120
|
+
);
|
|
3121
|
+
} else if (
|
|
3122
|
+
transformed.every((item) => item.options?.replace !== true) &&
|
|
3123
|
+
this.index.putBatch
|
|
3124
|
+
) {
|
|
3125
|
+
await this.index.putBatch(
|
|
3126
|
+
transformed.map(
|
|
3127
|
+
(item) => new this.wrappedIndexedType(item.indexable, item.context),
|
|
3128
|
+
),
|
|
3129
|
+
);
|
|
3130
|
+
} else {
|
|
3131
|
+
const contextualPut = this.transformerIsIdentity
|
|
3132
|
+
? (this.index as ContextualIndexPut<I>).putWithContext
|
|
3133
|
+
: undefined;
|
|
3134
|
+
for (const item of transformed) {
|
|
3135
|
+
if (contextualPut) {
|
|
3136
|
+
await contextualPut.call(
|
|
3137
|
+
this.index,
|
|
3138
|
+
item.indexable,
|
|
3139
|
+
item.id,
|
|
3140
|
+
item.context,
|
|
3141
|
+
this.withContextualEncodedValue(item.options, item.context),
|
|
3142
|
+
);
|
|
3143
|
+
} else {
|
|
3144
|
+
await this.index.put(
|
|
3145
|
+
new this.wrappedIndexedType(item.indexable, item.context),
|
|
3146
|
+
item.id,
|
|
3147
|
+
stripEncodedValue(item.options),
|
|
3148
|
+
);
|
|
3149
|
+
}
|
|
3150
|
+
}
|
|
1717
3151
|
}
|
|
3152
|
+
} catch (error) {
|
|
3153
|
+
if (error instanceof indexerTypes.NotStartedError && this.closed) {
|
|
3154
|
+
return transformed.map((item) => ({
|
|
3155
|
+
context: item.context,
|
|
3156
|
+
indexable: item.indexable,
|
|
3157
|
+
}));
|
|
3158
|
+
}
|
|
3159
|
+
throw error;
|
|
1718
3160
|
}
|
|
3161
|
+
|
|
3162
|
+
return transformed.map((item) => ({
|
|
3163
|
+
context: item.context,
|
|
3164
|
+
indexable: item.indexable,
|
|
3165
|
+
}));
|
|
3166
|
+
}
|
|
3167
|
+
|
|
3168
|
+
public _cacheResolvedIdentityValue(
|
|
3169
|
+
id: string | number | bigint,
|
|
3170
|
+
value: T,
|
|
3171
|
+
): void {
|
|
3172
|
+
this.cacheResolvedValue(id, value);
|
|
3173
|
+
}
|
|
3174
|
+
|
|
3175
|
+
private cacheResolvedValue(id: string | number | bigint, value: T): void {
|
|
3176
|
+
if (this.isProgramValued) {
|
|
3177
|
+
this._resolverProgramCache!.set(id, value);
|
|
3178
|
+
indexCacheLogger("cache:set:program", { id });
|
|
3179
|
+
} else if (this._resolverCache) {
|
|
3180
|
+
this._resolverCache.add(id, value);
|
|
3181
|
+
indexCacheLogger("cache:set:value", { id });
|
|
3182
|
+
}
|
|
3183
|
+
}
|
|
3184
|
+
|
|
3185
|
+
private withContextualEncodedValue(
|
|
3186
|
+
options: ContextualPutOptions | undefined,
|
|
3187
|
+
context: types.Context,
|
|
3188
|
+
): ContextualPutOptions | undefined {
|
|
3189
|
+
if (!options?.encodedValue) {
|
|
3190
|
+
return options;
|
|
3191
|
+
}
|
|
3192
|
+
const encodedValueParts = this.encodeContextualIndexedValueParts(
|
|
3193
|
+
options.encodedValue,
|
|
3194
|
+
context,
|
|
3195
|
+
);
|
|
3196
|
+
return encodedValueParts
|
|
3197
|
+
? { ...options, encodedValue: undefined, encodedValueParts }
|
|
3198
|
+
: options;
|
|
3199
|
+
}
|
|
3200
|
+
|
|
3201
|
+
private encodeContextualIndexedValueParts(
|
|
3202
|
+
encodedValue: Uint8Array | undefined,
|
|
3203
|
+
context: types.Context,
|
|
3204
|
+
): ContextualPutOptions["encodedValueParts"] | undefined {
|
|
3205
|
+
if (
|
|
3206
|
+
!encodedValue ||
|
|
3207
|
+
!this.transformerIsIdentity ||
|
|
3208
|
+
!this.indexedTypeIsDocumentType
|
|
3209
|
+
) {
|
|
3210
|
+
return;
|
|
3211
|
+
}
|
|
3212
|
+
return {
|
|
3213
|
+
prefix: encodedValue,
|
|
3214
|
+
suffix: encodeContextSuffix(context),
|
|
3215
|
+
};
|
|
3216
|
+
}
|
|
3217
|
+
|
|
3218
|
+
public del(key: indexerTypes.IdKey) {
|
|
3219
|
+
this.deleteResolvedCacheForKey(key);
|
|
1719
3220
|
return this.index.del({
|
|
1720
3221
|
query: [indexerTypes.getMatcher(this.indexBy, key.key)],
|
|
1721
3222
|
});
|
|
1722
3223
|
}
|
|
1723
3224
|
|
|
3225
|
+
public async delMany(keys: indexerTypes.IdKey[]): Promise<void> {
|
|
3226
|
+
if (keys.length === 0) {
|
|
3227
|
+
return;
|
|
3228
|
+
}
|
|
3229
|
+
for (const key of keys) {
|
|
3230
|
+
this.deleteResolvedCacheForKey(key);
|
|
3231
|
+
}
|
|
3232
|
+
const delIdsNoReturn = (this.index as ExactDeleteIndex).delIdsNoReturn;
|
|
3233
|
+
if (delIdsNoReturn) {
|
|
3234
|
+
await delIdsNoReturn.call(this.index, keys);
|
|
3235
|
+
return;
|
|
3236
|
+
}
|
|
3237
|
+
const delIds = (this.index as ExactDeleteIndex).delIds;
|
|
3238
|
+
if (delIds) {
|
|
3239
|
+
await delIds.call(this.index, keys);
|
|
3240
|
+
return;
|
|
3241
|
+
}
|
|
3242
|
+
await Promise.all(keys.map((key) => this.del(key)));
|
|
3243
|
+
}
|
|
3244
|
+
|
|
3245
|
+
public clearResolvedCacheForKeys(keys: indexerTypes.IdKey[]): void {
|
|
3246
|
+
for (const key of keys) {
|
|
3247
|
+
this.deleteResolvedCacheForKey(key);
|
|
3248
|
+
}
|
|
3249
|
+
}
|
|
3250
|
+
|
|
3251
|
+
public delManyMaybe(keys: indexerTypes.IdKey[]): MaybePromise<void> {
|
|
3252
|
+
if (keys.length === 0) {
|
|
3253
|
+
return;
|
|
3254
|
+
}
|
|
3255
|
+
for (const key of keys) {
|
|
3256
|
+
this.deleteResolvedCacheForKey(key);
|
|
3257
|
+
}
|
|
3258
|
+
const delIdsNoReturn = (this.index as ExactDeleteIndex).delIdsNoReturn;
|
|
3259
|
+
if (delIdsNoReturn) {
|
|
3260
|
+
const result = delIdsNoReturn.call(this.index, keys);
|
|
3261
|
+
return isPromiseLike(result) ? result.then(() => undefined) : undefined;
|
|
3262
|
+
}
|
|
3263
|
+
const delIds = (this.index as ExactDeleteIndex).delIds;
|
|
3264
|
+
if (delIds) {
|
|
3265
|
+
const result = delIds.call(this.index, keys);
|
|
3266
|
+
return isPromiseLike(result) ? result.then(() => undefined) : undefined;
|
|
3267
|
+
}
|
|
3268
|
+
return Promise.all(keys.map((key) => this.del(key))).then(() => undefined);
|
|
3269
|
+
}
|
|
3270
|
+
|
|
1724
3271
|
public async getDetailed<
|
|
1725
3272
|
Options extends QueryOptions<T, I, D, Resolve>,
|
|
1726
3273
|
Resolve extends boolean | undefined = ExtractResolveFromOptions<Options>,
|
|
@@ -2198,15 +3745,18 @@ export class DocumentIndex<
|
|
|
2198
3745
|
}
|
|
2199
3746
|
|
|
2200
3747
|
get countIteratorsInProgress() {
|
|
2201
|
-
return this._resumableIterators
|
|
3748
|
+
return this._resumableIterators?.queues.size ?? 0;
|
|
2202
3749
|
}
|
|
2203
3750
|
|
|
2204
3751
|
private clearAllResultQueues() {
|
|
3752
|
+
if (!this._resultQueue) {
|
|
3753
|
+
return;
|
|
3754
|
+
}
|
|
2205
3755
|
for (const [key, queue] of this._resultQueue) {
|
|
2206
3756
|
clearTimeout(queue.timeout);
|
|
2207
3757
|
this._resultQueue.delete(key);
|
|
2208
3758
|
this.cancelIteratorKeepAlive(key);
|
|
2209
|
-
this._resumableIterators
|
|
3759
|
+
this._resumableIterators?.close({ idString: key });
|
|
2210
3760
|
}
|
|
2211
3761
|
}
|
|
2212
3762
|
|
|
@@ -2501,9 +4051,10 @@ export class DocumentIndex<
|
|
|
2501
4051
|
signal: options?.signal,
|
|
2502
4052
|
});
|
|
2503
4053
|
|
|
2504
|
-
// Cold start: cover can be temporarily self-only
|
|
2505
|
-
// converges. For explicit remote searches,
|
|
2506
|
-
// instead of waiting for replicator
|
|
4054
|
+
// Cold start: cover can be temporarily self-only or empty while
|
|
4055
|
+
// replication metadata converges. For explicit bounded remote searches,
|
|
4056
|
+
// query bounded connected peers instead of waiting for replicator
|
|
4057
|
+
// metadata to catch up.
|
|
2507
4058
|
if (!options?.remote?.from && isDefaultDomainArgs && remoteWasExplicit) {
|
|
2508
4059
|
const selfHash = this.node.identity.publicKey.hashcode();
|
|
2509
4060
|
const remoteCount = replicatorGroups.filter(
|
|
@@ -2513,25 +4064,53 @@ export class DocumentIndex<
|
|
|
2513
4064
|
const waitEnabled = Boolean(remote.wait);
|
|
2514
4065
|
const coverIsSelfOnly =
|
|
2515
4066
|
replicatorGroups.length === 1 && replicatorGroups[0] === selfHash;
|
|
4067
|
+
const coverIsColdEmpty =
|
|
4068
|
+
replicatorGroups.length === 0 && remote.timeout != null;
|
|
4069
|
+
|
|
4070
|
+
// If the cover is explicitly empty (no shards), don't override it
|
|
4071
|
+
// unless the caller requested waiting for joins (e.g. get(waitFor))
|
|
4072
|
+
// or bounded the remote query with a timeout.
|
|
4073
|
+
if (waitEnabled || coverIsSelfOnly || coverIsColdEmpty) {
|
|
4074
|
+
const extra: string[] = [];
|
|
4075
|
+
const addExtra = (hash: string | undefined) => {
|
|
4076
|
+
if (!hash || hash === selfHash || extra.includes(hash)) {
|
|
4077
|
+
return;
|
|
4078
|
+
}
|
|
4079
|
+
extra.push(hash);
|
|
4080
|
+
};
|
|
2516
4081
|
|
|
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
4082
|
const peerMap: Map<string, unknown> | undefined = (
|
|
2521
4083
|
this.node.services.pubsub as any
|
|
2522
4084
|
)?.peers;
|
|
4085
|
+
|
|
4086
|
+
// Only consider replicators that are currently reachable.
|
|
4087
|
+
// The replicator index can contain stale (offline) peers, e.g.
|
|
4088
|
+
// persisted replicators after a restart; querying those would
|
|
4089
|
+
// block the first batch for the full wait timeout instead of
|
|
4090
|
+
// letting joining peers be merged as they arrive.
|
|
4091
|
+
if (peerMap?.has) {
|
|
4092
|
+
try {
|
|
4093
|
+
for (const hash of await this._log.getReplicators()) {
|
|
4094
|
+
if (peerMap.has(hash)) {
|
|
4095
|
+
addExtra(hash);
|
|
4096
|
+
}
|
|
4097
|
+
if (extra.length >= 8) break;
|
|
4098
|
+
}
|
|
4099
|
+
} catch {
|
|
4100
|
+
// Fall through to connected peers when the local replicator
|
|
4101
|
+
// index is not ready yet.
|
|
4102
|
+
}
|
|
4103
|
+
}
|
|
4104
|
+
|
|
2523
4105
|
if (peerMap?.keys) {
|
|
2524
|
-
const extra: string[] = [];
|
|
2525
4106
|
for (const hash of peerMap.keys()) {
|
|
2526
|
-
|
|
2527
|
-
extra.push(hash);
|
|
4107
|
+
addExtra(hash);
|
|
2528
4108
|
if (extra.length >= 8) break;
|
|
2529
4109
|
}
|
|
2530
|
-
|
|
2531
|
-
|
|
2532
|
-
|
|
2533
|
-
|
|
2534
|
-
}
|
|
4110
|
+
}
|
|
4111
|
+
|
|
4112
|
+
if (extra.length > 0) {
|
|
4113
|
+
replicatorGroups = [...new Set([...replicatorGroups, ...extra])];
|
|
2535
4114
|
}
|
|
2536
4115
|
}
|
|
2537
4116
|
}
|
|
@@ -3805,6 +5384,8 @@ export class DocumentIndex<
|
|
|
3805
5384
|
return [];
|
|
3806
5385
|
}
|
|
3807
5386
|
|
|
5387
|
+
await pendingUpdateProcessing;
|
|
5388
|
+
|
|
3808
5389
|
const bufferedBeforeFetch = peerBuffers().length;
|
|
3809
5390
|
const localHash = this.node.identity.publicKey.hashcode();
|
|
3810
5391
|
const hasBufferedRemoteResults = [...peerBufferMap.entries()].some(
|
|
@@ -4087,6 +5668,7 @@ export class DocumentIndex<
|
|
|
4087
5668
|
| Extract<UpdateReason, "join" | "change" | "push">
|
|
4088
5669
|
| undefined;
|
|
4089
5670
|
let hasDeliveredResults = false;
|
|
5671
|
+
let pendingUpdateProcessing: Promise<void> = Promise.resolve();
|
|
4090
5672
|
|
|
4091
5673
|
const emitOnBatch = async (
|
|
4092
5674
|
batch: ValueTypeFromRequest<Resolve, T, I>[],
|
|
@@ -4381,7 +5963,7 @@ export class DocumentIndex<
|
|
|
4381
5963
|
return value as WithContext<I>;
|
|
4382
5964
|
};
|
|
4383
5965
|
|
|
4384
|
-
const
|
|
5966
|
+
const processChange = async (evt: CustomEvent<DocumentsChange<T, I>>) => {
|
|
4385
5967
|
// Optional filter to mutate/suppress change events
|
|
4386
5968
|
indexIteratorLogger.trace(
|
|
4387
5969
|
"processing live update change event",
|
|
@@ -4523,6 +6105,13 @@ export class DocumentIndex<
|
|
|
4523
6105
|
}
|
|
4524
6106
|
signalUpdate();
|
|
4525
6107
|
};
|
|
6108
|
+
const onChange = (evt: CustomEvent<DocumentsChange<T, I>>) => {
|
|
6109
|
+
const task = pendingUpdateProcessing.then(() => processChange(evt));
|
|
6110
|
+
pendingUpdateProcessing = task.catch((error) => {
|
|
6111
|
+
warn("Failed to process iterator update", error);
|
|
6112
|
+
});
|
|
6113
|
+
return task;
|
|
6114
|
+
};
|
|
4526
6115
|
|
|
4527
6116
|
this.documentEvents.addEventListener("change", onChange);
|
|
4528
6117
|
updatesCleanup = () => {
|