Skip to content
This repository was archived by the owner on Feb 12, 2024. It is now read-only.

Commit 7f3a2d7

Browse files
committed
refactor: remove proxy api object and detect initialisaion state
Ask the repo if it has been initialised, if so, allow the user to skip the `.init()` step and move on to `.start()` Removes the proxy api object in favour of vanilla functions because it was causing errors to be thrown if you even referenced properties that were from a different api state. E.g. with an unitialised repo: ```javascript const ipfs = await IPFS.create({ init: false, start: false }) // no invocation, just referencing the property causes an error to be thrown console.info(ipfs.start) ``` I'd looked at changing the proxy behaviour to return a function that throws if invoked, but at the time the proxy is called you don't know what the calling code is going to do with the return value so it's hard to know if it's accessing a function or a property - the return value is just put on the stack and interacted with so it seemed simpler to just pull it out and define the API up front. A nice future improvement might be to have `.init`, `.start` and `.stop` export functions that update the API - that way after `.stop` has been invoked, it could restore the API from the post-`.init` state, but this can come later. Also upgrades `ipfsd-ctl` to pass refs only during factory creation. Depends on: - [ ] ipfs/js-ipfsd-ctl#457 - [ ] ipfs/js-ipfs-repo#219 - [ ] ipfs-inactive/npm-go-ipfs-dep#40 fix: do not allow parallel init, start or stop If the user is calling `.init`, `.start` or `.stop` from the code in multiple places simultaneously, they probably have a bug in their code and we should let them know instead of masking it.
1 parent 60fe851 commit 7f3a2d7

12 files changed

+496
-72
lines changed

package.json

+1-2
Original file line numberDiff line numberDiff line change
@@ -102,7 +102,7 @@
102102
"ipfs-http-response": "^0.5.0",
103103
"ipfs-mfs": "^1.0.0",
104104
"ipfs-multipart": "^0.3.0",
105-
"ipfs-repo": "^0.30.0",
105+
"ipfs-repo": "^1.0.0",
106106
"ipfs-unixfs": "^0.3.0",
107107
"ipfs-unixfs-exporter": "^0.41.0",
108108
"ipfs-unixfs-importer": "^0.44.0",
@@ -155,7 +155,6 @@
155155
"multicodec": "^1.0.0",
156156
"multihashes": "~0.4.14",
157157
"multihashing-async": "^0.8.0",
158-
"p-defer": "^3.0.0",
159158
"p-queue": "^6.1.0",
160159
"parse-duration": "^0.1.2",
161160
"peer-id": "^0.13.5",

src/core/api-manager.js

+116-11
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,128 @@
11
'use strict'
22

3+
const noop = () => {}
4+
const defaultApi = (onUndef = noop) => ({
5+
add: onUndef,
6+
bitswap: {
7+
stat: onUndef,
8+
unwant: onUndef,
9+
wantlist: onUndef
10+
},
11+
block: {
12+
get: onUndef,
13+
put: onUndef,
14+
rm: onUndef,
15+
stat: onUndef
16+
},
17+
bootstrap: {
18+
add: onUndef,
19+
list: onUndef,
20+
rm: onUndef
21+
},
22+
cat: onUndef,
23+
config: onUndef,
24+
dag: {
25+
get: onUndef,
26+
put: onUndef,
27+
resolve: onUndef,
28+
tree: onUndef
29+
},
30+
dns: onUndef,
31+
files: {
32+
chmod: onUndef,
33+
cp: onUndef,
34+
flush: onUndef,
35+
ls: onUndef,
36+
mkdir: onUndef,
37+
mv: onUndef,
38+
read: onUndef,
39+
rm: onUndef,
40+
stat: onUndef,
41+
touch: onUndef,
42+
write: onUndef
43+
},
44+
get: onUndef,
45+
id: onUndef,
46+
init: onUndef,
47+
isOnline: onUndef,
48+
key: {
49+
export: onUndef,
50+
gen: onUndef,
51+
import: onUndef,
52+
info: onUndef,
53+
list: onUndef,
54+
rename: onUndef,
55+
rm: onUndef
56+
},
57+
ls: onUndef,
58+
name: {
59+
publish: onUndef,
60+
pubsub: {
61+
cancel: onUndef,
62+
state: onUndef,
63+
subs: onUndef
64+
}
65+
},
66+
object: {
67+
data: onUndef,
68+
get: onUndef,
69+
links: onUndef,
70+
new: onUndef,
71+
patch: {
72+
addLink: onUndef,
73+
appendData: onUndef,
74+
rmLink: onUndef,
75+
setData: onUndef
76+
},
77+
put: onUndef,
78+
stat: onUndef
79+
},
80+
pin: onUndef,
81+
ping: onUndef,
82+
pubsub: {
83+
subscribe: onUndef,
84+
unsubscribe: onUndef,
85+
publish: onUndef,
86+
ls: onUndef,
87+
peers: onUndef
88+
},
89+
refs: onUndef,
90+
repo: {
91+
gc: onUndef,
92+
stat: onUndef,
93+
version: onUndef
94+
},
95+
resolve: onUndef,
96+
start: onUndef,
97+
stats: {
98+
bitswap: onUndef,
99+
bw: onUndef,
100+
repo: onUndef
101+
},
102+
stop: onUndef,
103+
swarm: {
104+
addrs: onUndef,
105+
connect: onUndef,
106+
disconnect: onUndef,
107+
localAddrs: onUndef,
108+
peers: onUndef
109+
},
110+
version: onUndef
111+
})
112+
3113
module.exports = class ApiManager {
4114
constructor () {
5-
this._api = {}
6-
this._onUndef = () => undefined
7-
this.api = new Proxy(this._api, {
8-
get: (_, prop) => {
9-
if (prop === 'then') return undefined // Not a promise!
10-
return this._api[prop] === undefined ? this._onUndef(prop) : this._api[prop]
11-
}
12-
})
115+
this.api = {
116+
...defaultApi()
117+
}
13118
}
14119

15120
update (nextApi, onUndef) {
16121
const prevApi = { ...this._api }
17122
const prevUndef = this._onUndef
18-
Object.keys(this._api).forEach(k => { delete this._api[k] })
19-
Object.assign(this._api, nextApi)
20-
if (onUndef) this._onUndef = onUndef
123+
Object.keys(this.api).forEach(k => { delete this.api[k] })
124+
Object.assign(this.api, defaultApi(onUndef), nextApi)
125+
this._onUndef = onUndef || noop
21126
return { cancel: () => this.update(prevApi, prevUndef), api: this.api }
22127
}
23128
}

src/core/components/init.js

+11-29
Original file line numberDiff line numberDiff line change
@@ -5,24 +5,21 @@ const PeerId = require('peer-id')
55
const PeerInfo = require('peer-info')
66
const mergeOptions = require('merge-options')
77
const getDefaultConfig = require('../runtime/config-nodejs.js')
8-
const createRepo = require('../runtime/repo-nodejs')
98
const Keychain = require('libp2p-keychain')
109
const NoKeychain = require('./no-keychain')
1110
const mortice = require('mortice')
1211
const { DAGNode } = require('ipld-dag-pb')
1312
const UnixFs = require('ipfs-unixfs')
1413
const multicodec = require('multicodec')
1514
const {
16-
AlreadyInitializingError,
1715
AlreadyInitializedError,
18-
NotStartedError,
19-
NotEnabledError
16+
AlreadyInitializingError,
17+
NotStartedError
2018
} = require('../errors')
2119
const BlockService = require('ipfs-block-service')
2220
const Ipld = require('ipld')
2321
const getDefaultIpldOptions = require('../runtime/ipld-nodejs')
2422
const createPreloader = require('../preload')
25-
const { ERR_REPO_NOT_INITIALIZED } = require('ipfs-repo').errors
2623
const IPNS = require('../ipns')
2724
const OfflineDatastore = require('../ipns/routing/offline-datastore')
2825
const initAssets = require('../runtime/init-assets-nodejs')
@@ -32,9 +29,14 @@ const Components = require('./')
3229
module.exports = ({
3330
apiManager,
3431
print,
35-
options: constructorOptions
32+
options: constructorOptions,
33+
repo
3634
}) => async function init (options) {
37-
const { cancel } = apiManager.update({ init: () => { throw new AlreadyInitializingError() } })
35+
const { cancel } = apiManager.update({
36+
init: () => {
37+
throw new AlreadyInitializingError()
38+
}
39+
})
3840

3941
try {
4042
options = options || {}
@@ -49,30 +51,9 @@ module.exports = ({
4951
options.config = mergeOptions(options.config, constructorOptions.config)
5052
}
5153

52-
options.repo = options.repo || constructorOptions.repo
5354
options.repoAutoMigrate = options.repoAutoMigrate || constructorOptions.repoAutoMigrate
5455

55-
const repo = typeof options.repo === 'string' || options.repo == null
56-
? createRepo({ path: options.repo, autoMigrate: options.repoAutoMigrate })
57-
: options.repo
58-
59-
let isInitialized = true
60-
61-
if (repo.closed) {
62-
try {
63-
await repo.open()
64-
} catch (err) {
65-
if (err.code === ERR_REPO_NOT_INITIALIZED) {
66-
isInitialized = false
67-
} else {
68-
throw err
69-
}
70-
}
71-
}
72-
73-
if (!isInitialized && options.allowNew === false) {
74-
throw new NotEnabledError('new repo initialization is not enabled')
75-
}
56+
const isInitialized = await repo.isInitialized()
7657

7758
const { peerId, keychain } = isInitialized
7859
? await initExistingRepo(repo, options)
@@ -212,6 +193,7 @@ async function initNewRepo (repo, { privateKey, emptyRepo, bits, profiles, confi
212193
}
213194

214195
async function initExistingRepo (repo, { config: newConfig, profiles, pass }) {
196+
await repo.open()
215197
let config = await repo.config.get()
216198

217199
if (newConfig || profiles) {

src/core/components/start.js

+13-8
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,14 @@
33
const Bitswap = require('ipfs-bitswap')
44
const multiaddr = require('multiaddr')
55
const get = require('dlv')
6-
const defer = require('p-defer')
76
const IPNS = require('../ipns')
87
const routingConfig = require('../ipns/routing/config')
9-
const { AlreadyInitializedError, NotEnabledError } = require('../errors')
8+
const {
9+
AlreadyInitializedError,
10+
AlreadyStartingError,
11+
AlreadyStartedError,
12+
NotEnabledError
13+
} = require('../errors')
1014
const Components = require('./')
1115
const createMfsPreload = require('../mfs-preload')
1216

@@ -24,8 +28,11 @@ module.exports = ({
2428
print,
2529
repo
2630
}) => async function start () {
27-
const startPromise = defer()
28-
const { cancel } = apiManager.update({ start: () => startPromise.promise })
31+
const { cancel } = apiManager.update({
32+
start: () => {
33+
throw new AlreadyStartingError()
34+
}
35+
})
2936

3037
try {
3138
// The repo may be closed if previously stopped
@@ -97,14 +104,12 @@ module.exports = ({
97104
repo
98105
})
99106

100-
apiManager.update(api, () => undefined)
107+
apiManager.update(api)
101108
} catch (err) {
102109
cancel()
103-
startPromise.reject(err)
104110
throw err
105111
}
106112

107-
startPromise.resolve(apiManager.api)
108113
return apiManager.api
109114
}
110115

@@ -234,7 +239,7 @@ function createApi ({
234239
version: Components.repo.version({ repo })
235240
},
236241
resolve,
237-
start: () => apiManager.api,
242+
start: async () => { throw new AlreadyStartedError() }, // eslint-disable-line require-await
238243
stats: {
239244
bitswap: Components.bitswap.stat({ bitswap }),
240245
bw: libp2p.metrics

src/core/components/stop.js

+11-7
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,10 @@
11
'use strict'
22

3-
const defer = require('p-defer')
4-
const { NotStartedError, AlreadyInitializedError } = require('../errors')
3+
const {
4+
NotStartedError,
5+
AlreadyInitializedError,
6+
AlreadyStoppingError
7+
} = require('../errors')
58
const Components = require('./')
69

710
module.exports = ({
@@ -22,8 +25,11 @@ module.exports = ({
2225
print,
2326
repo
2427
}) => async function stop () {
25-
const stopPromise = defer()
26-
const { cancel } = apiManager.update({ stop: () => stopPromise.promise })
28+
const { cancel } = apiManager.update({
29+
stop: () => {
30+
throw new AlreadyStoppingError()
31+
}
32+
})
2733

2834
try {
2935
blockService.unsetExchange()
@@ -58,11 +64,9 @@ module.exports = ({
5864
apiManager.update(api, () => { throw new NotStartedError() })
5965
} catch (err) {
6066
cancel()
61-
stopPromise.reject(err)
6267
throw err
6368
}
6469

65-
stopPromise.resolve(apiManager.api)
6670
return apiManager.api
6771
}
6872

@@ -182,7 +186,7 @@ function createApi ({
182186
bw: notStarted,
183187
repo: Components.repo.stat({ repo })
184188
},
185-
stop: () => apiManager.api,
189+
stop: notStarted,
186190
swarm: {
187191
addrs: notStarted,
188192
connect: notStarted,

src/core/errors.js

+33
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,39 @@ class NotStartedError extends Error {
4444
NotStartedError.code = 'ERR_NOT_STARTED'
4545
exports.NotStartedError = NotStartedError
4646

47+
class AlreadyStartingError extends Error {
48+
constructor (message = 'already starting') {
49+
super(message)
50+
this.name = 'AlreadyStartingError'
51+
this.code = AlreadyStartingError.code
52+
}
53+
}
54+
55+
AlreadyStartingError.code = 'ERR_ALREADY_STARTING'
56+
exports.AlreadyStartingError = AlreadyStartingError
57+
58+
class AlreadyStartedError extends Error {
59+
constructor (message = 'already started') {
60+
super(message)
61+
this.name = 'AlreadyStartedError'
62+
this.code = AlreadyStartedError.code
63+
}
64+
}
65+
66+
AlreadyStartedError.code = 'ERR_ALREADY_STARTED'
67+
exports.AlreadyStartedError = AlreadyStartedError
68+
69+
class AlreadyStoppingError extends Error {
70+
constructor (message = 'already started') {
71+
super(message)
72+
this.name = 'AlreadyStoppingError'
73+
this.code = AlreadyStartedError.code
74+
}
75+
}
76+
77+
AlreadyStoppingError.code = 'ERR_ALREADY_STOPPING'
78+
exports.AlreadyStoppingError = AlreadyStoppingError
79+
4780
class NotEnabledError extends Error {
4881
constructor (message = 'not enabled') {
4982
super(message)

0 commit comments

Comments
 (0)