hypercore 10.9.1 → 10.10.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 +34 -0
- package/index.js +20 -3
- package/lib/bitfield.js +25 -15
- package/lib/core.js +19 -0
- package/lib/remote-bitfield.js +26 -16
- package/lib/replicator.js +6 -4
- package/lib/streams.js +74 -0
- package/package.json +3 -2
package/README.md
CHANGED
|
@@ -197,6 +197,36 @@ for await (const data of fullStream) {
|
|
|
197
197
|
}
|
|
198
198
|
```
|
|
199
199
|
|
|
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
|
+
|
|
200
230
|
#### `await core.clear(start, [end])`
|
|
201
231
|
|
|
202
232
|
Clear stored blocks between `start` and `end`, reclaiming storage when possible.
|
|
@@ -215,6 +245,10 @@ Truncate the core to a smaller length.
|
|
|
215
245
|
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
246
|
Note that the fork id should be monotonely incrementing.
|
|
217
247
|
|
|
248
|
+
#### `await core.purge()`
|
|
249
|
+
|
|
250
|
+
Purge the hypercore from your storage, completely removing all data.
|
|
251
|
+
|
|
218
252
|
#### `const hash = await core.treeHash([length])`
|
|
219
253
|
|
|
220
254
|
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
|
|
|
@@ -742,6 +750,11 @@ module.exports = class Hypercore extends EventEmitter {
|
|
|
742
750
|
await this.core.clear(start, end)
|
|
743
751
|
}
|
|
744
752
|
|
|
753
|
+
async purge () {
|
|
754
|
+
await this._closeAllSessions(null)
|
|
755
|
+
await this.core.purge()
|
|
756
|
+
}
|
|
757
|
+
|
|
745
758
|
async _get (index, opts) {
|
|
746
759
|
let block
|
|
747
760
|
|
|
@@ -794,6 +807,10 @@ module.exports = class Hypercore extends EventEmitter {
|
|
|
794
807
|
return new WriteStream(this, opts)
|
|
795
808
|
}
|
|
796
809
|
|
|
810
|
+
createByteStream (opts) {
|
|
811
|
+
return new ByteStream(this, opts)
|
|
812
|
+
}
|
|
813
|
+
|
|
797
814
|
download (range) {
|
|
798
815
|
const req = this._download(range)
|
|
799
816
|
|
package/lib/bitfield.js
CHANGED
|
@@ -123,19 +123,24 @@ class BitfieldSegment {
|
|
|
123
123
|
findFirst (val, position) {
|
|
124
124
|
position = this.tree.skipFirst(!val, position)
|
|
125
125
|
|
|
126
|
-
|
|
127
|
-
|
|
126
|
+
let j = position & (BITS_PER_PAGE - 1)
|
|
127
|
+
let i = (position - j) / BITS_PER_PAGE
|
|
128
128
|
|
|
129
|
-
if (i >= PAGES_PER_SEGMENT) return
|
|
129
|
+
if (i >= PAGES_PER_SEGMENT) return -1
|
|
130
|
+
|
|
131
|
+
while (i < this.pages.length) {
|
|
132
|
+
const p = this.pages[i]
|
|
130
133
|
|
|
131
|
-
|
|
134
|
+
let index = -1
|
|
132
135
|
|
|
133
|
-
|
|
136
|
+
if (p) index = p.findFirst(val, j)
|
|
137
|
+
else if (!val) index = j
|
|
134
138
|
|
|
135
|
-
|
|
136
|
-
else if (!val) index = j
|
|
139
|
+
if (index !== -1) return i * BITS_PER_PAGE + index
|
|
137
140
|
|
|
138
|
-
|
|
141
|
+
j = 0
|
|
142
|
+
i++
|
|
143
|
+
}
|
|
139
144
|
|
|
140
145
|
return -1
|
|
141
146
|
}
|
|
@@ -143,19 +148,24 @@ class BitfieldSegment {
|
|
|
143
148
|
findLast (val, position) {
|
|
144
149
|
position = this.tree.skipLast(!val, position)
|
|
145
150
|
|
|
146
|
-
|
|
147
|
-
|
|
151
|
+
let j = position & (BITS_PER_PAGE - 1)
|
|
152
|
+
let i = (position - j) / BITS_PER_PAGE
|
|
148
153
|
|
|
149
154
|
if (i >= PAGES_PER_SEGMENT) return -1
|
|
150
155
|
|
|
151
|
-
|
|
156
|
+
while (i >= 0) {
|
|
157
|
+
const p = this.pages[i]
|
|
158
|
+
|
|
159
|
+
let index = -1
|
|
152
160
|
|
|
153
|
-
|
|
161
|
+
if (p) index = p.findLast(val, j)
|
|
162
|
+
else if (!val) index = j
|
|
154
163
|
|
|
155
|
-
|
|
156
|
-
else if (!val) index = j
|
|
164
|
+
if (index !== -1) return i * BITS_PER_PAGE + index
|
|
157
165
|
|
|
158
|
-
|
|
166
|
+
j = BITS_PER_PAGE - 1
|
|
167
|
+
i--
|
|
168
|
+
}
|
|
159
169
|
|
|
160
170
|
return -1
|
|
161
171
|
}
|
package/lib/core.js
CHANGED
|
@@ -280,6 +280,25 @@ module.exports = class Core {
|
|
|
280
280
|
}
|
|
281
281
|
}
|
|
282
282
|
|
|
283
|
+
async purge () {
|
|
284
|
+
return new Promise((resolve, reject) => {
|
|
285
|
+
let missing = 4
|
|
286
|
+
let error = null
|
|
287
|
+
|
|
288
|
+
this.oplog.storage.unlink(done)
|
|
289
|
+
this.tree.storage.unlink(done)
|
|
290
|
+
this.bitfield.storage.unlink(done)
|
|
291
|
+
this.blocks.storage.unlink(done)
|
|
292
|
+
|
|
293
|
+
function done (err) {
|
|
294
|
+
if (err) error = err
|
|
295
|
+
if (--missing) return
|
|
296
|
+
if (error) reject(error)
|
|
297
|
+
else resolve()
|
|
298
|
+
}
|
|
299
|
+
})
|
|
300
|
+
}
|
|
301
|
+
|
|
283
302
|
async append (values, auth = this.defaultAuth, hooks = {}) {
|
|
284
303
|
await this._mutex.lock()
|
|
285
304
|
|
package/lib/remote-bitfield.js
CHANGED
|
@@ -84,19 +84,24 @@ class RemoteBitfieldSegment {
|
|
|
84
84
|
findFirst (val, position) {
|
|
85
85
|
position = this.tree.skipFirst(!val, position)
|
|
86
86
|
|
|
87
|
-
|
|
88
|
-
|
|
87
|
+
let j = position & (BITS_PER_PAGE - 1)
|
|
88
|
+
let i = (position - j) / BITS_PER_PAGE
|
|
89
89
|
|
|
90
|
-
if (i >= PAGES_PER_SEGMENT) return
|
|
90
|
+
if (i >= PAGES_PER_SEGMENT) return -1
|
|
91
91
|
|
|
92
|
-
|
|
92
|
+
while (i < this.pages.length) {
|
|
93
|
+
const p = this.pages[i]
|
|
93
94
|
|
|
94
|
-
|
|
95
|
+
let index = -1
|
|
96
|
+
|
|
97
|
+
if (p) index = p.findFirst(val, j)
|
|
98
|
+
else if (!val) index = j
|
|
95
99
|
|
|
96
|
-
|
|
97
|
-
else if (!val) index = j
|
|
100
|
+
if (index !== -1) return i * BITS_PER_PAGE + index
|
|
98
101
|
|
|
99
|
-
|
|
102
|
+
j = 0
|
|
103
|
+
i++
|
|
104
|
+
}
|
|
100
105
|
|
|
101
106
|
return -1
|
|
102
107
|
}
|
|
@@ -104,19 +109,24 @@ class RemoteBitfieldSegment {
|
|
|
104
109
|
findLast (val, position) {
|
|
105
110
|
position = this.tree.skipLast(!val, position)
|
|
106
111
|
|
|
107
|
-
|
|
108
|
-
|
|
112
|
+
let j = position & (BITS_PER_PAGE - 1)
|
|
113
|
+
let i = (position - j) / BITS_PER_PAGE
|
|
109
114
|
|
|
110
|
-
if (i >= PAGES_PER_SEGMENT) return
|
|
115
|
+
if (i >= PAGES_PER_SEGMENT) return -1
|
|
111
116
|
|
|
112
|
-
|
|
117
|
+
while (i >= 0) {
|
|
118
|
+
const p = this.pages[i]
|
|
119
|
+
|
|
120
|
+
let index = -1
|
|
113
121
|
|
|
114
|
-
|
|
122
|
+
if (p) index = p.findLast(val, j)
|
|
123
|
+
else if (!val) index = j
|
|
115
124
|
|
|
116
|
-
|
|
117
|
-
else if (!val) index = j
|
|
125
|
+
if (index !== -1) return i * BITS_PER_PAGE + index
|
|
118
126
|
|
|
119
|
-
|
|
127
|
+
j = BITS_PER_PAGE - 1
|
|
128
|
+
i--
|
|
129
|
+
}
|
|
120
130
|
|
|
121
131
|
return -1
|
|
122
132
|
}
|
package/lib/replicator.js
CHANGED
|
@@ -1674,12 +1674,14 @@ function clampRange (core, r) {
|
|
|
1674
1674
|
const start = core.bitfield.firstUnset(r.start)
|
|
1675
1675
|
|
|
1676
1676
|
if (r.end === -1) r.start = start === -1 ? core.tree.length : start
|
|
1677
|
-
else if (start === -1) r.start = r.end
|
|
1677
|
+
else if (start === -1 || start >= r.end) r.start = r.end
|
|
1678
1678
|
else {
|
|
1679
|
-
const end = core.bitfield.lastUnset(r.end)
|
|
1680
|
-
|
|
1681
1679
|
r.start = start
|
|
1682
|
-
|
|
1680
|
+
|
|
1681
|
+
const end = core.bitfield.lastUnset(r.end - 1)
|
|
1682
|
+
|
|
1683
|
+
if (end === -1 || start >= end + 1) r.end = r.start
|
|
1684
|
+
else r.end = end + 1
|
|
1683
1685
|
}
|
|
1684
1686
|
} else {
|
|
1685
1687
|
while (r.start < r.end && core.bitfield.get(r.blocks[r.start])) r.start++
|
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.10.0",
|
|
4
4
|
"description": "Hypercore is a secure, distributed append-only log",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"scripts": {
|
|
@@ -43,7 +43,7 @@
|
|
|
43
43
|
"hypercore-crypto": "^3.2.1",
|
|
44
44
|
"is-options": "^1.0.1",
|
|
45
45
|
"protomux": "^3.4.0",
|
|
46
|
-
"quickbit-universal": "^2.1.
|
|
46
|
+
"quickbit-universal": "^2.1.1",
|
|
47
47
|
"random-access-file": "^4.0.0",
|
|
48
48
|
"random-array-iterator": "^1.0.0",
|
|
49
49
|
"safety-catch": "^1.0.1",
|
|
@@ -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
|
}
|