hypercore 10.9.2 → 10.11.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/README.md +42 -1
- package/index.js +26 -5
- package/lib/core.js +28 -1
- package/lib/info.js +16 -16
- package/lib/streams.js +74 -0
- package/package.json +2 -1
package/README.md
CHANGED
|
@@ -197,7 +197,37 @@ for await (const data of fullStream) {
|
|
|
197
197
|
}
|
|
198
198
|
```
|
|
199
199
|
|
|
200
|
-
#### `
|
|
200
|
+
#### `const bs = core.createByteStream([options])`
|
|
201
|
+
|
|
202
|
+
Make a byte stream to read a range of bytes.
|
|
203
|
+
|
|
204
|
+
``` js
|
|
205
|
+
// Read the full core
|
|
206
|
+
const fullStream = core.createByteStream()
|
|
207
|
+
|
|
208
|
+
// Read from byte 3, and from there read 50 bytes
|
|
209
|
+
const partialStream = core.createByteStream({ byteOffset: 3, byteLength: 50 })
|
|
210
|
+
|
|
211
|
+
// Consume it as an async iterator
|
|
212
|
+
for await (const data of fullStream) {
|
|
213
|
+
console.log('data:', data)
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
// Or pipe it somewhere like any stream:
|
|
217
|
+
partialStream.pipe(process.stdout)
|
|
218
|
+
```
|
|
219
|
+
|
|
220
|
+
`options` include:
|
|
221
|
+
|
|
222
|
+
``` js
|
|
223
|
+
{
|
|
224
|
+
byteOffset: 0,
|
|
225
|
+
byteLength: core.byteLength - options.byteOffset,
|
|
226
|
+
prefetch: 32
|
|
227
|
+
}
|
|
228
|
+
```
|
|
229
|
+
|
|
230
|
+
#### `const cleared = await core.clear(start, [end], [options])`
|
|
201
231
|
|
|
202
232
|
Clear stored blocks between `start` and `end`, reclaiming storage when possible.
|
|
203
233
|
|
|
@@ -208,6 +238,13 @@ await core.clear(0, 10) // clear block 0-10 from your local cache
|
|
|
208
238
|
|
|
209
239
|
The core will also gossip to peers it is connected to, that is no longer has these blocks.
|
|
210
240
|
|
|
241
|
+
`options` include:
|
|
242
|
+
```js
|
|
243
|
+
{
|
|
244
|
+
diff: false // Returned `cleared` bytes object is null unless you enable this
|
|
245
|
+
}
|
|
246
|
+
```
|
|
247
|
+
|
|
211
248
|
#### `await core.truncate(newLength, [forkId])`
|
|
212
249
|
|
|
213
250
|
Truncate the core to a smaller length.
|
|
@@ -215,6 +252,10 @@ Truncate the core to a smaller length.
|
|
|
215
252
|
Per default this will update the fork id of the core to `+ 1`, but you can set the fork id you prefer with the option.
|
|
216
253
|
Note that the fork id should be monotonely incrementing.
|
|
217
254
|
|
|
255
|
+
#### `await core.purge()`
|
|
256
|
+
|
|
257
|
+
Purge the hypercore from your storage, completely removing all data.
|
|
258
|
+
|
|
218
259
|
#### `const hash = await core.treeHash([length])`
|
|
219
260
|
|
|
220
261
|
Get the Merkle Tree hash of the core at a given length, defaulting to the current length of the core.
|
package/index.js
CHANGED
|
@@ -14,7 +14,7 @@ const Core = require('./lib/core')
|
|
|
14
14
|
const BlockEncryption = require('./lib/block-encryption')
|
|
15
15
|
const Info = require('./lib/info')
|
|
16
16
|
const Download = require('./lib/download')
|
|
17
|
-
const { ReadStream, WriteStream } = require('./lib/streams')
|
|
17
|
+
const { ReadStream, WriteStream, ByteStream } = require('./lib/streams')
|
|
18
18
|
const { BAD_ARGUMENT, SESSION_CLOSED, SESSION_NOT_WRITABLE, SNAPSHOT_NOT_AVAILABLE } = require('./lib/errors')
|
|
19
19
|
|
|
20
20
|
const promises = Symbol.for('hypercore.promises')
|
|
@@ -173,13 +173,14 @@ module.exports = class Hypercore extends EventEmitter {
|
|
|
173
173
|
const directory = storage
|
|
174
174
|
const toLock = opts.unlocked ? null : (opts.lock || 'oplog')
|
|
175
175
|
const pool = opts.pool || (opts.poolSize ? RAF.createPool(opts.poolSize) : null)
|
|
176
|
+
const rmdir = !!opts.rmdir
|
|
176
177
|
|
|
177
178
|
return createFile
|
|
178
179
|
|
|
179
180
|
function createFile (name) {
|
|
180
181
|
const lock = toLock === null ? false : isFile(name, toLock)
|
|
181
182
|
const sparse = isFile(name, 'data') || isFile(name, 'bitfield') || isFile(name, 'tree')
|
|
182
|
-
return new RAF(name, { directory, lock, sparse, pool: lock ? null : pool })
|
|
183
|
+
return new RAF(name, { directory, lock, sparse, pool: lock ? null : pool, rmdir })
|
|
183
184
|
}
|
|
184
185
|
|
|
185
186
|
function isFile (name, n) {
|
|
@@ -528,9 +529,16 @@ module.exports = class Hypercore extends EventEmitter {
|
|
|
528
529
|
for (const s of this.sessions) s.emit('conflict', proof.upgrade.length, proof.fork, proof)
|
|
529
530
|
|
|
530
531
|
const err = new Error('Two conflicting signatures exist for length ' + proof.upgrade.length)
|
|
532
|
+
await this._closeAllSessions(err)
|
|
533
|
+
}
|
|
534
|
+
|
|
535
|
+
async _closeAllSessions (err) {
|
|
536
|
+
// this.sessions modifies itself when a session closes
|
|
537
|
+
// This way we ensure we indeed iterate over all sessions
|
|
538
|
+
const sessions = [...this.sessions]
|
|
531
539
|
|
|
532
540
|
const all = []
|
|
533
|
-
for (const s of
|
|
541
|
+
for (const s of sessions) all.push(s.close(err))
|
|
534
542
|
await Promise.allSettled(all)
|
|
535
543
|
}
|
|
536
544
|
|
|
@@ -737,9 +745,18 @@ module.exports = class Hypercore extends EventEmitter {
|
|
|
737
745
|
end = start + 1
|
|
738
746
|
}
|
|
739
747
|
|
|
740
|
-
|
|
748
|
+
const cleared = (opts && opts.diff) ? { blocks: 0 } : null
|
|
749
|
+
|
|
750
|
+
if (start >= end) return cleared
|
|
751
|
+
|
|
752
|
+
await this.core.clear(start, end, cleared)
|
|
741
753
|
|
|
742
|
-
|
|
754
|
+
return cleared
|
|
755
|
+
}
|
|
756
|
+
|
|
757
|
+
async purge () {
|
|
758
|
+
await this._closeAllSessions(null)
|
|
759
|
+
await this.core.purge()
|
|
743
760
|
}
|
|
744
761
|
|
|
745
762
|
async _get (index, opts) {
|
|
@@ -794,6 +811,10 @@ module.exports = class Hypercore extends EventEmitter {
|
|
|
794
811
|
return new WriteStream(this, opts)
|
|
795
812
|
}
|
|
796
813
|
|
|
814
|
+
createByteStream (opts) {
|
|
815
|
+
return new ByteStream(this, opts)
|
|
816
|
+
}
|
|
817
|
+
|
|
797
818
|
download (range) {
|
|
798
819
|
const req = this._download(range)
|
|
799
820
|
|
package/lib/core.js
CHANGED
|
@@ -5,6 +5,7 @@ const Mutex = require('./mutex')
|
|
|
5
5
|
const MerkleTree = require('./merkle-tree')
|
|
6
6
|
const BlockStore = require('./block-store')
|
|
7
7
|
const Bitfield = require('./bitfield')
|
|
8
|
+
const Info = require('./info')
|
|
8
9
|
const { BAD_ARGUMENT, STORAGE_EMPTY, STORAGE_CONFLICT, INVALID_SIGNATURE } = require('./errors')
|
|
9
10
|
const m = require('./messages')
|
|
10
11
|
|
|
@@ -238,7 +239,7 @@ module.exports = class Core {
|
|
|
238
239
|
}
|
|
239
240
|
}
|
|
240
241
|
|
|
241
|
-
async clear (start, end) {
|
|
242
|
+
async clear (start, end, cleared) {
|
|
242
243
|
await this._mutex.lock()
|
|
243
244
|
|
|
244
245
|
try {
|
|
@@ -265,13 +266,20 @@ module.exports = class Core {
|
|
|
265
266
|
end = this.bitfield.firstSet(end)
|
|
266
267
|
|
|
267
268
|
if (end === -1) end = this.tree.length
|
|
269
|
+
if (start >= end || start >= this.tree.length) return
|
|
268
270
|
|
|
269
271
|
const offset = await this.tree.byteOffset(start * 2)
|
|
270
272
|
const [byteEnd, byteEndLength] = await this.tree.byteRange((end - 1) * 2)
|
|
271
273
|
const length = (byteEnd + byteEndLength) - offset
|
|
272
274
|
|
|
275
|
+
const before = cleared ? await Info.bytesUsed(this.blocks.storage) : null
|
|
276
|
+
|
|
273
277
|
await this.blocks.clear(offset, length)
|
|
274
278
|
|
|
279
|
+
const after = cleared ? await Info.bytesUsed(this.blocks.storage) : null
|
|
280
|
+
|
|
281
|
+
if (cleared) cleared.blocks = Math.max(before - after, 0)
|
|
282
|
+
|
|
275
283
|
this.onupdate(0, entry.bitfield, null, null)
|
|
276
284
|
|
|
277
285
|
if (this._shouldFlush()) await this._flushOplog()
|
|
@@ -280,6 +288,25 @@ module.exports = class Core {
|
|
|
280
288
|
}
|
|
281
289
|
}
|
|
282
290
|
|
|
291
|
+
async purge () {
|
|
292
|
+
return new Promise((resolve, reject) => {
|
|
293
|
+
let missing = 4
|
|
294
|
+
let error = null
|
|
295
|
+
|
|
296
|
+
this.oplog.storage.unlink(done)
|
|
297
|
+
this.tree.storage.unlink(done)
|
|
298
|
+
this.bitfield.storage.unlink(done)
|
|
299
|
+
this.blocks.storage.unlink(done)
|
|
300
|
+
|
|
301
|
+
function done (err) {
|
|
302
|
+
if (err) error = err
|
|
303
|
+
if (--missing) return
|
|
304
|
+
if (error) reject(error)
|
|
305
|
+
else resolve()
|
|
306
|
+
}
|
|
307
|
+
})
|
|
308
|
+
}
|
|
309
|
+
|
|
283
310
|
async append (values, auth = this.defaultAuth, hooks = {}) {
|
|
284
311
|
await this._mutex.lock()
|
|
285
312
|
|
package/lib/info.js
CHANGED
|
@@ -27,27 +27,27 @@ module.exports = class Info {
|
|
|
27
27
|
const { oplog, tree, blocks, bitfield } = session.core
|
|
28
28
|
try {
|
|
29
29
|
return {
|
|
30
|
-
oplog: await bytesUsed(oplog.storage),
|
|
31
|
-
tree: await bytesUsed(tree.storage),
|
|
32
|
-
blocks: await bytesUsed(blocks.storage),
|
|
33
|
-
bitfield: await bytesUsed(bitfield.storage)
|
|
30
|
+
oplog: await Info.bytesUsed(oplog.storage),
|
|
31
|
+
tree: await Info.bytesUsed(tree.storage),
|
|
32
|
+
blocks: await Info.bytesUsed(blocks.storage),
|
|
33
|
+
bitfield: await Info.bytesUsed(bitfield.storage)
|
|
34
34
|
}
|
|
35
35
|
} catch {
|
|
36
36
|
return null
|
|
37
37
|
}
|
|
38
|
+
}
|
|
38
39
|
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
})
|
|
40
|
+
static bytesUsed (file) {
|
|
41
|
+
return new Promise((resolve, reject) => {
|
|
42
|
+
file.stat((err, st) => {
|
|
43
|
+
if (err) {
|
|
44
|
+
resolve(0) // prob just file not found (TODO, improve)
|
|
45
|
+
} else if (typeof st.blocks !== 'number') {
|
|
46
|
+
reject(new Error('cannot determine bytes used'))
|
|
47
|
+
} else {
|
|
48
|
+
resolve(st.blocks * 512)
|
|
49
|
+
}
|
|
50
50
|
})
|
|
51
|
-
}
|
|
51
|
+
})
|
|
52
52
|
}
|
|
53
53
|
}
|
package/lib/streams.js
CHANGED
|
@@ -54,3 +54,77 @@ class WriteStream extends Writable {
|
|
|
54
54
|
}
|
|
55
55
|
|
|
56
56
|
exports.WriteStream = WriteStream
|
|
57
|
+
|
|
58
|
+
class ByteStream extends Readable {
|
|
59
|
+
constructor (core, opts = {}) {
|
|
60
|
+
super()
|
|
61
|
+
|
|
62
|
+
this._core = core
|
|
63
|
+
this._index = 0
|
|
64
|
+
this._range = null
|
|
65
|
+
|
|
66
|
+
this._byteOffset = opts.byteOffset || 0
|
|
67
|
+
this._byteLength = typeof opts.byteLength === 'number' ? opts.byteLength : -1
|
|
68
|
+
this._prefetch = typeof opts.prefetch === 'number' ? opts.prefetch : 32
|
|
69
|
+
|
|
70
|
+
this._applyOffset = this._byteOffset > 0
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
_open (cb) {
|
|
74
|
+
this._openp().then(cb, cb)
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
_read (cb) {
|
|
78
|
+
this._readp().then(cb, cb)
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
async _openp () {
|
|
82
|
+
if (this._byteLength === -1) {
|
|
83
|
+
await this._core.update()
|
|
84
|
+
this._byteLength = Math.max(this._core.byteLength - this._byteOffset, 0)
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
async _readp () {
|
|
89
|
+
let data = null
|
|
90
|
+
|
|
91
|
+
if (this._byteLength === 0) {
|
|
92
|
+
this.push(null)
|
|
93
|
+
return
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
let relativeOffset = 0
|
|
97
|
+
|
|
98
|
+
if (this._applyOffset) {
|
|
99
|
+
this._applyOffset = false
|
|
100
|
+
|
|
101
|
+
const [block, byteOffset] = await this._core.seek(this._byteOffset)
|
|
102
|
+
|
|
103
|
+
this._index = block
|
|
104
|
+
relativeOffset = byteOffset
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
this._predownload(this._index + 1)
|
|
108
|
+
data = await this._core.get(this._index++)
|
|
109
|
+
|
|
110
|
+
if (relativeOffset > 0) data = data.subarray(relativeOffset)
|
|
111
|
+
|
|
112
|
+
if (data.byteLength > this._byteLength) data = data.subarray(0, this._byteLength)
|
|
113
|
+
this._byteLength -= data.byteLength
|
|
114
|
+
|
|
115
|
+
this.push(data)
|
|
116
|
+
if (this._byteLength === 0) this.push(null)
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
_predownload (index) {
|
|
120
|
+
if (this._range) this._range.destroy()
|
|
121
|
+
this._range = this._core.download({ start: index, end: index + this._prefetch, linear: true })
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
_destroy (cb) {
|
|
125
|
+
if (this._range) this._range.destroy()
|
|
126
|
+
cb(null)
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
exports.ByteStream = ByteStream
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "hypercore",
|
|
3
|
-
"version": "10.
|
|
3
|
+
"version": "10.11.0",
|
|
4
4
|
"description": "Hypercore is a secure, distributed append-only log",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"scripts": {
|
|
@@ -57,6 +57,7 @@
|
|
|
57
57
|
"hyperswarm": "^4.3.6",
|
|
58
58
|
"random-access-memory": "^6.1.0",
|
|
59
59
|
"random-access-memory-overlay": "^3.0.0",
|
|
60
|
+
"range-parser": "^1.2.1",
|
|
60
61
|
"standard": "^17.0.0",
|
|
61
62
|
"tmp-promise": "^3.0.2"
|
|
62
63
|
}
|