hypercore 10.0.0-alpha.10 → 10.0.0-alpha.14

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
@@ -98,6 +98,19 @@ Truncate the core to a smaller length.
98
98
  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.
99
99
  Note that the fork id should be monotonely incrementing.
100
100
 
101
+ #### `const stream = core.createReadStream([options])`
102
+
103
+ Make a read stream. Options include:
104
+
105
+ ``` js
106
+ {
107
+ start: 0,
108
+ end: core.length,
109
+ live: false,
110
+ snapshot: true // auto set end to core.length on open or update it on every read
111
+ }
112
+ ```
113
+
101
114
  #### `const range = core.download([range])`
102
115
 
103
116
  Download a range of data.
package/index.js CHANGED
@@ -14,6 +14,7 @@ const Replicator = require('./lib/replicator')
14
14
  const Extensions = require('./lib/extensions')
15
15
  const Core = require('./lib/core')
16
16
  const BlockEncryption = require('./lib/block-encryption')
17
+ const { ReadStream } = require('./lib/streams')
17
18
 
18
19
  const promises = Symbol.for('hypercore.promises')
19
20
  const inspect = Symbol.for('nodejs.util.inspect.custom')
@@ -65,7 +66,7 @@ module.exports = class Hypercore extends EventEmitter {
65
66
  this.autoClose = !!opts.autoClose
66
67
 
67
68
  this.closing = null
68
- this.opening = opts._opening || this._open(key, storage, opts)
69
+ this.opening = this._openSession(key, storage, opts)
69
70
  this.opening.catch(noop)
70
71
 
71
72
  this._preappend = preappend.bind(this)
@@ -114,31 +115,22 @@ module.exports = class Hypercore extends EventEmitter {
114
115
  }
115
116
 
116
117
  const Clz = opts.class || Hypercore
117
- const keyPair = opts.keyPair && opts.keyPair.secretKey && { ...opts.keyPair }
118
-
119
- // This only works if the hypercore was fully loaded,
120
- // but we only do this to validate the keypair to help catch bugs so yolo
121
- if (this.key && keyPair) keyPair.publicKey = this.key
122
-
123
118
  const s = new Clz(this.storage, this.key, {
124
119
  ...opts,
125
- sign: opts.sign || (keyPair && keyPair.secretKey && Core.createSigner(this.crypto, keyPair)) || this.sign,
126
- valueEncoding: this.valueEncoding,
127
120
  extensions: this.extensions,
128
121
  _opening: this.opening,
129
122
  _sessions: this.sessions
130
123
  })
131
124
 
132
- s._initSession(this)
125
+ s._passCapabilities(this)
133
126
  this.sessions.push(s)
134
127
 
135
128
  return s
136
129
  }
137
130
 
138
- _initSession (o) {
131
+ _passCapabilities (o) {
139
132
  if (!this.sign) this.sign = o.sign
140
133
  this.crypto = o.crypto
141
- this.opened = o.opened
142
134
  this.key = o.key
143
135
  this.discoveryKey = o.discoveryKey
144
136
  this.core = o.core
@@ -148,6 +140,98 @@ module.exports = class Hypercore extends EventEmitter {
148
140
  this.autoClose = o.autoClose
149
141
  }
150
142
 
143
+ async _openFromExisting (from, opts) {
144
+ await from.opening
145
+
146
+ for (const [name, ext] of this.extensions) {
147
+ from.extensions.register(name, null, ext)
148
+ }
149
+
150
+ this._passCapabilities(from)
151
+ this.extensions = from.extensions
152
+ this.sessions = from.sessions
153
+ this.storage = from.storage
154
+
155
+ this.sessions.push(this)
156
+ }
157
+
158
+ async _openSession (key, storage, opts) {
159
+ const isFirst = !opts._opening
160
+
161
+ if (!isFirst) await opts._opening
162
+ if (opts.preload) opts = { ...opts, ...(await opts.preload()) }
163
+
164
+ const keyPair = (key && opts.keyPair)
165
+ ? { ...opts.keyPair, publicKey: key }
166
+ : key
167
+ ? { publicKey: key, secretKey: null }
168
+ : opts.keyPair
169
+
170
+ // This only works if the hypercore was fully loaded,
171
+ // but we only do this to validate the keypair to help catch bugs so yolo
172
+ if (this.key && keyPair) keyPair.publicKey = this.key
173
+
174
+ if (opts.sign) {
175
+ this.sign = opts.sign
176
+ } else if (keyPair && keyPair.secretKey) {
177
+ this.sign = Core.createSigner(this.crypto, keyPair)
178
+ }
179
+
180
+ if (isFirst) {
181
+ await this._openCapabilities(keyPair, storage, opts)
182
+ // Only the root session should pass capabilities to other sessions.
183
+ for (let i = 0; i < this.sessions.length; i++) {
184
+ const s = this.sessions[i]
185
+ if (s !== this) s._passCapabilities(this)
186
+ }
187
+ }
188
+
189
+ if (!this.sign) this.sign = this.core.defaultSign
190
+ this.writable = !!this.sign
191
+
192
+ if (opts.valueEncoding) {
193
+ this.valueEncoding = c.from(codecs(opts.valueEncoding))
194
+ }
195
+
196
+ // This is a hidden option that's only used by Corestore.
197
+ // It's required so that corestore can load a name from userData before 'ready' is emitted.
198
+ if (opts._preready) await opts._preready(this)
199
+
200
+ this.opened = true
201
+ this.emit('ready')
202
+ }
203
+
204
+ async _openCapabilities (keyPair, storage, opts) {
205
+ if (opts.from) return this._openFromExisting(opts.from, opts)
206
+
207
+ this.storage = Hypercore.defaultStorage(opts.storage || storage)
208
+
209
+ this.core = await Core.open(this.storage, {
210
+ keyPair,
211
+ crypto: this.crypto,
212
+ onupdate: this._oncoreupdate.bind(this)
213
+ })
214
+
215
+ if (opts.userData) {
216
+ for (const [key, value] of Object.entries(opts.userData)) {
217
+ await this.core.userData(key, value)
218
+ }
219
+ }
220
+
221
+ this.replicator = new Replicator(this.core, {
222
+ onupdate: this._onpeerupdate.bind(this)
223
+ })
224
+
225
+ this.discoveryKey = this.crypto.discoveryKey(this.core.header.signer.publicKey)
226
+ this.key = this.core.header.signer.publicKey
227
+
228
+ if (!this.encryption && opts.encryptionKey) {
229
+ this.encryption = new BlockEncryption(opts.encryptionKey, this.key)
230
+ }
231
+
232
+ this.extensions.attach(this.replicator)
233
+ }
234
+
151
235
  close () {
152
236
  if (this.closing) return this.closing
153
237
  this.closing = this._close()
@@ -236,71 +320,6 @@ module.exports = class Hypercore extends EventEmitter {
236
320
  return this.opening
237
321
  }
238
322
 
239
- async _open (key, storage, opts) {
240
- if (opts.preload) opts = { ...opts, ...(await opts.preload()) }
241
-
242
- this.valueEncoding = opts.valueEncoding ? c.from(codecs(opts.valueEncoding)) : null
243
-
244
- const keyPair = (key && opts.keyPair)
245
- ? { ...opts.keyPair, publicKey: key }
246
- : key
247
- ? { publicKey: key, secretKey: null }
248
- : opts.keyPair
249
-
250
- if (opts.from) {
251
- const from = opts.from
252
- await from.opening
253
- for (const [name, ext] of this.extensions) from.extensions.register(name, null, ext)
254
- this._initSession(from)
255
- this.extensions = from.extensions
256
- this.sessions = from.sessions
257
- this.storage = from.storage
258
- if (!this.sign) this.sign = opts.sign || ((keyPair && keyPair.secretKey) ? Core.createSigner(this.crypto, keyPair) : null)
259
- this.writable = !!this.sign
260
- this.sessions.push(this)
261
- return
262
- }
263
-
264
- if (!this.storage) this.storage = Hypercore.defaultStorage(opts.storage || storage)
265
-
266
- this.core = await Core.open(this.storage, {
267
- keyPair,
268
- crypto: this.crypto,
269
- onupdate: this._oncoreupdate.bind(this)
270
- })
271
-
272
- if (opts.userData) {
273
- for (const [key, value] of Object.entries(opts.userData)) {
274
- await this.core.userData(key, value)
275
- }
276
- }
277
-
278
- this.replicator = new Replicator(this.core, {
279
- onupdate: this._onpeerupdate.bind(this)
280
- })
281
-
282
- if (!this.sign) this.sign = opts.sign || this.core.defaultSign
283
-
284
- this.discoveryKey = this.crypto.discoveryKey(this.core.header.signer.publicKey)
285
- this.key = this.core.header.signer.publicKey
286
- this.writable = !!this.sign
287
-
288
- if (!this.encryption && opts.encryptionKey) {
289
- this.encryption = new BlockEncryption(opts.encryptionKey, this.key)
290
- }
291
-
292
- this.extensions.attach(this.replicator)
293
- this.opened = true
294
-
295
- if (opts.postload) await opts.postload(this)
296
-
297
- for (let i = 0; i < this.sessions.length; i++) {
298
- const s = this.sessions[i]
299
- if (s !== this) s._initSession(this)
300
- s.emit('ready')
301
- }
302
- }
303
-
304
323
  _oncoreupdate (status, bitfield, value, from) {
305
324
  if (status !== 0) {
306
325
  for (let i = 0; i < this.sessions.length; i++) {
@@ -400,6 +419,10 @@ module.exports = class Hypercore extends EventEmitter {
400
419
  return this._decode(encoding, block)
401
420
  }
402
421
 
422
+ createReadStream (opts) {
423
+ return new ReadStream(this, opts)
424
+ }
425
+
403
426
  download (range) {
404
427
  const linear = !!(range && range.linear)
405
428
 
package/lib/streams.js ADDED
@@ -0,0 +1,39 @@
1
+ const { Readable } = require('streamx')
2
+
3
+ class ReadStream extends Readable {
4
+ constructor (core, opts = {}) {
5
+ super()
6
+
7
+ this.core = core
8
+ this.start = opts.start || 0
9
+ this.end = typeof opts.end === 'number' ? opts.end : -1
10
+ this.snapshot = !opts.live && opts.snapshot !== false
11
+ this.live = !!opts.live
12
+ }
13
+
14
+ _open (cb) {
15
+ this._openP().then(cb, cb)
16
+ }
17
+
18
+ _read (cb) {
19
+ this._readP().then(cb, cb)
20
+ }
21
+
22
+ async _openP () {
23
+ if (this.end === -1) await this.core.update()
24
+ else await this.core.ready()
25
+ if (this.snapshot && this.end === -1) this.end = this.core.length
26
+ }
27
+
28
+ async _readP () {
29
+ const end = this.live ? -1 : (this.end === -1 ? this.core.length : this.end)
30
+ if (end >= 0 && this.start >= end) {
31
+ this.push(null)
32
+ return
33
+ }
34
+
35
+ this.push(await this.core.get(this.start++))
36
+ }
37
+ }
38
+
39
+ exports.ReadStream = ReadStream
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "hypercore",
3
- "version": "10.0.0-alpha.10",
3
+ "version": "10.0.0-alpha.14",
4
4
  "description": "Hypercore 10",
5
5
  "main": "index.js",
6
6
  "scripts": {
@@ -27,6 +27,10 @@
27
27
  "url": "https://github.com/hypercore-protocol/hypercore/issues"
28
28
  },
29
29
  "homepage": "https://github.com/hypercore-protocol/hypercore#readme",
30
+ "files": [
31
+ "index.js",
32
+ "lib/**.js"
33
+ ],
30
34
  "dependencies": {
31
35
  "@hyperswarm/secret-stream": "^5.0.0",
32
36
  "b4a": "^1.1.0",
@@ -35,7 +39,7 @@
35
39
  "compact-encoding": "^2.5.0",
36
40
  "crc32-universal": "^1.0.1",
37
41
  "flat-tree": "^1.9.0",
38
- "hypercore-crypto": "^2.1.1",
42
+ "hypercore-crypto": "^3.1.0",
39
43
  "is-options": "^1.0.1",
40
44
  "random-access-file": "^2.1.4",
41
45
  "random-array-iterator": "^1.0.0",
@@ -1,23 +0,0 @@
1
- name: Build Status
2
- on:
3
- push:
4
- branches:
5
- - master
6
- pull_request:
7
- branches:
8
- - master
9
- jobs:
10
- build:
11
- strategy:
12
- matrix:
13
- node-version: [lts/*]
14
- os: [ubuntu-latest, macos-latest, windows-latest]
15
- runs-on: ${{ matrix.os }}
16
- steps:
17
- - uses: actions/checkout@v2
18
- - name: Use Node.js ${{ matrix.node-version }}
19
- uses: actions/setup-node@v2
20
- with:
21
- node-version: ${{ matrix.node-version }}
22
- - run: npm install
23
- - run: npm test
package/UPGRADE.md DELETED
@@ -1,9 +0,0 @@
1
- # Upgrade Notes
2
-
3
- Notes for downstream developers who are upgrading their modules to new, breaking versions of hypercore.
4
-
5
- ## 9.0.0
6
-
7
- - The format of signatures [has been changed](https://github.com/mafintosh/hypercore/issues/260). This is backwards-compatible (v9 can read v8 signatures), but forward-incompatible (v8 cannot read v9 signatures). If a v8 peer replicates with a v9 peer, it will emit a "REMOTE SIGNTURE INVALID" error on the replication stream.
8
- - The encryption ([NOISE](https://github.com/emilbayes/noise-protocol)) handshake has been changed in an backwards- and forwards-incompatible way. v8 peers can not handshake with v9 peers, and vice-versa. A NOISE-related error is emitted on the replication stream.
9
- - There is no way (yet) to detect whether a peer is running an incompatible version of hypercore at the replication level. One workaround for downstream developers is to include their own application-level handshake before piping to the replication stream, to communicate a "app protocol version" (maybe "v8" and "v9") and abort the connection if the peer is running an incompatible version.