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 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 this.sessions) all.push(s.close(err))
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
- const j = position & (BITS_PER_PAGE - 1)
127
- const i = (position - j) / BITS_PER_PAGE
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 val ? -1 : position
129
+ if (i >= PAGES_PER_SEGMENT) return -1
130
+
131
+ while (i < this.pages.length) {
132
+ const p = this.pages[i]
130
133
 
131
- const p = this.pages[i]
134
+ let index = -1
132
135
 
133
- let index = -1
136
+ if (p) index = p.findFirst(val, j)
137
+ else if (!val) index = j
134
138
 
135
- if (p) index = p.findFirst(val, j)
136
- else if (!val) index = j
139
+ if (index !== -1) return i * BITS_PER_PAGE + index
137
140
 
138
- if (index !== -1) return i * BITS_PER_PAGE + index
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
- const j = position & (BITS_PER_PAGE - 1)
147
- const i = (position - j) / BITS_PER_PAGE
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
- const p = this.pages[i]
156
+ while (i >= 0) {
157
+ const p = this.pages[i]
158
+
159
+ let index = -1
152
160
 
153
- let index = -1
161
+ if (p) index = p.findLast(val, j)
162
+ else if (!val) index = j
154
163
 
155
- if (p) index = p.findLast(val, j)
156
- else if (!val) index = j
164
+ if (index !== -1) return i * BITS_PER_PAGE + index
157
165
 
158
- if (index !== -1) return i * BITS_PER_PAGE + index
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
 
@@ -84,19 +84,24 @@ class RemoteBitfieldSegment {
84
84
  findFirst (val, position) {
85
85
  position = this.tree.skipFirst(!val, position)
86
86
 
87
- const j = position & (BITS_PER_PAGE - 1)
88
- const i = (position - j) / BITS_PER_PAGE
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 val ? -1 : position
90
+ if (i >= PAGES_PER_SEGMENT) return -1
91
91
 
92
- const p = this.pages[i]
92
+ while (i < this.pages.length) {
93
+ const p = this.pages[i]
93
94
 
94
- let index = -1
95
+ let index = -1
96
+
97
+ if (p) index = p.findFirst(val, j)
98
+ else if (!val) index = j
95
99
 
96
- if (p) index = p.findFirst(val, j)
97
- else if (!val) index = j
100
+ if (index !== -1) return i * BITS_PER_PAGE + index
98
101
 
99
- if (index !== -1) return i * BITS_PER_PAGE + index
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
- const j = position & (BITS_PER_PAGE - 1)
108
- const i = (position - j) / BITS_PER_PAGE
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 val ? -1 : position
115
+ if (i >= PAGES_PER_SEGMENT) return -1
111
116
 
112
- const p = this.pages[i]
117
+ while (i >= 0) {
118
+ const p = this.pages[i]
119
+
120
+ let index = -1
113
121
 
114
- let index = -1
122
+ if (p) index = p.findLast(val, j)
123
+ else if (!val) index = j
115
124
 
116
- if (p) index = p.findLast(val, j)
117
- else if (!val) index = j
125
+ if (index !== -1) return i * BITS_PER_PAGE + index
118
126
 
119
- if (index !== -1) return i * BITS_PER_PAGE + index
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
- r.end = end
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.9.1",
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.0",
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
  }