@peerbit/native-backbone 0.2.7 → 0.2.9
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/index.d.ts +58 -0
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/index.js +191 -0
- package/dist/src/index.js.map +1 -1
- package/dist/wasm/native_backbone.d.ts +48 -40
- package/dist/wasm/native_backbone.js +92 -0
- package/dist/wasm/native_backbone_bg.wasm +0 -0
- package/dist/wasm/native_backbone_bg.wasm.d.ts +44 -40
- package/package.json +1 -1
- package/src/index.ts +367 -0
- package/src/shared_log_plan.rs +24 -0
package/dist/src/index.js
CHANGED
|
@@ -168,6 +168,19 @@ const nativeBackboneAppendProfileKeys = [
|
|
|
168
168
|
"nativeBackboneRawReceiveGraphPutMs",
|
|
169
169
|
"nativeBackboneRawReceiveCoordinateCommitMs",
|
|
170
170
|
];
|
|
171
|
+
export class NativeBackboneCoordinatePersistenceReadLimitError extends Error {
|
|
172
|
+
file;
|
|
173
|
+
maxBytes;
|
|
174
|
+
observedBytes;
|
|
175
|
+
code = "ERR_NATIVE_BACKBONE_COORDINATE_READ_LIMIT";
|
|
176
|
+
constructor(file, maxBytes, observedBytes) {
|
|
177
|
+
super(`Native backbone coordinate persistence file ${file} exceeds the ${maxBytes} byte read limit (${observedBytes.toString()} bytes)`);
|
|
178
|
+
this.file = file;
|
|
179
|
+
this.maxBytes = maxBytes;
|
|
180
|
+
this.observedBytes = observedBytes;
|
|
181
|
+
this.name = "NativeBackboneCoordinatePersistenceReadLimitError";
|
|
182
|
+
}
|
|
183
|
+
}
|
|
171
184
|
const nativeBackboneCoordinatePersistenceFiles = {
|
|
172
185
|
snapshot: "coordinates.bin",
|
|
173
186
|
journal: "coordinates.wal",
|
|
@@ -280,6 +293,17 @@ const validateCoordinatePersistenceName = (name) => {
|
|
|
280
293
|
}
|
|
281
294
|
return name;
|
|
282
295
|
};
|
|
296
|
+
const validateCoordinatePersistenceReadMaxBytes = (maxBytes) => {
|
|
297
|
+
if (!Number.isSafeInteger(maxBytes) || maxBytes < 0) {
|
|
298
|
+
throw new RangeError("Native backbone coordinate persistence read limit must be a non-negative safe integer");
|
|
299
|
+
}
|
|
300
|
+
return maxBytes;
|
|
301
|
+
};
|
|
302
|
+
const assertCoordinatePersistenceReadWithinLimit = (name, maxBytes, observedBytes) => {
|
|
303
|
+
if (BigInt(observedBytes) > BigInt(maxBytes)) {
|
|
304
|
+
throw new NativeBackboneCoordinatePersistenceReadLimitError(name, maxBytes, BigInt(observedBytes));
|
|
305
|
+
}
|
|
306
|
+
};
|
|
283
307
|
const nativeBackboneCoordinateDropTombstoneBodyBytes = (body) => new TextEncoder().encode(JSON.stringify(body));
|
|
284
308
|
const nativeBackboneCoordinateDropChecksum = (bytes) => {
|
|
285
309
|
let checksum = 0xffffffff;
|
|
@@ -1945,6 +1969,19 @@ export class NativePeerbitBackbone {
|
|
|
1945
1969
|
}
|
|
1946
1970
|
return this.native.entry_hash_numbers_in_range_u64(integerString(range.start1), integerString(range.end1), integerString(range.start2), integerString(range.end2));
|
|
1947
1971
|
}
|
|
1972
|
+
getEntryHashNumbersInRangeLimited(range) {
|
|
1973
|
+
if (typeof this.native.entry_hash_numbers_in_range_limited !== "function") {
|
|
1974
|
+
return undefined;
|
|
1975
|
+
}
|
|
1976
|
+
return rowsToNumbers("u64", this.native.entry_hash_numbers_in_range_limited(integerString(range.start1), integerString(range.end1), integerString(range.start2), integerString(range.end2), range.limit));
|
|
1977
|
+
}
|
|
1978
|
+
getEntryHashNumbersInRangeU64Limited(range) {
|
|
1979
|
+
if (typeof BigUint64Array === "undefined" ||
|
|
1980
|
+
typeof this.native.entry_hash_numbers_in_range_u64_limited !== "function") {
|
|
1981
|
+
return undefined;
|
|
1982
|
+
}
|
|
1983
|
+
return this.native.entry_hash_numbers_in_range_u64_limited(integerString(range.start1), integerString(range.end1), integerString(range.start2), integerString(range.end2), range.limit);
|
|
1984
|
+
}
|
|
1948
1985
|
countEntryCoordinatesInRanges(ranges, options) {
|
|
1949
1986
|
const start1 = [];
|
|
1950
1987
|
const end1 = [];
|
|
@@ -2948,6 +2985,15 @@ export class NativeBackboneMemoryCoordinatePersistenceStore {
|
|
|
2948
2985
|
const file = this.files.get(validateCoordinatePersistenceName(name));
|
|
2949
2986
|
return file ? copyBytes(file) : undefined;
|
|
2950
2987
|
}
|
|
2988
|
+
async readLimited(name, maxBytes) {
|
|
2989
|
+
const validName = validateCoordinatePersistenceName(name);
|
|
2990
|
+
const limit = validateCoordinatePersistenceReadMaxBytes(maxBytes);
|
|
2991
|
+
const file = this.files.get(validName);
|
|
2992
|
+
if (file) {
|
|
2993
|
+
assertCoordinatePersistenceReadWithinLimit(validName, limit, file.byteLength);
|
|
2994
|
+
}
|
|
2995
|
+
return file ? copyBytes(file) : undefined;
|
|
2996
|
+
}
|
|
2951
2997
|
async write(name, bytes) {
|
|
2952
2998
|
this.files.set(validateCoordinatePersistenceName(name), copyBytes(bytes));
|
|
2953
2999
|
}
|
|
@@ -2968,6 +3014,7 @@ export class NativeBackboneNodeCoordinatePersistenceStore {
|
|
|
2968
3014
|
filePaths = new Map();
|
|
2969
3015
|
directoryEnsured = false;
|
|
2970
3016
|
appendFailure;
|
|
3017
|
+
readLimited;
|
|
2971
3018
|
durableBarrier;
|
|
2972
3019
|
constructor(directory, fs) {
|
|
2973
3020
|
this.directory = directory;
|
|
@@ -2978,6 +3025,9 @@ export class NativeBackboneNodeCoordinatePersistenceStore {
|
|
|
2978
3025
|
if (!fs || typeof fs.open === "function") {
|
|
2979
3026
|
this.durableBarrier = (name) => this.syncDurably(name);
|
|
2980
3027
|
}
|
|
3028
|
+
if (!fs || typeof fs.openBoundedRead === "function") {
|
|
3029
|
+
this.readLimited = (name, maxBytes) => this.readWithinLimit(name, maxBytes);
|
|
3030
|
+
}
|
|
2981
3031
|
}
|
|
2982
3032
|
async nodeFs() {
|
|
2983
3033
|
return this.fs ?? (await importNodeFs());
|
|
@@ -3030,6 +3080,79 @@ export class NativeBackboneNodeCoordinatePersistenceStore {
|
|
|
3030
3080
|
throw error;
|
|
3031
3081
|
}
|
|
3032
3082
|
}
|
|
3083
|
+
async readWithinLimit(name, maxBytes) {
|
|
3084
|
+
const validName = validateCoordinatePersistenceName(name);
|
|
3085
|
+
const limit = validateCoordinatePersistenceReadMaxBytes(maxBytes);
|
|
3086
|
+
const fs = await this.nodeFs();
|
|
3087
|
+
const path = await this.filePath(validName);
|
|
3088
|
+
let handle;
|
|
3089
|
+
try {
|
|
3090
|
+
if (this.fs) {
|
|
3091
|
+
handle = await this.fs.openBoundedRead(path);
|
|
3092
|
+
}
|
|
3093
|
+
else {
|
|
3094
|
+
if (!fs.open) {
|
|
3095
|
+
throw new Error("Default Node coordinate persistence does not expose FileHandle.open");
|
|
3096
|
+
}
|
|
3097
|
+
const opened = await fs.open(path, "r");
|
|
3098
|
+
if (!opened.stat || !opened.read) {
|
|
3099
|
+
await opened.close();
|
|
3100
|
+
throw new Error("Default Node coordinate persistence bounded reads require FileHandle.stat and FileHandle.read");
|
|
3101
|
+
}
|
|
3102
|
+
handle = opened;
|
|
3103
|
+
}
|
|
3104
|
+
}
|
|
3105
|
+
catch (error) {
|
|
3106
|
+
if (isNotFoundError(error)) {
|
|
3107
|
+
return undefined;
|
|
3108
|
+
}
|
|
3109
|
+
throw error;
|
|
3110
|
+
}
|
|
3111
|
+
try {
|
|
3112
|
+
const initial = await handle.stat({ bigint: true });
|
|
3113
|
+
if (typeof initial.size !== "bigint" || initial.size < 0n) {
|
|
3114
|
+
throw new Error("Node coordinate persistence returned an invalid bigint file size");
|
|
3115
|
+
}
|
|
3116
|
+
assertCoordinatePersistenceReadWithinLimit(validName, limit, initial.size);
|
|
3117
|
+
if (initial.size > BigInt(Number.MAX_SAFE_INTEGER)) {
|
|
3118
|
+
throw new RangeError("Node coordinate persistence file is too large to materialize safely");
|
|
3119
|
+
}
|
|
3120
|
+
const byteLength = Number(initial.size);
|
|
3121
|
+
const bytes = new Uint8Array(byteLength);
|
|
3122
|
+
let offset = 0;
|
|
3123
|
+
while (offset < byteLength) {
|
|
3124
|
+
const { bytesRead } = await handle.read(bytes, offset, byteLength - offset, offset);
|
|
3125
|
+
if (!Number.isSafeInteger(bytesRead) ||
|
|
3126
|
+
bytesRead <= 0 ||
|
|
3127
|
+
bytesRead > byteLength - offset) {
|
|
3128
|
+
throw new Error("Node coordinate persistence file changed during a bounded read");
|
|
3129
|
+
}
|
|
3130
|
+
offset += bytesRead;
|
|
3131
|
+
}
|
|
3132
|
+
const growthProbe = new Uint8Array(1);
|
|
3133
|
+
const { bytesRead: growthBytes } = await handle.read(growthProbe, 0, 1, byteLength);
|
|
3134
|
+
if (!Number.isSafeInteger(growthBytes) ||
|
|
3135
|
+
growthBytes < 0 ||
|
|
3136
|
+
growthBytes > 1) {
|
|
3137
|
+
throw new Error("Node coordinate persistence returned invalid bounded read progress");
|
|
3138
|
+
}
|
|
3139
|
+
const final = await handle.stat({ bigint: true });
|
|
3140
|
+
if (typeof final.size !== "bigint" || final.size < 0n) {
|
|
3141
|
+
throw new Error("Node coordinate persistence returned an invalid bigint file size");
|
|
3142
|
+
}
|
|
3143
|
+
if (growthBytes !== 0 || final.size > initial.size) {
|
|
3144
|
+
assertCoordinatePersistenceReadWithinLimit(validName, limit, final.size > initial.size ? final.size : initial.size + 1n);
|
|
3145
|
+
throw new Error("Node coordinate persistence file changed during a bounded read");
|
|
3146
|
+
}
|
|
3147
|
+
if (final.size !== initial.size) {
|
|
3148
|
+
throw new Error("Node coordinate persistence file changed during a bounded read");
|
|
3149
|
+
}
|
|
3150
|
+
return bytes;
|
|
3151
|
+
}
|
|
3152
|
+
finally {
|
|
3153
|
+
await handle.close();
|
|
3154
|
+
}
|
|
3155
|
+
}
|
|
3033
3156
|
async write(name, bytes) {
|
|
3034
3157
|
const fs = await this.ensureDirectory();
|
|
3035
3158
|
const path = await this.filePath(name);
|
|
@@ -3201,6 +3324,31 @@ export class NativeBackboneOPFSCoordinatePersistenceStore {
|
|
|
3201
3324
|
throw error;
|
|
3202
3325
|
}
|
|
3203
3326
|
}
|
|
3327
|
+
async readLimited(name, maxBytes) {
|
|
3328
|
+
const validName = validateCoordinatePersistenceName(name);
|
|
3329
|
+
const limit = validateCoordinatePersistenceReadMaxBytes(maxBytes);
|
|
3330
|
+
try {
|
|
3331
|
+
const handle = await this.directory.getFileHandle(validName, {
|
|
3332
|
+
create: false,
|
|
3333
|
+
});
|
|
3334
|
+
const file = await handle.getFile();
|
|
3335
|
+
if (!Number.isSafeInteger(file.size) || file.size < 0) {
|
|
3336
|
+
throw new Error("OPFS coordinate persistence returned an invalid file size");
|
|
3337
|
+
}
|
|
3338
|
+
assertCoordinatePersistenceReadWithinLimit(validName, limit, file.size);
|
|
3339
|
+
const buffer = await file.arrayBuffer();
|
|
3340
|
+
if (buffer.byteLength !== file.size) {
|
|
3341
|
+
throw new Error("OPFS coordinate persistence file changed during a bounded read");
|
|
3342
|
+
}
|
|
3343
|
+
return new Uint8Array(buffer);
|
|
3344
|
+
}
|
|
3345
|
+
catch (error) {
|
|
3346
|
+
if (isNotFoundError(error)) {
|
|
3347
|
+
return undefined;
|
|
3348
|
+
}
|
|
3349
|
+
throw error;
|
|
3350
|
+
}
|
|
3351
|
+
}
|
|
3204
3352
|
async write(name, bytes) {
|
|
3205
3353
|
const handle = await this.directory.getFileHandle(validateCoordinatePersistenceName(name), { create: true });
|
|
3206
3354
|
const writable = await handle.createWritable();
|
|
@@ -3307,6 +3455,7 @@ export class NativeBackboneBufferedCoordinatePersistenceStore {
|
|
|
3307
3455
|
options;
|
|
3308
3456
|
buffers = new Map();
|
|
3309
3457
|
bufferedBytes = 0;
|
|
3458
|
+
readLimited;
|
|
3310
3459
|
supportsRemoval;
|
|
3311
3460
|
durableBarrier;
|
|
3312
3461
|
constructor(inner, options = {}) {
|
|
@@ -3314,6 +3463,10 @@ export class NativeBackboneBufferedCoordinatePersistenceStore {
|
|
|
3314
3463
|
this.options = options;
|
|
3315
3464
|
this.supportsRemoval =
|
|
3316
3465
|
inner.supportsRemoval ?? typeof inner.remove === "function";
|
|
3466
|
+
if (typeof inner.readLimited === "function") {
|
|
3467
|
+
const innerReadLimited = inner.readLimited.bind(inner);
|
|
3468
|
+
this.readLimited = (name, maxBytes) => this.readWithinLimit(name, maxBytes, innerReadLimited);
|
|
3469
|
+
}
|
|
3317
3470
|
if (typeof inner.durableBarrier === "function") {
|
|
3318
3471
|
this.durableBarrier = async (name) => {
|
|
3319
3472
|
await this.flush(name);
|
|
@@ -3334,6 +3487,44 @@ export class NativeBackboneBufferedCoordinatePersistenceStore {
|
|
|
3334
3487
|
await this.flush(name);
|
|
3335
3488
|
return this.inner.read(name);
|
|
3336
3489
|
}
|
|
3490
|
+
async readWithinLimit(name, maxBytes, innerReadLimited) {
|
|
3491
|
+
const validName = validateCoordinatePersistenceName(name);
|
|
3492
|
+
const limit = validateCoordinatePersistenceReadMaxBytes(maxBytes);
|
|
3493
|
+
const pending = this.buffers.get(validName);
|
|
3494
|
+
if (!pending || pending.length === 0) {
|
|
3495
|
+
return innerReadLimited(validName, limit);
|
|
3496
|
+
}
|
|
3497
|
+
const pendingLength = pending.length;
|
|
3498
|
+
let pendingBytes = 0n;
|
|
3499
|
+
for (const chunk of pending) {
|
|
3500
|
+
pendingBytes += BigInt(chunk.byteLength);
|
|
3501
|
+
}
|
|
3502
|
+
assertCoordinatePersistenceReadWithinLimit(validName, limit, pendingBytes);
|
|
3503
|
+
const remaining = limit - Number(pendingBytes);
|
|
3504
|
+
let existingBytes;
|
|
3505
|
+
try {
|
|
3506
|
+
existingBytes = (await innerReadLimited(validName, remaining))
|
|
3507
|
+
?.byteLength;
|
|
3508
|
+
}
|
|
3509
|
+
catch (error) {
|
|
3510
|
+
if (error instanceof NativeBackboneCoordinatePersistenceReadLimitError) {
|
|
3511
|
+
throw new NativeBackboneCoordinatePersistenceReadLimitError(validName, limit, pendingBytes + error.observedBytes);
|
|
3512
|
+
}
|
|
3513
|
+
throw error;
|
|
3514
|
+
}
|
|
3515
|
+
let confirmedPendingBytes = 0n;
|
|
3516
|
+
for (const chunk of pending) {
|
|
3517
|
+
confirmedPendingBytes += BigInt(chunk.byteLength);
|
|
3518
|
+
}
|
|
3519
|
+
if (this.buffers.get(validName) !== pending ||
|
|
3520
|
+
pending.length !== pendingLength ||
|
|
3521
|
+
confirmedPendingBytes !== pendingBytes) {
|
|
3522
|
+
throw new Error("Native backbone coordinate persistence pending bytes changed during a bounded read");
|
|
3523
|
+
}
|
|
3524
|
+
assertCoordinatePersistenceReadWithinLimit(validName, limit, pendingBytes + BigInt(existingBytes ?? 0));
|
|
3525
|
+
await this.flush(validName);
|
|
3526
|
+
return innerReadLimited(validName, limit);
|
|
3527
|
+
}
|
|
3337
3528
|
async write(name, bytes) {
|
|
3338
3529
|
await this.flush(name);
|
|
3339
3530
|
await this.inner.write(name, bytes);
|