From 7ac10f36bc94e8d5ce1ff369a9d914cbb4910784 Mon Sep 17 00:00:00 2001 From: c43721 Date: Tue, 14 Oct 2025 19:42:28 -0500 Subject: [PATCH 1/4] feat: explicit type the message events This will help #7 with having untyped values (or explicitly typing values) --- .vscode/settings.json | 2 +- src/logReceiver.ts | 9 +++++++-- src/types.ts | 43 +++++++++++++++++++++++++++++++++++++++++++ tests/parser.bench.ts | 2 +- tests/parser.test.ts | 2 +- 5 files changed, 53 insertions(+), 5 deletions(-) diff --git a/.vscode/settings.json b/.vscode/settings.json index b080f0d..c58fe3a 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -7,4 +7,4 @@ "[json]": { "editor.defaultFormatter": "denoland.vscode-deno" } -} \ No newline at end of file +} diff --git a/src/logReceiver.ts b/src/logReceiver.ts index 24705f2..2b85771 100644 --- a/src/logReceiver.ts +++ b/src/logReceiver.ts @@ -2,7 +2,7 @@ import { EventEmitter } from "node:events"; import { createSocket, type RemoteInfo, type Socket } from "node:dgram"; import { parsePacket } from "./parser.ts"; -import type { EventData } from "./types.ts"; +import type { EventData, TypedEventEmitter } from "./types.ts"; /** * The socket options for the UDP socket @@ -30,6 +30,11 @@ export interface LogReceiverOptions { signal?: AbortSignal; } +type MessageEvents = { + error: (error: Error) => void; + event: (message: EventData) => void; +}; + /** * An event emitter that will emit a message event when a valid UDP log is created on the server * @@ -94,7 +99,7 @@ export interface LogReceiverOptions { * * For security reasons, you should always use a log secret to prevent evaluation of potentially malicious messages. Do this by looking at the password field. In order to set up the log secret, you can use the `sv_logsecret` command */ -export class LogReceiver extends EventEmitter implements Disposable { +export class LogReceiver extends (EventEmitter as new () => TypedEventEmitter) implements Disposable { #socket: Socket; /** diff --git a/src/types.ts b/src/types.ts index 11aef8c..e3c80d5 100644 --- a/src/types.ts +++ b/src/types.ts @@ -24,3 +24,46 @@ export interface EventData extends ParsedLogMessage { */ socket: RemoteInfo; } + +type EventMap = { + [key: string]: (...args: any[]) => void; +}; + +/** + * Copied from https://github.com/andywer/typed-emitter/blob/master/index.d.ts + * Type-safe event emitter + * + * Use it like this: + * + * ```ts + * type MyEvents = { + * error: (error: Error) => void; + * message: (from: string, content: string) => void; + * } + * + * const myEmitter = new EventEmitter() as TypedEmitter; + * + * myEmitter.emit("error", "x") // <- Will catch this type error; + * ``` + */ +interface TypedEventEmitter { + addListener(event: E, listener: Events[E]): this; + on(event: E, listener: Events[E]): this; + once(event: E, listener: Events[E]): this; + prependListener(event: E, listener: Events[E]): this; + prependOnceListener(event: E, listener: Events[E]): this; + + off(event: E, listener: Events[E]): this; + removeAllListeners(event?: E): this; + removeListener(event: E, listener: Events[E]): this; + + emit(event: E, ...args: Parameters): boolean; + // The sloppy `eventNames()` return type is to mitigate type incompatibilities - see #5 + eventNames(): (keyof Events | string | symbol)[]; + rawListeners(event: E): Events[E][]; + listeners(event: E): Events[E][]; + listenerCount(event: E): number; + + getMaxListeners(): number; + setMaxListeners(maxListeners: number): this; +} diff --git a/tests/parser.bench.ts b/tests/parser.bench.ts index 4748e3b..29db165 100644 --- a/tests/parser.bench.ts +++ b/tests/parser.bench.ts @@ -22,4 +22,4 @@ Deno.bench("Can parse a password", { baseline: true, group: "password" }, () => assertEquals(result?.message, body.substring(2)); assertEquals(result?.password, password); -}); \ No newline at end of file +}); diff --git a/tests/parser.test.ts b/tests/parser.test.ts index 2096d85..f04c628 100644 --- a/tests/parser.test.ts +++ b/tests/parser.test.ts @@ -57,4 +57,4 @@ Deno.test("Can parse a password", () => { assertEquals(result?.message, body.substring(2)); assertEquals(result?.password, password); -}); \ No newline at end of file +}); From 152723b627f665cb33859be9596808bf806b735a Mon Sep 17 00:00:00 2001 From: c43721 Date: Tue, 14 Oct 2025 19:53:13 -0500 Subject: [PATCH 2/4] fix: export from lib, fix missing type --- src/types.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/types.ts b/src/types.ts index e3c80d5..ff6275a 100644 --- a/src/types.ts +++ b/src/types.ts @@ -46,7 +46,7 @@ type EventMap = { * myEmitter.emit("error", "x") // <- Will catch this type error; * ``` */ -interface TypedEventEmitter { +export interface TypedEventEmitter { addListener(event: E, listener: Events[E]): this; on(event: E, listener: Events[E]): this; once(event: E, listener: Events[E]): this; From f29a85d09a49dfd8cc8aea8fcbce0f0c7d1c92cf Mon Sep 17 00:00:00 2001 From: c43721 Date: Tue, 14 Oct 2025 19:59:16 -0500 Subject: [PATCH 3/4] fix: remove previously implemented forwards, just forward error and message Realistically, you should be using abort controllers since you dont controll the socket. So stop trying to snoop. Bad mistake on my part and now I need to release a breaking change.... <.< --- examples/abort-controller.ts | 3 ++- src/logReceiver.ts | 3 --- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/examples/abort-controller.ts b/examples/abort-controller.ts index 338a7c4..3e4cdf2 100644 --- a/examples/abort-controller.ts +++ b/examples/abort-controller.ts @@ -12,6 +12,7 @@ const receiver = new LogReceiver({ console.log("Log receiver running.. "); receiver.on("event", (message) => console.log(message)); -receiver.on("close", () => console.log("Closed the socket")); controller.abort(); + +console.log("Closed the socket") diff --git a/src/logReceiver.ts b/src/logReceiver.ts index 2b85771..f040371 100644 --- a/src/logReceiver.ts +++ b/src/logReceiver.ts @@ -123,10 +123,7 @@ export class LogReceiver extends (EventEmitter as new () => TypedEventEmitter this.emit("close")); - this.#socket.on("connect", () => this.emit("connect")); this.#socket.on("error", (error) => this.emit("error", error)); - this.#socket.on("listening", () => this.emit("listening")); this.#socket.on("message", (buffer, serverInfo) => this.#handleMessage(buffer, serverInfo)); } From bbb170f435069d8db6b78f931b1f15b7300b1c17 Mon Sep 17 00:00:00 2001 From: c43721 Date: Tue, 14 Oct 2025 20:06:08 -0500 Subject: [PATCH 4/4] fix(lint): ignore the any --- src/types.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/types.ts b/src/types.ts index ff6275a..ae97b66 100644 --- a/src/types.ts +++ b/src/types.ts @@ -26,6 +26,7 @@ export interface EventData extends ParsedLogMessage { } type EventMap = { + // deno-lint-ignore no-explicit-any [key: string]: (...args: any[]) => void; };