From ff3551e3667ffd39148745184058f8664adbde03 Mon Sep 17 00:00:00 2001 From: Julian Waller Date: Mon, 2 Oct 2023 13:45:19 +0100 Subject: [PATCH] feat: use typed eventemitter3 --- .gitignore | 1 + package.json | 2 +- src/Ember/Client/index.ts | 12 +++++++++-- src/Ember/Server/index.ts | 39 +++++++++++++++++----------------- src/Ember/Socket/S101Server.ts | 9 ++++++-- src/Ember/Socket/S101Socket.ts | 27 ++++++++++------------- src/S101/S101Codec.ts | 18 +++++++--------- src/index.ts | 7 +++--- yarn.lock | 8 +++++++ 9 files changed, 70 insertions(+), 53 deletions(-) diff --git a/.gitignore b/.gitignore index a820fa6..2b0331f 100755 --- a/.gitignore +++ b/.gitignore @@ -7,6 +7,7 @@ package-lock.json yarn-error.log /lib /dist +/scratch .pnp.* .yarn/* diff --git a/package.json b/package.json index 20c32be..fc25f79 100755 --- a/package.json +++ b/package.json @@ -28,7 +28,6 @@ "validate:dependencies": "yarn npm audit --environment production && run license-validate", "validate:dev-dependencies": "yarn npm audit --environment development", "license-validate": "sofie-licensecheck --allowPackages caniuse-lite@1.0.30001541", - "eslint": "./node_modules/.bin/eslint", "prettier": "./node_modules/.bin/prettier", "lint-staged": "./node_modules/.bin/lint-staged" @@ -48,6 +47,7 @@ "dependencies": { "asn1": "evs-broadcast/node-asn1", "debug": "^4.3.4", + "eventemitter3": "^4.0.7", "long": "^3.2.0", "smart-buffer": "^3.0.3", "tslib": "^2.6.2" diff --git a/src/Ember/Client/index.ts b/src/Ember/Client/index.ts index aeac98e..11f84ef 100644 --- a/src/Ember/Client/index.ts +++ b/src/Ember/Client/index.ts @@ -23,7 +23,7 @@ import { ConnectionOperation } from '../../model/Connection' import { Root } from '../../types/types' import { EmberNode } from '../../model/EmberNode' -import { EventEmitter } from 'events' +import { EventEmitter } from 'eventemitter3' import { S101Client } from '../Socket' import { getPath, assertQualifiedEmberNode, insertCommand, updateProps } from '../Lib/util' import { berEncode } from '../..' @@ -67,7 +67,15 @@ export enum ConnectionStatus { Connected, } -export class EmberClient extends EventEmitter { +export type EmberClientEvents = { + error: [Error] + warn: [Error] + + connected: [] + disconnected: [] +} + +export class EmberClient extends EventEmitter { host: string port: number tree: Collection> = [] diff --git a/src/Ember/Server/index.ts b/src/Ember/Server/index.ts index 3560b7d..2d2e361 100644 --- a/src/Ember/Server/index.ts +++ b/src/Ember/Server/index.ts @@ -1,6 +1,5 @@ -import { EventEmitter } from 'events' +import { EventEmitter } from 'eventemitter3' import { S101Server } from '../Socket/S101Server' -import { S101Client } from '../Socket' import { EmberElement, NumberedTreeNodeImpl, @@ -28,12 +27,14 @@ import { berEncode } from '../../encodings/ber' import { Command, CommandType, FieldFlags, GetDirectory, Invoke } from '../../model/Command' import { Connection, ConnectionOperation, ConnectionImpl } from '../../model/Connection' import { InvocationResultImpl } from '../../model/InvocationResult' +import S101Socket from '../Socket/S101Socket' -const ServerEvents = null - -export { EmberServer, ServerEvents } +export type EmberServerEvents = { + error: [Error] + clientError: [client: S101Socket, error: Error] +} -class EmberServer extends EventEmitter { +export class EmberServer extends EventEmitter { address: string | undefined port: number tree: Collection> = {} @@ -46,8 +47,8 @@ class EmberServer extends EventEmitter { onMatrixOperation?: (Matrix: NumberedTreeNode, connection: Connections) => Promise private _server: S101Server - private _clients: Set = new Set() - private _subscriptions: { [path: string]: Array } = {} + private _clients: Set = new Set() + private _subscriptions: { [path: string]: Array } = {} constructor(port: number, address?: string) { super() @@ -56,7 +57,7 @@ class EmberServer extends EventEmitter { this.port = port this._server = new S101Server(port, address) - this._server.on('connection', (client: S101Client) => { + this._server.on('connection', (client: S101Socket) => { this._clients.add(client) client.on('emberTree', (tree: DecodeResult>) => this._handleIncoming(tree, client)) @@ -123,7 +124,7 @@ class EmberServer extends EventEmitter { elPath = elPath.slice(0, -2) // remove the last element number } - for (const [path, clients] of Object.entries(this._subscriptions)) { + for (const [path, clients] of Object.entries(this._subscriptions)) { if (elPath === path) { clients.forEach((client) => { client.sendBER(data) @@ -166,7 +167,7 @@ class EmberServer extends EventEmitter { }) const data = berEncode([qualified], RootType.Elements) - for (const [path, clients] of Object.entries(this._subscriptions)) { + for (const [path, clients] of Object.entries(this._subscriptions)) { if (qualified.path === path) { clients.forEach((client) => { client.sendBER(data) @@ -175,7 +176,7 @@ class EmberServer extends EventEmitter { } } - private _handleIncoming(incoming: DecodeResult>, client: S101Client) { + private _handleIncoming(incoming: DecodeResult>, client: S101Socket) { for (const rootEl of Object.values(incoming.value)) { if (rootEl.contents.type === ElementType.Command) { // command on root @@ -191,7 +192,7 @@ class EmberServer extends EventEmitter { private _handleNode( path: string, el: QualifiedElement | NumberedTreeNode, - client: S101Client + client: S101Socket ) { const children = Object.values>(el.children || {}) @@ -229,7 +230,7 @@ class EmberServer extends EventEmitter { private async _handleSetValue( path: string, el: QualifiedElement | NumberedTreeNode, - client: S101Client + client: S101Socket ) { const tree = this.getElementByPath(path) if (!tree || tree.contents.type !== ElementType.Parameter || el.contents.value === undefined) return @@ -247,7 +248,7 @@ class EmberServer extends EventEmitter { } } - private async _handleCommand(path: string, el: NumberedTreeNode, client: S101Client) { + private async _handleCommand(path: string, el: NumberedTreeNode, client: S101Socket) { const tree = path ? this.getElementByPath(path) : this.tree if (!tree) return @@ -303,10 +304,10 @@ class EmberServer extends EventEmitter { return tree } - private _subscribe(path: string, client: S101Client) { + private _subscribe(path: string, client: S101Socket) { this._subscriptions[path] = [...(this._subscriptions[path] || []), client] } - private _unsubscribe(path: string, client: S101Client) { + private _unsubscribe(path: string, client: S101Socket) { if (!this._subscriptions[path]) return this._subscriptions[path].forEach((c, i) => { @@ -315,7 +316,7 @@ class EmberServer extends EventEmitter { } }) } - private _clearSubscription(client: S101Client) { + private _clearSubscription(client: S101Socket) { for (const path of Object.keys(this._subscriptions)) { this._unsubscribe(path, client) } @@ -324,7 +325,7 @@ class EmberServer extends EventEmitter { private _handleGetDirectory( tree: Collection> | NumberedTreeNode, _dirFieldMasks: FieldFlags, - client: S101Client + client: S101Socket ) { if (tree === this.tree) { // getDir on root diff --git a/src/Ember/Socket/S101Server.ts b/src/Ember/Socket/S101Server.ts index 298caf6..f50051a 100644 --- a/src/Ember/Socket/S101Server.ts +++ b/src/Ember/Socket/S101Server.ts @@ -1,8 +1,13 @@ -import { EventEmitter } from 'events' +import { EventEmitter } from 'eventemitter3' import { Socket, createServer, Server } from 'net' import S101Socket from './S101Socket' -export class S101Server extends EventEmitter { +export type S101ServerEvents = { + error: [Error] + listening: [] + connection: [client: S101Socket] +} +export class S101Server extends EventEmitter { port: number address: string | undefined server: Server | null diff --git a/src/Ember/Socket/S101Socket.ts b/src/Ember/Socket/S101Socket.ts index bfe219c..cbd446d 100644 --- a/src/Ember/Socket/S101Socket.ts +++ b/src/Ember/Socket/S101Socket.ts @@ -1,4 +1,4 @@ -import { EventEmitter } from 'events' +import { EventEmitter } from 'eventemitter3' import { Socket } from 'net' import { S101Codec } from '../../S101' @@ -8,7 +8,16 @@ import { normalizeError } from '../Lib/util' export type Request = any -export default class S101Socket extends EventEmitter { +export type S101SocketEvents = { + error: [Error] + emberPacket: [packet: Buffer] + emberTree: [root: any] + connecting: [] + connected: [] + disconnected: [] +} + +export default class S101Socket extends EventEmitter { socket: Socket | undefined keepaliveInterval = 10 keepaliveMaxResponseTime = 500 @@ -50,20 +59,6 @@ export default class S101Socket extends EventEmitter { this._initSocket() } - // Overide EventEmitter.on() for stronger typings: - on: ((event: 'emberPacket', listener: (packet: Buffer) => void) => this) & - ((event: 'emberTree', listener: (root: any) => void) => this) & - ((event: 'error', listener: (error: Error) => void) => this) & - ((event: 'connecting', listener: () => void) => this) & - ((event: 'connected', listener: () => void) => this) & - ((event: 'disconnected', listener: () => void) => this) = super.on - emit: ((event: 'emberPacket', packet: Buffer) => boolean) & - ((event: 'emberTree', root: any) => boolean) & - ((event: 'error', error: Error) => boolean) & - ((event: 'connecting') => boolean) & - ((event: 'connected') => boolean) & - ((event: 'disconnected') => boolean) = super.emit - _initSocket(): void { if (this.socket != null) { this.socket.on('data', (data) => { diff --git a/src/S101/S101Codec.ts b/src/S101/S101Codec.ts index 4842be3..616dddb 100644 --- a/src/S101/S101Codec.ts +++ b/src/S101/S101Codec.ts @@ -1,4 +1,4 @@ -import { EventEmitter } from 'events' +import { EventEmitter } from 'eventemitter3' import { SmartBuffer } from 'smart-buffer' import Debug from 'debug' import { format } from 'util' @@ -52,7 +52,13 @@ const CRC_TABLE = [ 0x3de3, 0x2c6a, 0x1ef1, 0x0f78, ] -export default class S101Codec extends EventEmitter { +export type S101CodecEvents = { + emberPacket: [packet: Buffer] + keepaliveReq: [] + keepaliveResp: [] +} + +export default class S101Codec extends EventEmitter { inbuf = new SmartBuffer() emberbuf = new SmartBuffer() escaped = false @@ -226,14 +232,6 @@ export default class S101Codec extends EventEmitter { return this._finalizeBuffer(frame) } - // Overide EventEmitter.on() for stronger typings: - on: ((event: 'emberPacket', listener: (packet: Buffer) => void) => this) & - ((event: 'keepaliveReq', listener: () => void) => this) & - ((event: 'keepaliveResp', listener: () => void) => this) = super.on - emit: ((event: 'emberPacket', packet: Buffer) => boolean) & - ((event: 'keepaliveReq') => boolean) & - ((event: 'keepaliveResp') => boolean) = super.emit - private _finalizeBuffer(smartbuf: SmartBuffer) { const crc = ~this._calculateCRCCE(smartbuf.toBuffer().slice(1, smartbuf.length)) & 0xffff const crcHi = crc >> 8 diff --git a/src/index.ts b/src/index.ts index dd1cc31..49a0fd1 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,6 +1,6 @@ -import { EmberClient } from './Ember/Client/index' +import { EmberClient, EmberClientEvents } from './Ember/Client/index' import { EmberLib } from './Ember/Lib/index' -import { EmberServer, ServerEvents } from './Ember/Server/index' +import { EmberServer, EmberServerEvents } from './Ember/Server/index' import { S101Codec } from './S101/index' import { S101Client } from './Ember/Socket/index' // import { EmberTreeNode, TreeElement } from './types/types' @@ -40,10 +40,11 @@ const Decoder = EmberLib.DecodeBuffer export { EmberClient, + EmberClientEvents, Decoder, EmberLib, EmberServer, - ServerEvents, + EmberServerEvents, S101Codec, S101Client, berEncode, diff --git a/yarn.lock b/yarn.lock index bccde53..62c30e0 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2028,6 +2028,7 @@ asn1@evs-broadcast/node-asn1: "@types/long": ^4.0.1 asn1: evs-broadcast/node-asn1 debug: ^4.3.4 + eventemitter3: ^5.0.1 jest: ^29.7.0 long: ^3.2.0 rimraf: ^5.0.5 @@ -2341,6 +2342,13 @@ asn1@evs-broadcast/node-asn1: languageName: node linkType: hard +"eventemitter3@npm:^5.0.1": + version: 5.0.1 + resolution: "eventemitter3@npm:5.0.1" + checksum: 543d6c858ab699303c3c32e0f0f47fc64d360bf73c3daf0ac0b5079710e340d6fe9f15487f94e66c629f5f82cd1a8678d692f3dbb6f6fcd1190e1b97fcad36f8 + languageName: node + linkType: hard + "execa@npm:^5.0.0": version: 5.1.1 resolution: "execa@npm:5.1.1"