hypercore 10.12.0 → 10.14.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 +10 -0
- package/index.js +26 -4
- package/lib/batch.js +114 -0
- package/lib/core.js +2 -1
- package/lib/errors.js +12 -0
- package/lib/oplog.js +4 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -303,6 +303,16 @@ To cancel downloading a range simply destroy the range instance.
|
|
|
303
303
|
range.destroy()
|
|
304
304
|
```
|
|
305
305
|
|
|
306
|
+
#### `const session = await core.session([options])`
|
|
307
|
+
|
|
308
|
+
Creates a new Hypercore instance that shares the same underlying core.
|
|
309
|
+
|
|
310
|
+
You must close any session you make.
|
|
311
|
+
|
|
312
|
+
Options are inherited from the parent instance, unless they are re-set.
|
|
313
|
+
|
|
314
|
+
`options` are the same as in the constructor.
|
|
315
|
+
|
|
306
316
|
#### `const info = await core.info([options])`
|
|
307
317
|
|
|
308
318
|
Get information about this core, such as its total size in bytes.
|
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 {
|
|
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')
|
|
@@ -82,6 +90,7 @@ module.exports = class Hypercore extends EventEmitter {
|
|
|
82
90
|
|
|
83
91
|
this._preappend = preappend.bind(this)
|
|
84
92
|
this._snapshot = null
|
|
93
|
+
this._batch = opts._batch || null
|
|
85
94
|
this._findingPeers = 0
|
|
86
95
|
}
|
|
87
96
|
|
|
@@ -175,13 +184,14 @@ module.exports = class Hypercore extends EventEmitter {
|
|
|
175
184
|
const toLock = opts.unlocked ? null : (opts.lock || 'oplog')
|
|
176
185
|
const pool = opts.pool || (opts.poolSize ? RAF.createPool(opts.poolSize) : null)
|
|
177
186
|
const rmdir = !!opts.rmdir
|
|
187
|
+
const writable = opts.writable !== false
|
|
178
188
|
|
|
179
189
|
return createFile
|
|
180
190
|
|
|
181
191
|
function createFile (name) {
|
|
182
192
|
const lock = toLock === null ? false : isFile(name, toLock)
|
|
183
193
|
const sparse = isFile(name, 'data') || isFile(name, 'bitfield') || isFile(name, 'tree')
|
|
184
|
-
return new RAF(name, { directory, lock, sparse, pool: lock ? null : pool, rmdir })
|
|
194
|
+
return new RAF(name, { directory, lock, sparse, pool: lock ? null : pool, rmdir, writable })
|
|
185
195
|
}
|
|
186
196
|
|
|
187
197
|
function isFile (name, n) {
|
|
@@ -214,7 +224,8 @@ module.exports = class Hypercore extends EventEmitter {
|
|
|
214
224
|
timeout,
|
|
215
225
|
writable,
|
|
216
226
|
_opening: this.opening,
|
|
217
|
-
_sessions: this.sessions
|
|
227
|
+
_sessions: this.sessions,
|
|
228
|
+
_batch: this._batch
|
|
218
229
|
})
|
|
219
230
|
|
|
220
231
|
s._passCapabilities(this)
|
|
@@ -321,11 +332,13 @@ module.exports = class Hypercore extends EventEmitter {
|
|
|
321
332
|
async _openCapabilities (keyPair, storage, opts) {
|
|
322
333
|
if (opts.from) return this._openFromExisting(opts.from, opts)
|
|
323
334
|
|
|
324
|
-
|
|
335
|
+
const unlocked = !!opts.unlocked
|
|
336
|
+
this.storage = Hypercore.defaultStorage(opts.storage || storage, { unlocked, writable: !unlocked })
|
|
325
337
|
|
|
326
338
|
this.core = await Core.open(this.storage, {
|
|
327
339
|
force: opts.force,
|
|
328
340
|
createIfMissing: opts.createIfMissing,
|
|
341
|
+
readonly: unlocked,
|
|
329
342
|
overwrite: opts.overwrite,
|
|
330
343
|
keyPair,
|
|
331
344
|
crypto: this.crypto,
|
|
@@ -684,6 +697,13 @@ module.exports = class Hypercore extends EventEmitter {
|
|
|
684
697
|
return true
|
|
685
698
|
}
|
|
686
699
|
|
|
700
|
+
batch () {
|
|
701
|
+
if (this._batch !== null) throw BATCH_ALREADY_EXISTS()
|
|
702
|
+
const batch = new Batch(this)
|
|
703
|
+
for (const session of this.sessions) session._batch = batch
|
|
704
|
+
return batch
|
|
705
|
+
}
|
|
706
|
+
|
|
687
707
|
async seek (bytes, opts) {
|
|
688
708
|
if (this.opened === false) await this.opening
|
|
689
709
|
|
|
@@ -846,6 +866,7 @@ module.exports = class Hypercore extends EventEmitter {
|
|
|
846
866
|
}
|
|
847
867
|
|
|
848
868
|
async truncate (newLength = 0, fork = -1) {
|
|
869
|
+
if (this._batch && !this._batch.flushed) throw BATCH_UNFLUSHED()
|
|
849
870
|
if (this.opened === false) await this.opening
|
|
850
871
|
if (this.writable === false) throw SESSION_NOT_WRITABLE()
|
|
851
872
|
|
|
@@ -857,6 +878,7 @@ module.exports = class Hypercore extends EventEmitter {
|
|
|
857
878
|
}
|
|
858
879
|
|
|
859
880
|
async append (blocks) {
|
|
881
|
+
if (this._batch && !this._batch.flushed) throw BATCH_UNFLUSHED()
|
|
860
882
|
if (this.opened === false) await this.opening
|
|
861
883
|
if (this.writable === false) throw SESSION_NOT_WRITABLE()
|
|
862
884
|
|
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/core.js
CHANGED
|
@@ -72,7 +72,8 @@ module.exports = class Core {
|
|
|
72
72
|
|
|
73
73
|
const oplog = new Oplog(oplogFile, {
|
|
74
74
|
headerEncoding: m.oplog.header,
|
|
75
|
-
entryEncoding: m.oplog.entry
|
|
75
|
+
entryEncoding: m.oplog.entry,
|
|
76
|
+
headerOnly: opts.readonly
|
|
76
77
|
})
|
|
77
78
|
|
|
78
79
|
let { header, entries } = await oplog.open()
|
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/lib/oplog.js
CHANGED
|
@@ -4,10 +4,11 @@ const { crc32 } = require('crc-universal')
|
|
|
4
4
|
const { OPLOG_CORRUPT } = require('./errors')
|
|
5
5
|
|
|
6
6
|
module.exports = class Oplog {
|
|
7
|
-
constructor (storage, { pageSize = 4096, headerEncoding = cenc.raw, entryEncoding = cenc.raw } = {}) {
|
|
7
|
+
constructor (storage, { pageSize = 4096, headerEncoding = cenc.raw, entryEncoding = cenc.raw, headerOnly = false } = {}) {
|
|
8
8
|
this.storage = storage
|
|
9
9
|
this.headerEncoding = headerEncoding
|
|
10
10
|
this.entryEncoding = entryEncoding
|
|
11
|
+
this.headerOnly = headerOnly
|
|
11
12
|
this.flushed = false
|
|
12
13
|
this.byteLength = 0
|
|
13
14
|
this.length = 0
|
|
@@ -101,6 +102,8 @@ module.exports = class Oplog {
|
|
|
101
102
|
|
|
102
103
|
result.header = header ? h2.message : h1.message
|
|
103
104
|
|
|
105
|
+
if (this.headerOnly) return result
|
|
106
|
+
|
|
104
107
|
while (true) {
|
|
105
108
|
const entry = this._decodeEntry(state, this.entryEncoding)
|
|
106
109
|
if (!entry) break
|