Skip to content

Commit 5fe8f64

Browse files
committed
fix!: remove @libp2p/components
`@libp2p/components` is a choke-point for our dependency graph as it depends on every interface, meaning when one interface revs a major `@libp2p/components` major has to change too which means every module depending on it also needs a major. Switch instead to constructor injection of simple objects that let modules declare their dependencies on interfaces directly instead of indirectly via `@libp2p/components` Refs libp2p/js-libp2p-components#6 BREAKING CHANGE: modules no longer implement `Initializable` instead switching to constructor injection
1 parent 99d9f16 commit 5fe8f64

File tree

3 files changed

+51
-31
lines changed

3 files changed

+51
-31
lines changed

package.json

+4-2
Original file line numberDiff line numberDiff line change
@@ -143,18 +143,20 @@
143143
"any-signal": "^3.0.1",
144144
"err-code": "^3.0.1",
145145
"it-drain": "^1.0.5",
146+
"multiformats": "^10.0.0",
146147
"p-defer": "^4.0.0",
147148
"p-queue": "^7.2.0"
148149
},
149150
"devDependencies": {
150151
"aegir": "^37.5.3",
151-
"go-ipfs": "^0.15.0",
152+
"go-ipfs": "^0.16.0",
152153
"ipfs-core-types": "^0.12.0",
153154
"ipfs-http-client": "^58.0.0",
154155
"ipfsd-ctl": "^12.0.2",
155156
"it-all": "^1.0.6",
156157
"it-drain": "^1.0.5",
157-
"uint8arrays": "^3.0.0",
158+
"timeout-abort-controller": "^3.0.0",
159+
"uint8arrays": "^4.0.2",
158160
"wherearewe": "^2.0.1"
159161
},
160162
"browser": {

src/index.ts

+17-5
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,13 @@ import PQueue from 'p-queue'
44
import defer from 'p-defer'
55
import errCode from 'err-code'
66
import anySignal from 'any-signal'
7-
import type { IPFSHTTPClient, CID, HTTPClientExtraOptions } from 'ipfs-http-client'
7+
import { CID as IPFSCCID } from 'ipfs-http-client'
8+
import type { IPFSHTTPClient, HTTPClientExtraOptions } from 'ipfs-http-client'
89
import type { AbortOptions } from 'ipfs-core-types/src/utils'
910
import type { ContentRouting } from '@libp2p/interface-content-routing'
1011
import type { PeerInfo } from '@libp2p/interface-peer-info'
1112
import type { Startable } from '@libp2p/interfaces/startable'
13+
import type { CID } from 'multiformats/cid'
1214

1315
const log = logger('libp2p:delegated-content-routing')
1416

@@ -19,7 +21,7 @@ const CONCURRENT_HTTP_REFS_REQUESTS = 2
1921
/**
2022
* An implementation of content routing, using a delegated peer
2123
*/
22-
export class DelegatedContentRouting implements ContentRouting, Startable {
24+
class DelegatedContentRouting implements ContentRouting, Startable {
2325
private readonly client: IPFSHTTPClient
2426
private readonly httpQueue: PQueue
2527
private readonly httpQueueRefs: PQueue
@@ -95,7 +97,10 @@ export class DelegatedContentRouting implements ContentRouting, Startable {
9597
try {
9698
await onStart.promise
9799

98-
for await (const event of this.client.dht.findProvs(key, options)) {
100+
// can be removed after ipfs ships with [email protected]
101+
const ipfsCid = IPFSCCID.parse(key.toString())
102+
103+
for await (const event of this.client.dht.findProvs(ipfsCid, options)) {
99104
if (event.name === 'PROVIDER') {
100105
yield * event.providers.map(prov => {
101106
const peerInfo: PeerInfo = {
@@ -134,9 +139,12 @@ export class DelegatedContentRouting implements ContentRouting, Startable {
134139
options.timeout = options.timeout ?? DEFAULT_TIMEOUT
135140
options.signal = anySignal([this.abortController.signal].concat((options.signal != null) ? [options.signal] : []))
136141

142+
// can be removed after ipfs ships with [email protected]
143+
const ipfsCid = IPFSCCID.parse(key.toString())
144+
137145
await this.httpQueueRefs.add(async () => {
138-
await this.client.block.stat(key, options)
139-
await drain(this.client.dht.provide(key, options))
146+
await this.client.block.stat(ipfsCid, options)
147+
await drain(this.client.dht.provide(ipfsCid, options))
140148
})
141149
log('provide finished: %c', key)
142150
}
@@ -180,3 +188,7 @@ export class DelegatedContentRouting implements ContentRouting, Startable {
180188
})
181189
}
182190
}
191+
192+
export function delegatedContentRouting (client: IPFSHTTPClient): (components?: any) => ContentRouting {
193+
return () => new DelegatedContentRouting(client)
194+
}

test/index.spec.ts

+30-24
Original file line numberDiff line numberDiff line change
@@ -2,18 +2,21 @@
22

33
import { expect } from 'aegir/chai'
44
import { Controller, createFactory } from 'ipfsd-ctl'
5-
import { create, CID } from 'ipfs-http-client'
5+
import { create, CID as IPFSCCID } from 'ipfs-http-client'
66
import all from 'it-all'
77
import drain from 'it-drain'
88
import { isElectronMain, isNode } from 'wherearewe'
99
import { fromString as uint8ArrayFromString } from 'uint8arrays/from-string'
10-
import { DelegatedContentRouting } from '../src/index.js'
10+
import { delegatedContentRouting } from '../src/index.js'
1111
// @ts-expect-error no types
1212
import goIpfs from 'go-ipfs'
1313
import pDefer from 'p-defer'
14+
import { CID } from 'multiformats/cid'
1415
import type { PeerId } from '@libp2p/interface-peer-id'
1516
import type { IDResult } from 'ipfs-core-types/src/root'
1617
import type { PeerInfo } from '@libp2p/interface-peer-info'
18+
import { stop } from '@libp2p/interfaces/startable'
19+
import { TimeoutController } from 'timeout-abort-controller'
1720

1821
const factory = createFactory({
1922
type: 'go',
@@ -78,7 +81,7 @@ describe('DelegatedContentRouting', function () {
7881
describe('create', () => {
7982
it('should require ipfs http client', () => {
8083
// @ts-expect-error missing parameters
81-
expect(() => new DelegatedContentRouting()).to.throw()
84+
expect(() => delegatedContentRouting()()).to.throw()
8285
})
8386

8487
it('should accept an http api client instance at construction time', () => {
@@ -87,7 +90,7 @@ describe('DelegatedContentRouting', function () {
8790
port: 8000,
8891
host: 'localhost'
8992
})
90-
const router = new DelegatedContentRouting(client)
93+
const router = delegatedContentRouting(client)()
9194

9295
expect(router).to.have.property('client')
9396
.that.has.property('getEndpointConfig')
@@ -103,7 +106,7 @@ describe('DelegatedContentRouting', function () {
103106

104107
describe('findProviders', () => {
105108
const data = uint8ArrayFromString('some data')
106-
const cid = CID.parse('QmVv4Wz46JaZJeH5PMV4LGbRiiMKEmszPYY3g6fjGnVXBS') // 'some data'
109+
const cid = IPFSCCID.parse('QmVv4Wz46JaZJeH5PMV4LGbRiiMKEmszPYY3g6fjGnVXBS') // 'some data'
107110

108111
before('register providers', async () => {
109112
await Promise.all([
@@ -118,13 +121,13 @@ describe('DelegatedContentRouting', function () {
118121

119122
it('should be able to find providers through the delegate node', async function () {
120123
const opts = delegateNode.apiAddr.toOptions()
121-
const routing = new DelegatedContentRouting(create({
124+
const routing = delegatedContentRouting(create({
122125
protocol: 'http',
123126
port: opts.port,
124127
host: opts.host
125-
}))
128+
}))()
126129

127-
const providers = await all(routing.findProviders(cid))
130+
const providers = await all(routing.findProviders(CID.parse(cid.toString())))
128131

129132
// We should get the bootstrap node as provider
130133
// The delegate node is not included, because it is handling the requests
@@ -134,30 +137,33 @@ describe('DelegatedContentRouting', function () {
134137

135138
it('should be able to specify a timeout', async () => {
136139
const opts = delegateNode.apiAddr.toOptions()
137-
const routing = new DelegatedContentRouting(create({
140+
const routing = delegatedContentRouting(create({
138141
protocol: 'http',
139142
port: opts.port,
140143
host: opts.host
141-
}))
144+
}))()
145+
const controller = new TimeoutController(5e3)
142146

143-
const providers = await all(routing.findProviders(cid, { timeout: 5e3 }))
147+
const providers = await all(routing.findProviders(CID.parse(cid.toString()), { signal: controller.signal }))
144148

145149
expect(providers.map((p) => p.id.toString())).to.include(bootstrapId.id.toString(), 'Did not include bootstrap node')
150+
151+
controller.clear()
146152
})
147153
})
148154

149155
describe('provide', () => {
150156
it('should be able to register as a content provider to the delegate node', async () => {
151157
const opts = delegateNode.apiAddr.toOptions()
152-
const contentRouter = new DelegatedContentRouting(create({
158+
const contentRouter = delegatedContentRouting(create({
153159
protocol: 'http',
154160
port: opts.port,
155161
host: opts.host
156-
}))
162+
}))()
157163

158164
const { cid } = await selfNode.api.add(uint8ArrayFromString(`hello-${Math.random()}`))
159165

160-
await contentRouter.provide(cid)
166+
await contentRouter.provide(CID.parse(cid.toString()))
161167

162168
const providers: PeerInfo[] = []
163169

@@ -173,18 +179,18 @@ describe('DelegatedContentRouting', function () {
173179

174180
it('should provide non-dag-pb nodes via the delegate node', async () => {
175181
const opts = delegateNode.apiAddr.toOptions()
176-
const contentRouter = new DelegatedContentRouting(create({
182+
const contentRouter = delegatedContentRouting(create({
177183
protocol: 'http',
178184
port: opts.port,
179185
host: opts.host
180-
}))
186+
}))()
181187

182188
const cid = await selfNode.api.dag.put(`hello-${Math.random()}`, {
183189
storeCodec: 'dag-cbor',
184190
hashAlg: 'sha2-256'
185191
})
186192

187-
await contentRouter.provide(cid)
193+
await contentRouter.provide(CID.parse(cid.toString()))
188194

189195
const providers: PeerInfo[] = []
190196

@@ -202,11 +208,11 @@ describe('DelegatedContentRouting', function () {
202208
describe('get', () => {
203209
it('should get a value', async () => {
204210
const opts = delegateNode.apiAddr.toOptions()
205-
const contentRouter = new DelegatedContentRouting(create({
211+
const contentRouter = delegatedContentRouting(create({
206212
protocol: 'http',
207213
port: opts.port,
208214
host: opts.host
209-
}))
215+
}))()
210216

211217
const cid = await selfNode.api.dag.put(`hello-${Math.random()}`, {
212218
storeCodec: 'dag-cbor',
@@ -224,11 +230,11 @@ describe('DelegatedContentRouting', function () {
224230
describe('put', () => {
225231
it('should put a value', async () => {
226232
const opts = delegateNode.apiAddr.toOptions()
227-
const contentRouter = new DelegatedContentRouting(create({
233+
const contentRouter = delegatedContentRouting(create({
228234
protocol: 'http',
229235
port: opts.port,
230236
host: opts.host
231-
}))
237+
}))()
232238

233239
const cid = await selfNode.api.dag.put(`hello-${Math.random()}`, {
234240
storeCodec: 'dag-cbor',
@@ -259,11 +265,11 @@ describe('DelegatedContentRouting', function () {
259265
describe('stop', () => {
260266
it('should cancel in-flight requests when stopping', async () => {
261267
const opts = delegateNode.apiAddr.toOptions()
262-
const contentRouter = new DelegatedContentRouting(create({
268+
const contentRouter = delegatedContentRouting(create({
263269
protocol: 'http',
264270
port: opts.port,
265271
host: opts.host
266-
}))
272+
}))()
267273

268274
const deferred = pDefer<Error>()
269275
// non-existent CID
@@ -277,7 +283,7 @@ describe('DelegatedContentRouting', function () {
277283
deferred.resolve(err)
278284
})
279285

280-
await contentRouter.stop()
286+
await stop(contentRouter)
281287
await expect(deferred.promise).to.eventually.have.property('message').that.matches(/aborted/)
282288
})
283289
})

0 commit comments

Comments
 (0)