@peerbit/document 15.0.12 → 15.0.14
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/src/callback-detachment.d.ts +14 -0
- package/dist/src/callback-detachment.d.ts.map +1 -0
- package/dist/src/callback-detachment.js +234 -0
- package/dist/src/callback-detachment.js.map +1 -0
- package/dist/src/domain.d.ts.map +1 -1
- package/dist/src/domain.js +5 -3
- package/dist/src/domain.js.map +1 -1
- package/dist/src/program.d.ts +5 -0
- package/dist/src/program.d.ts.map +1 -1
- package/dist/src/program.js +170 -43
- package/dist/src/program.js.map +1 -1
- package/dist/src/search.d.ts +5 -2
- package/dist/src/search.d.ts.map +1 -1
- package/dist/src/search.js +20 -6
- package/dist/src/search.js.map +1 -1
- package/package.json +15 -13
- package/src/callback-detachment.ts +308 -0
- package/src/domain.ts +11 -3
- package/src/program.ts +265 -52
- package/src/search.ts +44 -9
package/src/program.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import {
|
|
2
2
|
type AbstractType,
|
|
3
3
|
BorshError,
|
|
4
|
+
deserialize,
|
|
4
5
|
field,
|
|
5
6
|
serialize,
|
|
6
7
|
variant,
|
|
@@ -44,6 +45,10 @@ import {
|
|
|
44
45
|
SharedLog,
|
|
45
46
|
type SharedLogOptions,
|
|
46
47
|
} from "@peerbit/shared-log";
|
|
48
|
+
import {
|
|
49
|
+
detachCanPerformCallbackProperties,
|
|
50
|
+
detachEntryPayloadForCallback,
|
|
51
|
+
} from "./callback-detachment.js";
|
|
47
52
|
import { MAX_BATCH_SIZE } from "./constants.js";
|
|
48
53
|
import type { CustomDocumentDomain } from "./domain.js";
|
|
49
54
|
import type { DocumentEvents, DocumentsChange } from "./events.js";
|
|
@@ -74,6 +79,7 @@ import {
|
|
|
74
79
|
type CanRead,
|
|
75
80
|
type CanSearch,
|
|
76
81
|
DocumentIndex,
|
|
82
|
+
type DocumentIndexWriteSession,
|
|
77
83
|
type GetOptions,
|
|
78
84
|
INDEX_CONTEXT_SHAPE,
|
|
79
85
|
type IndexedContextOnly,
|
|
@@ -1087,12 +1093,23 @@ export class Documents<
|
|
|
1087
1093
|
private _documentChangeListenerTrackingInitialized = false;
|
|
1088
1094
|
private _documentBackend!: DocumentBackend<T>;
|
|
1089
1095
|
private _canAppendDecodedDocuments = new WeakMap<PutOperation, T>();
|
|
1096
|
+
private acceptPutOperation(
|
|
1097
|
+
operation: PutOperation,
|
|
1098
|
+
document: T | undefined,
|
|
1099
|
+
hasDocument: boolean,
|
|
1100
|
+
): PutOperation {
|
|
1101
|
+
if (hasDocument) {
|
|
1102
|
+
this._canAppendDecodedDocuments.set(operation, document!);
|
|
1103
|
+
}
|
|
1104
|
+
return operation;
|
|
1105
|
+
}
|
|
1090
1106
|
private _nativeDocumentIdExtractionPlan?: SimpleDocumentFieldExtractionPlan;
|
|
1091
1107
|
private _nativeDocumentFieldExtractionPlans?: Map<
|
|
1092
1108
|
string,
|
|
1093
1109
|
SimpleDocumentFieldExtractionPlan | undefined
|
|
1094
1110
|
>;
|
|
1095
1111
|
private _hasLogTrim = false;
|
|
1112
|
+
private _hasCustomIdResolver = false;
|
|
1096
1113
|
private idResolver!: (any: any) => indexerTypes.Ideable;
|
|
1097
1114
|
private domain?: CustomDocumentDomain<InferR<D>>;
|
|
1098
1115
|
private strictHistory: boolean;
|
|
@@ -1130,6 +1147,84 @@ export class Documents<
|
|
|
1130
1147
|
);
|
|
1131
1148
|
}
|
|
1132
1149
|
|
|
1150
|
+
private detachDomainEntryCallback(
|
|
1151
|
+
domain: CustomDocumentDomain<InferR<D>>,
|
|
1152
|
+
): CustomDocumentDomain<InferR<D>> {
|
|
1153
|
+
const boundMethods = new Map<
|
|
1154
|
+
PropertyKey,
|
|
1155
|
+
{ source: Function; bound: Function }
|
|
1156
|
+
>();
|
|
1157
|
+
const detachedFromEntry = (
|
|
1158
|
+
entry: Parameters<CustomDocumentDomain<InferR<D>>["fromEntry"]>[0],
|
|
1159
|
+
) => {
|
|
1160
|
+
const fromEntry = Reflect.get(
|
|
1161
|
+
domain,
|
|
1162
|
+
"fromEntry",
|
|
1163
|
+
domain,
|
|
1164
|
+
) as CustomDocumentDomain<InferR<D>>["fromEntry"];
|
|
1165
|
+
return Reflect.apply(fromEntry, domain, [
|
|
1166
|
+
entry instanceof Entry
|
|
1167
|
+
? detachEntryPayloadForCallback(entry)
|
|
1168
|
+
: entry,
|
|
1169
|
+
]);
|
|
1170
|
+
};
|
|
1171
|
+
const facade = Object.create(
|
|
1172
|
+
Object.getPrototypeOf(domain),
|
|
1173
|
+
) as CustomDocumentDomain<InferR<D>>;
|
|
1174
|
+
let callbackDomain: CustomDocumentDomain<InferR<D>>;
|
|
1175
|
+
callbackDomain = new Proxy(facade, {
|
|
1176
|
+
get(_target, property) {
|
|
1177
|
+
if (property === "fromEntry") {
|
|
1178
|
+
return detachedFromEntry;
|
|
1179
|
+
}
|
|
1180
|
+
if (property === "valueOf") {
|
|
1181
|
+
return () => callbackDomain;
|
|
1182
|
+
}
|
|
1183
|
+
const value = Reflect.get(domain, property, domain);
|
|
1184
|
+
if (property === "constructor" || typeof value !== "function") {
|
|
1185
|
+
return value;
|
|
1186
|
+
}
|
|
1187
|
+
const existing = boundMethods.get(property);
|
|
1188
|
+
if (existing && existing.source === value) {
|
|
1189
|
+
return existing.bound;
|
|
1190
|
+
}
|
|
1191
|
+
const bound = value.bind(domain);
|
|
1192
|
+
boundMethods.set(property, { source: value, bound });
|
|
1193
|
+
return bound;
|
|
1194
|
+
},
|
|
1195
|
+
set(_target, property, value) {
|
|
1196
|
+
return Reflect.set(domain, property, value, domain);
|
|
1197
|
+
},
|
|
1198
|
+
has(_target, property) {
|
|
1199
|
+
return Reflect.has(domain, property);
|
|
1200
|
+
},
|
|
1201
|
+
ownKeys() {
|
|
1202
|
+
return Reflect.ownKeys(domain);
|
|
1203
|
+
},
|
|
1204
|
+
getOwnPropertyDescriptor(_target, property) {
|
|
1205
|
+
const descriptor = Reflect.getOwnPropertyDescriptor(domain, property);
|
|
1206
|
+
if (!descriptor) {
|
|
1207
|
+
return undefined;
|
|
1208
|
+
}
|
|
1209
|
+
if (property === "fromEntry") {
|
|
1210
|
+
return "value" in descriptor
|
|
1211
|
+
? {
|
|
1212
|
+
...descriptor,
|
|
1213
|
+
value: detachedFromEntry,
|
|
1214
|
+
configurable: true,
|
|
1215
|
+
}
|
|
1216
|
+
: {
|
|
1217
|
+
...descriptor,
|
|
1218
|
+
get: () => detachedFromEntry,
|
|
1219
|
+
configurable: true,
|
|
1220
|
+
};
|
|
1221
|
+
}
|
|
1222
|
+
return { ...descriptor, configurable: true };
|
|
1223
|
+
},
|
|
1224
|
+
});
|
|
1225
|
+
return callbackDomain;
|
|
1226
|
+
}
|
|
1227
|
+
|
|
1133
1228
|
private createNativeDocumentBackendContext(): NativeDocumentBackendContext<
|
|
1134
1229
|
T,
|
|
1135
1230
|
I
|
|
@@ -2025,6 +2120,7 @@ export class Documents<
|
|
|
2025
2120
|
*/
|
|
2026
2121
|
private getLocalIndexedContextForChange(
|
|
2027
2122
|
key: indexerTypes.IdKey,
|
|
2123
|
+
orderedSession?: DocumentIndexWriteSession<I>,
|
|
2028
2124
|
): Promise<indexerTypes.IndexedResult<IndexedContextOnly<I>> | undefined> {
|
|
2029
2125
|
// Not every backing index resolves asynchronously (the native one answers
|
|
2030
2126
|
// synchronously, and may throw synchronously), so guard both shapes without
|
|
@@ -2033,9 +2129,9 @@ export class Documents<
|
|
|
2033
2129
|
| indexerTypes.IndexedResult<IndexedContextOnly<I>>
|
|
2034
2130
|
| undefined;
|
|
2035
2131
|
try {
|
|
2036
|
-
const result =
|
|
2037
|
-
key,
|
|
2038
|
-
|
|
2132
|
+
const result = (orderedSession
|
|
2133
|
+
? orderedSession.get(key, { shape: INDEX_CONTEXT_SHAPE })
|
|
2134
|
+
: this.getLocalIndexedContext(key)) as MaybePromise<LocalIndexedContext>;
|
|
2039
2135
|
return (
|
|
2040
2136
|
isPromiseLike(result)
|
|
2041
2137
|
? result.catch((error) => this.recoverClosedIndexContextRead(error))
|
|
@@ -2354,6 +2450,7 @@ export class Documents<
|
|
|
2354
2450
|
: (obj: any) =>
|
|
2355
2451
|
indexerTypes.extractFieldValue(obj, idProperty as string[]));
|
|
2356
2452
|
|
|
2453
|
+
this._hasCustomIdResolver = options.id != null;
|
|
2357
2454
|
this.idResolver = idResolver;
|
|
2358
2455
|
this.strictHistory = options.strictHistory ?? false;
|
|
2359
2456
|
this._hasLogTrim = options.log?.trim != null;
|
|
@@ -2402,7 +2499,9 @@ export class Documents<
|
|
|
2402
2499
|
// 7 -> log v9) is retired; the rejection at the top of open() fires for
|
|
2403
2500
|
// any defined value before this point.
|
|
2404
2501
|
|
|
2405
|
-
this.domain = options.domain
|
|
2502
|
+
this.domain = options.domain
|
|
2503
|
+
? this.detachDomainEntryCallback(options.domain(this))
|
|
2504
|
+
: undefined;
|
|
2406
2505
|
|
|
2407
2506
|
let keepFunction:
|
|
2408
2507
|
| ((
|
|
@@ -2447,7 +2546,16 @@ export class Documents<
|
|
|
2447
2546
|
return false;
|
|
2448
2547
|
};
|
|
2449
2548
|
} else {
|
|
2450
|
-
|
|
2549
|
+
const keep = options?.keep;
|
|
2550
|
+
keepFunction = keep
|
|
2551
|
+
? function (this: unknown, entry) {
|
|
2552
|
+
return Reflect.apply(keep, this, [
|
|
2553
|
+
entry instanceof Entry
|
|
2554
|
+
? detachEntryPayloadForCallback(entry)
|
|
2555
|
+
: entry,
|
|
2556
|
+
]);
|
|
2557
|
+
}
|
|
2558
|
+
: undefined;
|
|
2451
2559
|
}
|
|
2452
2560
|
|
|
2453
2561
|
await this.log.open({
|
|
@@ -2471,7 +2579,10 @@ export class Documents<
|
|
|
2471
2579
|
distributionDebounceTime: options?.distributionDebounceTime,
|
|
2472
2580
|
strictFullReplicaFallback: false,
|
|
2473
2581
|
domain: options?.domain
|
|
2474
|
-
? () =>
|
|
2582
|
+
? () =>
|
|
2583
|
+
this.detachDomainEntryCallback(
|
|
2584
|
+
options.domain!(this),
|
|
2585
|
+
) as unknown as D
|
|
2475
2586
|
: undefined,
|
|
2476
2587
|
eagerBlocks: options?.eagerBlocks,
|
|
2477
2588
|
fanout: options?.fanout,
|
|
@@ -2563,7 +2674,7 @@ export class Documents<
|
|
|
2563
2674
|
}
|
|
2564
2675
|
|
|
2565
2676
|
try {
|
|
2566
|
-
|
|
2677
|
+
const operation: PutOperation | DeleteOperation = l0;
|
|
2567
2678
|
if (this._optionCanPerform) {
|
|
2568
2679
|
if (this._optionCanPerformNativePolicy && this.isNativeMode()) {
|
|
2569
2680
|
return this.nativeCanPerformAllowsAppend(
|
|
@@ -2573,22 +2684,22 @@ export class Documents<
|
|
|
2573
2684
|
reference?.document,
|
|
2574
2685
|
);
|
|
2575
2686
|
}
|
|
2576
|
-
let document: T | undefined
|
|
2577
|
-
if (
|
|
2578
|
-
|
|
2579
|
-
|
|
2580
|
-
|
|
2581
|
-
|
|
2582
|
-
|
|
2583
|
-
|
|
2584
|
-
|
|
2585
|
-
} else if (isDeleteOperation(l0)) {
|
|
2586
|
-
// Nothing to do here by default.
|
|
2587
|
-
// Checking if the document exists is not necessary since it
|
|
2588
|
-
// might already be deleted.
|
|
2589
|
-
} else {
|
|
2590
|
-
throw new Error("Unsupported operation");
|
|
2687
|
+
let document: T | undefined;
|
|
2688
|
+
if (isPutOperation(l0)) {
|
|
2689
|
+
const cachedDocument = this._canAppendDecodedDocuments.get(l0);
|
|
2690
|
+
this._canAppendDecodedDocuments.delete(l0);
|
|
2691
|
+
document =
|
|
2692
|
+
cachedDocument ??
|
|
2693
|
+
this._index.valueEncoding.decoder(new Uint8Array(l0.data));
|
|
2694
|
+
if (!document) {
|
|
2695
|
+
return false;
|
|
2591
2696
|
}
|
|
2697
|
+
} else if (isDeleteOperation(l0)) {
|
|
2698
|
+
// Nothing to do here by default.
|
|
2699
|
+
// Checking if the document exists is not necessary since it
|
|
2700
|
+
// might already be deleted.
|
|
2701
|
+
} else {
|
|
2702
|
+
throw new Error("Unsupported operation");
|
|
2592
2703
|
}
|
|
2593
2704
|
const previousEntries =
|
|
2594
2705
|
this._optionCanPerformNativePolicy &&
|
|
@@ -2596,7 +2707,9 @@ export class Documents<
|
|
|
2596
2707
|
canPerformPolicyNeedsPreviousEntries(
|
|
2597
2708
|
this._optionCanPerformNativePolicy,
|
|
2598
2709
|
)
|
|
2599
|
-
? await this.resolveCanPerformPreviousEntries(entry)
|
|
2710
|
+
? (await this.resolveCanPerformPreviousEntries(entry)).map(
|
|
2711
|
+
detachEntryPayloadForCallback,
|
|
2712
|
+
)
|
|
2600
2713
|
: undefined;
|
|
2601
2714
|
const deleteValue =
|
|
2602
2715
|
this._optionCanPerformNativePolicy &&
|
|
@@ -2608,20 +2721,22 @@ export class Documents<
|
|
|
2608
2721
|
: undefined;
|
|
2609
2722
|
if (
|
|
2610
2723
|
!(await this._optionCanPerform(
|
|
2611
|
-
|
|
2612
|
-
|
|
2613
|
-
|
|
2614
|
-
|
|
2615
|
-
|
|
2616
|
-
|
|
2617
|
-
|
|
2618
|
-
|
|
2619
|
-
|
|
2620
|
-
|
|
2621
|
-
|
|
2622
|
-
|
|
2623
|
-
|
|
2624
|
-
|
|
2724
|
+
detachCanPerformCallbackProperties(
|
|
2725
|
+
isPutOperation(operation)
|
|
2726
|
+
? {
|
|
2727
|
+
type: "put",
|
|
2728
|
+
value: document!,
|
|
2729
|
+
operation,
|
|
2730
|
+
entry: entry as unknown as Entry<PutOperation>,
|
|
2731
|
+
previousEntries,
|
|
2732
|
+
}
|
|
2733
|
+
: {
|
|
2734
|
+
type: "delete",
|
|
2735
|
+
value: deleteValue,
|
|
2736
|
+
operation,
|
|
2737
|
+
entry: entry as unknown as Entry<DeleteOperation>,
|
|
2738
|
+
},
|
|
2739
|
+
),
|
|
2625
2740
|
))
|
|
2626
2741
|
) {
|
|
2627
2742
|
return false;
|
|
@@ -2710,6 +2825,12 @@ export class Documents<
|
|
|
2710
2825
|
return entries;
|
|
2711
2826
|
}
|
|
2712
2827
|
|
|
2828
|
+
private detachDocumentValueForCallback(document: T): T {
|
|
2829
|
+
return this._index.valueEncoding.decoder(
|
|
2830
|
+
this._index.valueEncoding.encoder(document),
|
|
2831
|
+
);
|
|
2832
|
+
}
|
|
2833
|
+
|
|
2713
2834
|
private async resolveCanPerformDeleteValue(
|
|
2714
2835
|
operation: DeleteOperation,
|
|
2715
2836
|
options?: { allowEntryFallback?: boolean },
|
|
@@ -2726,12 +2847,15 @@ export class Documents<
|
|
|
2726
2847
|
const indexedDocument =
|
|
2727
2848
|
await this.getLocalIdentityDocumentByHead(existingHead);
|
|
2728
2849
|
if (indexedDocument) {
|
|
2729
|
-
return indexedDocument;
|
|
2850
|
+
return this.detachDocumentValueForCallback(indexedDocument);
|
|
2730
2851
|
}
|
|
2731
2852
|
const indexedPolicyDocument =
|
|
2732
2853
|
await this.getLocalIndexedDocumentForNativeDeletePolicy(key);
|
|
2733
2854
|
if (indexedPolicyDocument) {
|
|
2734
|
-
return
|
|
2855
|
+
return deserialize(
|
|
2856
|
+
serialize(indexedPolicyDocument),
|
|
2857
|
+
this._index.indexedType,
|
|
2858
|
+
) as unknown as T;
|
|
2735
2859
|
}
|
|
2736
2860
|
if (options?.allowEntryFallback === false) {
|
|
2737
2861
|
return;
|
|
@@ -2743,7 +2867,9 @@ export class Documents<
|
|
|
2743
2867
|
if (!isPutOperation(existingOperation)) {
|
|
2744
2868
|
return;
|
|
2745
2869
|
}
|
|
2746
|
-
return this._index.valueEncoding.decoder(
|
|
2870
|
+
return this._index.valueEncoding.decoder(
|
|
2871
|
+
new Uint8Array(existingOperation.data),
|
|
2872
|
+
);
|
|
2747
2873
|
}
|
|
2748
2874
|
|
|
2749
2875
|
protected async _canAppend(
|
|
@@ -2794,17 +2920,32 @@ export class Documents<
|
|
|
2794
2920
|
if (isPutOperation(operation)) {
|
|
2795
2921
|
// check nexts
|
|
2796
2922
|
const putOperation = operation as PutOperation;
|
|
2923
|
+
let decodedDocumentForCanPerform: T | undefined;
|
|
2924
|
+
let hasDecodedDocumentForCanPerform = false;
|
|
2797
2925
|
let keyValue: indexerTypes.Ideable | undefined;
|
|
2798
2926
|
if (reference?.document) {
|
|
2799
|
-
keyValue = this.idResolver(
|
|
2927
|
+
keyValue = this.idResolver(
|
|
2928
|
+
this._hasCustomIdResolver
|
|
2929
|
+
? this.index.valueEncoding.decoder(
|
|
2930
|
+
new Uint8Array(putOperation.data),
|
|
2931
|
+
)
|
|
2932
|
+
: reference.document,
|
|
2933
|
+
);
|
|
2800
2934
|
} else {
|
|
2801
2935
|
keyValue = await this.getNativeDocumentIdFromPutOperation(putOperation);
|
|
2802
2936
|
if (keyValue == null) {
|
|
2803
2937
|
if (this.isNativeMode()) {
|
|
2804
2938
|
return false;
|
|
2805
2939
|
}
|
|
2806
|
-
const value = this.index.valueEncoding.decoder(
|
|
2807
|
-
|
|
2940
|
+
const value = this.index.valueEncoding.decoder(
|
|
2941
|
+
this._hasCustomIdResolver || this._optionCanPerform
|
|
2942
|
+
? new Uint8Array(putOperation.data)
|
|
2943
|
+
: putOperation.data,
|
|
2944
|
+
);
|
|
2945
|
+
if (this._optionCanPerform && !this._hasCustomIdResolver) {
|
|
2946
|
+
decodedDocumentForCanPerform = value;
|
|
2947
|
+
hasDecodedDocumentForCanPerform = true;
|
|
2948
|
+
}
|
|
2808
2949
|
keyValue = this.idResolver(value);
|
|
2809
2950
|
}
|
|
2810
2951
|
}
|
|
@@ -2841,7 +2982,11 @@ export class Documents<
|
|
|
2841
2982
|
return false; // can not append to immutable document
|
|
2842
2983
|
}
|
|
2843
2984
|
|
|
2844
|
-
return
|
|
2985
|
+
return this.acceptPutOperation(
|
|
2986
|
+
putOperation,
|
|
2987
|
+
decodedDocumentForCanPerform,
|
|
2988
|
+
hasDecodedDocumentForCanPerform,
|
|
2989
|
+
);
|
|
2845
2990
|
} else {
|
|
2846
2991
|
if (this.strictHistory) {
|
|
2847
2992
|
// make sure that the next pointer exist and points to the existing documents
|
|
@@ -2849,7 +2994,11 @@ export class Documents<
|
|
|
2849
2994
|
return false;
|
|
2850
2995
|
}
|
|
2851
2996
|
if (entry.meta.next[0] === existingContext.head) {
|
|
2852
|
-
return
|
|
2997
|
+
return this.acceptPutOperation(
|
|
2998
|
+
putOperation,
|
|
2999
|
+
decodedDocumentForCanPerform,
|
|
3000
|
+
hasDecodedDocumentForCanPerform,
|
|
3001
|
+
);
|
|
2853
3002
|
}
|
|
2854
3003
|
|
|
2855
3004
|
const prevEntry = await this.log.log.entryIndex.get(
|
|
@@ -2864,14 +3013,29 @@ export class Documents<
|
|
|
2864
3013
|
}
|
|
2865
3014
|
const referenceHistoryCorrectly =
|
|
2866
3015
|
await pointsToHistory(prevEntry);
|
|
2867
|
-
return referenceHistoryCorrectly
|
|
3016
|
+
return referenceHistoryCorrectly
|
|
3017
|
+
? this.acceptPutOperation(
|
|
3018
|
+
putOperation,
|
|
3019
|
+
decodedDocumentForCanPerform,
|
|
3020
|
+
hasDecodedDocumentForCanPerform,
|
|
3021
|
+
)
|
|
3022
|
+
: false;
|
|
2868
3023
|
} else {
|
|
2869
|
-
return
|
|
3024
|
+
return this.acceptPutOperation(
|
|
3025
|
+
putOperation,
|
|
3026
|
+
decodedDocumentForCanPerform,
|
|
3027
|
+
hasDecodedDocumentForCanPerform,
|
|
3028
|
+
);
|
|
2870
3029
|
}
|
|
2871
3030
|
}
|
|
2872
3031
|
} else {
|
|
2873
3032
|
// Keep existing behavior: next pointers may express document dependencies.
|
|
2874
3033
|
}
|
|
3034
|
+
return this.acceptPutOperation(
|
|
3035
|
+
putOperation,
|
|
3036
|
+
decodedDocumentForCanPerform,
|
|
3037
|
+
hasDecodedDocumentForCanPerform,
|
|
3038
|
+
);
|
|
2875
3039
|
} else if (isDeleteOperation(operation)) {
|
|
2876
3040
|
if (entry.meta.next.length !== 1) {
|
|
2877
3041
|
return false;
|
|
@@ -2922,7 +3086,6 @@ export class Documents<
|
|
|
2922
3086
|
throw new Error("Unsupported operation");
|
|
2923
3087
|
}
|
|
2924
3088
|
|
|
2925
|
-
return operation;
|
|
2926
3089
|
} catch (error) {
|
|
2927
3090
|
if (error instanceof AccessError) {
|
|
2928
3091
|
return false; // we cant index because we can not decrypt
|
|
@@ -5342,6 +5505,24 @@ export class Documents<
|
|
|
5342
5505
|
reference?: PutChangeReference<T, I>,
|
|
5343
5506
|
): Promise<void> {
|
|
5344
5507
|
logger.trace("handleChanges called", change);
|
|
5508
|
+
if (
|
|
5509
|
+
reference == null &&
|
|
5510
|
+
change.added.length >= 2 &&
|
|
5511
|
+
!this.isNativeMode() &&
|
|
5512
|
+
this._index.canUseOrderedWriteSession()
|
|
5513
|
+
) {
|
|
5514
|
+
return this._index.withOrderedWriteSession((orderedSession) =>
|
|
5515
|
+
this.handleChangesInOrder(change, reference, orderedSession),
|
|
5516
|
+
);
|
|
5517
|
+
}
|
|
5518
|
+
return this.handleChangesInOrder(change, reference);
|
|
5519
|
+
}
|
|
5520
|
+
|
|
5521
|
+
private async handleChangesInOrder(
|
|
5522
|
+
change: Change<Operation>,
|
|
5523
|
+
reference?: PutChangeReference<T, I>,
|
|
5524
|
+
orderedSession?: DocumentIndexWriteSession<I>,
|
|
5525
|
+
): Promise<void> {
|
|
5345
5526
|
const isAppendOperation =
|
|
5346
5527
|
change?.added.length === 1 ? !!change.added[0] : false;
|
|
5347
5528
|
const removedSet = new Map<string, ShallowOrFullEntry<Operation>>();
|
|
@@ -5382,6 +5563,7 @@ export class Documents<
|
|
|
5382
5563
|
let modified: Set<string | number | bigint> = new Set();
|
|
5383
5564
|
for (const item of sortedEntries) {
|
|
5384
5565
|
if (!item) {
|
|
5566
|
+
await orderedSession?.flush();
|
|
5385
5567
|
continue;
|
|
5386
5568
|
}
|
|
5387
5569
|
|
|
@@ -5394,6 +5576,7 @@ export class Documents<
|
|
|
5394
5576
|
? reference.operation
|
|
5395
5577
|
: await this.getAppendOperation(item);
|
|
5396
5578
|
if (!payload) {
|
|
5579
|
+
await orderedSession?.flush();
|
|
5397
5580
|
continue;
|
|
5398
5581
|
}
|
|
5399
5582
|
|
|
@@ -5437,17 +5620,31 @@ export class Documents<
|
|
|
5437
5620
|
}
|
|
5438
5621
|
}
|
|
5439
5622
|
let value =
|
|
5440
|
-
|
|
5441
|
-
|
|
5623
|
+
isReferencedAppendEntry && reference?.document
|
|
5624
|
+
? reference.document
|
|
5625
|
+
: this.index.valueEncoding.decoder(
|
|
5626
|
+
new Uint8Array(payload.data),
|
|
5627
|
+
);
|
|
5442
5628
|
|
|
5443
5629
|
// get index key from value
|
|
5444
5630
|
const key =
|
|
5445
5631
|
isReferencedAppendEntry && reference?.key
|
|
5446
5632
|
? reference.key
|
|
5447
|
-
: indexerTypes.toId(
|
|
5633
|
+
: indexerTypes.toId(
|
|
5634
|
+
this.idResolver(
|
|
5635
|
+
this._hasCustomIdResolver
|
|
5636
|
+
? this.index.valueEncoding.decoder(
|
|
5637
|
+
new Uint8Array(payload.data),
|
|
5638
|
+
)
|
|
5639
|
+
: value,
|
|
5640
|
+
),
|
|
5641
|
+
);
|
|
5448
5642
|
|
|
5449
5643
|
// document is already updated with more recent entry
|
|
5450
5644
|
if (modified.has(key.primitive)) {
|
|
5645
|
+
// Duplicate ids deliberately retain the scalar boundary until
|
|
5646
|
+
// their callback and event semantics are proven independently.
|
|
5647
|
+
await orderedSession?.flush();
|
|
5451
5648
|
continue;
|
|
5452
5649
|
}
|
|
5453
5650
|
|
|
@@ -5459,7 +5656,10 @@ export class Documents<
|
|
|
5459
5656
|
? reference.existing
|
|
5460
5657
|
: this.isNativeMode()
|
|
5461
5658
|
? this.getNativeModeIndexedContext(key) || null
|
|
5462
|
-
: (await this.getLocalIndexedContextForChange(
|
|
5659
|
+
: (await this.getLocalIndexedContextForChange(
|
|
5660
|
+
key,
|
|
5661
|
+
orderedSession,
|
|
5662
|
+
)) || null;
|
|
5463
5663
|
if (!this.strictHistory && existing) {
|
|
5464
5664
|
// if immutable use oldest, else use newest
|
|
5465
5665
|
let shouldIgnoreChange = this.immutable
|
|
@@ -5468,10 +5668,19 @@ export class Documents<
|
|
|
5468
5668
|
: existing.value.__context.modified >
|
|
5469
5669
|
item.meta.clock.timestamp.wallTime;
|
|
5470
5670
|
if (shouldIgnoreChange) {
|
|
5671
|
+
await orderedSession?.flush();
|
|
5471
5672
|
continue;
|
|
5472
5673
|
}
|
|
5473
5674
|
}
|
|
5474
5675
|
|
|
5676
|
+
const useOrderedWrite =
|
|
5677
|
+
orderedSession != null &&
|
|
5678
|
+
existing == null &&
|
|
5679
|
+
!(value instanceof Program);
|
|
5680
|
+
if (!useOrderedWrite) {
|
|
5681
|
+
await orderedSession?.flush();
|
|
5682
|
+
}
|
|
5683
|
+
|
|
5475
5684
|
// Program specific
|
|
5476
5685
|
if (value instanceof Program) {
|
|
5477
5686
|
// if replicator, then open
|
|
@@ -5497,6 +5706,7 @@ export class Documents<
|
|
|
5497
5706
|
key,
|
|
5498
5707
|
item,
|
|
5499
5708
|
existing,
|
|
5709
|
+
useOrderedWrite ? orderedSession : undefined,
|
|
5500
5710
|
);
|
|
5501
5711
|
documentsChanged?.added.push(
|
|
5502
5712
|
coerceWithIndexed(coerceWithContext(value, context), indexable),
|
|
@@ -5508,6 +5718,7 @@ export class Documents<
|
|
|
5508
5718
|
isPutOperation(payload) ||
|
|
5509
5719
|
removedSet.has(item.hash)
|
|
5510
5720
|
) {
|
|
5721
|
+
await orderedSession?.flush();
|
|
5511
5722
|
if (
|
|
5512
5723
|
!documentsChanged &&
|
|
5513
5724
|
(await this.collectRemovedPutChangeFromNativeId(payload, modified))
|
|
@@ -5525,12 +5736,14 @@ export class Documents<
|
|
|
5525
5736
|
}
|
|
5526
5737
|
} catch (error) {
|
|
5527
5738
|
if (error instanceof AccessError) {
|
|
5739
|
+
await orderedSession?.flush();
|
|
5528
5740
|
continue;
|
|
5529
5741
|
}
|
|
5530
5742
|
throw error;
|
|
5531
5743
|
}
|
|
5532
5744
|
}
|
|
5533
5745
|
|
|
5746
|
+
await orderedSession?.flush();
|
|
5534
5747
|
if (canRemoveByIndexedHead && change.removed.length > 0) {
|
|
5535
5748
|
const handled = await this.collectRemovedDocumentChangesFromIndexedHeads(
|
|
5536
5749
|
change.removed,
|
package/src/search.ts
CHANGED
|
@@ -739,6 +739,9 @@ export type WithContext<I> = {
|
|
|
739
739
|
__context: types.Context;
|
|
740
740
|
} & I;
|
|
741
741
|
|
|
742
|
+
export type DocumentIndexWriteSession<I extends Record<string, any>> =
|
|
743
|
+
indexerTypes.OrderedIndexWriteSession<WithContext<I>>;
|
|
744
|
+
|
|
742
745
|
export const INDEX_CONTEXT_SHAPE = {
|
|
743
746
|
__context: {
|
|
744
747
|
created: true,
|
|
@@ -1135,6 +1138,23 @@ export class DocumentIndex<
|
|
|
1135
1138
|
return this._valueEncoding;
|
|
1136
1139
|
}
|
|
1137
1140
|
|
|
1141
|
+
public canUseOrderedWriteSession(): boolean {
|
|
1142
|
+
return (
|
|
1143
|
+
!this.isProgramValued &&
|
|
1144
|
+
typeof this.index.withOrderedWriteSession === "function"
|
|
1145
|
+
);
|
|
1146
|
+
}
|
|
1147
|
+
|
|
1148
|
+
public async withOrderedWriteSession<R>(
|
|
1149
|
+
operation: (session: DocumentIndexWriteSession<I>) => MaybePromise<R>,
|
|
1150
|
+
): Promise<R> {
|
|
1151
|
+
const withSession = this.index.withOrderedWriteSession;
|
|
1152
|
+
if (!withSession) {
|
|
1153
|
+
throw new Error("Backing index does not support ordered write sessions");
|
|
1154
|
+
}
|
|
1155
|
+
return withSession.call(this.index, operation) as MaybePromise<R>;
|
|
1156
|
+
}
|
|
1157
|
+
|
|
1138
1158
|
private ensurePrefetchAccumulator() {
|
|
1139
1159
|
if (!this._prefetch) {
|
|
1140
1160
|
this._prefetch = {
|
|
@@ -3090,10 +3110,11 @@ export class DocumentIndex<
|
|
|
3090
3110
|
| indexerTypes.IndexedResult<IndexedContextOnly<I>>
|
|
3091
3111
|
| null
|
|
3092
3112
|
| undefined,
|
|
3113
|
+
orderedSession?: DocumentIndexWriteSession<I>,
|
|
3093
3114
|
): Promise<{ context: types.Context; indexable: I }> {
|
|
3094
3115
|
const existingDefined =
|
|
3095
3116
|
existing === undefined
|
|
3096
|
-
? await this.index.get(id, {
|
|
3117
|
+
? await (orderedSession ?? this.index).get(id, {
|
|
3097
3118
|
shape: INDEX_CONTEXT_SHAPE,
|
|
3098
3119
|
})
|
|
3099
3120
|
: existing;
|
|
@@ -3106,10 +3127,16 @@ export class DocumentIndex<
|
|
|
3106
3127
|
gid: entry.meta.gid,
|
|
3107
3128
|
size: entry.payload.byteLength,
|
|
3108
3129
|
});
|
|
3109
|
-
return this.putWithContext(
|
|
3110
|
-
|
|
3111
|
-
|
|
3112
|
-
|
|
3130
|
+
return this.putWithContext(
|
|
3131
|
+
value,
|
|
3132
|
+
id,
|
|
3133
|
+
context,
|
|
3134
|
+
{
|
|
3135
|
+
replace: existingDefined != null,
|
|
3136
|
+
transformFacts: { entryPublicKeys: entry.publicKeys },
|
|
3137
|
+
},
|
|
3138
|
+
orderedSession,
|
|
3139
|
+
);
|
|
3113
3140
|
}
|
|
3114
3141
|
|
|
3115
3142
|
public async putWithContext(
|
|
@@ -3117,6 +3144,7 @@ export class DocumentIndex<
|
|
|
3117
3144
|
id: indexerTypes.IdKey,
|
|
3118
3145
|
context: types.Context,
|
|
3119
3146
|
options?: ContextualPutOptions,
|
|
3147
|
+
orderedSession?: DocumentIndexWriteSession<I>,
|
|
3120
3148
|
): Promise<{ context: types.Context; indexable: I }> {
|
|
3121
3149
|
const idString = id.primitive;
|
|
3122
3150
|
this.cacheResolvedValue(idString, value);
|
|
@@ -3129,10 +3157,17 @@ export class DocumentIndex<
|
|
|
3129
3157
|
coerceWithContext(value, context);
|
|
3130
3158
|
|
|
3131
3159
|
try {
|
|
3132
|
-
const contextualPut =
|
|
3133
|
-
|
|
3134
|
-
|
|
3135
|
-
|
|
3160
|
+
const contextualPut =
|
|
3161
|
+
!orderedSession && this.transformerIsIdentity
|
|
3162
|
+
? (this.index as ContextualIndexPut<I>).putWithContext
|
|
3163
|
+
: undefined;
|
|
3164
|
+
if (orderedSession) {
|
|
3165
|
+
await orderedSession.put(
|
|
3166
|
+
new this.wrappedIndexedType(valueToIndex, context),
|
|
3167
|
+
id,
|
|
3168
|
+
stripEncodedValue(options),
|
|
3169
|
+
);
|
|
3170
|
+
} else if (contextualPut) {
|
|
3136
3171
|
const encodedValueParts = this.encodeContextualIndexedValueParts(
|
|
3137
3172
|
options?.encodedValue,
|
|
3138
3173
|
context,
|