From 3520fe30b538ca9c2b9df42a193ae0525b90a65e Mon Sep 17 00:00:00 2001
From: Dani Martinez <dan256_6@hotmail.com>
Date: Wed, 30 Oct 2024 15:43:31 +0000
Subject: [PATCH] Migrate from typescript-event-target to eventemitter3 (#5)

---
 README.md                    | 10 +++----
 package.json                 |  2 +-
 src/client/bluetooth_impl.ts | 18 ++++++------
 src/client/index.ts          | 20 ++++++--------
 src/client/serial_impl.ts    | 20 +++++++-------
 src/{client => }/events.ts   | 53 ++++++++++++++++++++----------------
 src/index.ts                 |  1 +
 src/packets/abstraction.ts   | 25 +++++++++--------
 yarn.lock                    | 10 +++----
 9 files changed, 83 insertions(+), 76 deletions(-)
 rename src/{client => }/events.ts (53%)

diff --git a/README.md b/README.md
index 27c665c..d4bb916 100644
--- a/README.md
+++ b/README.md
@@ -28,23 +28,23 @@ import { Utils, RequestCommandId, ResponseCommandId, NiimbotBluetoothClient, Ima
 
 const client = new NiimbotBluetoothClient();
 
-client.addEventListener("packetsent", (e) => {
+client.on("packetsent", (e) => {
   console.log(`>> ${Utils.bufToHex(e.packet.toBytes())} (${RequestCommandId[e.packet.command]})`);
 });
 
-client.addEventListener("packetreceived", (e) => {
+client.on("packetreceived", (e) => {
   console.log(`<< ${Utils.bufToHex(e.packet.toBytes())} (${ResponseCommandId[e.packet.command]})`);
 });
 
-client.addEventListener("connect", () => {
+client.on("connect", () => {
   console.log("connected");
 });
 
-client.addEventListener("disconnect", () => {
+client.on("disconnect", () => {
   console.log("disconnected");
 });
 
-client.addEventListener("printprogress", (e) => {
+client.on("printprogress", (e) => {
   console.log(`Page ${e.page}/${e.pagesTotal}, Page print ${e.pagePrintProgress}%, Page feed ${e.pageFeedProgress}%`);
 });
 
diff --git a/package.json b/package.json
index 4d3e25f..e36b8f6 100644
--- a/package.json
+++ b/package.json
@@ -40,7 +40,7 @@
   },
   "dependencies": {
     "async-mutex": "^0.5.0",
-    "typescript-event-target": "^1.1.1"
+    "eventemitter3": "^5.0.1"
   },
   "packageManager": "yarn@1.22.22+sha512.a6b2f7906b721bba3d67d4aff083df04dad64c399707841b7acf00f6b133b7ac24255f2652fa22ae3534329dc6180534e98d17432037ff6fd140556e2bb3137e"
 }
diff --git a/src/client/bluetooth_impl.ts b/src/client/bluetooth_impl.ts
index 45ef205..719cc1d 100644
--- a/src/client/bluetooth_impl.ts
+++ b/src/client/bluetooth_impl.ts
@@ -5,7 +5,7 @@ import {
   PacketReceivedEvent,
   RawPacketReceivedEvent,
   RawPacketSentEvent,
-} from "./events";
+} from "../events";
 import { ConnectionInfo, NiimbotAbstractClient } from ".";
 import { NiimbotPacket } from "../packets/packet";
 import { ConnectResult, ResponseCommandId } from "../packets";
@@ -55,7 +55,7 @@ export class NiimbotBluetoothClient extends NiimbotAbstractClient {
       this.gattServer = undefined;
       this.channel = undefined;
       this.info = {};
-      this.dispatchTypedEvent("disconnect", new DisconnectEvent());
+      this.emit("disconnect", new DisconnectEvent());
       device.removeEventListener("gattserverdisconnected", disconnectListener);
     };
 
@@ -72,8 +72,8 @@ export class NiimbotBluetoothClient extends NiimbotAbstractClient {
       const data = new Uint8Array(target.value!.buffer);
       const packet = NiimbotPacket.fromBytes(data);
 
-      this.dispatchTypedEvent("rawpacketreceived", new RawPacketReceivedEvent(data));
-      this.dispatchTypedEvent("packetreceived", new PacketReceivedEvent(packet));
+      this.emit("rawpacketreceived", new RawPacketReceivedEvent(data));
+      this.emit("packetreceived", new PacketReceivedEvent(packet));
 
       if (!(packet.command in ResponseCommandId)) {
         console.warn(`Unknown response command: 0x${Utils.numberToHex(packet.command)}`);
@@ -98,7 +98,7 @@ export class NiimbotBluetoothClient extends NiimbotAbstractClient {
       result: this.info.connectResult ?? ConnectResult.FirmwareErrors,
     };
 
-    this.dispatchTypedEvent("connect", new ConnectEvent(result));
+    this.emit("connect", new ConnectEvent(result));
 
     return result;
   }
@@ -139,17 +139,17 @@ export class NiimbotBluetoothClient extends NiimbotAbstractClient {
             packet.validResponseIds.includes(evt.packet.command as ResponseCommandId)
           ) {
             clearTimeout(timeout);
-            this.removeEventListener("packetreceived", listener);
+            this.off("packetreceived", listener);
             resolve(evt.packet);
           }
         };
 
         timeout = setTimeout(() => {
-          this.removeEventListener("packetreceived", listener);
+          this.off("packetreceived", listener);
           reject(new Error(`Timeout waiting response (waited for ${Utils.bufToHex(packet.validResponseIds, ", ")})`));
         }, timeoutMs ?? 1000);
 
-        this.addEventListener("packetreceived", listener);
+        this.on("packetreceived", listener);
       });
     });
   }
@@ -161,7 +161,7 @@ export class NiimbotBluetoothClient extends NiimbotAbstractClient {
       }
       await Utils.sleep(this.packetIntervalMs);
       await this.channel.writeValueWithoutResponse(data);
-      this.dispatchTypedEvent("rawpacketsent", new RawPacketSentEvent(data));
+      this.emit("rawpacketsent", new RawPacketSentEvent(data));
     };
 
     if (force) {
diff --git a/src/client/index.ts b/src/client/index.ts
index 6a9b3ac..aa63927 100644
--- a/src/client/index.ts
+++ b/src/client/index.ts
@@ -1,4 +1,4 @@
-import { TypedEventTarget } from "typescript-event-target";
+import { EventEmitter } from "eventemitter3";
 import {
   Abstraction,
   AutoShutdownTime,
@@ -8,7 +8,7 @@ import {
   NiimbotPacket,
 } from "../packets";
 import { PrinterModelMeta, getPrinterMetaById } from "../printer_models";
-import { ClientEventMap, PacketSentEvent, PrinterInfoFetchedEvent, HeartbeatEvent, HeartbeatFailedEvent } from "./events";
+import { ClientEventMap, PacketSentEvent, PrinterInfoFetchedEvent, HeartbeatEvent, HeartbeatFailedEvent } from "../events";
 import { findPrintTask, PrintTaskName } from "../print_tasks";
 
 export type ConnectionInfo = {
@@ -29,7 +29,7 @@ export interface PrinterInfo {
   hardwareVersion?: string;
 }
 
-export abstract class NiimbotAbstractClient extends TypedEventTarget<ClientEventMap> {
+export abstract class NiimbotAbstractClient extends EventEmitter<ClientEventMap> {
   public readonly abstraction: Abstraction;
   protected info: PrinterInfo = {};
   private heartbeatTimer?: NodeJS.Timeout;
@@ -42,8 +42,8 @@ export abstract class NiimbotAbstractClient extends TypedEventTarget<ClientEvent
   constructor() {
     super();
     this.abstraction = new Abstraction(this);
-    this.addEventListener("connect", () => this.startHeartbeat())
-    this.addEventListener("disconnect", () => this.stopHeartbeat())
+    this.on("connect", () => this.startHeartbeat());
+    this.on("disconnect", () => this.stopHeartbeat());
   }
 
   /** Connect to printer port */
@@ -70,7 +70,7 @@ export abstract class NiimbotAbstractClient extends TypedEventTarget<ClientEvent
 
   public async sendPacket(packet: NiimbotPacket, force?: boolean) {
     await this.sendRaw(packet.toBytes(), force);
-    this.dispatchTypedEvent("packetsent", new PacketSentEvent(packet));
+    this.emit("packetsent", new PacketSentEvent(packet));
   }
 
   /** Send "connect" packet and fetch the protocol version */
@@ -98,7 +98,7 @@ export abstract class NiimbotAbstractClient extends TypedEventTarget<ClientEvent
     this.info.hardwareVersion = await this.abstraction.getHardwareVersion().catch(console.error) ?? undefined;
     this.info.softwareVersion = await this.abstraction.getSoftwareVersion().catch(console.error) ?? undefined;
 
-    this.dispatchTypedEvent("printerinfofetched", new PrinterInfoFetchedEvent(this.info));
+    this.emit("printerinfofetched", new PrinterInfoFetchedEvent(this.info));
     return this.info;
   }
 
@@ -115,7 +115,6 @@ export abstract class NiimbotAbstractClient extends TypedEventTarget<ClientEvent
     this.heartbeatIntervalMs = intervalMs;
   }
 
-
   /**
    * Starts the heartbeat timer, "heartbeat" is emitted after packet received.
    *
@@ -131,12 +130,12 @@ export abstract class NiimbotAbstractClient extends TypedEventTarget<ClientEvent
         .heartbeat()
         .then((data) => {
           this.heartbeatFails = 0;
-          this.dispatchTypedEvent("heartbeat", new HeartbeatEvent(data));
+          this.emit("heartbeat", new HeartbeatEvent(data));
         })
         .catch((e) => {
           console.error(e);
           this.heartbeatFails++;
-          this.dispatchTypedEvent("heartbeatfailed", new HeartbeatFailedEvent(this.heartbeatFails));
+          this.emit("heartbeatfailed", new HeartbeatFailedEvent(this.heartbeatFails));
         });
     }, this.heartbeatIntervalMs);
   }
@@ -174,6 +173,5 @@ export abstract class NiimbotAbstractClient extends TypedEventTarget<ClientEvent
   }
 }
 
-export * from "./events";
 export * from "./bluetooth_impl";
 export * from "./serial_impl";
diff --git a/src/client/serial_impl.ts b/src/client/serial_impl.ts
index 6beaed7..e5c846f 100644
--- a/src/client/serial_impl.ts
+++ b/src/client/serial_impl.ts
@@ -5,7 +5,7 @@ import {
   PacketReceivedEvent,
   RawPacketReceivedEvent,
   RawPacketSentEvent,
-} from "./events";
+} from "../events";
 import { ConnectionInfo, NiimbotAbstractClient } from ".";
 import { NiimbotPacket } from "../packets/packet";
 import { ConnectResult, ResponseCommandId } from "../packets";
@@ -25,7 +25,7 @@ export class NiimbotSerialClient extends NiimbotAbstractClient {
 
     _port.addEventListener("disconnect", () => {
       this.port = undefined;
-      this.dispatchTypedEvent("disconnect", new DisconnectEvent());
+      this.emit("disconnect", new DisconnectEvent());
     });
 
     await _port.open({ baudRate: 115200 });
@@ -62,7 +62,7 @@ export class NiimbotSerialClient extends NiimbotAbstractClient {
       result: this.info.connectResult ?? ConnectResult.FirmwareErrors,
     };
 
-    this.dispatchTypedEvent("connect", new ConnectEvent(result));
+    this.emit("connect", new ConnectEvent(result));
     return result;
   }
 
@@ -93,10 +93,10 @@ export class NiimbotSerialClient extends NiimbotAbstractClient {
         const packets: NiimbotPacket[] = NiimbotPacket.fromBytesMultiPacket(buf);
 
         if (packets.length > 0) {
-          this.dispatchTypedEvent("rawpacketreceived", new RawPacketReceivedEvent(buf));
+          this.emit("rawpacketreceived", new RawPacketReceivedEvent(buf));
 
           packets.forEach((p) => {
-            this.dispatchTypedEvent("packetreceived", new PacketReceivedEvent(p));
+            this.emit("packetreceived", new PacketReceivedEvent(p));
           });
 
           buf = new Uint8Array();
@@ -120,7 +120,7 @@ export class NiimbotSerialClient extends NiimbotAbstractClient {
 
     if (this.port !== undefined) {
       await this.port.close();
-      this.dispatchTypedEvent("disconnect", new DisconnectEvent());
+      this.emit("disconnect", new DisconnectEvent());
     }
 
     this.port = undefined;
@@ -152,17 +152,17 @@ export class NiimbotSerialClient extends NiimbotAbstractClient {
             packet.validResponseIds.includes(evt.packet.command as ResponseCommandId)
           ) {
             clearTimeout(timeout);
-            this.removeEventListener("packetreceived", listener);
+            this.off("packetreceived", listener);
             resolve(evt.packet);
           }
         };
 
         timeout = setTimeout(() => {
-          this.removeEventListener("packetreceived", listener);
+          this.off("packetreceived", listener);
           reject(new Error(`Timeout waiting response (waited for ${Utils.bufToHex(packet.validResponseIds, ", ")})`));
         }, timeoutMs ?? 1000);
 
-        this.addEventListener("packetreceived", listener);
+        this.on("packetreceived", listener);
       });
     });
   }
@@ -174,7 +174,7 @@ export class NiimbotSerialClient extends NiimbotAbstractClient {
       }
       await Utils.sleep(this.packetIntervalMs);
       await this.writer.write(data);
-      this.dispatchTypedEvent("rawpacketsent", new RawPacketSentEvent(data));
+      this.emit("rawpacketsent", new RawPacketSentEvent(data));
     };
 
     if (force) {
diff --git a/src/client/events.ts b/src/events.ts
similarity index 53%
rename from src/client/events.ts
rename to src/events.ts
index 42316aa..2d8b80f 100644
--- a/src/client/events.ts
+++ b/src/events.ts
@@ -1,9 +1,16 @@
 import { ConnectionInfo, PrinterInfo } from ".";
-import { HeartbeatData } from "../packets/abstraction";
-import { NiimbotPacket } from "../packets/packet";
+import { HeartbeatData } from "./packets/abstraction";
+import { NiimbotPacket } from "./packets/packet";
 
+export class NiimbotEvent {
+  readonly type: string;
 
-export class ConnectEvent extends Event {
+  constructor(type: string) {
+    this.type = type;
+  }
+}
+
+export class ConnectEvent extends NiimbotEvent {
   info: ConnectionInfo;
   constructor(info: ConnectionInfo) {
     super("connect");
@@ -11,13 +18,13 @@ export class ConnectEvent extends Event {
   }
 }
 
-export class DisconnectEvent extends Event {
+export class DisconnectEvent extends NiimbotEvent {
   constructor() {
     super("disconnect");
   }
 }
 
-export class PacketReceivedEvent extends Event {
+export class PacketReceivedEvent extends NiimbotEvent {
   packet: NiimbotPacket;
   constructor(packet: NiimbotPacket) {
     super("packetreceived");
@@ -25,7 +32,7 @@ export class PacketReceivedEvent extends Event {
   }
 }
 
-export class PacketSentEvent extends Event {
+export class PacketSentEvent extends NiimbotEvent {
   packet: NiimbotPacket;
   constructor(packet: NiimbotPacket) {
     super("packetsent");
@@ -33,7 +40,7 @@ export class PacketSentEvent extends Event {
   }
 }
 
-export class RawPacketSentEvent extends Event {
+export class RawPacketSentEvent extends NiimbotEvent {
   data: Uint8Array;
   constructor(data: Uint8Array) {
     super("rawpacketsent");
@@ -41,7 +48,7 @@ export class RawPacketSentEvent extends Event {
   }
 }
 
-export class RawPacketReceivedEvent extends Event {
+export class RawPacketReceivedEvent extends NiimbotEvent {
   data: Uint8Array;
   constructor(data: Uint8Array) {
     super("rawpacketreceived");
@@ -49,7 +56,7 @@ export class RawPacketReceivedEvent extends Event {
   }
 }
 
-export class HeartbeatEvent extends Event {
+export class HeartbeatEvent extends NiimbotEvent {
   data: HeartbeatData;
   constructor(data: HeartbeatData) {
     super("heartbeat");
@@ -57,7 +64,7 @@ export class HeartbeatEvent extends Event {
   }
 }
 
-export class HeartbeatFailedEvent extends Event {
+export class HeartbeatFailedEvent extends NiimbotEvent {
   failedAttempts: number;
   constructor(failedAttempts: number) {
     super("heartbeatfailed");
@@ -65,7 +72,7 @@ export class HeartbeatFailedEvent extends Event {
   }
 }
 
-export class PrinterInfoFetchedEvent extends Event {
+export class PrinterInfoFetchedEvent extends NiimbotEvent {
   info: PrinterInfo;
   constructor(info: PrinterInfo) {
     super("printerinfofetched");
@@ -73,7 +80,7 @@ export class PrinterInfoFetchedEvent extends Event {
   }
 }
 
-export class PrintProgressEvent extends Event {
+export class PrintProgressEvent extends NiimbotEvent {
   /** 0 – n */
   page: number;
 
@@ -92,15 +99,15 @@ export class PrintProgressEvent extends Event {
   }
 }
 
-export interface ClientEventMap {
-  connect: ConnectEvent;
-  disconnect: DisconnectEvent;
-  rawpacketsent: RawPacketSentEvent;
-  rawpacketreceived: RawPacketReceivedEvent;
-  packetreceived: PacketReceivedEvent;
-  packetsent: PacketSentEvent;
-  heartbeat: HeartbeatEvent;
-  heartbeatfailed: HeartbeatFailedEvent;
-  printerinfofetched: PrinterInfoFetchedEvent;
-  printprogress: PrintProgressEvent;
+export type ClientEventMap = {
+  connect: (event: ConnectEvent) => void;
+  disconnect: (event: DisconnectEvent) => void;
+  rawpacketsent: (event: RawPacketSentEvent) => void;
+  rawpacketreceived: (event: RawPacketReceivedEvent) => void;
+  packetreceived: (event: PacketReceivedEvent) => void;
+  packetsent: (event: PacketSentEvent) => void;
+  heartbeat: (event: HeartbeatEvent) => void;
+  heartbeatfailed: (event: HeartbeatFailedEvent) => void;
+  printerinfofetched: (event: PrinterInfoFetchedEvent) => void;
+  printprogress: (event: PrintProgressEvent) => void;
 }
diff --git a/src/index.ts b/src/index.ts
index 1769d2c..619dcf1 100644
--- a/src/index.ts
+++ b/src/index.ts
@@ -1,5 +1,6 @@
 export * from "./client";
 export * from "./packets";
+export * from "./events";
 export * from "./image_encoder";
 export * from "./utils";
 export * from "./printer_models";
diff --git a/src/packets/abstraction.ts b/src/packets/abstraction.ts
index 84eadd3..60da90d 100644
--- a/src/packets/abstraction.ts
+++ b/src/packets/abstraction.ts
@@ -10,7 +10,8 @@ import {
   SoundSettingsItemType,
   SoundSettingsType,
 } from ".";
-import { NiimbotAbstractClient, PacketReceivedEvent, PrintProgressEvent } from "../client";
+import { NiimbotAbstractClient } from "../client";
+import { PacketReceivedEvent, PrintProgressEvent } from "../events";
 import { PrintTaskName, printTasks } from "../print_tasks";
 import { AbstractPrintTask, PrintOptions } from "../print_tasks/AbstractPrintTask";
 import { Validators, Utils } from "../utils";
@@ -365,17 +366,17 @@ export class Abstraction {
           Validators.u8ArrayLengthEquals(evt.packet.data, 2);
           const page = Utils.bytesToI16(evt.packet.data);
 
-          this.client.dispatchTypedEvent("printprogress", new PrintProgressEvent(page, pagesToPrint, 100, 100));
+          this.client.emit("printprogress", new PrintProgressEvent(page, pagesToPrint, 100, 100));
 
           clearTimeout(this.statusTimeoutTimer);
           this.statusTimeoutTimer = setTimeout(() => {
-            this.client.removeEventListener("packetreceived", listener);
+            this.client.off("packetreceived", listener);
             reject(new Error("Timeout waiting print status"));
           }, timeoutMs ?? 5_000);
 
           if (page === pagesToPrint) {
             clearTimeout(this.statusTimeoutTimer);
-            this.client.removeEventListener("packetreceived", listener);
+            this.client.off("packetreceived", listener);
             resolve();
           }
         }
@@ -383,12 +384,12 @@ export class Abstraction {
 
       clearTimeout(this.statusTimeoutTimer);
       this.statusTimeoutTimer = setTimeout(() => {
-        this.client.removeEventListener("packetreceived", listener);
+        this.client.off("packetreceived", listener);
         reject(new Error("Timeout waiting print status"));
       }, timeoutMs);
 
-      this.client.dispatchTypedEvent("printprogress", new PrintProgressEvent(1, pagesToPrint, 0, 0));
-      this.client.addEventListener("packetreceived", listener);
+      this.client.emit("printprogress", new PrintProgressEvent(1, pagesToPrint, 0, 0));
+      this.client.on("packetreceived", listener);
     });
   }
 
@@ -402,12 +403,12 @@ export class Abstraction {
    */
   public async waitUntilPrintFinishedByStatusPoll(pagesToPrint: number, pollIntervalMs: number = 300): Promise<void> {
     return new Promise<void>((resolve, reject) => {
-      this.client.dispatchTypedEvent("printprogress", new PrintProgressEvent(1, pagesToPrint, 0, 0));
+      this.client.emit("printprogress", new PrintProgressEvent(1, pagesToPrint, 0, 0));
 
       this.statusPollTimer = setInterval(() => {
         this.getPrintStatus()
           .then((status: PrintStatus) => {
-            this.client.dispatchTypedEvent(
+            this.client.emit(
               "printprogress",
               new PrintProgressEvent(status.page, pagesToPrint, status.pagePrintProgress, status.pageFeedProgress)
             );
@@ -437,15 +438,15 @@ export class Abstraction {
    */
   public async waitUntilPrintFinishedByPrintEndPoll(pagesToPrint: number, pollIntervalMs: number = 500): Promise<void> {
     return new Promise<void>((resolve, reject) => {
-      this.client.dispatchTypedEvent("printprogress", new PrintProgressEvent(1, pagesToPrint, 0, 0));
+      this.client.emit("printprogress", new PrintProgressEvent(1, pagesToPrint, 0, 0));
 
       this.statusPollTimer = setInterval(() => {
         this.printEnd()
           .then((printEndDone: boolean) => {
             if(!printEndDone) {
-              this.client.dispatchTypedEvent("printprogress", new PrintProgressEvent(1, pagesToPrint, 0, 0));
+              this.client.emit("printprogress", new PrintProgressEvent(1, pagesToPrint, 0, 0));
             } else {
-              this.client.dispatchTypedEvent("printprogress", new PrintProgressEvent(pagesToPrint, pagesToPrint, 100, 100));
+              this.client.emit("printprogress", new PrintProgressEvent(pagesToPrint, pagesToPrint, 100, 100));
               clearInterval(this.statusPollTimer);
               resolve();
             }
diff --git a/yarn.lock b/yarn.lock
index ca7e47a..c653b1d 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -26,16 +26,16 @@ async-mutex@^0.5.0:
   dependencies:
     tslib "^2.4.0"
 
+eventemitter3@^5.0.1:
+  version "5.0.1"
+  resolved "https://registry.yarnpkg.com/eventemitter3/-/eventemitter3-5.0.1.tgz#53f5ffd0a492ac800721bb42c66b841de96423c4"
+  integrity sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA==
+
 tslib@^2.4.0:
   version "2.6.3"
   resolved "https://registry.npmjs.org/tslib/-/tslib-2.6.3.tgz"
   integrity sha512-xNvxJEOUiWPGhUuUdQgAJPKOOJfGnIyKySOc09XkKsgdUV/3E2zvwZYdejjmRgPCgcym1juLH3226yA7sEFJKQ==
 
-typescript-event-target@^1.1.1:
-  version "1.1.1"
-  resolved "https://registry.npmjs.org/typescript-event-target/-/typescript-event-target-1.1.1.tgz"
-  integrity sha512-dFSOFBKV6uwaloBCCUhxlD3Pr/P1a/tJdcmPrTXCHlEFD3faj0mztjcGn6VBAhQ0/Bdy8K3VWrrqwbt/ffsYsg==
-
 typescript@^5.4.5:
   version "5.4.5"
   resolved "https://registry.npmjs.org/typescript/-/typescript-5.4.5.tgz"