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/.github/workflows/test-node.yml +4 -5
- package/README.md +59 -2
- package/index.js +330 -79
- package/package.json +15 -25
- package/test/all.js +255 -223
- package/test/cache.js +46 -0
- package/test/helpers/index.js +2 -7
- package/lib/buffer-file.js +0 -26
- package/lib/db.js +0 -160
- package/lib/errors.js +0 -44
- package/lib/loader.js +0 -287
- package/lib/pending-file.js +0 -37
- package/lib/replicator.js +0 -66
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
|
-
}
|
package/lib/pending-file.js
DELETED
|
@@ -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
|
-
}
|