diff --git a/packages/libp2p-connection/package.json b/packages/libp2p-connection/package.json index 4f4f77c08..cea4208f4 100644 --- a/packages/libp2p-connection/package.json +++ b/packages/libp2p-connection/package.json @@ -142,10 +142,10 @@ }, "scripts": { "lint": "aegir lint", - "dep-check": "aegir dep-check dist/src/**/*.js", + "dep-check": "aegir dep-check dist/src/**/*.js dist/test/**/*.js", "build": "tsc", "pretest": "npm run build", - "test": "aegir test -f ./dist/test/**/*.js", + "test": "aegir test -f ./dist/test/*.js -f ./dist/test/**/*.js", "test:chrome": "npm run test -- -t browser", "test:chrome-webworker": "npm run test -- -t webworker", "test:firefox": "npm run test -- -t browser -- --browser firefox", @@ -161,6 +161,7 @@ "devDependencies": { "@libp2p/interface-compliance-tests": "^1.0.0", "@libp2p/peer-id-factory": "^1.0.0", - "aegir": "^36.1.3" + "aegir": "^36.1.3", + "it-pair": "^2.0.2" } } diff --git a/packages/libp2p-interface-compliance-tests/package.json b/packages/libp2p-interface-compliance-tests/package.json index 7756105a0..f4642b20e 100644 --- a/packages/libp2p-interface-compliance-tests/package.json +++ b/packages/libp2p-interface-compliance-tests/package.json @@ -96,6 +96,10 @@ "import": "./dist/src/utils/mock-connection-gater.js", "types": "./dist/src/utils/mock-connection-gater.d.ts" }, + "./utils/mock-connection-manager": { + "import": "./dist/src/utils/mock-connection-manager.js", + "types": "./dist/src/utils/mock-connection-manager.d.ts" + }, "./utils/mock-multiaddr-connection": { "import": "./dist/src/utils/mock-multiaddr-connection.js", "types": "./dist/src/utils/mock-multiaddr-connection.d.ts" @@ -108,6 +112,10 @@ "import": "./dist/src/utils/mock-peer-store.js", "types": "./dist/src/utils/mock-peer-store.d.ts" }, + "./utils/mock-registrar": { + "import": "./dist/src/utils/mock-registrar.js", + "types": "./dist/src/utils/mock-registrar.d.ts" + }, "./utils/mock-upgrader": { "import": "./dist/src/utils/mock-upgrader.js", "types": "./dist/src/utils/mock-upgrader.d.ts" @@ -206,10 +214,10 @@ }, "scripts": { "lint": "aegir lint", - "dep-check": "aegir dep-check dist/src/**/*.js", + "dep-check": "aegir dep-check dist/src/**/*.js dist/test/**/*.js", "build": "tsc", "pretest": "npm run build", - "test": "aegir test -f ./dist/test/**/*.js", + "test": "aegir test -f ./dist/test/*.js -f ./dist/test/**/*.js", "test:chrome": "npm run test -- -t browser", "test:chrome-webworker": "npm run test -- -t webworker", "test:firefox": "npm run test -- -t browser -- --browser firefox", diff --git a/packages/libp2p-interface-compliance-tests/src/peer-discovery/index.ts b/packages/libp2p-interface-compliance-tests/src/peer-discovery/index.ts index 765359830..5bdd3d59d 100644 --- a/packages/libp2p-interface-compliance-tests/src/peer-discovery/index.ts +++ b/packages/libp2p-interface-compliance-tests/src/peer-discovery/index.ts @@ -16,9 +16,6 @@ export default (common: TestSetup) => { afterEach('ensure discovery was stopped', async () => { await discovery.stop() - - discovery.removeAllListeners() - await common.teardown() }) @@ -44,7 +41,8 @@ export default (common: TestSetup) => { const defer = pDefer() await discovery.start() - discovery.on('peer', ({ id, multiaddrs }) => { + discovery.addEventListener('peer', (evt) => { + const { id, multiaddrs } = evt.detail expect(id).to.exist() expect(id).to.have.property('type').that.is.oneOf(['RSA', 'Ed25519', 'secp256k1']) expect(multiaddrs).to.exist() @@ -58,7 +56,7 @@ export default (common: TestSetup) => { }) it('should not receive a peer event before start', async () => { - discovery.on('peer', () => { + discovery.addEventListener('peer', () => { throw new Error('should not receive a peer event before start') }) @@ -70,14 +68,14 @@ export default (common: TestSetup) => { await discovery.start() - discovery.on('peer', () => { + discovery.addEventListener('peer', () => { deferStart.resolve() }) await deferStart.promise await discovery.stop() - discovery.on('peer', () => { + discovery.addEventListener('peer', () => { throw new Error('should not receive a peer event after stop') }) diff --git a/packages/libp2p-interface-compliance-tests/src/pubsub/api.ts b/packages/libp2p-interface-compliance-tests/src/pubsub/api.ts index 19c178d6d..21f934ccf 100644 --- a/packages/libp2p-interface-compliance-tests/src/pubsub/api.ts +++ b/packages/libp2p-interface-compliance-tests/src/pubsub/api.ts @@ -4,15 +4,15 @@ import pDefer from 'p-defer' import pWaitFor from 'p-wait-for' import { fromString as uint8ArrayFromString } from 'uint8arrays/from-string' import type { TestSetup } from '../index.js' -import type { PubSub, Message } from '@libp2p/interfaces/pubsub' -import type { Startable } from '@libp2p/interfaces' +import type { PubSub } from '@libp2p/interfaces/pubsub' +import type { EventMap } from './index.js' const topic = 'foo' const data = uint8ArrayFromString('bar') -export default (common: TestSetup) => { +export default (common: TestSetup>) => { describe('pubsub api', () => { - let pubsub: PubSub & Startable + let pubsub: PubSub // Create pubsub router beforeEach(async () => { @@ -51,7 +51,7 @@ export default (common: TestSetup) => { await pubsub.start() pubsub.subscribe(topic) - pubsub.on('topic', handler) + pubsub.addEventListener('topic', handler) await pWaitFor(() => { const topics = pubsub.getTopics() @@ -71,15 +71,14 @@ export default (common: TestSetup) => { it('can subscribe and publish correctly', async () => { const defer = pDefer() - const handler = (msg: Message) => { - expect(msg).to.not.eql(undefined) - defer.resolve() - } - await pubsub.start() pubsub.subscribe(topic) - pubsub.on(topic, handler) + pubsub.addEventListener(topic, (evt) => { + const msg = evt.detail + expect(msg).to.not.eql(undefined) + defer.resolve() + }) await pubsub.publish(topic, data) await defer.promise diff --git a/packages/libp2p-interface-compliance-tests/src/pubsub/connection-handlers.ts b/packages/libp2p-interface-compliance-tests/src/pubsub/connection-handlers.ts index 5e4ecf517..7467f13c5 100644 --- a/packages/libp2p-interface-compliance-tests/src/pubsub/connection-handlers.ts +++ b/packages/libp2p-interface-compliance-tests/src/pubsub/connection-handlers.ts @@ -7,12 +7,12 @@ import { fromString as uint8ArrayFromString } from 'uint8arrays/from-string' import { expectSet } from './utils.js' import type { TestSetup } from '../index.js' import type { PubSub, Message } from '@libp2p/interfaces/pubsub' -import type { Startable } from '@libp2p/interfaces' +import type { EventMap } from './index.js' -export default (common: TestSetup) => { +export default (common: TestSetup>) => { describe('pubsub connection handlers', () => { - let psA: PubSub & Startable - let psB: PubSub & Startable + let psA: PubSub + let psB: PubSub describe('nodes send state on connection', () => { // Create pubsub nodes and connect them @@ -48,8 +48,12 @@ export default (common: TestSetup) => { await Promise.all([ // @ts-expect-error protected fields psA._libp2p.dial(psB.peerId), - new Promise((resolve) => psA.once('pubsub:subscription-change', resolve)), - new Promise((resolve) => psB.once('pubsub:subscription-change', resolve)) + new Promise((resolve) => psA.addEventListener('pubsub:subscription-change', resolve, { + once: true + })), + new Promise((resolve) => psB.addEventListener('pubsub:subscription-change', resolve, { + once: true + })) ]) expect(psA.peers.size).to.equal(1) @@ -103,7 +107,8 @@ export default (common: TestSetup) => { let subscribedTopics = psA.getTopics() expect(subscribedTopics).to.not.include(topic) - psA.on(topic, (msg) => { + psA.addEventListener(topic, (evt) => { + const msg = evt.detail expect(msg.data).to.equalBytes(data) defer.resolve() }) @@ -174,7 +179,8 @@ export default (common: TestSetup) => { let subscribedTopics = psA.getTopics() expect(subscribedTopics).to.not.include(topic) - psA.on(topic, (msg) => { + psA.addEventListener(topic, (evt) => { + const msg = evt.detail expect(msg.data).to.equalBytes(data) defer.resolve() }) @@ -228,7 +234,8 @@ export default (common: TestSetup) => { let subscribedTopics = psA.getTopics() expect(subscribedTopics).to.not.include(topic) - psA.on(topic, (msg) => { + psA.addEventListener(topic, (evt) => { + const msg = evt.detail expect(msg.data).to.equalBytes(data) counter++ counter === 1 ? defer1.resolve() : defer2.resolve() @@ -285,7 +292,8 @@ export default (common: TestSetup) => { let bReceivedFirstMessageFromA = false let bReceivedSecondMessageFromA = false - const handlerSpyA = (message: Message) => { + const handlerSpyA = (evt: CustomEvent) => { + const message = evt.detail const data = uint8ArrayToString(message.data) if (data === 'message-from-b-1') { @@ -296,7 +304,8 @@ export default (common: TestSetup) => { aReceivedSecondMessageFromB = true } } - const handlerSpyB = (message: Message) => { + const handlerSpyB = (evt: CustomEvent) => { + const message = evt.detail const data = uint8ArrayToString(message.data) if (data === 'message-from-a-1') { @@ -310,8 +319,8 @@ export default (common: TestSetup) => { const topic = 'reconnect-channel' - psA.on(topic, handlerSpyA) - psB.on(topic, handlerSpyB) + psA.addEventListener(topic, handlerSpyA) + psB.addEventListener(topic, handlerSpyB) psA.subscribe(topic) psB.subscribe(topic) diff --git a/packages/libp2p-interface-compliance-tests/src/pubsub/emit-self.ts b/packages/libp2p-interface-compliance-tests/src/pubsub/emit-self.ts index abd7cc837..a7f76f74d 100644 --- a/packages/libp2p-interface-compliance-tests/src/pubsub/emit-self.ts +++ b/packages/libp2p-interface-compliance-tests/src/pubsub/emit-self.ts @@ -3,15 +3,15 @@ import sinon from 'sinon' import { fromString as uint8ArrayFromString } from 'uint8arrays/from-string' import type { TestSetup } from '../index.js' import type { PubSub, PubsubOptions } from '@libp2p/interfaces/pubsub' -import type { Startable } from '@libp2p/interfaces' +import type { EventMap } from './index.js' const topic = 'foo' const data = uint8ArrayFromString('bar') const shouldNotHappen = () => expect.fail() -export default (common: TestSetup>) => { +export default (common: TestSetup, Partial>) => { describe('emit self', () => { - let pubsub: PubSub & Startable + let pubsub: PubSub describe('enabled', () => { before(async () => { @@ -30,7 +30,9 @@ export default (common: TestSetup>) = }) it('should emit to self on publish', async () => { - const promise = new Promise((resolve) => pubsub.once(topic, resolve)) + const promise = new Promise((resolve) => pubsub.addEventListener(topic, resolve, { + once: true + })) void pubsub.publish(topic, data) @@ -55,7 +57,9 @@ export default (common: TestSetup>) = }) it('should not emit to self on publish', async () => { - pubsub.once(topic, () => shouldNotHappen) + pubsub.addEventListener(topic, () => shouldNotHappen, { + once: true + }) void pubsub.publish(topic, data) diff --git a/packages/libp2p-interface-compliance-tests/src/pubsub/index.ts b/packages/libp2p-interface-compliance-tests/src/pubsub/index.ts index 3847fe307..4df4f0146 100644 --- a/packages/libp2p-interface-compliance-tests/src/pubsub/index.ts +++ b/packages/libp2p-interface-compliance-tests/src/pubsub/index.ts @@ -5,10 +5,17 @@ import connectionHandlersTest from './connection-handlers.js' import twoNodesTest from './two-nodes.js' import multipleNodesTest from './multiple-nodes.js' import type { TestSetup } from '../index.js' -import type { PubSub } from '@libp2p/interfaces/pubsub' -import type { Startable } from '@libp2p/interfaces' +import type { PubSub, Message, PubsubEvents } from '@libp2p/interfaces/pubsub' -export default (common: TestSetup) => { +export interface EventMap extends PubsubEvents { + 'topic': CustomEvent + 'foo': CustomEvent + 'test-topic': CustomEvent + 'reconnect-channel': CustomEvent + 'Z': CustomEvent +} + +export default (common: TestSetup>) => { describe('interface-pubsub compliance tests', () => { apiTest(common) emitSelfTest(common) diff --git a/packages/libp2p-interface-compliance-tests/src/pubsub/messages.ts b/packages/libp2p-interface-compliance-tests/src/pubsub/messages.ts index 35f945c77..c0e27d1e8 100644 --- a/packages/libp2p-interface-compliance-tests/src/pubsub/messages.ts +++ b/packages/libp2p-interface-compliance-tests/src/pubsub/messages.ts @@ -6,14 +6,14 @@ import * as utils from '@libp2p/pubsub/utils' import { PeerStreams } from '@libp2p/pubsub/peer-streams' import type { TestSetup } from '../index.js' import type { PubSub } from '@libp2p/interfaces/pubsub' -import type { Startable } from '@libp2p/interfaces' +import type { EventMap } from './index.js' const topic = 'foo' const data = uint8ArrayFromString('bar') -export default (common: TestSetup) => { +export default (common: TestSetup>) => { describe('messages', () => { - let pubsub: PubSub & Startable + let pubsub: PubSub // Create pubsub router beforeEach(async () => { diff --git a/packages/libp2p-interface-compliance-tests/src/pubsub/multiple-nodes.ts b/packages/libp2p-interface-compliance-tests/src/pubsub/multiple-nodes.ts index 814b2926b..d42f48052 100644 --- a/packages/libp2p-interface-compliance-tests/src/pubsub/multiple-nodes.ts +++ b/packages/libp2p-interface-compliance-tests/src/pubsub/multiple-nodes.ts @@ -9,18 +9,18 @@ import { fromString as uint8ArrayFromString } from 'uint8arrays/from-string' import { expectSet } from './utils.js' import type { TestSetup } from '../index.js' import type { PubSub, Message } from '@libp2p/interfaces/pubsub' -import type { Startable } from '@libp2p/interfaces' +import type { EventMap } from './index.js' -export default (common: TestSetup) => { +export default (common: TestSetup>) => { describe('pubsub with multiple nodes', function () { describe('every peer subscribes to the topic', () => { describe('line', () => { // line // ◉────◉────◉ // a b c - let psA: PubSub & Startable - let psB: PubSub & Startable - let psC: PubSub & Startable + let psA: PubSub + let psB: PubSub + let psC: PubSub // Create and start pubsub nodes beforeEach(async () => { @@ -60,7 +60,9 @@ export default (common: TestSetup) => { psA.subscribe(topic) expectSet(psA.subscriptions, [topic]) - await new Promise((resolve) => psB.once('pubsub:subscription-change', resolve)) + await new Promise((resolve) => psB.addEventListener('pubsub:subscription-change', resolve, { + once: true + })) expect(psB.peers.size).to.equal(2) const aPeerId = psA.peerId.toString() @@ -76,8 +78,12 @@ export default (common: TestSetup) => { expectSet(psB.subscriptions, [topic]) await Promise.all([ - new Promise((resolve) => psA.once('pubsub:subscription-change', resolve)), - new Promise((resolve) => psC.once('pubsub:subscription-change', resolve)) + new Promise((resolve) => psA.addEventListener('pubsub:subscription-change', resolve, { + once: true + })), + new Promise((resolve) => psC.addEventListener('pubsub:subscription-change', resolve, { + once: true + })) ]) expect(psA.peers.size).to.equal(1) @@ -94,12 +100,14 @@ export default (common: TestSetup) => { psC.subscribe(topic) expectSet(psC.subscriptions, [topic]) - psB.once('pubsub:subscription-change', () => { + psB.addEventListener('pubsub:subscription-change', () => { expect(psA.peers.size).to.equal(1) expect(psB.peers.size).to.equal(2) expectSet(psB.topics.get(topic), [psC.peerId.toString()]) defer.resolve() + }, { + once: true }) return await defer.promise @@ -115,9 +123,15 @@ export default (common: TestSetup) => { // await subscription change await Promise.all([ - new Promise(resolve => psA.once('pubsub:subscription-change', () => resolve(null))), - new Promise(resolve => psB.once('pubsub:subscription-change', () => resolve(null))), - new Promise(resolve => psC.once('pubsub:subscription-change', () => resolve(null))) + new Promise(resolve => psA.addEventListener('pubsub:subscription-change', () => resolve(null), { + once: true + })), + new Promise(resolve => psB.addEventListener('pubsub:subscription-change', () => resolve(null), { + once: true + })), + new Promise(resolve => psC.addEventListener('pubsub:subscription-change', () => resolve(null), { + once: true + })) ]) // await a cycle @@ -125,22 +139,23 @@ export default (common: TestSetup) => { let counter = 0 - psA.on(topic, incMsg) - psB.on(topic, incMsg) - psC.on(topic, incMsg) + psA.addEventListener(topic, incMsg) + psB.addEventListener(topic, incMsg) + psC.addEventListener(topic, incMsg) void psA.publish(topic, uint8ArrayFromString('hey')) - function incMsg (msg: Message) { + function incMsg (evt: CustomEvent) { + const msg = evt.detail expect(uint8ArrayToString(msg.data)).to.equal('hey') check() } function check () { if (++counter === 3) { - psA.removeListener(topic, incMsg) - psB.removeListener(topic, incMsg) - psC.removeListener(topic, incMsg) + psA.removeEventListener(topic, incMsg) + psB.removeEventListener(topic, incMsg) + psC.removeEventListener(topic, incMsg) defer.resolve() } } @@ -168,30 +183,37 @@ export default (common: TestSetup) => { // await subscription change await Promise.all([ - new Promise(resolve => psA.once('pubsub:subscription-change', () => resolve(null))), - new Promise(resolve => psB.once('pubsub:subscription-change', () => resolve(null))), - new Promise(resolve => psC.once('pubsub:subscription-change', () => resolve(null))) + new Promise(resolve => psA.addEventListener('pubsub:subscription-change', () => resolve(null), { + once: true + })), + new Promise(resolve => psB.addEventListener('pubsub:subscription-change', () => resolve(null), { + once: true + })), + new Promise(resolve => psC.addEventListener('pubsub:subscription-change', () => resolve(null), { + once: true + })) ]) - psA.on(topic, incMsg) - psB.on(topic, incMsg) - psC.on(topic, incMsg) + psA.addEventListener(topic, incMsg) + psB.addEventListener(topic, incMsg) + psC.addEventListener(topic, incMsg) // await a cycle await delay(1000) void psB.publish(topic, uint8ArrayFromString('hey')) - function incMsg (msg: Message) { + function incMsg (evt: CustomEvent) { + const msg = evt.detail expect(uint8ArrayToString(msg.data)).to.equal('hey') check() } function check () { if (++counter === 3) { - psA.removeListener(topic, incMsg) - psB.removeListener(topic, incMsg) - psC.removeListener(topic, incMsg) + psA.removeEventListener(topic, incMsg) + psB.removeEventListener(topic, incMsg) + psC.removeEventListener(topic, incMsg) defer.resolve() } } @@ -209,11 +231,11 @@ export default (common: TestSetup) => { // │b d│ // ◉─┘ └─◉ // a - let psA: PubSub & Startable - let psB: PubSub & Startable - let psC: PubSub & Startable - let psD: PubSub & Startable - let psE: PubSub & Startable + let psA: PubSub + let psB: PubSub + let psC: PubSub + let psD: PubSub + let psE: PubSub // Create and start pubsub nodes beforeEach(async () => { @@ -271,22 +293,22 @@ export default (common: TestSetup) => { let counter = 0 psA.subscribe('Z') - psA.on('Z', incMsg) + psA.addEventListener('Z', incMsg) psB.subscribe('Z') - psB.on('Z', incMsg) + psB.addEventListener('Z', incMsg) psC.subscribe('Z') - psC.on('Z', incMsg) + psC.addEventListener('Z', incMsg) psD.subscribe('Z') - psD.on('Z', incMsg) + psD.addEventListener('Z', incMsg) psE.subscribe('Z') - psE.on('Z', incMsg) + psE.addEventListener('Z', incMsg) await Promise.all([ - new Promise((resolve) => psA.once('pubsub:subscription-change', resolve)), - new Promise((resolve) => psB.once('pubsub:subscription-change', resolve)), - new Promise((resolve) => psC.once('pubsub:subscription-change', resolve)), - new Promise((resolve) => psD.once('pubsub:subscription-change', resolve)), - new Promise((resolve) => psE.once('pubsub:subscription-change', resolve)) + new Promise((resolve) => psA.addEventListener('pubsub:subscription-change', resolve)), + new Promise((resolve) => psB.addEventListener('pubsub:subscription-change', resolve)), + new Promise((resolve) => psC.addEventListener('pubsub:subscription-change', resolve)), + new Promise((resolve) => psD.addEventListener('pubsub:subscription-change', resolve)), + new Promise((resolve) => psE.addEventListener('pubsub:subscription-change', resolve)) ]) // await a cycle @@ -294,7 +316,8 @@ export default (common: TestSetup) => { void psC.publish('Z', uint8ArrayFromString('hey from c')) - function incMsg (msg: Message) { + function incMsg (evt: CustomEvent) { + const msg = evt.detail expect(uint8ArrayToString(msg.data)).to.equal('hey from c') check() } diff --git a/packages/libp2p-interface-compliance-tests/src/pubsub/two-nodes.ts b/packages/libp2p-interface-compliance-tests/src/pubsub/two-nodes.ts index 39779a1df..b69fc438f 100644 --- a/packages/libp2p-interface-compliance-tests/src/pubsub/two-nodes.ts +++ b/packages/libp2p-interface-compliance-tests/src/pubsub/two-nodes.ts @@ -11,7 +11,7 @@ import { first, expectSet } from './utils.js' -import type { Startable } from '@libp2p/interfaces' +import type { EventMap } from './index.js' const topic = 'foo' @@ -19,10 +19,10 @@ function shouldNotHappen () { expect.fail() } -export default (common: TestSetup) => { +export default (common: TestSetup>) => { describe('pubsub with two nodes', () => { - let psA: PubSub & Startable - let psB: PubSub & Startable + let psA: PubSub + let psB: PubSub // Create pubsub nodes and connect them before(async () => { @@ -55,7 +55,8 @@ export default (common: TestSetup) => { it('Subscribe to a topic in nodeA', async () => { const defer = pDefer() - psB.once('pubsub:subscription-change', ({ peerId: changedPeerId, subscriptions: changedSubs }) => { + psB.addEventListener('pubsub:subscription-change', (evt) => { + const { peerId: changedPeerId, subscriptions: changedSubs } = evt.detail expectSet(psA.subscriptions, [topic]) expect(psB.peers.size).to.equal(1) expectSet(psB.topics.get(topic), [psA.peerId.toString()]) @@ -64,6 +65,8 @@ export default (common: TestSetup) => { expect(changedSubs[0].topicID).to.equal(topic) expect(changedSubs[0].subscribe).to.equal(true) defer.resolve() + }, { + once: true }) psA.subscribe(topic) @@ -73,13 +76,18 @@ export default (common: TestSetup) => { it('Publish to a topic in nodeA', async () => { const defer = pDefer() - psA.once(topic, (msg) => { + psA.addEventListener(topic, (evt) => { + const msg = evt.detail expect(uint8ArrayToString(msg.data)).to.equal('hey') - psB.removeListener(topic, shouldNotHappen) + psB.removeEventListener(topic, shouldNotHappen) defer.resolve() + }, { + once: true }) - psB.once(topic, shouldNotHappen) + psB.addEventListener(topic, shouldNotHappen, { + once: true + }) void psA.publish(topic, uint8ArrayFromString('hey')) @@ -89,19 +97,26 @@ export default (common: TestSetup) => { it('Publish to a topic in nodeB', async () => { const defer = pDefer() - psA.once(topic, (msg) => { - psA.once(topic, shouldNotHappen) + psA.addEventListener(topic, (evt) => { + const msg = evt.detail + psA.addEventListener(topic, shouldNotHappen, { + once: true + }) expect(uint8ArrayToString(msg.data)).to.equal('banana') setTimeout(() => { - psA.removeListener(topic, shouldNotHappen) - psB.removeListener(topic, shouldNotHappen) + psA.removeEventListener(topic, shouldNotHappen) + psB.removeEventListener(topic, shouldNotHappen) defer.resolve() }, 100) + }, { + once: true }) - psB.once(topic, shouldNotHappen) + psB.addEventListener(topic, shouldNotHappen, { + once: true + }) void psB.publish(topic, uint8ArrayFromString('banana')) @@ -112,18 +127,21 @@ export default (common: TestSetup) => { const defer = pDefer() let counter = 0 - psB.once(topic, shouldNotHappen) - psA.on(topic, receivedMsg) + psB.addEventListener(topic, shouldNotHappen, { + once: true + }) + psA.addEventListener(topic, receivedMsg) - function receivedMsg (msg: Message) { + function receivedMsg (evt: CustomEvent) { + const msg = evt.detail expect(uint8ArrayToString(msg.data)).to.equal('banana') expect(msg.from).to.be.eql(psB.peerId.toString()) expect(msg.seqno).to.be.a('Uint8Array') expect(msg.topicIDs).to.be.eql([topic]) if (++counter === 10) { - psA.removeListener(topic, receivedMsg) - psB.removeListener(topic, shouldNotHappen) + psA.removeEventListener(topic, receivedMsg) + psB.removeEventListener(topic, shouldNotHappen) defer.resolve() } @@ -140,7 +158,8 @@ export default (common: TestSetup) => { psA.unsubscribe(topic) expect(psA.subscriptions.size).to.equal(0) - psB.once('pubsub:subscription-change', ({ peerId: changedPeerId, subscriptions: changedSubs }) => { + psB.addEventListener('pubsub:subscription-change', (evt) => { + const { peerId: changedPeerId, subscriptions: changedSubs } = evt.detail expect(psB.peers.size).to.equal(1) expectSet(psB.topics.get(topic), []) expect(changedPeerId.toString()).to.equal(first(psB.peers).id.toString()) @@ -149,6 +168,8 @@ export default (common: TestSetup) => { expect(changedSubs[0].subscribe).to.equal(false) defer.resolve() + }, { + once: true }) return await defer.promise @@ -157,12 +178,16 @@ export default (common: TestSetup) => { it('Publish to a topic:Z in nodeA nodeB', async () => { const defer = pDefer() - psA.once('Z', shouldNotHappen) - psB.once('Z', shouldNotHappen) + psA.addEventListener('Z', shouldNotHappen, { + once: true + }) + psB.addEventListener('Z', shouldNotHappen, { + once: true + }) setTimeout(() => { - psA.removeListener('Z', shouldNotHappen) - psB.removeListener('Z', shouldNotHappen) + psA.removeEventListener('Z', shouldNotHappen) + psB.removeEventListener('Z', shouldNotHappen) defer.resolve() }, 100) diff --git a/packages/libp2p-interface-compliance-tests/src/transport/listen-test.ts b/packages/libp2p-interface-compliance-tests/src/transport/listen-test.ts index 7ba8ad05a..d94dfdf53 100644 --- a/packages/libp2p-interface-compliance-tests/src/transport/listen-test.ts +++ b/packages/libp2p-interface-compliance-tests/src/transport/listen-test.ts @@ -6,6 +6,7 @@ import { pipe } from 'it-pipe' import { fromString as uint8ArrayFromString } from 'uint8arrays/from-string' import { isValidTick, mockUpgrader } from './utils/index.js' import defer from 'p-defer' +import { CustomEvent } from '@libp2p/interfaces' import type { TestSetup } from '../index.js' import type { Transport } from '@libp2p/interfaces/transport' import type { TransportTestFixtures, SetupArgs } from './index.js' @@ -108,8 +109,8 @@ export default (common: TestSetup) => { const deferred = defer() let conn - listener.on('connection', (c) => { - conn = c + listener.addEventListener('connection', (evt) => { + conn = evt.detail deferred.resolve() }) @@ -127,7 +128,7 @@ export default (common: TestSetup) => { it('listening', (done) => { const listener = transport.createListener() - listener.on('listening', () => { + listener.addEventListener('listening', () => { listener.close().then(done, done) }) void listener.listen(addrs[0]) @@ -135,16 +136,18 @@ export default (common: TestSetup) => { it('error', (done) => { const listener = transport.createListener() - listener.on('error', (err) => { - expect(err).to.exist() + listener.addEventListener('error', (evt) => { + expect(evt.detail).to.be.an.instanceOf(Error) listener.close().then(done, done) }) - listener.emit('error', new Error('my err')) + listener.dispatchEvent(new CustomEvent('error', { + detail: new Error('my err') + })) }) it('close', (done) => { const listener = transport.createListener() - listener.on('close', done) + listener.addEventListener('close', done) void (async () => { await listener.listen(addrs[0]) diff --git a/packages/libp2p-interface-compliance-tests/src/utils/mock-connection-manager.ts b/packages/libp2p-interface-compliance-tests/src/utils/mock-connection-manager.ts new file mode 100644 index 000000000..b1acf41fc --- /dev/null +++ b/packages/libp2p-interface-compliance-tests/src/utils/mock-connection-manager.ts @@ -0,0 +1,30 @@ +import { EventEmitter } from '@libp2p/interfaces' +import type { Connection } from '@libp2p/interfaces/src/connection' +import type { PeerId } from '@libp2p/interfaces/src/peer-id' +import type { ConnectionManager, ConnectionManagerEvents } from '@libp2p/interfaces/src/registrar' + +class MockConnectionManager extends EventEmitter implements ConnectionManager { + getConnection (peerId: PeerId): Connection | undefined { + throw new Error('Method not implemented.') + } + + listenerCount (type: string): number { + throw new Error('Method not implemented.') + } + + addEventListener(type: U, callback: ((evt: ConnectionManagerEvents[U]) => void) | { handleEvent: (evt: ConnectionManagerEvents[U]) => void } | null, options?: boolean | AddEventListenerOptions): void { + throw new Error('Method not implemented.') + } + + removeEventListener(type: U, callback: (((evt: ConnectionManagerEvents[U]) => void) | { handleEvent: (evt: ConnectionManagerEvents[U]) => void } | null) | undefined, options?: boolean | EventListenerOptions): void { + throw new Error('Method not implemented.') + } + + dispatchEvent (event: Event): boolean { + throw new Error('Method not implemented.') + } +} + +export function mockConnectionManager () { + return new MockConnectionManager() +} diff --git a/packages/libp2p-interface-compliance-tests/src/utils/mock-registrar.ts b/packages/libp2p-interface-compliance-tests/src/utils/mock-registrar.ts new file mode 100644 index 000000000..e6a162a9b --- /dev/null +++ b/packages/libp2p-interface-compliance-tests/src/utils/mock-registrar.ts @@ -0,0 +1,49 @@ +import type { IncomingStreamEvent, Registrar } from '@libp2p/interfaces/registrar' +import type { PeerId } from '@libp2p/interfaces/peer-id' +import type { Connection } from '@libp2p/interfaces/connection' + +class MockRegistrar implements Registrar { + private readonly registrarRecord: Map> = new Map() + + handle (multicodecs: string | string[], handler: (event: IncomingStreamEvent) => void) { + if (!Array.isArray(multicodecs)) { + multicodecs = [multicodecs] + } + + const rec = this.registrarRecord.get(multicodecs[0]) ?? {} + + this.registrarRecord.set(multicodecs[0], { + ...rec, + handler + }) + } + + unhandle (multicodec: string) { + this.registrarRecord.delete(multicodec) + } + + register (topology: any) { + const { multicodecs } = topology + const rec = this.registrarRecord.get(multicodecs[0]) ?? {} + + this.registrarRecord.set(multicodecs[0], { + ...rec, + onConnect: topology._onConnect, + onDisconnect: topology._onDisconnect + }) + + return multicodecs[0] + } + + unregister (id: string) { + this.registrarRecord.delete(id) + } + + getConnection (peerId: PeerId): Connection | undefined { + throw new Error('Not implemented') + } +} + +export function mockRegistrar () { + return new MockRegistrar() +} diff --git a/packages/libp2p-interface-compliance-tests/test/peer-discovery/mock-discovery.ts b/packages/libp2p-interface-compliance-tests/test/peer-discovery/mock-discovery.ts index 3fcdf601d..b83879950 100644 --- a/packages/libp2p-interface-compliance-tests/test/peer-discovery/mock-discovery.ts +++ b/packages/libp2p-interface-compliance-tests/test/peer-discovery/mock-discovery.ts @@ -1,6 +1,7 @@ -import { EventEmitter } from 'events' import { Multiaddr } from '@multiformats/multiaddr' import * as PeerIdFactory from '@libp2p/peer-id-factory' +import { EventEmitter, CustomEvent } from '@libp2p/interfaces' +import type { PeerDiscovery, PeerDiscoveryEvents } from '@libp2p/interfaces/peer-discovery' interface MockDiscoveryOptions { discoveryDelay?: number @@ -9,7 +10,7 @@ interface MockDiscoveryOptions { /** * Emits 'peer' events on discovery. */ -export class MockDiscovery extends EventEmitter { +export class MockDiscovery extends EventEmitter implements PeerDiscovery { public readonly options: MockDiscoveryOptions private _isRunning: boolean private _timer: any @@ -41,10 +42,13 @@ export class MockDiscovery extends EventEmitter { PeerIdFactory.createEd25519PeerId() .then(peerId => { this._timer = setTimeout(() => { - this.emit('peer', { - id: peerId, - multiaddrs: [new Multiaddr('/ip4/127.0.0.1/tcp/8000')] - }) + this.dispatchEvent(new CustomEvent('peer', { + detail: { + id: peerId, + multiaddrs: [new Multiaddr('/ip4/127.0.0.1/tcp/8000')], + protocols: [] + } + })) }, this.options.discoveryDelay ?? 1000) }) .catch(() => {}) diff --git a/packages/libp2p-interfaces/package.json b/packages/libp2p-interfaces/package.json index e3b20affb..d277be0c2 100644 --- a/packages/libp2p-interfaces/package.json +++ b/packages/libp2p-interfaces/package.json @@ -214,7 +214,7 @@ }, "scripts": { "lint": "aegir lint", - "dep-check": "aegir dep-check dist/src/**/*.js", + "dep-check": "aegir dep-check dist/src/**/*.js dist/test/**/*.js", "build": "tsc" }, "dependencies": { diff --git a/packages/libp2p-interfaces/src/index.ts b/packages/libp2p-interfaces/src/index.ts index 52c92549d..6acffaaaf 100644 --- a/packages/libp2p-interfaces/src/index.ts +++ b/packages/libp2p-interfaces/src/index.ts @@ -21,3 +21,66 @@ export interface Dialer { export interface Addressable { multiaddrs: Multiaddr[] } + +interface EventCallback { (evt: EventType): void } +type EventHandler = EventCallback | ({ handleEvent: EventCallback }) | null + +/** + * Adds types to the EventTarget class. Hopefully this won't be necessary forever. + * + * https://github.com/microsoft/TypeScript/issues/28357 + * https://github.com/microsoft/TypeScript/issues/43477 + * https://github.com/microsoft/TypeScript/issues/299 + * etc + */ +export class EventEmitter extends EventTarget { + #listeners: Map = new Map() + + listenerCount (type: string) { + return this.#listeners.get(type) ?? 0 + } + + // @ts-expect-error EventTarget is not typed + addEventListener (type: U, callback: EventHandler, options?: AddEventListenerOptions | boolean) { + // @ts-expect-error EventTarget is not typed + super.addEventListener(type, callback) + + const count = this.#listeners.get(type) ?? 0 + + this.#listeners.set(type, count + 1) + } + + // @ts-expect-error EventTarget is not typed + removeEventListener (type: U, callback: EventHandler | undefined, options?: EventListenerOptions | boolean) { + // @ts-expect-error EventTarget is not typed + super.removeEventListener(type, callback) + + const count = this.#listeners.get(type) ?? 0 + + if (count === 1) { + this.#listeners.delete(type) + } else { + this.#listeners.set(type, count - 1) + } + } +} + +/** + * CustomEvent is a standard event but it's not supported by node. + * + * Remove this when https://github.com/nodejs/node/issues/40678 is closed. + * + * Ref: https://developer.mozilla.org/en-US/docs/Web/API/CustomEvent + */ +class CustomEventPolyfill extends Event { + /** Returns any custom data event was created with. Typically used for synthetic events. */ + public detail: T + + constructor (message: string, data?: EventInit & { detail: T }) { + super(message, data) + // @ts-expect-error could be undefined + this.detail = data?.detail + } +} + +export const CustomEvent = globalThis.CustomEvent ?? CustomEventPolyfill diff --git a/packages/libp2p-interfaces/src/peer-discovery/index.ts b/packages/libp2p-interfaces/src/peer-discovery/index.ts index a560113a0..1a229f549 100644 --- a/packages/libp2p-interfaces/src/peer-discovery/index.ts +++ b/packages/libp2p-interfaces/src/peer-discovery/index.ts @@ -1,26 +1,15 @@ -import type { EventEmitter } from 'events' import type { PeerData } from '../peer-data/index.js' -import type { Startable } from '../index.js' +import type { EventEmitter, Startable } from '../index.js' export interface PeerDiscoveryFactory { new (options?: any): PeerDiscovery tag: string } -interface PeerDiscoveryEvents { - 'peer': PeerData +export interface PeerDiscoveryEvents { + 'peer': CustomEvent } -export interface PeerDiscovery extends EventEmitter, Startable { +export interface PeerDiscovery extends EventEmitter, Startable { - on: ( (event: U, listener: (event: PeerDiscoveryEvents[U]) => void) => this) & - ((event: string, listener: (...args: any[]) => void) => this) - - once: ( (event: U, listener: (event: PeerDiscoveryEvents[U]) => void) => this) & - ((event: string, listener: (...args: any[]) => void) => this) - - emit: ( (name: U, event: PeerDiscoveryEvents[U]) => boolean) & - ((name: string, ...args: any[]) => boolean) } - -export default PeerDiscovery diff --git a/packages/libp2p-interfaces/src/peer-store/index.ts b/packages/libp2p-interfaces/src/peer-store/index.ts index 116ad1708..3c6fb9b2f 100644 --- a/packages/libp2p-interfaces/src/peer-store/index.ts +++ b/packages/libp2p-interfaces/src/peer-store/index.ts @@ -1,6 +1,6 @@ import type { PeerId } from '../peer-id/index.js' import type { Multiaddr } from '@multiformats/multiaddr' -import type { EventEmitter } from 'events' +import type { EventEmitter } from '../index.js' import type { Envelope } from '../record/index.js' export interface Address { @@ -182,22 +182,26 @@ export interface ProtoBook extends Book { remove: (peerId: PeerId, protocols: string[]) => Promise } -export interface PeerProtocolsChangeEvent { +export interface PeerData { + peerId: PeerId +} + +export interface PeerProtocolsChangeData { peerId: PeerId protocols: string[] } -export interface PeerMultiaddrsChangeEvent { +export interface PeerMultiaddrsChangeData { peerId: PeerId multiaddrs: Multiaddr[] } -export interface PeerPublicKeyChangeEvent { +export interface PeerPublicKeyChangeData { peerId: PeerId pubKey?: Uint8Array } -export interface PeerMetadataChangeEvent { +export interface PeerMetadataChangeData { peerId: PeerId metadata: Map } @@ -205,14 +209,14 @@ export interface PeerMetadataChangeEvent { export type EventName = 'peer' | 'change:protocols' | 'change:multiaddrs' | 'change:pubkey' | 'change:metadata' export interface PeerStoreEvents { - 'peer': (event: PeerId) => void - 'change:protocols': (event: PeerProtocolsChangeEvent) => void - 'change:multiaddrs': (event: PeerMultiaddrsChangeEvent) => void - 'change:pubkey': (event: PeerPublicKeyChangeEvent) => void - 'change:metadata': (event: PeerMetadataChangeEvent) => void + 'peer': CustomEvent + 'change:protocols': CustomEvent + 'change:multiaddrs': CustomEvent + 'change:pubkey': CustomEvent + 'change:metadata': CustomEvent } -export interface PeerStore extends EventEmitter { +export interface PeerStore extends EventEmitter { addressBook: AddressBook keyBook: KeyBook metadataBook: MetadataBook @@ -222,13 +226,4 @@ export interface PeerStore extends EventEmitter { delete: (peerId: PeerId) => Promise has: (peerId: PeerId) => Promise get: (peerId: PeerId) => Promise - on: ( - event: U, listener: PeerStoreEvents[U] - ) => this - once: ( - event: U, listener: PeerStoreEvents[U] - ) => this - emit: ( - event: U, ...args: Parameters - ) => boolean } diff --git a/packages/libp2p-interfaces/src/pubsub/index.ts b/packages/libp2p-interfaces/src/pubsub/index.ts index df81b7301..025452b4c 100644 --- a/packages/libp2p-interfaces/src/pubsub/index.ts +++ b/packages/libp2p-interfaces/src/pubsub/index.ts @@ -1,7 +1,7 @@ import type { PeerId } from '../peer-id' import type { Pushable } from 'it-pushable' import type { Registrar } from '../registrar' -import type { EventEmitter } from 'events' +import type { EventEmitter, Startable } from '../index.js' /** * On the producing side: @@ -73,16 +73,16 @@ interface Subscription { subscribe: boolean } -interface SubscriptionChangeEvent { +interface SubscriptionChangeData { peerId: PeerId subscriptions: Subscription[] } -interface PubSubEvents { - 'pubsub:subscription-change': SubscriptionChangeEvent +export interface PubsubEvents { + 'pubsub:subscription-change': CustomEvent } -export interface PubSub extends EventEmitter { +export interface PubSub extends EventEmitter, Startable { peerId: PeerId started: boolean peers: Map @@ -97,13 +97,10 @@ export interface PubSub extends EventEmitter { unsubscribe: (topic: string) => void publish: (topic: string, data: Uint8Array) => Promise validate: (message: Message) => Promise +} - on: ( (event: U, listener: (event: PubSubEvents[U]) => void) => this) & - ((event: string, listener: (event: Message) => void) => this) - - once: ( (event: U, listener: (event: PubSubEvents[U]) => void) => this) & - ((event: string, listener: (event: Message) => void) => this) - - emit: ( (name: U, event: PubSubEvents[U]) => boolean) & - ((name: string, event: Message) => boolean) +export interface PeerStreamEvents { + 'stream:inbound': CustomEvent + 'stream:outbound': CustomEvent + 'close': CustomEvent } diff --git a/packages/libp2p-interfaces/src/registrar/index.ts b/packages/libp2p-interfaces/src/registrar/index.ts index c44a6b5a3..70274f534 100644 --- a/packages/libp2p-interfaces/src/registrar/index.ts +++ b/packages/libp2p-interfaces/src/registrar/index.ts @@ -1,6 +1,6 @@ -import type { Connection, Stream } from '../connection' -import type { PeerId } from '../peer-id' -import type { PeerStore } from '../peer-store' +import type { EventEmitter } from '../index.js' +import type { Connection, Stream } from '../connection/index.js' +import type { PeerId } from '../peer-id/index.js' export interface IncomingStreamEvent { protocol: string @@ -8,16 +8,17 @@ export interface IncomingStreamEvent { connection: Connection } +export interface ConnectionManagerEvents { + 'peer:connect': CustomEvent +} + +export interface ConnectionManager extends EventEmitter { + getConnection: (peerId: PeerId) => Connection | undefined +} + export interface Registrar { handle: (multicodec: string | string[], handler: (event: IncomingStreamEvent) => void) => void unhandle: (multicodec: string) => void - register: (topology: any) => string unregister: (id: string) => void - getConnection: (peerId: PeerId) => Connection | undefined - peerStore: PeerStore - - connectionManager: { - on: (event: 'peer:connect', handler: (connection: Connection) => void) => void - } } diff --git a/packages/libp2p-interfaces/src/topology/index.ts b/packages/libp2p-interfaces/src/topology/index.ts index af0bbca33..5730c1396 100644 --- a/packages/libp2p-interfaces/src/topology/index.ts +++ b/packages/libp2p-interfaces/src/topology/index.ts @@ -1,5 +1,7 @@ -import type { PeerId } from '../peer-id' -import type { Connection } from '../connection' +import type { PeerId } from '../peer-id/index.js' +import type { Connection } from '../connection/index.js' +import type { ConnectionManager } from '../registrar/index.js' +import type { PeerStore } from '../peer-store/index.js' export interface onConnectHandler { (peerId: PeerId, conn: Connection): void } export interface onDisconnectHandler { (peerId: PeerId, conn?: Connection): void } @@ -32,6 +34,8 @@ export interface Topology { export interface MulticodecTopologyOptions extends TopologyOptions { multicodecs: string[] + peerStore: PeerStore + connectionManager: ConnectionManager } export interface MulticodecTopology extends Topology { diff --git a/packages/libp2p-interfaces/src/transport/index.ts b/packages/libp2p-interfaces/src/transport/index.ts index 01a063964..111e22973 100644 --- a/packages/libp2p-interfaces/src/transport/index.ts +++ b/packages/libp2p-interfaces/src/transport/index.ts @@ -1,7 +1,6 @@ -import type { EventEmitter } from 'events' +import type { EventEmitter, AbortOptions } from '../index.js' import type { Multiaddr } from '@multiformats/multiaddr' import type { Connection } from '../connection/index.js' -import type { AbortOptions } from '../index.js' import type { Duplex } from 'it-stream-types' export interface TransportFactory { @@ -34,7 +33,14 @@ export interface Transport + 'listening': CustomEvent + 'error': CustomEvent + 'close': CustomEvent +} + +export interface Listener extends EventEmitter { /** * Start a listener */ diff --git a/packages/libp2p-logger/package.json b/packages/libp2p-logger/package.json index de4e3aaec..346528e26 100644 --- a/packages/libp2p-logger/package.json +++ b/packages/libp2p-logger/package.json @@ -120,10 +120,10 @@ }, "scripts": { "lint": "aegir lint", - "dep-check": "aegir dep-check dist/src/**/*.js", + "dep-check": "aegir dep-check dist/src/**/*.js dist/test/**/*.js", "build": "tsc", "pretest": "npm run build", - "test": "aegir test -f ./dist/test/**/*.js", + "test": "aegir test -f ./dist/test/*.js -f ./dist/test/**/*.js", "test:chrome": "npm run test -- -t browser", "test:chrome-webworker": "npm run test -- -t webworker", "test:firefox": "npm run test -- -t browser -- --browser firefox", diff --git a/packages/libp2p-peer-id-factory/package.json b/packages/libp2p-peer-id-factory/package.json index 0c34b92bc..e04ba8bfd 100644 --- a/packages/libp2p-peer-id-factory/package.json +++ b/packages/libp2p-peer-id-factory/package.json @@ -124,10 +124,10 @@ }, "scripts": { "lint": "aegir lint", - "dep-check": "aegir dep-check dist/src/**/*.js", + "dep-check": "aegir dep-check dist/src/**/*.js dist/test/**/*.js", "build": "tsc", "pretest": "npm run build", - "test": "aegir test -f ./dist/test/**/*.js", + "test": "aegir test -f ./dist/test/*.js -f ./dist/test/**/*.js", "test:chrome": "npm run test -- -t browser", "test:chrome-webworker": "npm run test -- -t webworker", "test:firefox": "npm run test -- -t browser -- --browser firefox", diff --git a/packages/libp2p-peer-id/package.json b/packages/libp2p-peer-id/package.json index 572dddcf1..bd29663c2 100644 --- a/packages/libp2p-peer-id/package.json +++ b/packages/libp2p-peer-id/package.json @@ -120,10 +120,10 @@ }, "scripts": { "lint": "aegir lint", - "dep-check": "aegir dep-check dist/src/**/*.js", + "dep-check": "aegir dep-check dist/src/**/*.js dist/test/**/*.js", "build": "tsc", "pretest": "npm run build", - "test": "aegir test -f ./dist/test/**/*.js", + "test": "aegir test -f ./dist/test/*.js -f ./dist/test/**/*.js", "test:chrome": "npm run test -- -t browser", "test:chrome-webworker": "npm run test -- -t webworker", "test:firefox": "npm run test -- -t browser -- --browser firefox", diff --git a/packages/libp2p-peer-record/package.json b/packages/libp2p-peer-record/package.json index 37f836ca7..0f31d2a8e 100644 --- a/packages/libp2p-peer-record/package.json +++ b/packages/libp2p-peer-record/package.json @@ -126,7 +126,7 @@ }, "scripts": { "lint": "aegir lint", - "dep-check": "aegir dep-check dist/src/**/*.js", + "dep-check": "aegir dep-check dist/src/**/*.js dist/test/**/*.js", "build": "tsc", "postbuild": "npm run build:copy-proto-files", "generate": "npm run generate:envelope && npm run generate:envelope-types && npm run generate:peer-record && npm run generate:peer-record-types", @@ -136,7 +136,7 @@ "generate:peer-record-types": "pbts -o src/peer-record/peer-record.d.ts src/peer-record/peer-record.js", "build:copy-proto-files": "cp src/envelope/envelope.js dist/src/envelope && cp src/envelope/*.d.ts dist/src/envelope && cp src/peer-record/peer-record.js dist/src/peer-record && cp src/peer-record/*.d.ts dist/src/peer-record", "pretest": "npm run build", - "test": "aegir test -f ./dist/test", + "test": "aegir test -f ./dist/test/*.js -f ./dist/test/**/*.js", "test:chrome": "npm run test -- -t browser", "test:chrome-webworker": "npm run test -- -t webworker", "test:firefox": "npm run test -- -t browser -- --browser firefox", diff --git a/packages/libp2p-peer-store/package.json b/packages/libp2p-peer-store/package.json index 3deeea2ea..8d7e36633 100644 --- a/packages/libp2p-peer-store/package.json +++ b/packages/libp2p-peer-store/package.json @@ -124,7 +124,7 @@ }, "scripts": { "lint": "aegir lint", - "dep-check": "aegir dep-check dist/src/**/*.js", + "dep-check": "aegir dep-check dist/src/**/*.js dist/test/**/*.js", "build": "tsc", "postbuild": "npm run build:copy-proto-files", "generate": "npm run generate:proto && npm run generate:proto-types && tsc", @@ -132,7 +132,7 @@ "generate:proto-types": "pbts -o src/pb/peer.d.ts src/pb/peer.js", "build:copy-proto-files": "mkdirp dist/src/pb && cp src/pb/*.js dist/src/pb && cp src/pb/*.d.ts dist/src/pb", "pretest": "npm run build", - "test": "aegir test -f ./dist/test", + "test": "aegir test -f ./dist/test/*.js -f ./dist/test/**/*.js", "test:chrome": "npm run test -- -t browser", "test:chrome-webworker": "npm run test -- -t webworker", "test:firefox": "npm run test -- -t browser -- --browser firefox", @@ -156,8 +156,16 @@ "protobufjs": "^6.10.2" }, "devDependencies": { + "@libp2p/interface-compliance-tests": "^1.1.0", + "@libp2p/peer-id": "^1.1.0", + "@libp2p/peer-id-factory": "^1.0.3", + "@libp2p/utils": "^1.0.5", "aegir": "^36.1.3", "datastore-core": "^7.0.1", - "sinon": "^13.0.1" + "err-code": "^3.0.1", + "p-defer": "^4.0.0", + "p-wait-for": "^4.1.0", + "sinon": "^13.0.1", + "uint8arrays": "^3.0.0" } } diff --git a/packages/libp2p-peer-store/src/address-book.ts b/packages/libp2p-peer-store/src/address-book.ts index a0723d2c5..9be6313e9 100644 --- a/packages/libp2p-peer-store/src/address-book.ts +++ b/packages/libp2p-peer-store/src/address-book.ts @@ -10,6 +10,7 @@ import map from 'it-map' import each from 'it-foreach' import { base58btc } from 'multiformats/bases/base58' import { PeerId } from '@libp2p/peer-id' +import { CustomEvent } from '@libp2p/interfaces' import type { PeerStore } from '@libp2p/interfaces/peer-store' import type { Store } from './store.js' import type { AddressFilter, AddressSorter } from './index.js' @@ -23,12 +24,12 @@ async function allowAll () { } export class PeerStoreAddressBook { - private readonly emit: PeerStore['emit'] + private readonly dispatchEvent: PeerStore['dispatchEvent'] private readonly store: Store private readonly addressFilter: AddressFilter - constructor (emit: PeerStore['emit'], store: Store, addressFilter?: AddressFilter) { - this.emit = emit + constructor (dispatchEvent: PeerStore['dispatchEvent'], store: Store, addressFilter?: AddressFilter) { + this.dispatchEvent = dispatchEvent this.store = store this.addressFilter = addressFilter ?? allowAll } @@ -96,7 +97,9 @@ export class PeerStoreAddressBook { release() } - this.emit(EVENT_NAME, { peerId, multiaddrs: updatedPeer.addresses.map(({ multiaddr }) => multiaddr) }) + this.dispatchEvent(new CustomEvent(EVENT_NAME, { + detail: { peerId, multiaddrs: updatedPeer.addresses.map(({ multiaddr }) => multiaddr) } + })) return true } @@ -205,11 +208,17 @@ export class PeerStoreAddressBook { release() } - this.emit(EVENT_NAME, { peerId, multiaddrs: updatedPeer.addresses.map(addr => addr.multiaddr) }) + this.dispatchEvent(new CustomEvent(EVENT_NAME, { + detail: { peerId, multiaddrs: updatedPeer.addresses.map(addr => addr.multiaddr) } + })) // Notify the existence of a new peer if (!hasPeer) { - this.emit('peer', peerId) + this.dispatchEvent(new CustomEvent('peer', { + detail: { + peerId + } + })) } } @@ -260,11 +269,15 @@ export class PeerStoreAddressBook { release() } - this.emit(EVENT_NAME, { peerId, multiaddrs: updatedPeer.addresses.map(addr => addr.multiaddr) }) + this.dispatchEvent(new CustomEvent(EVENT_NAME, { + detail: { peerId, multiaddrs: updatedPeer.addresses.map(addr => addr.multiaddr) } + })) // Notify the existence of a new peer if (hasPeer === true) { - this.emit('peer', peerId) + this.dispatchEvent(new CustomEvent('peer', { + detail: { peerId } + })) } } @@ -289,7 +302,9 @@ export class PeerStoreAddressBook { } if (has) { - this.emit(EVENT_NAME, { peerId, multiaddrs: [] }) + this.dispatchEvent(new CustomEvent(EVENT_NAME, { + detail: { peerId, multiaddrs: [] } + })) } } diff --git a/packages/libp2p-peer-store/src/index.ts b/packages/libp2p-peer-store/src/index.ts index b4564886d..d811436e1 100644 --- a/packages/libp2p-peer-store/src/index.ts +++ b/packages/libp2p-peer-store/src/index.ts @@ -1,11 +1,11 @@ import { logger } from '@libp2p/logger' -import { EventEmitter } from 'events' +import { EventEmitter } from '@libp2p/interfaces' import { PeerStoreAddressBook } from './address-book.js' import { PeerStoreKeyBook } from './key-book.js' import { PeerStoreMetadataBook } from './metadata-book.js' import { PeerStoreProtoBook } from './proto-book.js' import { PersistentStore, Store } from './store.js' -import type { PeerStore, Address, AddressBook, KeyBook, MetadataBook, ProtoBook } from '@libp2p/interfaces/peer-store' +import type { PeerStore, Address, AddressBook, KeyBook, MetadataBook, ProtoBook, PeerStoreEvents } from '@libp2p/interfaces/peer-store' import type { PeerId } from '@libp2p/interfaces/peer-id' import type { Multiaddr } from '@multiformats/multiaddr' import type { Datastore } from 'interface-datastore' @@ -30,7 +30,7 @@ export interface PeerStoreOptions { /** * An implementation of PeerStore that stores data in a Datastore */ -export class DefaultPeerStore extends EventEmitter implements PeerStore { +export class DefaultPeerStore extends EventEmitter implements PeerStore { public addressBook: AddressBook public keyBook: KeyBook public metadataBook: MetadataBook @@ -47,10 +47,10 @@ export class DefaultPeerStore extends EventEmitter implements PeerStore { this.peerId = peerId this.store = new PersistentStore(datastore) - this.addressBook = new PeerStoreAddressBook(this.emit.bind(this), this.store, addressFilter) - this.keyBook = new PeerStoreKeyBook(this.emit.bind(this), this.store) - this.metadataBook = new PeerStoreMetadataBook(this.emit.bind(this), this.store) - this.protoBook = new PeerStoreProtoBook(this.emit.bind(this), this.store) + this.addressBook = new PeerStoreAddressBook(this.dispatchEvent.bind(this), this.store, addressFilter) + this.keyBook = new PeerStoreKeyBook(this.dispatchEvent.bind(this), this.store) + this.metadataBook = new PeerStoreMetadataBook(this.dispatchEvent.bind(this), this.store) + this.protoBook = new PeerStoreProtoBook(this.dispatchEvent.bind(this), this.store) } async * getPeers () { diff --git a/packages/libp2p-peer-store/src/key-book.ts b/packages/libp2p-peer-store/src/key-book.ts index 90b25530e..dae8f262a 100644 --- a/packages/libp2p-peer-store/src/key-book.ts +++ b/packages/libp2p-peer-store/src/key-book.ts @@ -3,6 +3,7 @@ import errcode from 'err-code' import { codes } from './errors.js' import { PeerId } from '@libp2p/peer-id' import { equals as uint8arrayEquals } from 'uint8arrays/equals' +import { CustomEvent } from '@libp2p/interfaces' import type { Store } from './store.js' import type { PeerStore, KeyBook } from '@libp2p/interfaces/src/peer-store' @@ -17,14 +18,14 @@ const log = logger('libp2p:peer-store:key-book') const EVENT_NAME = 'change:pubkey' export class PeerStoreKeyBook implements KeyBook { - private readonly emit: PeerStore['emit'] + private readonly dispatchEvent: PeerStore['dispatchEvent'] private readonly store: Store /** * The KeyBook is responsible for keeping the known public keys of a peer */ - constructor (emit: PeerStore['emit'], store: Store) { - this.emit = emit + constructor (dispatchEvent: PeerStore['dispatchEvent'], store: Store) { + this.dispatchEvent = dispatchEvent this.store = store } @@ -68,7 +69,9 @@ export class PeerStoreKeyBook implements KeyBook { } if (updatedKey) { - this.emit(EVENT_NAME, { peerId, pubKey: publicKey }) + this.dispatchEvent(new CustomEvent(EVENT_NAME, { + detail: { peerId, pubKey: publicKey } + })) } } @@ -112,6 +115,8 @@ export class PeerStoreKeyBook implements KeyBook { release() } - this.emit(EVENT_NAME, { peerId, pubKey: undefined }) + this.dispatchEvent(new CustomEvent(EVENT_NAME, { + detail: { peerId, pubKey: undefined } + })) } } diff --git a/packages/libp2p-peer-store/src/metadata-book.ts b/packages/libp2p-peer-store/src/metadata-book.ts index 920592dd3..731d54fcd 100644 --- a/packages/libp2p-peer-store/src/metadata-book.ts +++ b/packages/libp2p-peer-store/src/metadata-book.ts @@ -3,6 +3,7 @@ import errcode from 'err-code' import { codes } from './errors.js' import { PeerId } from '@libp2p/peer-id' import { equals as uint8ArrayEquals } from 'uint8arrays/equals' +import { CustomEvent } from '@libp2p/interfaces' import type { Store } from './store.js' import type { PeerStore, MetadataBook } from '@libp2p/interfaces/src/peer-store' @@ -11,15 +12,15 @@ const log = logger('libp2p:peer-store:metadata-book') const EVENT_NAME = 'change:metadata' export class PeerStoreMetadataBook implements MetadataBook { - private readonly emit: PeerStore['emit'] + private readonly dispatchEvent: PeerStore['dispatchEvent'] private readonly store: Store /** * The MetadataBook is responsible for keeping metadata * about known peers */ - constructor (emit: PeerStore['emit'], store: Store) { - this.emit = emit + constructor (dispatchEvent: PeerStore['dispatchEvent'], store: Store) { + this.dispatchEvent = dispatchEvent this.store = store } @@ -94,7 +95,9 @@ export class PeerStoreMetadataBook implements MetadataBook { release() } - this.emit(EVENT_NAME, { peerId, metadata }) + this.dispatchEvent(new CustomEvent(EVENT_NAME, { + detail: { peerId, metadata } + })) } /** @@ -136,7 +139,9 @@ export class PeerStoreMetadataBook implements MetadataBook { release() } - this.emit(EVENT_NAME, { peerId, metadata: updatedPeer.metadata }) + this.dispatchEvent(new CustomEvent(EVENT_NAME, { + detail: { peerId, metadata: updatedPeer.metadata } + })) } async delete (peerId: PeerId) { @@ -162,7 +167,9 @@ export class PeerStoreMetadataBook implements MetadataBook { } if (has) { - this.emit(EVENT_NAME, { peerId, metadata: new Map() }) + this.dispatchEvent(new CustomEvent(EVENT_NAME, { + detail: { peerId, metadata: new Map() } + })) } } @@ -194,7 +201,9 @@ export class PeerStoreMetadataBook implements MetadataBook { } if (metadata != null) { - this.emit(EVENT_NAME, { peerId, metadata }) + this.dispatchEvent(new CustomEvent(EVENT_NAME, { + detail: { peerId, metadata } + })) } } } diff --git a/packages/libp2p-peer-store/src/proto-book.ts b/packages/libp2p-peer-store/src/proto-book.ts index 82f51ba08..8e5bde68b 100644 --- a/packages/libp2p-peer-store/src/proto-book.ts +++ b/packages/libp2p-peer-store/src/proto-book.ts @@ -2,24 +2,25 @@ import { logger } from '@libp2p/logger' import errcode from 'err-code' import { codes } from './errors.js' import { PeerId } from '@libp2p/peer-id' +import { base58btc } from 'multiformats/bases/base58' +import { CustomEvent } from '@libp2p/interfaces' import type { Store } from './store.js' import type { PeerStore, ProtoBook } from '@libp2p/interfaces/src/peer-store' -import { base58btc } from 'multiformats/bases/base58' const log = logger('libp2p:peer-store:proto-book') const EVENT_NAME = 'change:protocols' export class PeerStoreProtoBook implements ProtoBook { - private readonly emit: PeerStore['emit'] + private readonly dispatchEvent: PeerStore['dispatchEvent'] private readonly store: Store /** * The ProtoBook is responsible for keeping the known supported * protocols of a peer */ - constructor (emit: PeerStore['emit'], store: Store) { - this.emit = emit + constructor (dispatchEvent: PeerStore['dispatchEvent'], store: Store) { + this.dispatchEvent = dispatchEvent this.store = store } @@ -83,7 +84,9 @@ export class PeerStoreProtoBook implements ProtoBook { release() } - this.emit(EVENT_NAME, { peerId, protocols: updatedPeer.protocols }) + this.dispatchEvent(new CustomEvent(EVENT_NAME, { + detail: { peerId, protocols: updatedPeer.protocols } + })) } async add (peerId: PeerId, protocols: string[]) { @@ -126,7 +129,9 @@ export class PeerStoreProtoBook implements ProtoBook { release() } - this.emit(EVENT_NAME, { peerId, protocols: updatedPeer.protocols }) + this.dispatchEvent(new CustomEvent(EVENT_NAME, { + detail: { peerId, protocols: updatedPeer.protocols } + })) } async remove (peerId: PeerId, protocols: string[]) { @@ -171,7 +176,9 @@ export class PeerStoreProtoBook implements ProtoBook { release() } - this.emit(EVENT_NAME, { peerId, protocols: updatedPeer.protocols }) + this.dispatchEvent(new CustomEvent(EVENT_NAME, { + detail: { peerId, protocols: updatedPeer.protocols } + })) } async delete (peerId: PeerId) { @@ -198,7 +205,9 @@ export class PeerStoreProtoBook implements ProtoBook { } if (has === true) { - this.emit(EVENT_NAME, { peerId, protocols: [] }) + this.dispatchEvent(new CustomEvent(EVENT_NAME, { + detail: { peerId, protocols: [] } + })) } } } diff --git a/packages/libp2p-peer-store/test/address-book.spec.ts b/packages/libp2p-peer-store/test/address-book.spec.ts index bdbd33ef4..468df9eea 100644 --- a/packages/libp2p-peer-store/test/address-book.spec.ts +++ b/packages/libp2p-peer-store/test/address-book.spec.ts @@ -47,10 +47,6 @@ describe('addressBook', () => { ab = peerStore.addressBook }) - afterEach(() => { - peerStore.removeAllListeners() - }) - it('throws invalid parameters error if invalid PeerId is provided', async () => { try { // @ts-expect-error invalid input @@ -88,10 +84,13 @@ describe('addressBook', () => { const defer = pDefer() const supportedMultiaddrs = [addr1, addr2] - peerStore.once('change:multiaddrs', ({ peerId, multiaddrs }) => { + peerStore.addEventListener('change:multiaddrs', (evt) => { + const { peerId, multiaddrs } = evt.detail expect(peerId).to.exist() expect(multiaddrs).to.eql(supportedMultiaddrs) defer.resolve() + }, { + once: true }) await ab.set(peerId, supportedMultiaddrs) @@ -109,7 +108,7 @@ describe('addressBook', () => { const supportedMultiaddrsB = [addr2] let changeCounter = 0 - peerStore.on('change:multiaddrs', () => { + peerStore.addEventListener('change:multiaddrs', () => { changeCounter++ if (changeCounter > 1) { defer.resolve() @@ -134,7 +133,7 @@ describe('addressBook', () => { const supportedMultiaddrs = [addr1, addr2] let changeCounter = 0 - peerStore.on('change:multiaddrs', () => { + peerStore.addEventListener('change:multiaddrs', () => { changeCounter++ if (changeCounter > 1) { defer.reject() @@ -169,10 +168,6 @@ describe('addressBook', () => { ab = peerStore.addressBook }) - afterEach(() => { - peerStore.removeAllListeners() - }) - it('throws invalid parameters error if invalid PeerId is provided', async () => { try { // @ts-expect-error invalid input @@ -209,7 +204,7 @@ describe('addressBook', () => { it('does not emit event if no addresses are added', async () => { const defer = pDefer() - peerStore.on('peer', () => { + peerStore.addEventListener('peer', () => { defer.reject() }) @@ -231,7 +226,8 @@ describe('addressBook', () => { const finalMultiaddrs = supportedMultiaddrsA.concat(supportedMultiaddrsB) let changeTrigger = 2 - peerStore.on('change:multiaddrs', ({ multiaddrs }) => { + peerStore.addEventListener('change:multiaddrs', (evt) => { + const { multiaddrs } = evt.detail changeTrigger-- if (changeTrigger === 0 && arrayEquals(multiaddrs, finalMultiaddrs)) { defer.resolve() @@ -261,7 +257,7 @@ describe('addressBook', () => { const finalMultiaddrs = supportedMultiaddrsA.concat(supportedMultiaddrsB) let changeCounter = 0 - peerStore.on('change:multiaddrs', () => { + peerStore.addEventListener('change:multiaddrs', () => { changeCounter++ if (changeCounter > 1) { defer.resolve() @@ -287,7 +283,7 @@ describe('addressBook', () => { const supportedMultiaddrsB = [addr2] let changeCounter = 0 - peerStore.on('change:multiaddrs', () => { + peerStore.addEventListener('change:multiaddrs', () => { changeCounter++ if (changeCounter > 1) { defer.reject() @@ -439,7 +435,7 @@ describe('addressBook', () => { it('does not emit an event if no records exist for the peer', async () => { const defer = pDefer() - peerStore.on('change:multiaddrs', () => { + peerStore.addEventListener('change:multiaddrs', () => { defer.reject() }) @@ -460,7 +456,8 @@ describe('addressBook', () => { await ab.set(peerId, supportedMultiaddrs) // Listen after set - peerStore.on('change:multiaddrs', ({ multiaddrs }) => { + peerStore.addEventListener('change:multiaddrs', (evt) => { + const { multiaddrs } = evt.detail expect(multiaddrs.length).to.eql(0) defer.resolve() }) @@ -516,10 +513,13 @@ describe('addressBook', () => { }) const envelope = await RecordEnvelope.seal(peerRecord, peerId) - peerStore.once('change:multiaddrs', ({ peerId, multiaddrs }) => { + peerStore.addEventListener('change:multiaddrs', (evt) => { + const { peerId, multiaddrs } = evt.detail expect(peerId).to.exist() expect(multiaddrs).to.eql(multiaddrs) defer.resolve() + }, { + once: true }) // consume peer record @@ -553,10 +553,13 @@ describe('addressBook', () => { }) const envelope = await RecordEnvelope.seal(peerRecord, peerId) - peerStore.once('change:multiaddrs', ({ peerId, multiaddrs }) => { + peerStore.addEventListener('change:multiaddrs', (evt) => { + const { peerId, multiaddrs } = evt.detail expect(peerId).to.exist() expect(multiaddrs).to.eql(multiaddrs) defer.resolve() + }, { + once: true }) // consume peer record @@ -597,10 +600,13 @@ describe('addressBook', () => { }) const envelope = await RecordEnvelope.seal(peerRecord, peerId) - peerStore.once('change:multiaddrs', ({ peerId, multiaddrs }) => { + peerStore.addEventListener('change:multiaddrs', (evt) => { + const { peerId, multiaddrs } = evt.detail expect(peerId).to.exist() expect(multiaddrs).to.eql(multiaddrs) defer.resolve() + }, { + once: true }) // consume peer record @@ -644,10 +650,13 @@ describe('addressBook', () => { }) const envelope = await RecordEnvelope.seal(peerRecord, peerId) - peerStore.once('change:multiaddrs', ({ peerId, multiaddrs }) => { + peerStore.addEventListener('change:multiaddrs', (evt) => { + const { peerId, multiaddrs } = evt.detail expect(peerId).to.exist() expect(multiaddrs).to.eql(multiaddrs) defer.resolve() + }, { + once: true }) // consume peer record diff --git a/packages/libp2p-peer-store/test/key-book.spec.ts b/packages/libp2p-peer-store/test/key-book.spec.ts index c8c00872c..e55e257c9 100644 --- a/packages/libp2p-peer-store/test/key-book.spec.ts +++ b/packages/libp2p-peer-store/test/key-book.spec.ts @@ -85,7 +85,8 @@ describe('keyBook', () => { it('should emit an event when setting a key', async () => { const defer = pDefer() - peerStore.on('change:pubkey', ({ peerId: id, pubKey }) => { + peerStore.addEventListener('change:pubkey', (evt) => { + const { peerId: id, pubKey } = evt.detail if (peerId.publicKey == null) { throw new Error('Public key was missing') } @@ -124,7 +125,8 @@ describe('keyBook', () => { await kb.set(peerId, peerId.publicKey) - peerStore.on('change:pubkey', ({ peerId: id, pubKey }) => { + peerStore.addEventListener('change:pubkey', (evt) => { + const { peerId: id, pubKey } = evt.detail expect(id.toString(base58btc)).to.equal(peerId.toString(base58btc)) expect(pubKey).to.be.undefined() defer.resolve() diff --git a/packages/libp2p-peer-store/test/metadata-book.spec.ts b/packages/libp2p-peer-store/test/metadata-book.spec.ts index d1dfcde1e..0acd51caa 100644 --- a/packages/libp2p-peer-store/test/metadata-book.spec.ts +++ b/packages/libp2p-peer-store/test/metadata-book.spec.ts @@ -30,10 +30,6 @@ describe('metadataBook', () => { mb = peerStore.metadataBook }) - afterEach(() => { - peerStore.removeAllListeners() - }) - it('throws invalid parameters error if invalid PeerId is provided', async () => { try { // @ts-expect-error invalid input @@ -83,10 +79,13 @@ describe('metadataBook', () => { const metadataKey = 'location' const metadataValue = uint8ArrayFromString('mars') - peerStore.once('change:metadata', ({ peerId, metadata }) => { + peerStore.addEventListener('change:metadata', (evt) => { + const { peerId, metadata } = evt.detail expect(peerId).to.exist() expect(metadata.get(metadataKey)).to.equalBytes(metadataValue) defer.resolve() + }, { + once: true }) await mb.setValue(peerId, metadataKey, metadataValue) @@ -108,7 +107,7 @@ describe('metadataBook', () => { const metadataValue2 = uint8ArrayFromString('saturn') let changeCounter = 0 - peerStore.on('change:metadata', () => { + peerStore.addEventListener('change:metadata', () => { changeCounter++ if (changeCounter > 1) { defer.resolve() @@ -137,7 +136,7 @@ describe('metadataBook', () => { const metadataValue = uint8ArrayFromString('mars') let changeCounter = 0 - peerStore.on('change:metadata', () => { + peerStore.addEventListener('change:metadata', () => { changeCounter++ if (changeCounter > 1) { defer.reject() @@ -281,7 +280,7 @@ describe('metadataBook', () => { it('should not emit event if no records exist for the peer', async () => { const defer = pDefer() - peerStore.on('change:metadata', () => { + peerStore.addEventListener('change:metadata', () => { defer.reject() }) @@ -303,7 +302,7 @@ describe('metadataBook', () => { await mb.setValue(peerId, metadataKey, metadataValue) // Listen after set - peerStore.on('change:metadata', () => { + peerStore.addEventListener('change:metadata', () => { defer.resolve() }) @@ -340,7 +339,7 @@ describe('metadataBook', () => { const defer = pDefer() const metadataKey = 'location' - peerStore.on('change:metadata', () => { + peerStore.addEventListener('change:metadata', () => { defer.reject() }) @@ -362,7 +361,7 @@ describe('metadataBook', () => { await mb.setValue(peerId, metadataKey, metadataValue) // Listen after set - peerStore.on('change:metadata', () => { + peerStore.addEventListener('change:metadata', () => { defer.resolve() }) diff --git a/packages/libp2p-peer-store/test/proto-book.spec.ts b/packages/libp2p-peer-store/test/proto-book.spec.ts index f3aec77f6..e64f56b2e 100644 --- a/packages/libp2p-peer-store/test/proto-book.spec.ts +++ b/packages/libp2p-peer-store/test/proto-book.spec.ts @@ -38,10 +38,6 @@ describe('protoBook', () => { pb = peerStore.protoBook }) - afterEach(() => { - peerStore.removeAllListeners() - }) - it('throws invalid parameters error if invalid PeerId is provided', async () => { // @ts-expect-error invalid input await expect(pb.set('invalid peerId')).to.eventually.be.rejected().with.property('code', codes.ERR_INVALID_PARAMETERS) @@ -56,10 +52,13 @@ describe('protoBook', () => { const defer = pDefer() const supportedProtocols = ['protocol1', 'protocol2'] - peerStore.once('change:protocols', ({ peerId, protocols }) => { + peerStore.addEventListener('change:protocols', (evt) => { + const { peerId, protocols } = evt.detail expect(peerId).to.exist() expect(protocols).to.have.deep.members(supportedProtocols) defer.resolve() + }, { + once: true }) await pb.set(peerId, supportedProtocols) @@ -76,7 +75,7 @@ describe('protoBook', () => { const supportedProtocolsB = ['protocol2'] let changeCounter = 0 - peerStore.on('change:protocols', () => { + peerStore.addEventListener('change:protocols', () => { changeCounter++ if (changeCounter > 1) { defer.resolve() @@ -100,7 +99,7 @@ describe('protoBook', () => { const supportedProtocols = ['protocol1', 'protocol2'] let changeCounter = 0 - peerStore.on('change:protocols', () => { + peerStore.addEventListener('change:protocols', () => { changeCounter++ if (changeCounter > 1) { defer.reject() @@ -134,10 +133,6 @@ describe('protoBook', () => { pb = peerStore.protoBook }) - afterEach(() => { - peerStore.removeAllListeners() - }) - it('throws invalid parameters error if invalid PeerId is provided', async () => { // @ts-expect-error invalid input await expect(pb.add('invalid peerId')).to.eventually.be.rejected().with.property('code', codes.ERR_INVALID_PARAMETERS) @@ -156,7 +151,8 @@ describe('protoBook', () => { const finalProtocols = supportedProtocolsA.concat(supportedProtocolsB) let changeTrigger = 2 - peerStore.on('change:protocols', ({ protocols }) => { + peerStore.addEventListener('change:protocols', (evt) => { + const { protocols } = evt.detail changeTrigger-- if (changeTrigger === 0 && arraysAreEqual(protocols, finalProtocols)) { defer.resolve() @@ -184,7 +180,7 @@ describe('protoBook', () => { const finalProtocols = supportedProtocolsA.concat(supportedProtocolsB) let changeCounter = 0 - peerStore.on('change:protocols', () => { + peerStore.addEventListener('change:protocols', () => { changeCounter++ if (changeCounter > 1) { defer.resolve() @@ -209,7 +205,7 @@ describe('protoBook', () => { const supportedProtocolsB = ['protocol2'] let changeCounter = 0 - peerStore.on('change:protocols', () => { + peerStore.addEventListener('change:protocols', () => { changeCounter++ if (changeCounter > 1) { defer.reject() @@ -243,10 +239,6 @@ describe('protoBook', () => { pb = peerStore.protoBook }) - afterEach(() => { - peerStore.removeAllListeners() - }) - it('throws invalid parameters error if invalid PeerId is provided', async () => { // @ts-expect-error invalid input await expect(pb.remove('invalid peerId')).to.eventually.be.rejected().with.property('code', codes.ERR_INVALID_PARAMETERS) @@ -264,7 +256,7 @@ describe('protoBook', () => { const removedProtocols = ['protocol1'] const finalProtocols = supportedProtocols.filter(p => !removedProtocols.includes(p)) - peerStore.on('change:protocols', spy) + peerStore.addEventListener('change:protocols', spy) // Replace await pb.set(peerId, supportedProtocols) @@ -280,8 +272,8 @@ describe('protoBook', () => { const [firstCallArgs] = spy.firstCall.args const [secondCallArgs] = spy.secondCall.args - expect(arraysAreEqual(firstCallArgs.protocols, supportedProtocols)) - expect(arraysAreEqual(secondCallArgs.protocols, finalProtocols)) + expect(arraysAreEqual(firstCallArgs.detail.protocols, supportedProtocols)) + expect(arraysAreEqual(secondCallArgs.detail.protocols, finalProtocols)) }) it('emits on remove if the content changes', async () => { @@ -291,7 +283,7 @@ describe('protoBook', () => { const removedProtocols = ['protocol2'] const finalProtocols = supportedProtocols.filter(p => !removedProtocols.includes(p)) - peerStore.on('change:protocols', spy) + peerStore.addEventListener('change:protocols', spy) // set await pb.set(peerId, supportedProtocols) @@ -310,7 +302,7 @@ describe('protoBook', () => { const supportedProtocols = ['protocol1', 'protocol2'] const removedProtocols = ['protocol3'] - peerStore.on('change:protocols', spy) + peerStore.addEventListener('change:protocols', spy) // set await pb.set(peerId, supportedProtocols) @@ -376,7 +368,7 @@ describe('protoBook', () => { it('should not emit event if no records exist for the peer', async () => { const defer = pDefer() - peerStore.on('change:protocols', () => { + peerStore.addEventListener('change:protocols', () => { defer.reject() }) @@ -397,7 +389,8 @@ describe('protoBook', () => { await pb.set(peerId, supportedProtocols) // Listen after set - peerStore.on('change:protocols', ({ protocols }) => { + peerStore.addEventListener('change:protocols', (evt) => { + const { protocols } = evt.detail expect(protocols.length).to.eql(0) defer.resolve() }) diff --git a/packages/libp2p-pubsub/package.json b/packages/libp2p-pubsub/package.json index 80059af25..8fead4e56 100644 --- a/packages/libp2p-pubsub/package.json +++ b/packages/libp2p-pubsub/package.json @@ -166,11 +166,11 @@ }, "scripts": { "lint": "aegir lint", - "dep-check": "aegir dep-check dist/src/**/*.js", + "dep-check": "aegir dep-check dist/src/**/*.js dist/test/**/*.js", "build": "tsc", "postbuild": "npm run build:copy-proto-files", "pretest": "npm run build", - "test": "aegir test -f ./dist/test/**/*.js", + "test": "aegir test -f ./dist/test/*.js -f ./dist/test/**/*.js", "test:chrome": "npm run test -- -t browser", "test:chrome-webworker": "npm run test -- -t webworker", "test:firefox": "npm run test -- -t browser -- --browser firefox", @@ -203,7 +203,10 @@ }, "devDependencies": { "@types/bl": "^5.0.2", + "abortable-iterator": "^4.0.2", "aegir": "^36.1.3", + "it-pair": "^2.0.2", + "it-pushable": "^2.0.1", "protobufjs": "^6.10.2", "util": "^0.12.4" } diff --git a/packages/libp2p-pubsub/src/index.ts b/packages/libp2p-pubsub/src/index.ts index 3c82abaa8..3d2f956b0 100644 --- a/packages/libp2p-pubsub/src/index.ts +++ b/packages/libp2p-pubsub/src/index.ts @@ -1,5 +1,5 @@ import { logger } from '@libp2p/logger' -import { EventEmitter } from 'events' +import { EventEmitter, CustomEvent } from '@libp2p/interfaces' import errcode from 'err-code' import { pipe } from 'it-pipe' import Queue from 'p-queue' @@ -8,15 +8,15 @@ import { codes } from './errors.js' import { RPC, IRPC } from './message/rpc.js' import { PeerStreams } from './peer-streams.js' import * as utils from './utils.js' -import type { PeerId } from '@libp2p/interfaces/peer-id' -import type { Registrar, IncomingStreamEvent } from '@libp2p/interfaces/registrar' -import type { Connection } from '@libp2p/interfaces/connection' -import type BufferList from 'bl' import { signMessage, verifySignature } from './message/sign.js' -import type { PubSub, Message, StrictNoSign, StrictSign, PubsubOptions } from '@libp2p/interfaces/pubsub' +import type { PeerId } from '@libp2p/interfaces/peer-id' +import type { Registrar, IncomingStreamEvent } from '@libp2p/interfaces/registrar' +import type { Connection } from '@libp2p/interfaces/connection' +import type BufferList from 'bl' +import type { PubSub, Message, StrictNoSign, StrictSign, PubsubOptions, PubsubEvents } from '@libp2p/interfaces/pubsub' import type { Startable } from '@libp2p/interfaces' import type { Logger } from '@libp2p/logger' @@ -26,7 +26,7 @@ export interface TopicValidator { (topic: string, message: Message): Promise extends EventEmitter implements PubSub, Startable { public peerId: PeerId public started: boolean /** @@ -122,6 +122,8 @@ export abstract class PubsubBaseProtocol extends EventEmitter implements PubSub, // register protocol with topology // Topology callbacks called on connection manager changes const topology = new MulticodecTopology({ + peerStore: this._libp2p.peerStore, + connectionManager: this._libp2p.connectionManager, multicodecs: this.multicodecs, handlers: { onConnect: this._onPeerConnected, @@ -225,7 +227,9 @@ export abstract class PubsubBaseProtocol extends EventEmitter implements PubSub, }) this.peers.set(id, peerStreams) - peerStreams.once('close', () => this._removePeer(peerId)) + peerStreams.addEventListener('close', () => this._removePeer(peerId), { + once: true + }) return peerStreams } @@ -236,10 +240,11 @@ export abstract class PubsubBaseProtocol extends EventEmitter implements PubSub, protected _removePeer (peerId: PeerId) { const id = peerId.toString() const peerStreams = this.peers.get(id) - if (peerStreams == null) return + if (peerStreams == null) { + return + } // close peer streams - peerStreams.removeAllListeners() peerStreams.close() // delete peer streams @@ -295,7 +300,9 @@ export abstract class PubsubBaseProtocol extends EventEmitter implements PubSub, subs.forEach((subOpt) => { this._processRpcSubOpt(idB58Str, subOpt) }) - this.emit('pubsub:subscription-change', { peerId: peerStreams.id, subscriptions: subs }) + this.dispatchEvent(new CustomEvent('pubsub:subscription-change', { + detail: { peerId: peerStreams.id, subscriptions: subs } + })) } if (!this._acceptFrom(idB58Str)) { @@ -379,7 +386,9 @@ export abstract class PubsubBaseProtocol extends EventEmitter implements PubSub, _emitMessage (message: Message) { message.topicIDs.forEach((topic) => { if (this.subscriptions.has(topic)) { - this.emit(topic, message) + this.dispatchEvent(new CustomEvent(topic, { + detail: message + })) } }) } diff --git a/packages/libp2p-pubsub/src/peer-streams.ts b/packages/libp2p-pubsub/src/peer-streams.ts index 26bec6b48..0345974b0 100644 --- a/packages/libp2p-pubsub/src/peer-streams.ts +++ b/packages/libp2p-pubsub/src/peer-streams.ts @@ -1,5 +1,5 @@ import { logger } from '@libp2p/logger' -import { EventEmitter } from 'events' +import { EventEmitter, CustomEvent } from '@libp2p/interfaces' import * as lp from 'it-length-prefixed' import { pushable } from 'it-pushable' import { pipe } from 'it-pipe' @@ -7,6 +7,7 @@ import { abortableSource } from 'abortable-iterator' import type { PeerId } from '@libp2p/interfaces/peer-id' import type { Stream } from '@libp2p/interfaces/connection' import type { Pushable } from 'it-pushable' +import type { PeerStreamEvents } from '@libp2p/interfaces/pubsub' const log = logger('libp2p-pubsub:peer-streams') @@ -18,7 +19,7 @@ export interface Options { /** * Thin wrapper around a peer's inbound / outbound pubsub streams */ -export class PeerStreams extends EventEmitter { +export class PeerStreams extends EventEmitter { public readonly id: PeerId public readonly protocol: string /** @@ -41,6 +42,7 @@ export class PeerStreams extends EventEmitter { * An AbortController for controlled shutdown of the inbound stream */ private readonly _inboundAbortController: AbortController + private closed: boolean constructor (opts: Options) { super() @@ -49,6 +51,7 @@ export class PeerStreams extends EventEmitter { this.protocol = opts.protocol this._inboundAbortController = new AbortController() + this.closed = false } /** @@ -96,7 +99,7 @@ export class PeerStreams extends EventEmitter { { returnOnAbort: true } ) - this.emit('stream:inbound') + this.dispatchEvent(new CustomEvent('stream:inbound')) return this.inboundStream } @@ -122,7 +125,7 @@ export class PeerStreams extends EventEmitter { this._rawOutboundStream = undefined this.outboundStream = undefined if (shouldEmit != null) { - this.emit('close') + this.dispatchEvent(new CustomEvent('close')) } } }) @@ -137,7 +140,7 @@ export class PeerStreams extends EventEmitter { // Only emit if the connection is new if (_prevStream == null) { - this.emit('stream:outbound') + this.dispatchEvent(new CustomEvent('stream:outbound')) } } @@ -145,6 +148,12 @@ export class PeerStreams extends EventEmitter { * Closes the open connection to peer */ close () { + if (this.closed) { + return + } + + this.closed = true + // End the outbound stream if (this.outboundStream != null) { this.outboundStream.end() @@ -158,6 +167,6 @@ export class PeerStreams extends EventEmitter { this.outboundStream = undefined this._rawInboundStream = undefined this.inboundStream = undefined - this.emit('close') + this.dispatchEvent(new CustomEvent('close')) } } diff --git a/packages/libp2p-pubsub/test/emit-self.spec.ts b/packages/libp2p-pubsub/test/emit-self.spec.ts index 4dbed1d6d..5ee2b5e8d 100644 --- a/packages/libp2p-pubsub/test/emit-self.spec.ts +++ b/packages/libp2p-pubsub/test/emit-self.spec.ts @@ -39,7 +39,7 @@ describe('emitSelf', () => { }) it('should emit to self on publish', async () => { - const promise = new Promise((resolve) => pubsub.once(topic, resolve)) + const promise = new Promise((resolve) => pubsub.addEventListener(topic, resolve)) await pubsub.publish(topic, data) @@ -71,7 +71,9 @@ describe('emitSelf', () => { }) it('should not emit to self on publish', async () => { - pubsub.once(topic, () => shouldNotHappen) + pubsub.addEventListener(topic, () => shouldNotHappen, { + once: true + }) await pubsub.publish(topic, data) diff --git a/packages/libp2p-pubsub/test/instance.spec.ts b/packages/libp2p-pubsub/test/instance.spec.ts index a2362d6fb..3ce530a27 100644 --- a/packages/libp2p-pubsub/test/instance.spec.ts +++ b/packages/libp2p-pubsub/test/instance.spec.ts @@ -7,7 +7,7 @@ import { import type { PeerId } from '@libp2p/interfaces/peer-id' import type { Message } from '@libp2p/interfaces/pubsub' -class PubsubProtocol extends PubsubBaseProtocol { +class PubsubProtocol extends PubsubBaseProtocol<{}> { async _publish (message: Message): Promise { throw new Error('Method not implemented.') } diff --git a/packages/libp2p-pubsub/test/lifecycle.spec.ts b/packages/libp2p-pubsub/test/lifecycle.spec.ts index c1f61e355..54d854ac9 100644 --- a/packages/libp2p-pubsub/test/lifecycle.spec.ts +++ b/packages/libp2p-pubsub/test/lifecycle.spec.ts @@ -11,7 +11,7 @@ import type { PeerId } from '@libp2p/interfaces/peer-id' import type { Registrar } from '@libp2p/interfaces/registrar' import type { Message } from '@libp2p/interfaces/pubsub' -class PubsubProtocol extends PubsubBaseProtocol { +class PubsubProtocol extends PubsubBaseProtocol<{}> { async _publish (message: Message): Promise { throw new Error('Method not implemented.') } @@ -112,7 +112,7 @@ describe('pubsub base lifecycle', () => { afterEach(async () => { sinon.restore() - return await Promise.all([ + await Promise.all([ pubsubA.stop(), pubsubB.stop() ]) diff --git a/packages/libp2p-pubsub/test/message.spec.ts b/packages/libp2p-pubsub/test/message.spec.ts index ea1befcf7..cad9ede5c 100644 --- a/packages/libp2p-pubsub/test/message.spec.ts +++ b/packages/libp2p-pubsub/test/message.spec.ts @@ -10,7 +10,7 @@ import { import type { PeerId } from '@libp2p/interfaces/peer-id' import type { Message } from '@libp2p/interfaces/pubsub' -class PubsubProtocol extends PubsubBaseProtocol { +class PubsubProtocol extends PubsubBaseProtocol<{}> { async _publish (message: Message): Promise { throw new Error('Method not implemented') } diff --git a/packages/libp2p-pubsub/test/utils/index.ts b/packages/libp2p-pubsub/test/utils/index.ts index 8fe4e03bb..b66367042 100644 --- a/packages/libp2p-pubsub/test/utils/index.ts +++ b/packages/libp2p-pubsub/test/utils/index.ts @@ -3,7 +3,6 @@ import * as PeerIdFactory from '@libp2p/peer-id-factory' import { PubsubBaseProtocol } from '../../src/index.js' import { RPC, IRPC } from '../../src/message/rpc.js' import type { Registrar } from '@libp2p/interfaces/registrar' -import type { PeerId } from '@libp2p/interfaces/peer-id' import type { Ed25519PeerId } from '@libp2p/peer-id' export const createPeerId = async (): Promise => { @@ -12,7 +11,11 @@ export const createPeerId = async (): Promise => { return peerId } -export class PubsubImplementation extends PubsubBaseProtocol { +interface EventMap { + 'foo': CustomEvent +} + +export class PubsubImplementation extends PubsubBaseProtocol { async _publish () { // ... } @@ -45,6 +48,9 @@ export const createMockRegistrar = (registrarRecord: Map { const { multicodecs } = topology @@ -60,32 +66,6 @@ export const createMockRegistrar = (registrarRecord: Map { registrarRecord.delete(id) - }, - - getConnection (peerId: PeerId) { - throw new Error('Not implemented') - }, - - peerStore: { - on: () => { - throw new Error('Not implemented') - }, - // @ts-expect-error use protobook type - protoBook: { - get: () => { - throw new Error('Not implemented') - } - }, - peers: new Map(), - get: (peerId) => { - throw new Error('Not implemented') - } - }, - - connectionManager: { - on: () => { - throw new Error('Not implemented') - } } } diff --git a/packages/libp2p-pubsub/tsconfig.json b/packages/libp2p-pubsub/tsconfig.json index 6527c2e56..bb56caf50 100644 --- a/packages/libp2p-pubsub/tsconfig.json +++ b/packages/libp2p-pubsub/tsconfig.json @@ -19,6 +19,12 @@ }, { "path": "../libp2p-topology" + }, + { + "path": "../libp2p-peer-id" + }, + { + "path": "../libp2p-peer-id-factory" } ] } diff --git a/packages/libp2p-topology/package.json b/packages/libp2p-topology/package.json index 63f3bbc21..2d6650326 100644 --- a/packages/libp2p-topology/package.json +++ b/packages/libp2p-topology/package.json @@ -142,7 +142,7 @@ }, "scripts": { "lint": "aegir lint", - "dep-check": "aegir dep-check dist/src/**/*.js", + "dep-check": "aegir dep-check dist/src/**/*.js dist/test/**/*.js", "build": "tsc" }, "dependencies": { diff --git a/packages/libp2p-topology/src/multicodec-topology.ts b/packages/libp2p-topology/src/multicodec-topology.ts index 60a6a8f7e..7b2549ea3 100644 --- a/packages/libp2p-topology/src/multicodec-topology.ts +++ b/packages/libp2p-topology/src/multicodec-topology.ts @@ -2,9 +2,9 @@ import { Topology } from './index.js' import all from 'it-all' import { logger } from '@libp2p/logger' import type { PeerId } from '@libp2p/interfaces/peer-id' -import type { Peer } from '@libp2p/interfaces/peer-store' +import type { Peer, PeerStore } from '@libp2p/interfaces/peer-store' import type { Connection } from '@libp2p/interfaces/connection' -import type { Registrar } from '@libp2p/interfaces/registrar' +import type { ConnectionManager, Registrar } from '@libp2p/interfaces/registrar' import type { MulticodecTopologyOptions } from '@libp2p/interfaces/topology' const log = logger('libp2p:topology:multicodec-topology') @@ -18,11 +18,15 @@ const multicodecTopologySymbol = Symbol.for('@libp2p/js-interfaces/topology/mult export class MulticodecTopology extends Topology { public readonly multicodecs: string[] + private readonly peerStore: PeerStore + private readonly connectionManager: ConnectionManager constructor (options: MulticodecTopologyOptions) { super(options) this.multicodecs = options.multicodecs + this.peerStore = options.peerStore + this.connectionManager = options.connectionManager } get [Symbol.toStringTag] () { @@ -46,12 +50,15 @@ export class MulticodecTopology extends Topology { } this._registrar = registrar - - registrar.peerStore.on('change:protocols', this._onProtocolChange.bind(this)) - registrar.connectionManager.on('peer:connect', this._onPeerConnect.bind(this)) + this.peerStore.addEventListener('change:protocols', (evt) => { + this._onProtocolChange(evt.detail) + }) + this.connectionManager.addEventListener('peer:connect', (evt) => { + this._onPeerConnect(evt.detail) + }) // Update topology peers - await this._updatePeers(registrar.peerStore.getPeers()) + await this._updatePeers(this.peerStore.getPeers()) } get registrar () { @@ -69,8 +76,8 @@ export class MulticodecTopology extends Topology { // Add the peer regardless of whether or not there is currently a connection this.peers.add(id.toString()) // If there is a connection, call _onConnect - if (this._registrar != null) { - const connection = this._registrar.getConnection(id) + if (this.connectionManager != null) { + const connection = this.connectionManager.getConnection(id) ;(connection != null) && this._onConnect(id, connection) } } else { @@ -102,7 +109,7 @@ export class MulticodecTopology extends Topology { // New to protocol support for (const protocol of protocols) { if (this.multicodecs.includes(protocol)) { - p = this._registrar.peerStore.get(peerId).then(async peerData => await this._updatePeers([peerData])) + p = this.peerStore.get(peerId).then(async peerData => await this._updatePeers([peerData])) break } } @@ -121,7 +128,7 @@ export class MulticodecTopology extends Topology { } const peerId = connection.remotePeer - this._registrar.peerStore.protoBook.get(peerId) + this.peerStore.protoBook.get(peerId) .then(protocols => { if (this.multicodecs.find(multicodec => protocols.includes(multicodec)) != null) { this.peers.add(peerId.toString()) diff --git a/packages/libp2p-tracked-map/package.json b/packages/libp2p-tracked-map/package.json index 7720104b3..a9d18c931 100644 --- a/packages/libp2p-tracked-map/package.json +++ b/packages/libp2p-tracked-map/package.json @@ -120,10 +120,10 @@ }, "scripts": { "lint": "aegir lint", - "dep-check": "aegir dep-check dist/src/**/*.js", + "dep-check": "aegir dep-check dist/src/**/*.js dist/test/**/*.js", "build": "tsc", "pretest": "npm run build", - "test": "aegir test -f ./dist/test/**/*.js", + "test": "aegir test -f ./dist/test/*.js -f ./dist/test/**/*.js", "test:chrome": "npm run test -- -t browser", "test:chrome-webworker": "npm run test -- -t webworker", "test:firefox": "npm run test -- -t browser -- --browser firefox",