corestore 6.0.0-beta2 → 6.0.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/lib/db.js DELETED
@@ -1,160 +0,0 @@
1
- const p = require('path')
2
- const fs = require('fs').promises
3
-
4
- const varint = require('varint')
5
- const Hyperbee = require('hyperbee')
6
- const hypercore = require('hypercore')
7
- const hypercoreCrypto = require('hypercore-crypto')
8
- const readdir = require('@jsdevtools/readdir-enhanced')
9
- const { NanoresourcePromise: Nanoresource } = require('nanoresource-promise/emitter')
10
-
11
- const INDEX_PATH = 'index'
12
- const INDEX_VERSION = '@corestore/v1'
13
- const MIGRATION_KEY = '@migrated'
14
- const KEYS_NAMESPACE = 'by-key'
15
- const DKEYS_NAMESPACE = 'by-dkey'
16
-
17
- module.exports = class Index extends Nanoresource {
18
- constructor (storage, opts = {}) {
19
- super()
20
-
21
- this.storage = storage
22
- this._migrationRoot = opts.migrationRoot
23
- this._core = hypercore(p => this.storage(INDEX_PATH + '/' + p))
24
- this._db = new Hyperbee(this._core, {
25
- keyEncoding: 'utf-8',
26
- valueEncoding: 'json'
27
- }).sub(INDEX_VERSION)
28
-
29
- this._byKey = this._db.sub(KEYS_NAMESPACE)
30
- this._byDKey = this._db.sub(DKEYS_NAMESPACE)
31
-
32
- this._core.on('error', err => this.emit('error', err))
33
- }
34
-
35
- // Nanoresource Methods
36
-
37
- async _open () {
38
- await new Promise((resolve, reject) => {
39
- this._core.open(err => {
40
- if (err) return reject(err)
41
- return resolve()
42
- })
43
- })
44
- return this._maybeMigrate()
45
- }
46
-
47
- _close () {
48
- return new Promise((resolve, reject) => {
49
- this._core.close(err => {
50
- if (err) return reject(err)
51
- return resolve()
52
- })
53
- })
54
- }
55
-
56
- // Private Methods
57
-
58
- async _maybeMigrate () {
59
- if (!this._migrationRoot) return
60
- const isMigrated = await this._db.get(MIGRATION_KEY)
61
- if (isMigrated) return
62
-
63
- const basePath = p.resolve(this._migrationRoot)
64
- const nameIterator = readdir.iterator(basePath, {
65
- basePath,
66
- deep: true,
67
- filter: /.*name$/
68
- })
69
-
70
- const b = this._db.batch()
71
- await b.put(MIGRATION_KEY, { migrated: true })
72
-
73
- for await (const namePath of nameIterator) {
74
- let name = await fs.readFile(namePath)
75
- if (!varint.decode(name)) continue
76
- name = name.slice(varint.decode.bytes).toString('utf-8')
77
-
78
- const keyPath = p.join(p.dirname(namePath), 'key')
79
- const publicKey = await loadKey(keyPath)
80
- if (!publicKey) continue
81
-
82
- const discoveryKey = hypercoreCrypto.discoveryKey(publicKey)
83
- await this._putCoreBatch({
84
- name,
85
- publicKey,
86
- discoveryKey
87
- }, b)
88
- }
89
-
90
- return b.flush()
91
-
92
- async function loadKey (path) {
93
- try {
94
- const key = await fs.readFile(path)
95
- return key
96
- } catch (err) {
97
- if (err.code !== 'ENOENT') throw err
98
- return null
99
- }
100
- }
101
- }
102
-
103
- async _putCoreBatch (keys, batch) {
104
- const record = {
105
- name: keys.name,
106
- publicKey: toString(keys.publicKey),
107
- discoveryKey: toString(keys.discoveryKey)
108
- }
109
- const b = batch || this._db.batch()
110
- await b.put(KEYS_NAMESPACE + this._db.sep + record.publicKey, record)
111
- await b.put(DKEYS_NAMESPACE + this._db.sep + record.discoveryKey, record)
112
- if (!batch) await b.flush()
113
- }
114
-
115
- // Public Methods
116
-
117
- async getName (keys) {
118
- const node = await this._byKey.get(toString(keys.publicKey))
119
- return node && node.value.name
120
- }
121
-
122
- async getPassiveCoreKeys (dkey) {
123
- const node = await this._byDKey.get(toString(dkey))
124
- return node && toKeys(node.value)
125
- }
126
-
127
- async getAllCores () {
128
- const allCores = []
129
- for await (const { value } of this._byKey.createReadStream()) {
130
- allCores.push(value)
131
- }
132
- return allCores
133
- }
134
-
135
- async saveKeys (keys) {
136
- const existing = await this.getName(keys)
137
- if (existing) return
138
- return this._putCoreBatch(keys)
139
- }
140
-
141
- async restore (manifest) {
142
- if (!manifest.cores || !Array.isArray(manifest.cores)) throw new Error('Malformed manifest.')
143
- for (const keys of manifest.cores) {
144
- await this.saveKeys(keys)
145
- }
146
- }
147
- }
148
-
149
- function toString (buf) {
150
- if (typeof buf === 'string') return buf
151
- return buf.toString('hex')
152
- }
153
-
154
- function toKeys (record) {
155
- return {
156
- name: record.name,
157
- publicKey: Buffer.from(record.publicKey, 'hex'),
158
- discoveryKey: Buffer.from(record.discoveryKey, 'hex')
159
- }
160
- }
package/lib/errors.js DELETED
@@ -1,44 +0,0 @@
1
- const CustomError = require('custom-error-class')
2
-
3
- class InvalidKeyError extends CustomError {
4
- constructor () {
5
- super('Invalid hypercore key')
6
- this.invalidKey = true
7
- }
8
- }
9
-
10
- class InvalidOptionsError extends CustomError {
11
- constructor () {
12
- super('Invalid get options')
13
- this.invalidOptions = true
14
- }
15
- }
16
-
17
- class InvalidStorageError extends CustomError {
18
- constructor () {
19
- super('Storage should be a function or a string')
20
- this.invalidStorage = true
21
- }
22
- }
23
-
24
- class InvalidNamespaceError extends CustomError {
25
- constructor () {
26
- super('Invalid namespace')
27
- this.invalidNamespace = true
28
- }
29
- }
30
-
31
- class MalformedManifestError extends CustomError {
32
- constructor () {
33
- super('Invalid backup/restore manifest')
34
- this.invalidManifest = true
35
- }
36
- }
37
-
38
- module.exports = {
39
- InvalidStorageError,
40
- InvalidKeyError,
41
- InvalidOptionsError,
42
- InvalidNamespaceError,
43
- MalformedManifestError
44
- }
package/lib/loader.js DELETED
@@ -1,287 +0,0 @@
1
- const { NanoresourcePromise: Nanoresource } = require('nanoresource-promise/emitter')
2
- const hypercore = require('hypercore')
3
- const hypercoreCrypto = require('hypercore-crypto')
4
- const deriveSeed = require('derive-key')
5
- const RefPool = require('refpool')
6
-
7
- const BufferFile = require('./buffer-file')
8
- const PendingFile = require('./pending-file')
9
- const errors = require('./errors')
10
-
11
- const SEED_NAMESPACE = 'corestore'
12
- const NAMESPACE_SEPARATOR = ':'
13
- const MASTER_KEY_FILENAME = 'master_key'
14
- const REF_TOKEN = '@corestore/ref-token'
15
-
16
- module.exports = class Loader extends Nanoresource {
17
- constructor (storage, db, opts = {}) {
18
- super()
19
-
20
- this.storage = storage
21
- this.db = db
22
- this.masterKey = opts.masterKey
23
- this.overwriteMasterKey = opts.overwriteMasterKey
24
- this.opts = opts
25
-
26
- this.readyCache = new Map()
27
- this.cache = new RefPool({
28
- maxSize: opts.cacheSize ?? 1000,
29
- close: core => {
30
- core.close(err => {
31
- if (err) this.emit('error', err)
32
- })
33
- }
34
- })
35
- this.registry = new FinalizationRegistry(id => {
36
- this.cache.decrement(id)
37
- })
38
- }
39
-
40
- _loadMasterKey (cb) {
41
- if (this.masterKey && !this.overwriteMasterKey) return cb(null)
42
- const keyStorage = this.storage(MASTER_KEY_FILENAME)
43
- keyStorage.stat((err, st) => {
44
- if (err && err.code !== 'ENOENT') return cb(err)
45
- if (err || st.size < 32 || this.overwriteMasterKey) {
46
- this.masterKey = this.masterKey ?? hypercoreCrypto.randomBytes(32)
47
- return keyStorage.write(0, this.masterKey, err => {
48
- if (err) return cb(err)
49
- keyStorage.close(cb)
50
- })
51
- }
52
- keyStorage.read(0, 32, (err, key) => {
53
- if (err) return cb(err)
54
- this.masterKey = key
55
- keyStorage.close(cb)
56
- })
57
- })
58
- }
59
-
60
- _flushReadyCache () {
61
- for (const { refs, core, opts, namespace } of this.readyCache.values()) {
62
- const keys = this._generateKeys(namespace, opts)
63
- const id = this._getCacheId(keys)
64
- this.cache.set(id, core)
65
- for (let i = 0; i < refs; i++) this.cache.increment(id)
66
- }
67
- this.readyCache.clear()
68
- }
69
-
70
- _generateKeyPair (namespace, name) {
71
- if (namespace) name = [...namespace, ...name].join(NAMESPACE_SEPARATOR)
72
- const seed = deriveSeed(SEED_NAMESPACE, this.masterKey, name)
73
- const keyPair = hypercoreCrypto.keyPair(seed)
74
- const discoveryKey = hypercoreCrypto.discoveryKey(keyPair.publicKey)
75
- return { name, publicKey: keyPair.publicKey, secretKey: keyPair.secretKey, discoveryKey }
76
- }
77
-
78
- _generateKeys (namespace, opts) {
79
- // The full name is stored in the index, so if we're loading from disk it should override.
80
- if (opts.fullName) return this._generateKeyPair(null, opts.fullName)
81
- if (opts.name) return this._generateKeyPair(namespace, opts.name)
82
- if (opts.keyPair) {
83
- const publicKey = opts.keyPair.publicKey
84
- const secretKey = opts.keyPair.secretKey
85
- return {
86
- publicKey,
87
- secretKey,
88
- discoveryKey: hypercoreCrypto.discoveryKey(publicKey),
89
- name: null
90
- }
91
- }
92
- if (opts.key || opts.publicKey) {
93
- const publicKey = opts.key || opts.publicKey
94
- return {
95
- publicKey,
96
- secretKey: null,
97
- discoveryKey: hypercoreCrypto.discoveryKey(publicKey),
98
- name: null
99
- }
100
- }
101
- throw new errors.InvalidOptionsError()
102
- }
103
-
104
- _getCacheId (keys) {
105
- return keys.discoveryKey.toString('hex')
106
- }
107
-
108
- // Nanoresource Methods
109
-
110
- async _open () {
111
- await this.db.open()
112
- await new Promise((resolve, reject) => {
113
- this._loadMasterKey(err => {
114
- if (err) return reject(err)
115
- return resolve()
116
- })
117
- })
118
- await this._flushReadyCache()
119
- }
120
-
121
- async _close () {
122
- if (!this.cache.size) return
123
- const activeCores = this.getOpenCores()
124
- const closePromises = activeCores.map(core => new Promise((resolve, reject) => {
125
- core.close(err => {
126
- if (err) return reject(err)
127
- return resolve(null)
128
- })
129
- }))
130
- return Promise.allSettled(closePromises)
131
- }
132
-
133
- // Hypercore Loading
134
-
135
- _createStorage (namespace, opts, keys) {
136
- let storageRoot = null
137
- const configure = () => {
138
- keys = keys || this._generateKeys(namespace, opts)
139
- const id = this._getCacheId(keys)
140
- storageRoot = [id.slice(0, 2), id.slice(2, 4), id].join('/')
141
- }
142
- if (keys) configure()
143
- const loadSecretKey = cb => {
144
- this.db.saveKeys(keys, NAMESPACE_SEPARATOR)
145
- .then(() => keys.name ? keys.name : this.db.getName(keys))
146
- .then(name => keys.secretKey
147
- ? keys
148
- : this._generateKeys(namespace, {
149
- ...opts,
150
- name,
151
- fullName: name
152
- }))
153
- .then(keys => cb(null, keys.secretKey), err => cb(err))
154
- }
155
- const storage = name => {
156
- if (!keys) configure()
157
- if (name === 'key') return new BufferFile(keys.publicKey)
158
- if (name === 'secret_key') return new BufferFile(loadSecretKey)
159
- else return this.storage(storageRoot + '/' + name)
160
- }
161
- return name => {
162
- if (this.opened) return storage(name)
163
- return new PendingFile(cb => {
164
- this.open().then(() => cb(null, storage(name)), err => cb(err))
165
- })
166
- }
167
- }
168
-
169
- _create (id, namespace, opts, keys) {
170
- const cacheOpts = { ...this.opts.cache }
171
- if (opts.cache) {
172
- if (opts.cache.data === false) delete cacheOpts.data
173
- if (opts.cache.tree === false) delete cacheOpts.tree
174
- }
175
- if (cacheOpts.data) cacheOpts.data = cacheOpts.data.namespace()
176
- if (cacheOpts.tree) cacheOpts.tree = cacheOpts.tree.namespace()
177
-
178
- const publicKey = keys && keys.publicKey
179
- const storage = this._createStorage(namespace, opts, keys)
180
- const core = hypercore(storage, publicKey, {
181
- ...this.opts,
182
- ...opts,
183
- cache: cacheOpts,
184
- storeSecretKey: false,
185
- createIfMissing: !!publicKey
186
- })
187
-
188
- if (this.opened) {
189
- this.cache.set(id, core)
190
- } else {
191
- this.readyCache.set(id, { core, opts, namespace, refs: 0 })
192
- }
193
-
194
- core.ifAvailable.wait()
195
- let errored = false
196
-
197
- const onerror = () => {
198
- errored = true
199
- core.ifAvailable.continue()
200
- this.readyCache.delete(id)
201
- this.cache.delete(id)
202
- }
203
- const onready = () => {
204
- if (errored) return
205
- this.emit('core', core, opts)
206
- core.removeListener('error', onerror)
207
- core.ifAvailable.continue()
208
- }
209
- const onclose = () => {
210
- this.readyCache.delete(id)
211
- this.cache.delete(id)
212
- }
213
- core.once('ready', onready)
214
- core.once('error', onerror)
215
- core.once('close', onclose)
216
-
217
- return core
218
- }
219
-
220
- _makeRef (id, core, isActive) {
221
- const token = { id }
222
- const close = cb => {
223
- if (!isActive) return process.nextTick(cb, null)
224
- this.registry.unregister(token)
225
- core.ready(() => {
226
- const id = core.discoveryKey.toString('hex')
227
- this.cache.decrement(id)
228
- return cb(null)
229
- })
230
- }
231
- const ref = new Proxy(core, {
232
- get (obj, prop) {
233
- if (prop === REF_TOKEN) return token
234
- if (prop === 'close') return close
235
- return obj[prop]
236
- }
237
- })
238
- if (isActive) this.registry.register(token, id)
239
- return ref
240
- }
241
-
242
- _getBeforeReady (namespace, opts) {
243
- let id = null
244
- if (opts.name) {
245
- id = [...namespace, ...opts.name].join(NAMESPACE_SEPARATOR)
246
- } else {
247
- id = opts.key ?? opts
248
- if (Buffer.isBuffer(id)) id = id.toString('hex')
249
- }
250
-
251
- let cached = this.readyCache.get(id)
252
- if (cached) cached.refs++
253
-
254
- const core = (cached && cached.core) || this._create(id, namespace, opts)
255
- cached = this.readyCache.get(id)
256
- cached.refs++
257
-
258
- return this._makeRef(id, core, !opts.passive)
259
- }
260
-
261
- _get (namespace, opts) {
262
- const keys = this._generateKeys(namespace, opts)
263
- const id = this._getCacheId(keys)
264
-
265
- const cached = this.cache.get(id)
266
- const core = cached ?? this._create(id, namespace, opts, keys)
267
- if (!opts.passive) this.cache.increment(id)
268
-
269
- return this._makeRef(id, core, !opts.passive)
270
- }
271
-
272
- get (namespace, opts) {
273
- if (!this.opened) return this._getBeforeReady(namespace, opts)
274
- return this._get(namespace, opts)
275
- }
276
-
277
- async getPassiveCore (dkey) {
278
- if (Buffer.isBuffer(dkey)) dkey = dkey.toString('hex')
279
- const keys = await this.db.getPassiveCoreKeys(dkey)
280
- if (!keys) return null
281
- return this._get(null, { ...keys, passive: true })
282
- }
283
-
284
- getOpenCores () {
285
- return [...this.cache.entries.values()].map(({ value }) => value)
286
- }
287
- }
@@ -1,37 +0,0 @@
1
- const RAS = require('random-access-storage')
2
-
3
- module.exports = class PendingFile extends RAS {
4
- constructor (wait) {
5
- super()
6
- this._wait = wait
7
- this._storage = null
8
- }
9
-
10
- _open (req) {
11
- this._wait((err, storage) => {
12
- if (err) return req.callback(err)
13
- this._storage = storage
14
- req.callback(null)
15
- })
16
- }
17
-
18
- _stat (req) {
19
- this._storage.stat(req.callback.bind(req))
20
- }
21
-
22
- _read (req) {
23
- this._storage.read(req.offset, req.size, req.callback.bind(req))
24
- }
25
-
26
- _write (req) {
27
- this._storage.write(req.offset, req.data, req.callback.bind(req))
28
- }
29
-
30
- _del (req) {
31
- this._storage.delete(req.offset, req.size, req.callback.bind(req))
32
- }
33
-
34
- _close (req) {
35
- this._storage.close(req.callback.bind(req))
36
- }
37
- }
package/lib/replicator.js DELETED
@@ -1,66 +0,0 @@
1
- const { EventEmitter } = require('events')
2
- const HypercoreProtocol = require('hypercore-protocol')
3
- const eos = require('end-of-stream')
4
-
5
- module.exports = class Replicator extends EventEmitter {
6
- constructor (loader, opts = {}) {
7
- super()
8
-
9
- this.loader = loader
10
- this.opts = opts
11
- this.streams = []
12
-
13
- this.loader.on('core', core => {
14
- this.inject(core)
15
- })
16
- }
17
-
18
- _replicateCore (isInitiator, core, mainStream, opts) {
19
- core.ready(function (err) {
20
- if (err) return
21
- core.replicate(isInitiator, {
22
- ...opts,
23
- stream: mainStream
24
- })
25
- })
26
- }
27
-
28
- inject (core) {
29
- for (const { stream, opts } of this.streams) {
30
- this._replicateCore(false, core, stream, { ...opts })
31
- }
32
- }
33
-
34
- replicate (isInitiator, replicationOpts = {}) {
35
- const finalOpts = { ...this.opts, ...replicationOpts }
36
- const mainStream = replicationOpts.stream || new HypercoreProtocol(isInitiator, { ...finalOpts })
37
-
38
- for (const core of this.loader.getOpenCores()) {
39
- this._replicateCore(isInitiator, core, mainStream, { ...finalOpts })
40
- }
41
-
42
- const streamState = { stream: mainStream, opts: finalOpts }
43
- this.streams.push(streamState)
44
-
45
- mainStream.on('discovery-key', async (dkey) => {
46
- const core = await this.loader.getPassiveCore(dkey)
47
- if (!core) return mainStream.close(dkey)
48
- this._replicateCore(false, core, mainStream, { ...finalOpts })
49
- })
50
- eos(mainStream, err => {
51
- if (err) this.emit('replication-error', err)
52
- const idx = this.streams.indexOf(streamState)
53
- if (idx === -1) return
54
- this.streams.splice(idx, 1)
55
- })
56
-
57
- return mainStream
58
- }
59
-
60
- close () {
61
- if (!this.streams.length) return
62
- for (const stream of this.streams) {
63
- stream.destroy()
64
- }
65
- }
66
- }