@peerbit/document 13.1.4 → 13.1.5
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/program.d.ts +1 -0
- package/dist/src/program.d.ts.map +1 -1
- package/dist/src/program.js +50 -1
- package/dist/src/program.js.map +1 -1
- package/dist/src/search.d.ts +6 -0
- package/dist/src/search.d.ts.map +1 -1
- package/dist/src/search.js +27 -5
- package/dist/src/search.js.map +1 -1
- package/package.json +16 -15
- package/src/program.ts +54 -2
- package/src/search.ts +27 -5
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@peerbit/document",
|
|
3
|
-
"version": "13.1.
|
|
3
|
+
"version": "13.1.5",
|
|
4
4
|
"description": "Document store implementation",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"sideEffects": false,
|
|
@@ -59,20 +59,20 @@
|
|
|
59
59
|
"@multiformats/multiaddr": "^13.0.1",
|
|
60
60
|
"p-defer": "^4.0.0",
|
|
61
61
|
"uint8arrays": "^5.1.0",
|
|
62
|
-
"@peerbit/cache": "3.1.0",
|
|
63
|
-
"@peerbit/crypto": "3.1.2",
|
|
64
|
-
"@peerbit/document-interface": "3.2.46",
|
|
65
62
|
"@peerbit/indexer-cache": "0.2.8",
|
|
66
63
|
"@peerbit/indexer-interface": "3.0.5",
|
|
67
|
-
"@peerbit/
|
|
68
|
-
"@peerbit/
|
|
64
|
+
"@peerbit/cache": "3.1.0",
|
|
65
|
+
"@peerbit/document-interface": "3.2.47",
|
|
66
|
+
"@peerbit/indexer-simple": "1.2.9",
|
|
67
|
+
"@peerbit/crypto": "3.1.2",
|
|
68
|
+
"@peerbit/log": "6.2.4",
|
|
69
|
+
"@peerbit/logger": "2.0.1",
|
|
69
70
|
"@peerbit/indexer-sqlite3": "3.0.8",
|
|
70
|
-
"@peerbit/program": "6.0.33",
|
|
71
71
|
"@peerbit/pubsub": "5.3.0",
|
|
72
|
-
"@peerbit/
|
|
73
|
-
"@peerbit/rpc": "6.1.
|
|
74
|
-
"@peerbit/
|
|
75
|
-
"@peerbit/
|
|
72
|
+
"@peerbit/program": "6.0.34",
|
|
73
|
+
"@peerbit/rpc": "6.1.2",
|
|
74
|
+
"@peerbit/shared-log": "13.2.5",
|
|
75
|
+
"@peerbit/stream-interface": "6.0.11"
|
|
76
76
|
},
|
|
77
77
|
"optionalDependencies": {
|
|
78
78
|
"@peerbit/document-rust": "0.1.1"
|
|
@@ -83,11 +83,11 @@
|
|
|
83
83
|
"@types/pidusage": "^2.0.5",
|
|
84
84
|
"pidusage": "^4.0.1",
|
|
85
85
|
"uuid": "^10.0.0",
|
|
86
|
-
"@peerbit/native-backbone": "0.1.3",
|
|
87
|
-
"@peerbit/test-utils": "3.1.4",
|
|
88
86
|
"@peerbit/log-rust": "1.1.2",
|
|
89
|
-
"peerbit": "
|
|
90
|
-
"@peerbit/time": "3.0.0"
|
|
87
|
+
"@peerbit/native-backbone": "0.1.4",
|
|
88
|
+
"@peerbit/time": "3.0.0",
|
|
89
|
+
"@peerbit/test-utils": "3.1.5",
|
|
90
|
+
"peerbit": "5.3.5"
|
|
91
91
|
},
|
|
92
92
|
"repository": {
|
|
93
93
|
"type": "git",
|
|
@@ -102,6 +102,7 @@
|
|
|
102
102
|
"clean": "aegir clean",
|
|
103
103
|
"build": "aegir build --no-bundle",
|
|
104
104
|
"test": "aegir test --target node",
|
|
105
|
+
"test:document-rust-core": "npm run build && PEERBIT_SHARED_LOG_RUST_CORE=1 aegir test -t node --grep '^(?!.*(uses the validated local append path|uses the independent native prepared batch path|uses native shared-log planning for replicated target-none puts|uses commit-only local puts when coordinate persistence is deferred|can delete without being replicator)).*(native conformance guard |operations basic |operations get |operations index |operations search |iterate sort |count approximate |query distribution |returnIndexed |caching |custom index |prefetch )'",
|
|
105
106
|
"lint": "aegir lint",
|
|
106
107
|
"test:cov": "aegir test -t node --cov"
|
|
107
108
|
}
|
package/src/program.ts
CHANGED
|
@@ -112,6 +112,16 @@ export class NativeDocumentModeError extends Error {
|
|
|
112
112
|
|
|
113
113
|
type MaybePromise<T> = Promise<T> | T;
|
|
114
114
|
|
|
115
|
+
/**
|
|
116
|
+
* True when an error signals that an entry's payload bytes were never
|
|
117
|
+
* materialized on the JS side (a hollow entry backed by a native block store).
|
|
118
|
+
* `DecryptedThing.getValue` throws `Error("Missing data")` in that case. Used to
|
|
119
|
+
* decide whether the auto-mode append/delete read-back should recover the
|
|
120
|
+
* operation from the storage-bytes / block-store path instead.
|
|
121
|
+
*/
|
|
122
|
+
const isMissingPayloadDataError = (error: unknown): boolean =>
|
|
123
|
+
error instanceof Error && error.message === "Missing data";
|
|
124
|
+
|
|
115
125
|
const isPromiseLike = <T>(value: MaybePromise<T>): value is Promise<T> =>
|
|
116
126
|
!!value && typeof (value as Promise<T>).then === "function";
|
|
117
127
|
|
|
@@ -2837,17 +2847,44 @@ export class Documents<
|
|
|
2837
2847
|
return;
|
|
2838
2848
|
}
|
|
2839
2849
|
ensureInitialized?.();
|
|
2840
|
-
|
|
2850
|
+
try {
|
|
2851
|
+
return await entry.getPayloadValue();
|
|
2852
|
+
} catch (error) {
|
|
2853
|
+
// Auto (non-native document) mode with a native block store: the entry
|
|
2854
|
+
// materialized in the entry index can be a hollow shell whose in-memory
|
|
2855
|
+
// payload bytes were never loaded (the native store keeps the block
|
|
2856
|
+
// bytes at the storage layer, not on the JS entry). `getPayloadValue`
|
|
2857
|
+
// then throws "Missing data". The block itself is present, so recover
|
|
2858
|
+
// the operation via the storage-bytes / block-store read path. This is
|
|
2859
|
+
// a no-op for the pure-JS backend, where `getPayloadValue` succeeds.
|
|
2860
|
+
if (!isMissingPayloadDataError(error)) {
|
|
2861
|
+
throw error;
|
|
2862
|
+
}
|
|
2863
|
+
const operation = await this.getPlainEntryOperationFromStorage(entry);
|
|
2864
|
+
if (operation) {
|
|
2865
|
+
return operation;
|
|
2866
|
+
}
|
|
2867
|
+
throw error;
|
|
2868
|
+
}
|
|
2841
2869
|
}
|
|
2842
2870
|
|
|
2843
2871
|
private async getPlainEntryOperationFromStorage(
|
|
2844
2872
|
entry: Entry<Operation>,
|
|
2845
2873
|
): Promise<Operation | undefined> {
|
|
2846
|
-
let storageBytes: Uint8Array;
|
|
2874
|
+
let storageBytes: Uint8Array | undefined;
|
|
2847
2875
|
try {
|
|
2848
2876
|
storageBytes =
|
|
2849
2877
|
Entry.getPreparedStorageBytes(entry) ?? entry.getStorageBytes();
|
|
2850
2878
|
} catch {
|
|
2879
|
+
// The entry object is hollow (its payload bytes never materialized on
|
|
2880
|
+
// the JS side), so it cannot re-serialize itself. Fall through to the
|
|
2881
|
+
// block-store fallback below.
|
|
2882
|
+
storageBytes = undefined;
|
|
2883
|
+
}
|
|
2884
|
+
// Fall back to the raw block held by the block store, keyed by the entry
|
|
2885
|
+
// hash, whenever the entry could not (or did not) yield its own bytes.
|
|
2886
|
+
storageBytes ??= await this.getEntryStorageBytesFromBlocks(entry);
|
|
2887
|
+
if (!storageBytes) {
|
|
2851
2888
|
return;
|
|
2852
2889
|
}
|
|
2853
2890
|
try {
|
|
@@ -2869,6 +2906,21 @@ export class Documents<
|
|
|
2869
2906
|
}
|
|
2870
2907
|
}
|
|
2871
2908
|
|
|
2909
|
+
private async getEntryStorageBytesFromBlocks(
|
|
2910
|
+
entry: Entry<Operation>,
|
|
2911
|
+
): Promise<Uint8Array | undefined> {
|
|
2912
|
+
const hash = entry.hash;
|
|
2913
|
+
if (!hash) {
|
|
2914
|
+
return;
|
|
2915
|
+
}
|
|
2916
|
+
try {
|
|
2917
|
+
const bytes = await this.log.log.blocks.get(hash);
|
|
2918
|
+
return bytes ?? undefined;
|
|
2919
|
+
} catch {
|
|
2920
|
+
return;
|
|
2921
|
+
}
|
|
2922
|
+
}
|
|
2923
|
+
|
|
2872
2924
|
private getNativeDocumentFieldExtractionPlan(
|
|
2873
2925
|
path: string | readonly string[],
|
|
2874
2926
|
): SimpleDocumentFieldExtractionPlan | undefined {
|
package/src/search.ts
CHANGED
|
@@ -1179,6 +1179,28 @@ export class DocumentIndex<
|
|
|
1179
1179
|
}
|
|
1180
1180
|
}
|
|
1181
1181
|
|
|
1182
|
+
/** Head entry safe to borsh-serialize into ResultIndexedValue.entries over the
|
|
1183
|
+
* document RPC. Under the native block store, this._log.log.get can return a
|
|
1184
|
+
* hollow entry whose payload bytes were never materialized on the JS side;
|
|
1185
|
+
* serializing it throws and aborts the whole RPC response. The complete entry is
|
|
1186
|
+
* in the block store keyed by hash, so recover it there. No-op for pure JS. */
|
|
1187
|
+
private async getSerializableHead(
|
|
1188
|
+
hash: string,
|
|
1189
|
+
): Promise<Entry<any> | undefined> {
|
|
1190
|
+
const head = await this._log.log.get(hash);
|
|
1191
|
+
if (!head?.hash) return head ?? undefined;
|
|
1192
|
+
try {
|
|
1193
|
+
head.getStorageBytes(); // = serialize(head); throws on a hollow native entry
|
|
1194
|
+
return head;
|
|
1195
|
+
} catch {
|
|
1196
|
+
try {
|
|
1197
|
+
return await Entry.fromMultihash(this._log.log.blocks, head.hash);
|
|
1198
|
+
} catch {
|
|
1199
|
+
return head; // block absent (e.g. pruned) — preserve today's behavior
|
|
1200
|
+
}
|
|
1201
|
+
}
|
|
1202
|
+
}
|
|
1203
|
+
|
|
1182
1204
|
private async wrapPushResults(
|
|
1183
1205
|
matches: Array<WithContext<T> | WithContext<I>>,
|
|
1184
1206
|
resolve: boolean,
|
|
@@ -1220,7 +1242,7 @@ export class DocumentIndex<
|
|
|
1220
1242
|
continue;
|
|
1221
1243
|
}
|
|
1222
1244
|
|
|
1223
|
-
const head = await this.
|
|
1245
|
+
const head = await this.getSerializableHead(indexed.__context.head);
|
|
1224
1246
|
results.push(
|
|
1225
1247
|
new types.ResultIndexedValue({
|
|
1226
1248
|
context: indexed.__context,
|
|
@@ -1231,7 +1253,7 @@ export class DocumentIndex<
|
|
|
1231
1253
|
);
|
|
1232
1254
|
} else {
|
|
1233
1255
|
const indexed = match as WithContext<I>;
|
|
1234
|
-
const head = await this.
|
|
1256
|
+
const head = await this.getSerializableHead(indexed.__context.head);
|
|
1235
1257
|
results.push(
|
|
1236
1258
|
new types.ResultIndexedValue({
|
|
1237
1259
|
context: indexed.__context,
|
|
@@ -1276,7 +1298,7 @@ export class DocumentIndex<
|
|
|
1276
1298
|
continue;
|
|
1277
1299
|
}
|
|
1278
1300
|
|
|
1279
|
-
const head = await this.
|
|
1301
|
+
const head = await this.getSerializableHead(entry.value.__context.head);
|
|
1280
1302
|
results.push(
|
|
1281
1303
|
new types.ResultIndexedValue({
|
|
1282
1304
|
context: entry.value.__context,
|
|
@@ -1286,7 +1308,7 @@ export class DocumentIndex<
|
|
|
1286
1308
|
}),
|
|
1287
1309
|
);
|
|
1288
1310
|
} else {
|
|
1289
|
-
const head = await this.
|
|
1311
|
+
const head = await this.getSerializableHead(entry.value.__context.head);
|
|
1290
1312
|
results.push(
|
|
1291
1313
|
new types.ResultIndexedValue({
|
|
1292
1314
|
context: entry.value.__context,
|
|
@@ -3653,7 +3675,7 @@ export class DocumentIndex<
|
|
|
3653
3675
|
);
|
|
3654
3676
|
} else {
|
|
3655
3677
|
const context = result.value.__context;
|
|
3656
|
-
const head = await this.
|
|
3678
|
+
const head = await this.getSerializableHead(context.head);
|
|
3657
3679
|
if (replicateIndexFlag) {
|
|
3658
3680
|
if (!head) {
|
|
3659
3681
|
continue;
|