From ecc96d02d9ae03e8a0b5762e2e5bc169c8045f61 Mon Sep 17 00:00:00 2001 From: Patrick Seal Date: Wed, 8 Nov 2023 20:01:19 -0800 Subject: [PATCH] feat(UdpServer)!: UdpServer export was changed to a class instance (#40) BREAKING CHANGE: Changed from an object to a class instance that may break consuming code in some edge cases. --- package-lock.json | 20 ++++++++++++++++ package.json | 1 + src/udp-server.ts | 58 ++++++++++++++++++++++------------------------ test/udp-server.js | 13 ++++++++--- 4 files changed, 59 insertions(+), 33 deletions(-) diff --git a/package-lock.json b/package-lock.json index 23d93e6..91bd384 100644 --- a/package-lock.json +++ b/package-lock.json @@ -44,6 +44,7 @@ "source-map-support": "^0.5.21", "ts-essentials": "^9.4.1", "ts-node": "^10.9.1", + "typed-emitter": "^2.1.0", "typescript": "~5.2.2" }, "engines": { @@ -7009,6 +7010,16 @@ "queue-microtask": "^1.2.2" } }, + "node_modules/rxjs": { + "version": "7.8.1", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.1.tgz", + "integrity": "sha512-AA3TVj+0A2iuIoQkWEK/tqFjBq2j+6PO6Y0zJcvzLAFhEFIO3HL0vls9hWLncZbAAbK0mar7oZ4V079I/qPMxg==", + "dev": true, + "optional": true, + "dependencies": { + "tslib": "^2.1.0" + } + }, "node_modules/safe-array-concat": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/safe-array-concat/-/safe-array-concat-1.0.1.tgz", @@ -7874,6 +7885,15 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/typed-emitter": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/typed-emitter/-/typed-emitter-2.1.0.tgz", + "integrity": "sha512-g/KzbYKbH5C2vPkaXGu8DJlHrGKHLsM25Zg9WuC9pMGfuvT+X25tZQWo5fK1BjBm8+UrVE9LDCvaY0CQk+fXDA==", + "dev": true, + "optionalDependencies": { + "rxjs": "*" + } + }, "node_modules/typedarray": { "version": "0.0.6", "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz", diff --git a/package.json b/package.json index f81605d..ea24848 100644 --- a/package.json +++ b/package.json @@ -75,6 +75,7 @@ "source-map-support": "^0.5.21", "ts-essentials": "^9.4.1", "ts-node": "^10.9.1", + "typed-emitter": "^2.1.0", "typescript": "~5.2.2" }, "engines": { diff --git a/src/udp-server.ts b/src/udp-server.ts index a06c083..c93e086 100644 --- a/src/udp-server.ts +++ b/src/udp-server.ts @@ -1,48 +1,47 @@ -import EventEmitter from 'events'; -import dgram from 'dgram'; +import dgram from 'node:dgram'; +import EventEmitter from 'node:events'; + import debug from 'debug'; +import type TypedEmitter from 'typed-emitter'; const log = debug('tplink-simulator:udp-server'); const logErr = debug('tplink-simulator:udp-server:error'); -interface UdpServerType extends EventEmitter { - start: () => Promise; - stop: () => void; - socketBound: boolean; - socket?: dgram.Socket; -} - -const Emitter = new EventEmitter(); -Emitter.setMaxListeners(25); +type UdpServerEvents = { + message: (msg: Buffer, rinfo: dgram.RemoteInfo) => void; +}; -const UdpServer: UdpServerType = Object.assign(Emitter, { - socketBound: false, +class UdpServer extends (EventEmitter as new () => TypedEmitter) { + socketBound = false; - socket: undefined, + socket: dgram.Socket | undefined = undefined; - start: function start({ port = 9999 } = {}): Promise { - const self = UdpServer; + constructor() { + super(); + this.setMaxListeners(25); + } + start({ port = 9999 } = {}): Promise { return new Promise((resolve, reject) => { try { const socket = dgram.createSocket({ type: 'udp4', reuseAddr: true }); - self.socket = socket; + this.socket = socket; socket.on('listening', () => { const address = socket.address(); - self.socketBound = true; + this.socketBound = true; log('UDP server listening', address); - resolve(UdpServer); + resolve(this); }); socket.on('message', (msg, rinfo) => { - self.emit('message', msg, rinfo); + this.emit('message', msg, rinfo); }); socket.on('error', (exception) => { logErr(exception); socket.close(); - self.socketBound = false; + this.socketBound = false; reject(exception); }); @@ -51,15 +50,14 @@ const UdpServer: UdpServerType = Object.assign(Emitter, { reject(err); } }); - }, + } - stop: function stop() { - const self = UdpServer; - if (self.socketBound) { - if (self.socket != null) self.socket.close(); - self.socketBound = false; + stop() { + if (this.socketBound) { + if (this.socket != null) this.socket.close(); + this.socketBound = false; } - }, -}); + } +} -export default UdpServer as UdpServerType; +export default new UdpServer(); diff --git a/test/udp-server.js b/test/udp-server.js index 08cf81c..6643807 100644 --- a/test/udp-server.js +++ b/test/udp-server.js @@ -9,6 +9,12 @@ const { UdpServer } = require('../src'); describe('UdpServer', function () { this.retries(2); + describe('self', function () { + it('is a singleton', function () { + // eslint-disable-next-line global-require + expect(UdpServer).to.equal(require('../src').UdpServer); + }); + }); describe('.start()', function () { it('defaults', async function () { await UdpServer.start(); @@ -28,12 +34,13 @@ describe('UdpServer', function () { expect(UdpServer.socketBound).to.be.false; }); it('does nothing if not started', function () { - expect(UdpServer.stop).to.not.throw(); + expect(() => UdpServer.stop()).to.not.throw(); }); it('does nothing when stopped twice', async function () { await UdpServer.start(); - expect(UdpServer.stop).to.not.throw(); - expect(UdpServer.stop).to.not.throw(); + + expect(() => UdpServer.stop()).to.not.throw(); + expect(() => UdpServer.stop()).to.not.throw(); }); }); });