hypercore 10.11.0 → 10.13.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
@@ -72,7 +72,8 @@ Note that `tree`, `data`, and `bitfield` are normally heavily sparse files.
72
72
  encodeBatch: batch => { ... }, // optionally apply an encoding to complete batches
73
73
  keyPair: kp, // optionally pass the public key and secret key as a key pair
74
74
  encryptionKey: k, // optionally pass an encryption key to enable block encryption
75
- onwait: () => {} // hook that is called if gets are waiting for download
75
+ onwait: () => {}, // hook that is called if gets are waiting for download
76
+ writable: true // disable appends and truncates
76
77
  }
77
78
  ```
78
79
 
package/index.js CHANGED
@@ -14,8 +14,16 @@ 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 Batch = require('./lib/batch')
17
18
  const { ReadStream, WriteStream, ByteStream } = require('./lib/streams')
18
- const { BAD_ARGUMENT, SESSION_CLOSED, SESSION_NOT_WRITABLE, SNAPSHOT_NOT_AVAILABLE } = require('./lib/errors')
19
+ const {
20
+ BAD_ARGUMENT,
21
+ BATCH_ALREADY_EXISTS,
22
+ BATCH_UNFLUSHED,
23
+ SESSION_CLOSED,
24
+ SESSION_NOT_WRITABLE,
25
+ SNAPSHOT_NOT_AVAILABLE
26
+ } = require('./lib/errors')
19
27
 
20
28
  const promises = Symbol.for('hypercore.promises')
21
29
  const inspect = Symbol.for('nodejs.util.inspect.custom')
@@ -74,6 +82,7 @@ module.exports = class Hypercore extends EventEmitter {
74
82
  this.onwait = opts.onwait || null
75
83
  this.wait = opts.wait !== false
76
84
  this.timeout = opts.timeout || 0
85
+ this._readonly = opts.writable === false
77
86
 
78
87
  this.closing = null
79
88
  this.opening = this._openSession(key, storage, opts)
@@ -81,6 +90,7 @@ module.exports = class Hypercore extends EventEmitter {
81
90
 
82
91
  this._preappend = preappend.bind(this)
83
92
  this._snapshot = null
93
+ this._batch = opts._batch || null
84
94
  this._findingPeers = 0
85
95
  }
86
96
 
@@ -201,6 +211,7 @@ module.exports = class Hypercore extends EventEmitter {
201
211
 
202
212
  const sparse = opts.sparse === false ? false : this.sparse
203
213
  const wait = opts.wait === false ? false : this.wait
214
+ const writable = opts.writable === false ? false : !this._readonly
204
215
  const onwait = opts.onwait === undefined ? this.onwait : opts.onwait
205
216
  const timeout = opts.timeout === undefined ? this.timeout : opts.timeout
206
217
  const Clz = opts.class || Hypercore
@@ -210,8 +221,10 @@ module.exports = class Hypercore extends EventEmitter {
210
221
  wait,
211
222
  onwait,
212
223
  timeout,
224
+ writable,
213
225
  _opening: this.opening,
214
- _sessions: this.sessions
226
+ _sessions: this.sessions,
227
+ _batch: this._batch
215
228
  })
216
229
 
217
230
  s._passCapabilities(this)
@@ -237,7 +250,7 @@ module.exports = class Hypercore extends EventEmitter {
237
250
  this.core = o.core
238
251
  this.replicator = o.replicator
239
252
  this.encryption = o.encryption
240
- this.writable = !!(this.auth && this.auth.sign)
253
+ this.writable = !this._readonly && !!(this.auth && this.auth.sign)
241
254
  this.autoClose = o.autoClose
242
255
 
243
256
  if (this.snapshotted && this.core && !this._snapshot) this._updateSnapshot()
@@ -295,7 +308,7 @@ module.exports = class Hypercore extends EventEmitter {
295
308
  }
296
309
 
297
310
  if (!this.auth) this.auth = this.core.defaultAuth
298
- this.writable = !!this.auth.sign
311
+ this.writable = !this._readonly && !!this.auth.sign
299
312
 
300
313
  if (opts.valueEncoding) {
301
314
  this.valueEncoding = c.from(opts.valueEncoding)
@@ -681,6 +694,13 @@ module.exports = class Hypercore extends EventEmitter {
681
694
  return true
682
695
  }
683
696
 
697
+ batch () {
698
+ if (this._batch !== null) throw BATCH_ALREADY_EXISTS()
699
+ const batch = new Batch(this)
700
+ for (const session of this.sessions) session._batch = batch
701
+ return batch
702
+ }
703
+
684
704
  async seek (bytes, opts) {
685
705
  if (this.opened === false) await this.opening
686
706
 
@@ -843,6 +863,7 @@ module.exports = class Hypercore extends EventEmitter {
843
863
  }
844
864
 
845
865
  async truncate (newLength = 0, fork = -1) {
866
+ if (this._batch && !this._batch.flushed) throw BATCH_UNFLUSHED()
846
867
  if (this.opened === false) await this.opening
847
868
  if (this.writable === false) throw SESSION_NOT_WRITABLE()
848
869
 
@@ -854,6 +875,7 @@ module.exports = class Hypercore extends EventEmitter {
854
875
  }
855
876
 
856
877
  async append (blocks) {
878
+ if (this._batch && !this._batch.flushed) throw BATCH_UNFLUSHED()
857
879
  if (this.opened === false) await this.opening
858
880
  if (this.writable === false) throw SESSION_NOT_WRITABLE()
859
881
 
package/lib/batch.js ADDED
@@ -0,0 +1,114 @@
1
+ const { SESSION_NOT_WRITABLE, BATCH_ALREADY_FLUSHED } = require('./errors')
2
+
3
+ module.exports = class Batch {
4
+ constructor (session) {
5
+ this.session = session
6
+ this.flushed = false
7
+
8
+ this._appends = []
9
+ this._byteLength = 0
10
+ }
11
+
12
+ get length () {
13
+ return this.session.length + this._appends.length
14
+ }
15
+
16
+ ready () {
17
+ return this.session.ready()
18
+ }
19
+
20
+ async info (opts) {
21
+ const session = this.session
22
+ const info = await session.info(opts)
23
+
24
+ if (info.contiguousLength === info.length) {
25
+ info.contiguousLength = info.length += this._appends.length
26
+ } else {
27
+ info.length += this._appends.length
28
+ }
29
+
30
+ info.byteLength += this._byteLength
31
+
32
+ return info
33
+ }
34
+
35
+ async get (index, opts) {
36
+ const session = this.session
37
+ if (session.opened === false) await session.opening
38
+
39
+ const length = this.session.length
40
+ if (index < length) return this.session.get(index, opts)
41
+
42
+ return this._appends[index - length] || null
43
+ }
44
+
45
+ async truncate (newLength) {
46
+ if (this.flushed) throw BATCH_ALREADY_FLUSHED()
47
+
48
+ const session = this.session
49
+ if (session.opened === false) await session.opening
50
+ if (session.writable === false) throw SESSION_NOT_WRITABLE()
51
+
52
+ const length = session.length
53
+ if (newLength < length) throw new Error('Cannot truncate committed blocks')
54
+
55
+ this._appends.length = newLength - length
56
+ }
57
+
58
+ async append (blocks) {
59
+ if (this.flushed) throw BATCH_ALREADY_FLUSHED()
60
+
61
+ const session = this.session
62
+ if (session.opened === false) await session.opening
63
+ if (session.writable === false) throw SESSION_NOT_WRITABLE()
64
+
65
+ blocks = Array.isArray(blocks) ? blocks : [blocks]
66
+
67
+ const buffers = session.encodeBatch !== null
68
+ ? session.encodeBatch(blocks)
69
+ : new Array(blocks.length)
70
+
71
+ if (session.encodeBatch === null) {
72
+ for (let i = 0; i < blocks.length; i++) {
73
+ const buffer = session._encode(session.valueEncoding, blocks[i])
74
+ buffers[i] = buffer
75
+ this._byteLength += buffer.byteLength
76
+ }
77
+ }
78
+
79
+ this._appends.push(...buffers)
80
+
81
+ const byteLength = session.byteLength + this._byteLength
82
+
83
+ return { length: this.length, byteLength }
84
+ }
85
+
86
+ async flush () {
87
+ if (this.flushed) throw BATCH_ALREADY_FLUSHED()
88
+ this.flushed = true
89
+
90
+ try {
91
+ if (this._appends.length) await this.session.append(this._appends)
92
+ } finally {
93
+ this._clearBatch()
94
+ this._clearAppends()
95
+ }
96
+ }
97
+
98
+ async close () {
99
+ if (this.flushed) throw BATCH_ALREADY_FLUSHED()
100
+ this.flushed = true
101
+
102
+ this._clearBatch()
103
+ this._clearAppends()
104
+ }
105
+
106
+ _clearAppends () {
107
+ this._appends = []
108
+ this._byteLength = 0
109
+ }
110
+
111
+ _clearBatch () {
112
+ for (const session of this.session.sessions) session._batch = null
113
+ }
114
+ }
package/lib/errors.js CHANGED
@@ -64,6 +64,18 @@ module.exports = class HypercoreError extends Error {
64
64
  return new HypercoreError(msg, 'SESSION_CLOSED', HypercoreError.SESSION_CLOSED)
65
65
  }
66
66
 
67
+ static BATCH_UNFLUSHED (msg = 'Batch not yet flushed') {
68
+ return new HypercoreError(msg, 'BATCH_UNFLUSHED', HypercoreError.BATCH_UNFLUSHED)
69
+ }
70
+
71
+ static BATCH_ALREADY_EXISTS (msg = 'Batch already exists') {
72
+ return new HypercoreError(msg, 'BATCH_ALREADY_EXISTS', HypercoreError.BATCH_ALREADY_EXISTS)
73
+ }
74
+
75
+ static BATCH_ALREADY_FLUSHED (msg = 'Batch has already been flushed') {
76
+ return new HypercoreError(msg, 'BATCH_ALREADY_FLUSHED', HypercoreError.BATCH_ALREADY_FLUSHED)
77
+ }
78
+
67
79
  static OPLOG_CORRUPT (msg = 'Oplog file appears corrupt or out of date') {
68
80
  return new HypercoreError(msg, 'OPLOG_CORRUPT', HypercoreError.OPLOG_CORRUPT)
69
81
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "hypercore",
3
- "version": "10.11.0",
3
+ "version": "10.13.0",
4
4
  "description": "Hypercore is a secure, distributed append-only log",
5
5
  "main": "index.js",
6
6
  "scripts": {