hypercore 10.0.0 → 10.2.1
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 +89 -32
- package/index.js +11 -16
- package/lib/bitfield.js +1 -1
- package/lib/block-store.js +6 -3
- package/lib/core.js +23 -12
- package/lib/download.js +22 -0
- package/lib/info.js +1 -0
- package/lib/merkle-tree.js +1 -1
- package/lib/oplog.js +2 -2
- package/package.json +9 -7
package/README.md
CHANGED
|
@@ -83,12 +83,31 @@ valueEncodings will be applied to individually blocks, even if you append batche
|
|
|
83
83
|
Append a block of data (or an array of blocks) to the core.
|
|
84
84
|
Returns the new length and byte length of the core.
|
|
85
85
|
|
|
86
|
+
``` js
|
|
87
|
+
// simple call append with a new block of data
|
|
88
|
+
await core.append(Buffer.from('I am a block of data'))
|
|
89
|
+
|
|
90
|
+
// pass an array to append multiple blocks as a batch
|
|
91
|
+
await core.append([Buffer.from('batch block 1'), Buffer.from('batch block 2')])
|
|
92
|
+
```
|
|
93
|
+
|
|
86
94
|
#### `const block = await core.get(index, [options])`
|
|
87
95
|
|
|
88
96
|
Get a block of data.
|
|
89
97
|
If the data is not available locally this method will prioritize and wait for the data to be downloaded.
|
|
90
98
|
|
|
91
|
-
|
|
99
|
+
``` js
|
|
100
|
+
// get block #42
|
|
101
|
+
const block = await core.get(42)
|
|
102
|
+
|
|
103
|
+
// get block #43, but only wait 5s
|
|
104
|
+
const blockIfFast = await core.get(43, { timeout: 5000 })
|
|
105
|
+
|
|
106
|
+
// get block #44, but only if we have it locally
|
|
107
|
+
const blockLocal = await core.get(44, { wait: false })
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
Additional options include
|
|
92
111
|
|
|
93
112
|
``` js
|
|
94
113
|
{
|
|
@@ -99,20 +118,52 @@ Options include
|
|
|
99
118
|
}
|
|
100
119
|
```
|
|
101
120
|
|
|
102
|
-
#### `await core.
|
|
121
|
+
#### `const updated = await core.update()`
|
|
103
122
|
|
|
104
|
-
|
|
123
|
+
Wait for the core to try and find a signed update to it's length.
|
|
124
|
+
Does not download any data from peers except for a proof of the new core length.
|
|
105
125
|
|
|
106
|
-
|
|
107
|
-
|
|
126
|
+
``` js
|
|
127
|
+
const updated = await core.update()
|
|
108
128
|
|
|
109
|
-
|
|
129
|
+
console.log('core was updated?', updated, 'length is', core.length)
|
|
130
|
+
```
|
|
110
131
|
|
|
111
|
-
|
|
132
|
+
#### `const [index, relativeOffset] = await core.seek(byteOffset)`
|
|
133
|
+
|
|
134
|
+
Seek to a byte offset.
|
|
135
|
+
|
|
136
|
+
Returns `[index, relativeOffset]`, where `index` is the data block the byteOffset is contained in and `relativeOffset` is
|
|
137
|
+
the relative byte offset in the data block.
|
|
138
|
+
|
|
139
|
+
``` js
|
|
140
|
+
await core.append([Buffer.from('abc'), Buffer.from('d'), Buffer.from('efg')])
|
|
141
|
+
|
|
142
|
+
const first = await core.seek(1) // returns [0, 1]
|
|
143
|
+
const second = await core.seek(3) // returns [1, 0]
|
|
144
|
+
const third = await core.seek(5) // returns [2, 1]
|
|
145
|
+
```
|
|
112
146
|
|
|
113
147
|
#### `const stream = core.createReadStream([options])`
|
|
114
148
|
|
|
115
|
-
Make a read stream
|
|
149
|
+
Make a read stream to read a range of data out at once.
|
|
150
|
+
|
|
151
|
+
``` js
|
|
152
|
+
// read the full core
|
|
153
|
+
const fullStream = core.createReadStream()
|
|
154
|
+
|
|
155
|
+
// read from block 10-15
|
|
156
|
+
const partialStream = core.createReadStream({ start: 10, end: 15 })
|
|
157
|
+
|
|
158
|
+
// pipe the stream somewhere using the .pipe method on Node.js or consume it as
|
|
159
|
+
// an async iterator
|
|
160
|
+
|
|
161
|
+
for await (const data of fullStream) {
|
|
162
|
+
console.log('data:', data)
|
|
163
|
+
}
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
Additional options include:
|
|
116
167
|
|
|
117
168
|
``` js
|
|
118
169
|
{
|
|
@@ -123,6 +174,28 @@ Make a read stream. Options include:
|
|
|
123
174
|
}
|
|
124
175
|
```
|
|
125
176
|
|
|
177
|
+
#### `await core.clear(start, [end])`
|
|
178
|
+
|
|
179
|
+
Clear stored blocks between `start` and `end`, reclaiming storage when possible.
|
|
180
|
+
|
|
181
|
+
``` js
|
|
182
|
+
await core.clear(4) // clear block 4 from your local cache
|
|
183
|
+
await core.clear(0, 10) // clear block 0-10 from your local cache
|
|
184
|
+
```
|
|
185
|
+
|
|
186
|
+
The core will also gossip to peers it is connected to, that is no longer has these blocks.
|
|
187
|
+
|
|
188
|
+
#### `await core.truncate(newLength, [forkId])`
|
|
189
|
+
|
|
190
|
+
Truncate the core to a smaller length.
|
|
191
|
+
|
|
192
|
+
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.
|
|
193
|
+
Note that the fork id should be monotonely incrementing.
|
|
194
|
+
|
|
195
|
+
#### `const hash = await core.treeHash([length])`
|
|
196
|
+
|
|
197
|
+
Get the Merkle Tree hash of the core at a given length, defaulting to the current length of the core.
|
|
198
|
+
|
|
126
199
|
#### `const range = core.download([range])`
|
|
127
200
|
|
|
128
201
|
Download a range of data.
|
|
@@ -130,7 +203,7 @@ Download a range of data.
|
|
|
130
203
|
You can await when the range has been fully downloaded by doing:
|
|
131
204
|
|
|
132
205
|
```js
|
|
133
|
-
await range.
|
|
206
|
+
await range.done()
|
|
134
207
|
```
|
|
135
208
|
|
|
136
209
|
A range can have the following properties:
|
|
@@ -165,23 +238,6 @@ To cancel downloading a range simply destroy the range instance.
|
|
|
165
238
|
range.destroy()
|
|
166
239
|
```
|
|
167
240
|
|
|
168
|
-
#### `const [index, relativeOffset] = await core.seek(byteOffset)`
|
|
169
|
-
|
|
170
|
-
Seek to a byte offset.
|
|
171
|
-
|
|
172
|
-
Returns `(index, relativeOffset)`, where `index` is the data block the byteOffset is contained in and `relativeOffset` is
|
|
173
|
-
the relative byte offset in the data block.
|
|
174
|
-
|
|
175
|
-
#### `const updated = await core.update()`
|
|
176
|
-
|
|
177
|
-
Wait for the core to try and find a signed update to it's length.
|
|
178
|
-
Does not download any data from peers except for a proof of the new core length.
|
|
179
|
-
|
|
180
|
-
``` js
|
|
181
|
-
const updated = await core.update()
|
|
182
|
-
console.log('core was updated?', updated, 'length is', core.length)
|
|
183
|
-
```
|
|
184
|
-
|
|
185
241
|
#### `const info = await core.info()`
|
|
186
242
|
|
|
187
243
|
Get information about this core, such as its total size in bytes.
|
|
@@ -191,6 +247,7 @@ The object will look like this:
|
|
|
191
247
|
```js
|
|
192
248
|
Info {
|
|
193
249
|
key: Buffer(...),
|
|
250
|
+
discoveryKey: Buffer(...),
|
|
194
251
|
length: 18,
|
|
195
252
|
contiguousLength: 16,
|
|
196
253
|
byteLength: 742,
|
|
@@ -232,6 +289,12 @@ Can we read from this core? After closing the core this will be false.
|
|
|
232
289
|
|
|
233
290
|
Populated after `ready` has been emitted. Will be `false` before the event.
|
|
234
291
|
|
|
292
|
+
#### `core.id`
|
|
293
|
+
|
|
294
|
+
String containing the id (z-base-32 of the public key) identifying this core.
|
|
295
|
+
|
|
296
|
+
Populated after `ready` has been emitted. Will be `null` before the event.
|
|
297
|
+
|
|
235
298
|
#### `core.key`
|
|
236
299
|
|
|
237
300
|
Buffer containing the public key identifying this core.
|
|
@@ -267,12 +330,6 @@ How many blocks are contiguously available starting from the first block of this
|
|
|
267
330
|
|
|
268
331
|
Populated after `ready` has been emitted. Will be `0` before the event.
|
|
269
332
|
|
|
270
|
-
#### `core.contiguousByteLength`
|
|
271
|
-
|
|
272
|
-
How much data is contiguously available starting from the first block of this core?
|
|
273
|
-
|
|
274
|
-
Populated after `ready` has been emitted. Will be `0` before the event.
|
|
275
|
-
|
|
276
333
|
#### `core.fork`
|
|
277
334
|
|
|
278
335
|
What is the current fork id of this core?
|
package/index.js
CHANGED
|
@@ -7,11 +7,13 @@ const b4a = require('b4a')
|
|
|
7
7
|
const Xache = require('xache')
|
|
8
8
|
const NoiseSecretStream = require('@hyperswarm/secret-stream')
|
|
9
9
|
const Protomux = require('protomux')
|
|
10
|
+
const z32 = require('z32')
|
|
10
11
|
|
|
11
12
|
const Replicator = require('./lib/replicator')
|
|
12
13
|
const Core = require('./lib/core')
|
|
13
14
|
const BlockEncryption = require('./lib/block-encryption')
|
|
14
15
|
const Info = require('./lib/info')
|
|
16
|
+
const Download = require('./lib/download')
|
|
15
17
|
const { ReadStream, WriteStream } = require('./lib/streams')
|
|
16
18
|
const { BAD_ARGUMENT, SESSION_CLOSED, SESSION_NOT_WRITABLE, SNAPSHOT_NOT_AVAILABLE } = require('./lib/errors')
|
|
17
19
|
|
|
@@ -57,6 +59,7 @@ module.exports = class Hypercore extends EventEmitter {
|
|
|
57
59
|
this.encodeBatch = null
|
|
58
60
|
this.activeRequests = []
|
|
59
61
|
|
|
62
|
+
this.id = null
|
|
60
63
|
this.key = key || null
|
|
61
64
|
this.keyPair = null
|
|
62
65
|
this.readable = true
|
|
@@ -145,7 +148,7 @@ module.exports = class Hypercore extends EventEmitter {
|
|
|
145
148
|
if (!noiseStream) throw BAD_ARGUMENT('Invalid stream')
|
|
146
149
|
|
|
147
150
|
if (!noiseStream.userData) {
|
|
148
|
-
const protocol =
|
|
151
|
+
const protocol = Protomux.from(noiseStream)
|
|
149
152
|
|
|
150
153
|
if (opts.ondiscoverykey) {
|
|
151
154
|
protocol.pair({ protocol: 'hypercore/alpha' }, opts.ondiscoverykey)
|
|
@@ -224,6 +227,7 @@ module.exports = class Hypercore extends EventEmitter {
|
|
|
224
227
|
if (!this.auth) this.auth = o.auth
|
|
225
228
|
|
|
226
229
|
this.crypto = o.crypto
|
|
230
|
+
this.id = o.id
|
|
227
231
|
this.key = o.key
|
|
228
232
|
this.core = o.core
|
|
229
233
|
this.replicator = o.replicator
|
|
@@ -325,6 +329,7 @@ module.exports = class Hypercore extends EventEmitter {
|
|
|
325
329
|
|
|
326
330
|
this.key = this.core.header.signer.publicKey
|
|
327
331
|
this.keyPair = this.core.header.signer
|
|
332
|
+
this.id = z32.encode(this.key)
|
|
328
333
|
|
|
329
334
|
this.replicator = new Replicator(this.core, this.key, {
|
|
330
335
|
eagerUpdate: true,
|
|
@@ -612,8 +617,7 @@ module.exports = class Hypercore extends EventEmitter {
|
|
|
612
617
|
if (this.opened === false) await this.opening
|
|
613
618
|
if (this.closing !== null) return false
|
|
614
619
|
|
|
615
|
-
|
|
616
|
-
if (this.writable) {
|
|
620
|
+
if (this.writable && (!opts || opts.force !== true)) {
|
|
617
621
|
if (!this.snapshotted) return false
|
|
618
622
|
return this._updateSnapshot()
|
|
619
623
|
}
|
|
@@ -629,7 +633,7 @@ module.exports = class Hypercore extends EventEmitter {
|
|
|
629
633
|
const end = this.core.tree.length
|
|
630
634
|
const contig = this.contiguousLength
|
|
631
635
|
|
|
632
|
-
await this.download({ start, end, ifAvailable: true }).
|
|
636
|
+
await this.download({ start, end, ifAvailable: true }).done()
|
|
633
637
|
|
|
634
638
|
if (!upgraded) upgraded = this.contiguousLength !== contig
|
|
635
639
|
}
|
|
@@ -740,21 +744,12 @@ module.exports = class Hypercore extends EventEmitter {
|
|
|
740
744
|
}
|
|
741
745
|
|
|
742
746
|
download (range) {
|
|
743
|
-
const
|
|
747
|
+
const req = this._download(range)
|
|
744
748
|
|
|
745
749
|
// do not crash in the background...
|
|
746
|
-
|
|
750
|
+
req.catch(noop)
|
|
747
751
|
|
|
748
|
-
|
|
749
|
-
return {
|
|
750
|
-
async downloaded () {
|
|
751
|
-
const req = await reqP
|
|
752
|
-
return req.promise
|
|
753
|
-
},
|
|
754
|
-
destroy () {
|
|
755
|
-
reqP.then(req => req.context && req.context.detach(req), noop)
|
|
756
|
-
}
|
|
757
|
-
}
|
|
752
|
+
return new Download(req)
|
|
758
753
|
}
|
|
759
754
|
|
|
760
755
|
async _download (range) {
|
package/lib/bitfield.js
CHANGED
|
@@ -152,7 +152,7 @@ module.exports = class Bitfield {
|
|
|
152
152
|
|
|
153
153
|
clear () {
|
|
154
154
|
return new Promise((resolve, reject) => {
|
|
155
|
-
this.storage.
|
|
155
|
+
this.storage.truncate(0, (err) => {
|
|
156
156
|
if (err) return reject(err)
|
|
157
157
|
this.pages = new BigSparseArray()
|
|
158
158
|
this.unflushed = []
|
package/lib/block-store.js
CHANGED
|
@@ -20,12 +20,15 @@ module.exports = class BlockStore {
|
|
|
20
20
|
return this.put(i, batch.length === 1 ? batch[0] : b4a.concat(batch), offset)
|
|
21
21
|
}
|
|
22
22
|
|
|
23
|
-
clear (offset = 0, length =
|
|
23
|
+
clear (offset = 0, length = -1) {
|
|
24
24
|
return new Promise((resolve, reject) => {
|
|
25
|
-
this.storage.
|
|
25
|
+
if (length === -1) this.storage.truncate(offset, done)
|
|
26
|
+
else this.storage.del(offset, length, done)
|
|
27
|
+
|
|
28
|
+
function done (err) {
|
|
26
29
|
if (err) reject(err)
|
|
27
30
|
else resolve()
|
|
28
|
-
}
|
|
31
|
+
}
|
|
29
32
|
})
|
|
30
33
|
}
|
|
31
34
|
|
package/lib/core.js
CHANGED
|
@@ -37,18 +37,8 @@ module.exports = class Core {
|
|
|
37
37
|
try {
|
|
38
38
|
return await this.resume(oplogFile, treeFile, bitfieldFile, dataFile, opts)
|
|
39
39
|
} catch (err) {
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
oplogFile.close(done)
|
|
44
|
-
treeFile.close(done)
|
|
45
|
-
bitfieldFile.close(done)
|
|
46
|
-
dataFile.close(done)
|
|
47
|
-
|
|
48
|
-
function done () {
|
|
49
|
-
if (--missing === 0) reject(err)
|
|
50
|
-
}
|
|
51
|
-
})
|
|
40
|
+
await closeAll(oplogFile, treeFile, bitfieldFile, dataFile)
|
|
41
|
+
throw err
|
|
52
42
|
}
|
|
53
43
|
}
|
|
54
44
|
|
|
@@ -596,4 +586,25 @@ function updateUserData (list, key, value) {
|
|
|
596
586
|
if (value) list.push({ key, value })
|
|
597
587
|
}
|
|
598
588
|
|
|
589
|
+
function closeAll (...storages) {
|
|
590
|
+
let missing = 1
|
|
591
|
+
let error = null
|
|
592
|
+
|
|
593
|
+
return new Promise((resolve, reject) => {
|
|
594
|
+
for (const s of storages) {
|
|
595
|
+
missing++
|
|
596
|
+
s.close(done)
|
|
597
|
+
}
|
|
598
|
+
|
|
599
|
+
done(null)
|
|
600
|
+
|
|
601
|
+
function done (err) {
|
|
602
|
+
if (err) error = err
|
|
603
|
+
if (--missing) return
|
|
604
|
+
if (error) reject(error)
|
|
605
|
+
else resolve()
|
|
606
|
+
}
|
|
607
|
+
})
|
|
608
|
+
}
|
|
609
|
+
|
|
599
610
|
function noop () {}
|
package/lib/download.js
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
module.exports = class Download {
|
|
2
|
+
constructor (req) {
|
|
3
|
+
this.req = req
|
|
4
|
+
}
|
|
5
|
+
|
|
6
|
+
async done () {
|
|
7
|
+
return (await this.req).promise
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Deprecated. Use `range.done()`.
|
|
12
|
+
*/
|
|
13
|
+
downloaded () {
|
|
14
|
+
return this.done()
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
destroy () {
|
|
18
|
+
this.req.then(req => req.context && req.context.detach(req), noop)
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
function noop () {}
|
package/lib/info.js
CHANGED
package/lib/merkle-tree.js
CHANGED
|
@@ -454,7 +454,7 @@ module.exports = class MerkleTree {
|
|
|
454
454
|
const t = this.truncateTo
|
|
455
455
|
const offset = t === 0 ? 0 : (t - 1) * 80 + 40
|
|
456
456
|
|
|
457
|
-
this.storage.
|
|
457
|
+
this.storage.truncate(offset, (err) => {
|
|
458
458
|
if (err) return reject(err)
|
|
459
459
|
|
|
460
460
|
if (this.truncateTo === t) {
|
package/lib/oplog.js
CHANGED
|
@@ -121,7 +121,7 @@ module.exports = class Oplog {
|
|
|
121
121
|
if (size === buffer.byteLength) return result
|
|
122
122
|
|
|
123
123
|
await new Promise((resolve, reject) => {
|
|
124
|
-
this.storage.
|
|
124
|
+
this.storage.truncate(size, err => {
|
|
125
125
|
if (err) return reject(err)
|
|
126
126
|
resolve()
|
|
127
127
|
})
|
|
@@ -164,7 +164,7 @@ module.exports = class Oplog {
|
|
|
164
164
|
this.storage.write(i === 0 ? 0 : this._pageSize, buf, err => {
|
|
165
165
|
if (err) return reject(err)
|
|
166
166
|
|
|
167
|
-
this.storage.
|
|
167
|
+
this.storage.truncate(this._entryOffset, err => {
|
|
168
168
|
if (err) return reject(err)
|
|
169
169
|
|
|
170
170
|
this._headers[i] = bit
|
package/package.json
CHANGED
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "hypercore",
|
|
3
|
-
"version": "10.
|
|
3
|
+
"version": "10.2.1",
|
|
4
4
|
"description": "Hypercore is a secure, distributed append-only log",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"scripts": {
|
|
7
|
-
"test": "standard &&
|
|
7
|
+
"test": "standard && node test/all.js",
|
|
8
|
+
"test:generate": "brittle -r test/all.js test/*.js"
|
|
8
9
|
},
|
|
9
10
|
"repository": {
|
|
10
11
|
"type": "git",
|
|
@@ -42,18 +43,19 @@
|
|
|
42
43
|
"flat-tree": "^1.9.0",
|
|
43
44
|
"hypercore-crypto": "^3.2.1",
|
|
44
45
|
"is-options": "^1.0.1",
|
|
45
|
-
"protomux": "^3.
|
|
46
|
-
"random-access-file": "^3.
|
|
46
|
+
"protomux": "^3.4.0",
|
|
47
|
+
"random-access-file": "^3.2.2",
|
|
47
48
|
"random-array-iterator": "^1.0.0",
|
|
48
49
|
"safety-catch": "^1.0.1",
|
|
49
50
|
"sodium-universal": "^3.0.4",
|
|
50
51
|
"streamx": "^2.12.4",
|
|
51
|
-
"xache": "^1.1.0"
|
|
52
|
+
"xache": "^1.1.0",
|
|
53
|
+
"z32": "^1.0.0"
|
|
52
54
|
},
|
|
53
55
|
"devDependencies": {
|
|
54
|
-
"brittle": "^
|
|
56
|
+
"brittle": "^3.0.0",
|
|
55
57
|
"hyperswarm": "^4.1.1",
|
|
56
|
-
"random-access-memory": "^5.0.
|
|
58
|
+
"random-access-memory": "^5.0.1",
|
|
57
59
|
"random-access-memory-overlay": "^2.0.0",
|
|
58
60
|
"standard": "^16.0.3",
|
|
59
61
|
"tmp-promise": "^3.0.2"
|