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
|
@@ -1,5 +1,4 @@
|
|
|
1
|
-
name:
|
|
2
|
-
|
|
1
|
+
name: Build Status
|
|
3
2
|
on:
|
|
4
3
|
push:
|
|
5
4
|
branches:
|
|
@@ -11,13 +10,13 @@ jobs:
|
|
|
11
10
|
build:
|
|
12
11
|
strategy:
|
|
13
12
|
matrix:
|
|
14
|
-
node-version: [
|
|
15
|
-
os: [ubuntu-
|
|
13
|
+
node-version: [lts/*]
|
|
14
|
+
os: [ubuntu-latest, macos-latest, windows-latest]
|
|
16
15
|
runs-on: ${{ matrix.os }}
|
|
17
16
|
steps:
|
|
18
17
|
- uses: actions/checkout@v2
|
|
19
18
|
- name: Use Node.js ${{ matrix.node-version }}
|
|
20
|
-
uses: actions/setup-node@
|
|
19
|
+
uses: actions/setup-node@v2
|
|
21
20
|
with:
|
|
22
21
|
node-version: ${{ matrix.node-version }}
|
|
23
22
|
- run: npm install
|
package/README.md
CHANGED
|
@@ -1,2 +1,59 @@
|
|
|
1
|
-
#
|
|
2
|
-
|
|
1
|
+
# Corestore
|
|
2
|
+
|
|
3
|
+
Corestore is a Hypercore factory that makes it easier to manage large collections of named Hypercores.
|
|
4
|
+
|
|
5
|
+
Corestore provides:
|
|
6
|
+
1. __Key Derivation__ - All writable Hypercore keys are derived from a single master key and a user-provided name.
|
|
7
|
+
2. __Session Handling__ - If a single Hypercore is loaded multiple times through the `get` method, the underlying resources will only be opened once (using Hypercore 10's new session feature). Once all sessions are closed, the resources will be released.
|
|
8
|
+
3. __Storage Management__ - Hypercores can be stored in any random-access-storage instance, where they will be keyed by their discovery keys.
|
|
9
|
+
4. __Namespacing__ - You can share a single Corestore instance between multiple applications or components without worrying about naming collisions by creating "namespaces" (e.g. `corestore.namespace('my-app').get({ name: 'main' })
|
|
10
|
+
|
|
11
|
+
### Installation
|
|
12
|
+
`npm install corestore`
|
|
13
|
+
|
|
14
|
+
### Usage
|
|
15
|
+
A corestore instance can be constructed with a random-access-storage module, a function that returns a random-access-storage module given a path, or a string. If a string is specified, it will be assumed to be a path to a local storage directory:
|
|
16
|
+
```js
|
|
17
|
+
const Corestore = require('corestore')
|
|
18
|
+
|
|
19
|
+
const store = new Corestore('./my-storage')
|
|
20
|
+
const core1 = store.get({ name: 'core-1' })
|
|
21
|
+
const core2 = store.get({ name: 'core-2' })
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
### API
|
|
25
|
+
#### `const store = new Corestore(storage)`
|
|
26
|
+
Create a new Corestore instance.
|
|
27
|
+
|
|
28
|
+
`storage` can be either a random-access-storage module, a string, or a function that takes a path and returns an random-access-storage instance.
|
|
29
|
+
|
|
30
|
+
#### `const core = store.get(key | { name: 'a-name', ...hypercoreOpts})`
|
|
31
|
+
Loads a Hypercore, either by name (if the `name` option is provided), or from the provided key (if the first argument is a Buffer, or if the `key` options is set).
|
|
32
|
+
|
|
33
|
+
If that Hypercore has previously been loaded, subsequent calls to `get` will return a new Hypercore session on the existing core.
|
|
34
|
+
|
|
35
|
+
All other options besides `name` and `key` will be forwarded to the Hypercore constructor.
|
|
36
|
+
|
|
37
|
+
#### `const stream = store.replicate(opts)`
|
|
38
|
+
Creates a replication stream that's capable of replicating all Hypercores that are managed by the Corestore, assuming the remote peer has the correct capabilities.
|
|
39
|
+
|
|
40
|
+
`opts` will be forwarded to Hypercore's `replicate` function.
|
|
41
|
+
|
|
42
|
+
Corestore replicates in an "all-to-all" fashion, meaning that when replication begins, it will attempt to replicate every Hypercore that's currently loaded and in memory. These attempts will fail if the remote side doesn't have a Hypercore's capability -- Corestore replication does not exchange Hypercore keys.
|
|
43
|
+
|
|
44
|
+
If the remote side dynamically adds a new Hypercore to the replication stream, Corestore will load and replicatethat core if possible.
|
|
45
|
+
|
|
46
|
+
#### `const store = store.namespace(name)`
|
|
47
|
+
Create a new namespaced Corestore. Namespacing is useful if you're going to be sharing a single Corestore instance between many applications or components, as it prevents name collisions.
|
|
48
|
+
|
|
49
|
+
Namespaces can be chained:
|
|
50
|
+
```js
|
|
51
|
+
const ns1 = store.namespace('a')
|
|
52
|
+
const ns2 = ns1.namespace('b')
|
|
53
|
+
const core1 = ns1.get({ name: 'main' }) // These will load different Hypercores
|
|
54
|
+
const core2 = ns2.get({ name: 'main' })
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
### License
|
|
58
|
+
MIT
|
|
59
|
+
|
package/index.js
CHANGED
|
@@ -1,122 +1,373 @@
|
|
|
1
|
-
const {
|
|
2
|
-
const
|
|
1
|
+
const { EventEmitter } = require('events')
|
|
2
|
+
const safetyCatch = require('safety-catch')
|
|
3
|
+
const crypto = require('hypercore-crypto')
|
|
4
|
+
const sodium = require('sodium-universal')
|
|
5
|
+
const Hypercore = require('hypercore')
|
|
6
|
+
const Xache = require('xache')
|
|
7
|
+
const b4a = require('b4a')
|
|
3
8
|
|
|
4
|
-
const
|
|
5
|
-
const
|
|
6
|
-
const Loader = require('./lib/loader')
|
|
7
|
-
const errors = require('./lib/errors')
|
|
9
|
+
const [NS] = crypto.namespace('corestore', 1)
|
|
10
|
+
const DEFAULT_NAMESPACE = b4a.alloc(32) // This is meant to be 32 0-bytes
|
|
8
11
|
|
|
9
|
-
|
|
12
|
+
const CORES_DIR = 'cores'
|
|
13
|
+
const PRIMARY_KEY_FILE_NAME = 'primary-key'
|
|
14
|
+
const USERDATA_NAME_KEY = 'corestore/name'
|
|
15
|
+
const USERDATA_NAMESPACE_KEY = 'corestore/namespace'
|
|
16
|
+
|
|
17
|
+
module.exports = class Corestore extends EventEmitter {
|
|
10
18
|
constructor (storage, opts = {}) {
|
|
11
19
|
super()
|
|
12
20
|
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
this.
|
|
21
|
+
this.storage = Hypercore.defaultStorage(storage, { lock: PRIMARY_KEY_FILE_NAME })
|
|
22
|
+
this.cores = opts._cores || new Map()
|
|
23
|
+
this.primaryKey = null
|
|
24
|
+
this.cache = !!opts.cache
|
|
25
|
+
|
|
26
|
+
this._keyStorage = null
|
|
27
|
+
this._primaryKey = opts.primaryKey
|
|
28
|
+
this._namespace = opts.namespace || DEFAULT_NAMESPACE
|
|
29
|
+
this._replicationStreams = opts._streams || []
|
|
30
|
+
this._overwrite = opts.overwrite === true
|
|
31
|
+
|
|
32
|
+
this._streamSessions = opts._streamSessions || new Map()
|
|
33
|
+
this._sessions = new Set() // sessions for THIS namespace
|
|
16
34
|
|
|
17
|
-
this.
|
|
18
|
-
this.
|
|
19
|
-
this._loader = opts._loader || new Loader(this.storage, this._db, opts)
|
|
20
|
-
this._replicator = opts._replicator || new Replicator(this._loader, opts)
|
|
35
|
+
this._findingPeersCount = 0
|
|
36
|
+
this._findingPeers = []
|
|
21
37
|
|
|
22
|
-
this.
|
|
23
|
-
this._loader.on('error', err => this.emit('error', err))
|
|
24
|
-
this._loader.on('core', (core, opts) => this.emit('core', core, opts))
|
|
25
|
-
this._loader.on('core', (core, opts) => this.emit('feed', core, opts))
|
|
38
|
+
if (this._namespace.byteLength !== 32) throw new Error('Namespace must be a 32-byte Buffer or Uint8Array')
|
|
26
39
|
|
|
27
|
-
this.
|
|
40
|
+
this._opening = opts._opening ? opts._opening.then(() => this._open()) : this._open()
|
|
41
|
+
this._opening.catch(safetyCatch)
|
|
42
|
+
this.ready = () => this._opening
|
|
28
43
|
}
|
|
29
44
|
|
|
30
|
-
|
|
31
|
-
|
|
45
|
+
findingPeers () {
|
|
46
|
+
let done = false
|
|
47
|
+
this._incFindingPeers()
|
|
48
|
+
|
|
49
|
+
return () => {
|
|
50
|
+
if (done) return
|
|
51
|
+
done = true
|
|
52
|
+
this._decFindingPeers()
|
|
53
|
+
}
|
|
32
54
|
}
|
|
33
55
|
|
|
34
|
-
|
|
56
|
+
_incFindingPeers () {
|
|
57
|
+
if (++this._findingPeersCount !== 1) return
|
|
58
|
+
|
|
59
|
+
for (const core of this._sessions) {
|
|
60
|
+
this._findingPeers.push(core.findingPeers())
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
_decFindingPeers () {
|
|
65
|
+
if (--this._findingPeersCount !== 0) return
|
|
66
|
+
|
|
67
|
+
while (this._findingPeers.length > 0) {
|
|
68
|
+
this._findingPeers.pop()()
|
|
69
|
+
}
|
|
70
|
+
}
|
|
35
71
|
|
|
36
72
|
async _open () {
|
|
37
|
-
|
|
38
|
-
|
|
73
|
+
if (this._primaryKey) {
|
|
74
|
+
this.primaryKey = await this._primaryKey
|
|
75
|
+
return this.primaryKey
|
|
76
|
+
}
|
|
77
|
+
this._keyStorage = this.storage(PRIMARY_KEY_FILE_NAME)
|
|
78
|
+
this.primaryKey = await new Promise((resolve, reject) => {
|
|
79
|
+
this._keyStorage.stat((err, st) => {
|
|
80
|
+
if (err && err.code !== 'ENOENT') return reject(err)
|
|
81
|
+
if (err || st.size < 32 || this._overwrite) {
|
|
82
|
+
const key = crypto.randomBytes(32)
|
|
83
|
+
return this._keyStorage.write(0, key, err => {
|
|
84
|
+
if (err) return reject(err)
|
|
85
|
+
return resolve(key)
|
|
86
|
+
})
|
|
87
|
+
}
|
|
88
|
+
this._keyStorage.read(0, 32, (err, key) => {
|
|
89
|
+
if (err) return reject(err)
|
|
90
|
+
return resolve(key)
|
|
91
|
+
})
|
|
92
|
+
})
|
|
93
|
+
})
|
|
94
|
+
return this.primaryKey
|
|
39
95
|
}
|
|
40
96
|
|
|
41
|
-
async
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
97
|
+
async _generateKeys (opts) {
|
|
98
|
+
if (opts._discoveryKey) {
|
|
99
|
+
return {
|
|
100
|
+
keyPair: null,
|
|
101
|
+
auth: null,
|
|
102
|
+
discoveryKey: opts._discoveryKey
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
if (!opts.name) {
|
|
106
|
+
return {
|
|
107
|
+
keyPair: {
|
|
108
|
+
publicKey: opts.publicKey,
|
|
109
|
+
secretKey: opts.secretKey
|
|
110
|
+
},
|
|
111
|
+
sign: opts.sign,
|
|
112
|
+
auth: opts.auth,
|
|
113
|
+
discoveryKey: crypto.discoveryKey(opts.publicKey)
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
const { publicKey, auth } = await this.createKeyPair(opts.name)
|
|
117
|
+
return {
|
|
118
|
+
keyPair: {
|
|
119
|
+
publicKey,
|
|
120
|
+
secretKey: null
|
|
121
|
+
},
|
|
122
|
+
auth,
|
|
123
|
+
discoveryKey: crypto.discoveryKey(publicKey)
|
|
124
|
+
}
|
|
45
125
|
}
|
|
46
126
|
|
|
47
|
-
|
|
127
|
+
_getPrereadyUserData (core, key) {
|
|
128
|
+
// Need to manually read the header values before the Hypercore is ready, hence the ugliness.
|
|
129
|
+
for (const { key: savedKey, value } of core.core.header.userData) {
|
|
130
|
+
if (key === savedKey) return value
|
|
131
|
+
}
|
|
132
|
+
return null
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
async _preready (core) {
|
|
136
|
+
const name = this._getPrereadyUserData(core, USERDATA_NAME_KEY)
|
|
137
|
+
if (!name) return
|
|
138
|
+
|
|
139
|
+
const namespace = this._getPrereadyUserData(core, USERDATA_NAMESPACE_KEY)
|
|
140
|
+
const { publicKey, auth } = await this.createKeyPair(b4a.toString(name), namespace)
|
|
141
|
+
if (!b4a.equals(publicKey, core.key)) throw new Error('Stored core key does not match the provided name')
|
|
142
|
+
|
|
143
|
+
// TODO: Should Hypercore expose a helper for this, or should preready return keypair/auth?
|
|
144
|
+
core.auth = auth
|
|
145
|
+
core.key = publicKey
|
|
146
|
+
core.writable = true
|
|
147
|
+
}
|
|
48
148
|
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
149
|
+
async _preload (opts) {
|
|
150
|
+
await this.ready()
|
|
151
|
+
|
|
152
|
+
const { discoveryKey, keyPair, auth } = await this._generateKeys(opts)
|
|
153
|
+
const id = b4a.toString(discoveryKey, 'hex')
|
|
154
|
+
|
|
155
|
+
while (this.cores.has(id)) {
|
|
156
|
+
const existing = this.cores.get(id)
|
|
157
|
+
if (existing.opened && !existing.closing) return { from: existing, keyPair, auth }
|
|
158
|
+
if (existing.closing) {
|
|
159
|
+
await existing.close()
|
|
53
160
|
} else {
|
|
54
|
-
|
|
161
|
+
await existing.ready().catch(safetyCatch)
|
|
55
162
|
}
|
|
56
|
-
} else {
|
|
57
|
-
opts = { key: Buffer.from(opts, 'hex') }
|
|
58
163
|
}
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
if (opts.name
|
|
62
|
-
|
|
164
|
+
|
|
165
|
+
const userData = {}
|
|
166
|
+
if (opts.name) {
|
|
167
|
+
userData[USERDATA_NAME_KEY] = b4a.from(opts.name)
|
|
168
|
+
userData[USERDATA_NAMESPACE_KEY] = this._namespace
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
// No more async ticks allowed after this point -- necessary for caching
|
|
172
|
+
|
|
173
|
+
const storageRoot = [CORES_DIR, id.slice(0, 2), id.slice(2, 4), id].join('/')
|
|
174
|
+
const core = new Hypercore(p => this.storage(storageRoot + '/' + p), {
|
|
175
|
+
_preready: this._preready.bind(this),
|
|
176
|
+
autoClose: true,
|
|
177
|
+
encryptionKey: opts.encryptionKey || null,
|
|
178
|
+
userData,
|
|
179
|
+
auth,
|
|
180
|
+
cache: opts.cache,
|
|
181
|
+
createIfMissing: !opts._discoveryKey,
|
|
182
|
+
keyPair: keyPair && keyPair.publicKey
|
|
183
|
+
? {
|
|
184
|
+
publicKey: keyPair.publicKey,
|
|
185
|
+
secretKey: null
|
|
186
|
+
}
|
|
187
|
+
: null
|
|
188
|
+
})
|
|
189
|
+
|
|
190
|
+
this.cores.set(id, core)
|
|
191
|
+
core.ready().then(() => {
|
|
192
|
+
for (const { stream } of this._replicationStreams) {
|
|
193
|
+
const sessions = this._streamSessions.get(stream)
|
|
194
|
+
const session = core.session()
|
|
195
|
+
sessions.push(session)
|
|
196
|
+
core.replicate(stream)
|
|
197
|
+
}
|
|
198
|
+
}, () => {
|
|
199
|
+
this.cores.delete(id)
|
|
200
|
+
})
|
|
201
|
+
core.once('close', () => {
|
|
202
|
+
this.cores.delete(id)
|
|
203
|
+
})
|
|
204
|
+
|
|
205
|
+
return { from: core, keyPair, auth }
|
|
63
206
|
}
|
|
64
207
|
|
|
65
|
-
|
|
208
|
+
async createKeyPair (name, namespace = this._namespace) {
|
|
209
|
+
if (!this.primaryKey) await this._opening
|
|
66
210
|
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
211
|
+
const keyPair = {
|
|
212
|
+
publicKey: b4a.allocUnsafe(sodium.crypto_sign_PUBLICKEYBYTES),
|
|
213
|
+
secretKey: b4a.alloc(sodium.crypto_sign_SECRETKEYBYTES),
|
|
214
|
+
auth: {
|
|
215
|
+
sign: (msg) => sign(keyPair, msg),
|
|
216
|
+
verify: (signable, signature) => {
|
|
217
|
+
return crypto.verify(signable, signature, keyPair.publicKey)
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
const seed = deriveSeed(this.primaryKey, namespace, name)
|
|
223
|
+
sodium.crypto_sign_seed_keypair(keyPair.publicKey, keyPair.secretKey, seed)
|
|
224
|
+
|
|
225
|
+
return keyPair
|
|
71
226
|
}
|
|
72
227
|
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
228
|
+
get (opts = {}) {
|
|
229
|
+
opts = validateGetOptions(opts)
|
|
230
|
+
|
|
231
|
+
if (opts.cache !== false) {
|
|
232
|
+
opts.cache = opts.cache === true || (this.cache && !opts.cache) ? defaultCache() : opts.cache
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
const core = new Hypercore(null, {
|
|
236
|
+
...opts,
|
|
237
|
+
name: null,
|
|
238
|
+
preload: () => this._preload(opts)
|
|
81
239
|
})
|
|
82
|
-
}
|
|
83
240
|
|
|
84
|
-
|
|
85
|
-
|
|
241
|
+
this._sessions.add(core)
|
|
242
|
+
if (this._findingPeersCount > 0) {
|
|
243
|
+
this._findingPeers.push(core.findingPeers())
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
core.once('close', () => {
|
|
247
|
+
// technically better to also clear _findingPeers if we added it,
|
|
248
|
+
// but the lifecycle for those are pretty short so prob not worth the complexity
|
|
249
|
+
// as _decFindingPeers clear them all.
|
|
250
|
+
this._sessions.delete(core)
|
|
251
|
+
})
|
|
252
|
+
|
|
253
|
+
return core
|
|
86
254
|
}
|
|
87
255
|
|
|
88
|
-
|
|
256
|
+
replicate (isInitiator, opts) {
|
|
257
|
+
const isExternal = isStream(isInitiator) || !!(opts && opts.stream)
|
|
258
|
+
const stream = Hypercore.createProtocolStream(isInitiator, {
|
|
259
|
+
...opts,
|
|
260
|
+
ondiscoverykey: discoveryKey => {
|
|
261
|
+
const core = this.get({ _discoveryKey: discoveryKey })
|
|
262
|
+
return core.ready().catch(safetyCatch)
|
|
263
|
+
}
|
|
264
|
+
})
|
|
89
265
|
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
266
|
+
const sessions = []
|
|
267
|
+
for (const core of this.cores.values()) {
|
|
268
|
+
if (!core.opened) continue // If the core is not opened, it will be replicated in preload.
|
|
269
|
+
const session = core.session()
|
|
270
|
+
sessions.push(session)
|
|
271
|
+
core.replicate(stream)
|
|
96
272
|
}
|
|
273
|
+
|
|
274
|
+
const streamRecord = { stream, isExternal }
|
|
275
|
+
this._replicationStreams.push(streamRecord)
|
|
276
|
+
this._streamSessions.set(stream, sessions)
|
|
277
|
+
|
|
278
|
+
stream.once('close', () => {
|
|
279
|
+
this._replicationStreams.splice(this._replicationStreams.indexOf(streamRecord), 1)
|
|
280
|
+
this._streamSessions.delete(stream)
|
|
281
|
+
Promise.all(sessions.map(s => s.close())).catch(safetyCatch)
|
|
282
|
+
})
|
|
283
|
+
return stream
|
|
97
284
|
}
|
|
98
285
|
|
|
99
|
-
|
|
100
|
-
return this.
|
|
286
|
+
namespace (name) {
|
|
287
|
+
return new Corestore(this.storage, {
|
|
288
|
+
primaryKey: this._opening.then(() => this.primaryKey),
|
|
289
|
+
namespace: generateNamespace(this._namespace, name),
|
|
290
|
+
cache: this.cache,
|
|
291
|
+
_opening: this._opening,
|
|
292
|
+
_cores: this.cores,
|
|
293
|
+
_streams: this._replicationStreams,
|
|
294
|
+
_streamSessions: this._streamSessions
|
|
295
|
+
})
|
|
101
296
|
}
|
|
102
297
|
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
298
|
+
async _close () {
|
|
299
|
+
await this._opening
|
|
300
|
+
if (!b4a.equals(this._namespace, DEFAULT_NAMESPACE)) {
|
|
301
|
+
// namespaces should not release resources on close
|
|
302
|
+
// TODO: Refactor the namespace close logic to actually close sessions with ref counting
|
|
303
|
+
return
|
|
304
|
+
}
|
|
305
|
+
const closePromises = []
|
|
306
|
+
for (const core of this.cores.values()) {
|
|
307
|
+
closePromises.push(core.close())
|
|
308
|
+
}
|
|
309
|
+
await Promise.allSettled(closePromises)
|
|
310
|
+
for (const { stream, isExternal } of this._replicationStreams) {
|
|
311
|
+
// Only close streams that were created by the Corestore
|
|
312
|
+
if (!isExternal) stream.destroy()
|
|
313
|
+
}
|
|
314
|
+
if (!this._keyStorage) return
|
|
315
|
+
await new Promise((resolve, reject) => {
|
|
316
|
+
this._keyStorage.close(err => {
|
|
317
|
+
if (err) return reject(err)
|
|
318
|
+
return resolve(null)
|
|
319
|
+
})
|
|
108
320
|
})
|
|
109
|
-
await store.restore(manifest)
|
|
110
|
-
return store
|
|
111
321
|
}
|
|
322
|
+
|
|
323
|
+
close () {
|
|
324
|
+
if (this._closing) return this._closing
|
|
325
|
+
this._closing = this._close()
|
|
326
|
+
this._closing.catch(safetyCatch)
|
|
327
|
+
return this._closing
|
|
328
|
+
}
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
function sign (keyPair, message) {
|
|
332
|
+
if (!keyPair.secretKey) throw new Error('Invalid key pair')
|
|
333
|
+
return crypto.sign(message, keyPair.secretKey)
|
|
112
334
|
}
|
|
113
335
|
|
|
114
|
-
function
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
336
|
+
function validateGetOptions (opts) {
|
|
337
|
+
if (b4a.isBuffer(opts)) return { key: opts, publicKey: opts }
|
|
338
|
+
if (opts.key) {
|
|
339
|
+
opts.publicKey = opts.key
|
|
340
|
+
}
|
|
341
|
+
if (opts.keyPair) {
|
|
342
|
+
opts.publicKey = opts.keyPair.publicKey
|
|
343
|
+
opts.secretKey = opts.keyPair.secretKey
|
|
121
344
|
}
|
|
345
|
+
if (opts.name && typeof opts.name !== 'string') throw new Error('name option must be a String')
|
|
346
|
+
if (opts.name && opts.secretKey) throw new Error('Cannot provide both a name and a secret key')
|
|
347
|
+
if (opts.publicKey && !b4a.isBuffer(opts.publicKey)) throw new Error('publicKey option must be a Buffer or Uint8Array')
|
|
348
|
+
if (opts.secretKey && !b4a.isBuffer(opts.secretKey)) throw new Error('secretKey option must be a Buffer or Uint8Array')
|
|
349
|
+
if (!opts._discoveryKey && (!opts.name && !opts.publicKey)) throw new Error('Must provide either a name or a publicKey')
|
|
350
|
+
return opts
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
function generateNamespace (namespace, name) {
|
|
354
|
+
if (!b4a.isBuffer(name)) name = b4a.from(name)
|
|
355
|
+
const out = b4a.allocUnsafe(32)
|
|
356
|
+
sodium.crypto_generichash_batch(out, [namespace, name])
|
|
357
|
+
return out
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
function deriveSeed (primaryKey, namespace, name) {
|
|
361
|
+
if (!b4a.isBuffer(name)) name = b4a.from(name)
|
|
362
|
+
const out = b4a.alloc(32)
|
|
363
|
+
sodium.crypto_generichash_batch(out, [NS, namespace, name], primaryKey)
|
|
364
|
+
return out
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
function defaultCache () {
|
|
368
|
+
return new Xache({ maxSize: 65536, maxAge: 0 })
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
function isStream (s) {
|
|
372
|
+
return typeof s === 'object' && s && typeof s.pipe === 'function'
|
|
122
373
|
}
|
package/package.json
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "corestore",
|
|
3
|
-
"version": "6.0.0
|
|
4
|
-
"description": "A Hypercore factory that
|
|
3
|
+
"version": "6.0.0",
|
|
4
|
+
"description": "A Hypercore factory that simplifies managing collections of cores.",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"scripts": {
|
|
7
|
-
"test": "standard &&
|
|
7
|
+
"test": "standard && brittle test/*.js"
|
|
8
8
|
},
|
|
9
9
|
"repository": {
|
|
10
10
|
"type": "git",
|
|
11
|
-
"url": "git+https://github.com/
|
|
11
|
+
"url": "git+https://github.com/hypercore-protocol/corestore.git"
|
|
12
12
|
},
|
|
13
13
|
"keywords": [
|
|
14
14
|
"corestore"
|
|
@@ -16,30 +16,20 @@
|
|
|
16
16
|
"author": "Andrew Osheroff <andrewosh@gmail.com>",
|
|
17
17
|
"license": "MIT",
|
|
18
18
|
"bugs": {
|
|
19
|
-
"url": "https://github.com/
|
|
19
|
+
"url": "https://github.com/hypercore-protocol/corestore/issues"
|
|
20
20
|
},
|
|
21
|
-
"homepage": "https://github.com/
|
|
21
|
+
"homepage": "https://github.com/hypercore-protocol/corestore#readme",
|
|
22
22
|
"devDependencies": {
|
|
23
|
-
"
|
|
24
|
-
"
|
|
25
|
-
"
|
|
26
|
-
"standard": "^16.0.3",
|
|
27
|
-
"tape": "^5.0.1"
|
|
23
|
+
"brittle": "^3.0.0",
|
|
24
|
+
"random-access-memory": "^4.0.0",
|
|
25
|
+
"standardx": "^7.0.0"
|
|
28
26
|
},
|
|
29
27
|
"dependencies": {
|
|
30
|
-
"
|
|
31
|
-
"
|
|
32
|
-
"
|
|
33
|
-
"
|
|
34
|
-
"
|
|
35
|
-
"
|
|
36
|
-
"hypercore": "^9.6.0",
|
|
37
|
-
"hypercore-crypto": "^2.2.0",
|
|
38
|
-
"hypercore-protocol": "^8.0.7",
|
|
39
|
-
"nanoresource-promise": "^2.0.0",
|
|
40
|
-
"random-access-file": "^2.1.4",
|
|
41
|
-
"random-access-storage": "^1.4.1",
|
|
42
|
-
"refpool": "^1.2.2",
|
|
43
|
-
"varint": "^6.0.0"
|
|
28
|
+
"b4a": "^1.3.1",
|
|
29
|
+
"hypercore": "v10.0.0",
|
|
30
|
+
"hypercore-crypto": "^3.2.1",
|
|
31
|
+
"safety-catch": "^1.0.1",
|
|
32
|
+
"sodium-universal": "^3.0.4",
|
|
33
|
+
"xache": "^1.1.0"
|
|
44
34
|
}
|
|
45
35
|
}
|