Accept an async listener in the types and normalise any rejection reason
This commit is contained in:
@@ -0,0 +1,10 @@
|
||||
/** Whatever was thrown or rejected, as an Error. `String()` throws on some values; this cannot. */
|
||||
export function errorFrom(reason: unknown): Error {
|
||||
if (reason instanceof Error) return reason;
|
||||
|
||||
try {
|
||||
return new Error(String(reason));
|
||||
} catch {
|
||||
return new Error('A thrown value that cannot be converted to a string');
|
||||
}
|
||||
}
|
||||
+16
-4
@@ -9,6 +9,7 @@ import { bindTypeFromCommand, checkSessionOptions, undeclaredInterfaceVersion }
|
||||
import { createServer as createNetServer } from 'node:net';
|
||||
import { createServer as createTlsServer } from 'node:tls';
|
||||
import { defaultInterfaceVersion } from './defs/constants.ts';
|
||||
import { errorFrom } from './error-from.ts';
|
||||
import { paramText } from './defs/types.ts';
|
||||
import { silentLog } from './log.ts';
|
||||
|
||||
@@ -50,8 +51,19 @@ const defaults = {
|
||||
systemId: defaultSystemId,
|
||||
};
|
||||
|
||||
/** A listener may return a promise: an `async` one that rejects is routed like one that throws. */
|
||||
type ServerListener<K extends keyof ServerEvents> = (...args: ServerEvents[K]) => unknown;
|
||||
|
||||
/** A listening SMPP server. Sessions arrive as `session` events; `close()` stops listening. */
|
||||
export class SmppServer extends EventEmitter<ServerEvents> {
|
||||
declare addListener: <K extends keyof ServerEvents>(event: K, listener: ServerListener<K>) => this;
|
||||
declare off: <K extends keyof ServerEvents>(event: K, listener: ServerListener<K>) => this;
|
||||
declare on: <K extends keyof ServerEvents>(event: K, listener: ServerListener<K>) => this;
|
||||
declare once: <K extends keyof ServerEvents>(event: K, listener: ServerListener<K>) => this;
|
||||
declare prependListener: <K extends keyof ServerEvents>(event: K, listener: ServerListener<K>) => this;
|
||||
declare prependOnceListener: <K extends keyof ServerEvents>(event: K, listener: ServerListener<K>) => this;
|
||||
declare removeListener: <K extends keyof ServerEvents>(event: K, listener: ServerListener<K>) => this;
|
||||
|
||||
readonly sessions = new Set<Session>();
|
||||
|
||||
private readonly log: SmppLog;
|
||||
@@ -78,7 +90,7 @@ export class SmppServer extends EventEmitter<ServerEvents> {
|
||||
try {
|
||||
return super.emit(event, ...args);
|
||||
} catch (thrown: unknown) {
|
||||
const err = thrown instanceof Error ? thrown : new Error(String(thrown));
|
||||
const err = errorFrom(thrown);
|
||||
|
||||
this.log.error('server - a listener threw', { event, message: err.message });
|
||||
|
||||
@@ -95,7 +107,7 @@ export class SmppServer extends EventEmitter<ServerEvents> {
|
||||
...args: [event: keyof ServerEvents, ...rest: unknown[]]
|
||||
): void {
|
||||
const [event] = args;
|
||||
const error = reason instanceof Error ? reason : new Error(String(reason));
|
||||
const error = errorFrom(reason);
|
||||
|
||||
this.log.error('server - a listener rejected', { event, message: error.message });
|
||||
|
||||
@@ -109,7 +121,7 @@ export class SmppServer extends EventEmitter<ServerEvents> {
|
||||
try {
|
||||
session.close();
|
||||
} catch (thrown: unknown) {
|
||||
this.emit('serverError', thrown instanceof Error ? thrown : new Error(String(thrown)));
|
||||
this.emit('serverError', errorFrom(thrown));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -332,7 +344,7 @@ export function server(options: ServerOptions = {}): Promise<Result<{ server: Sm
|
||||
resolve({ server: smpp });
|
||||
});
|
||||
} catch (thrown: unknown) {
|
||||
onStartupError(thrown instanceof Error ? thrown : new Error(String(thrown)));
|
||||
onStartupError(errorFrom(thrown));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
+15
-3
@@ -15,6 +15,7 @@ import { PduFramer } from './pdu-framer.ts';
|
||||
import { PendingRequests } from './pending-requests.ts';
|
||||
import { ReconnectLoop } from './reconnect-loop.ts';
|
||||
import { SendWindow } from './send-window.ts';
|
||||
import { errorFrom } from './error-from.ts';
|
||||
import { optionalParamsMinVersion } from './defs/constants.ts';
|
||||
import { bindCarries, bindCommands, defaultSystemId, defaults } from './session-options.ts';
|
||||
import { isResp, objToPdu, pduReturn, pduToObj } from './pdu.ts';
|
||||
@@ -33,7 +34,18 @@ export type {
|
||||
export type { BindType };
|
||||
export { bindCommands, defaultSystemId };
|
||||
|
||||
/** A listener may return a promise: an `async` one that rejects is routed like one that throws. */
|
||||
type SessionListener<K extends keyof SessionEvents> = (...args: SessionEvents[K]) => unknown;
|
||||
|
||||
export class Session extends EventEmitter<SessionEvents> {
|
||||
declare addListener: <K extends keyof SessionEvents>(event: K, listener: SessionListener<K>) => this;
|
||||
declare off: <K extends keyof SessionEvents>(event: K, listener: SessionListener<K>) => this;
|
||||
declare on: <K extends keyof SessionEvents>(event: K, listener: SessionListener<K>) => this;
|
||||
declare once: <K extends keyof SessionEvents>(event: K, listener: SessionListener<K>) => this;
|
||||
declare prependListener: <K extends keyof SessionEvents>(event: K, listener: SessionListener<K>) => this;
|
||||
declare prependOnceListener: <K extends keyof SessionEvents>(event: K, listener: SessionListener<K>) => this;
|
||||
declare removeListener: <K extends keyof SessionEvents>(event: K, listener: SessionListener<K>) => this;
|
||||
|
||||
/** Replaced on reconnect, so hold the session rather than this. */
|
||||
sock: Socket;
|
||||
readonly log: SmppLog;
|
||||
@@ -65,7 +77,7 @@ export class Session extends EventEmitter<SessionEvents> {
|
||||
try {
|
||||
return super.emit(event, ...args);
|
||||
} catch (thrown: unknown) {
|
||||
const err = thrown instanceof Error ? thrown : new Error(String(thrown));
|
||||
const err = errorFrom(thrown);
|
||||
|
||||
this.log.error('session - a listener threw', { event, message: err.message });
|
||||
|
||||
@@ -82,7 +94,7 @@ export class Session extends EventEmitter<SessionEvents> {
|
||||
...args: [event: keyof SessionEvents, ...rest: unknown[]]
|
||||
): void {
|
||||
const [event] = args;
|
||||
const error = reason instanceof Error ? reason : new Error(String(reason));
|
||||
const error = errorFrom(reason);
|
||||
|
||||
this.log.error('session - a listener rejected', { event, message: error.message });
|
||||
|
||||
@@ -379,7 +391,7 @@ export class Session extends EventEmitter<SessionEvents> {
|
||||
this.emit('incomingPduObj', pduObj);
|
||||
// Every application hook and listener reached from an incoming PDU funnels through here.
|
||||
void this.incoming.handle(pduObj).catch((thrown: unknown) => {
|
||||
const err = thrown instanceof Error ? thrown : new Error(String(thrown));
|
||||
const err = errorFrom(thrown);
|
||||
|
||||
this.log.error('session - a handler threw', { message: err.message });
|
||||
this.emit('sessionError', err);
|
||||
|
||||
Reference in New Issue
Block a user