hypercore 10.0.0 → 10.1.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
@@ -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
- Options include
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.truncate(newLength, [forkId])`
121
+ #### `const updated = await core.update()`
103
122
 
104
- Truncate the core to a smaller length.
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
- 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.
107
- Note that the fork id should be monotonely incrementing.
126
+ ``` js
127
+ const updated = await core.update()
108
128
 
109
- #### `const hash = await core.treeHash([length])`
129
+ console.log('core was updated?', updated, 'length is', core.length)
130
+ ```
110
131
 
111
- Get the Merkle Tree hash of the core at a given length, defaulting to the current length of the core.
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. Options include:
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.downloaded()
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,
@@ -267,12 +324,6 @@ How many blocks are contiguously available starting from the first block of this
267
324
 
268
325
  Populated after `ready` has been emitted. Will be `0` before the event.
269
326
 
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
327
  #### `core.fork`
277
328
 
278
329
  What is the current fork id of this core?
package/index.js CHANGED
@@ -12,6 +12,7 @@ const Replicator = require('./lib/replicator')
12
12
  const Core = require('./lib/core')
13
13
  const BlockEncryption = require('./lib/block-encryption')
14
14
  const Info = require('./lib/info')
15
+ const Download = require('./lib/download')
15
16
  const { ReadStream, WriteStream } = require('./lib/streams')
16
17
  const { BAD_ARGUMENT, SESSION_CLOSED, SESSION_NOT_WRITABLE, SNAPSHOT_NOT_AVAILABLE } = require('./lib/errors')
17
18
 
@@ -612,8 +613,7 @@ module.exports = class Hypercore extends EventEmitter {
612
613
  if (this.opened === false) await this.opening
613
614
  if (this.closing !== null) return false
614
615
 
615
- // TODO: add an option where a writer can bootstrap it's state from the network also
616
- if (this.writable) {
616
+ if (this.writable && (!opts || opts.force !== true)) {
617
617
  if (!this.snapshotted) return false
618
618
  return this._updateSnapshot()
619
619
  }
@@ -629,7 +629,7 @@ module.exports = class Hypercore extends EventEmitter {
629
629
  const end = this.core.tree.length
630
630
  const contig = this.contiguousLength
631
631
 
632
- await this.download({ start, end, ifAvailable: true }).downloaded()
632
+ await this.download({ start, end, ifAvailable: true }).done()
633
633
 
634
634
  if (!upgraded) upgraded = this.contiguousLength !== contig
635
635
  }
@@ -740,21 +740,12 @@ module.exports = class Hypercore extends EventEmitter {
740
740
  }
741
741
 
742
742
  download (range) {
743
- const reqP = this._download(range)
743
+ const req = this._download(range)
744
744
 
745
745
  // do not crash in the background...
746
- reqP.catch(noop)
746
+ req.catch(noop)
747
747
 
748
- // TODO: turn this into an actual object...
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
- }
748
+ return new Download(req)
758
749
  }
759
750
 
760
751
  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.del(0, Infinity, (err) => {
155
+ this.storage.truncate(0, (err) => {
156
156
  if (err) return reject(err)
157
157
  this.pages = new BigSparseArray()
158
158
  this.unflushed = []
@@ -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 = Infinity) {
23
+ clear (offset = 0, length = -1) {
24
24
  return new Promise((resolve, reject) => {
25
- this.storage.del(offset, length, (err) => {
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
- return new Promise((resolve, reject) => {
41
- let missing = 4
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 () {}
@@ -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
@@ -9,6 +9,7 @@ module.exports = class Info {
9
9
  static async from (core, padding, snapshot) {
10
10
  return new Info({
11
11
  key: core.key,
12
+ discoveryKey: core.discoveryKey,
12
13
  length: snapshot
13
14
  ? snapshot.length
14
15
  : core.tree.length,
@@ -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.del(offset, Infinity, (err) => {
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.del(size, Infinity, err => {
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.del(this._entryOffset, Infinity, err => {
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.0.0",
3
+ "version": "10.1.0",
4
4
  "description": "Hypercore is a secure, distributed append-only log",
5
5
  "main": "index.js",
6
6
  "scripts": {
7
- "test": "standard && brittle test/*.js"
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",
@@ -43,7 +44,7 @@
43
44
  "hypercore-crypto": "^3.2.1",
44
45
  "is-options": "^1.0.1",
45
46
  "protomux": "^3.2.0",
46
- "random-access-file": "^3.0.1",
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",
@@ -51,9 +52,9 @@
51
52
  "xache": "^1.1.0"
52
53
  },
53
54
  "devDependencies": {
54
- "brittle": "^2.0.0",
55
+ "brittle": "^3.0.0",
55
56
  "hyperswarm": "^4.1.1",
56
- "random-access-memory": "^5.0.0",
57
+ "random-access-memory": "^5.0.1",
57
58
  "random-access-memory-overlay": "^2.0.0",
58
59
  "standard": "^16.0.3",
59
60
  "tmp-promise": "^3.0.2"