const SLACK_AUTH_ERROR_RE =
/account_inactive|invalid_auth|token_revoked|token_expired|not_authed|org_login_required|team_access_not_granted|missing_scope|cannot_find_service|invalid_token/i;
export const SLACK_SOCKET_RECONNECT_POLICY = {
initialMs: 2 _000 ,
maxMs: 30 _000 ,
factor: 1 .8 ,
jitter: 0 .25 ,
maxAttempts: 12 ,
} as const ;
export type SlackSocketDisconnectEvent = "disconnect" | "unable_to_socket_mode_start" | "error" ;
type EmitterLike = {
on: (event: string, listener: (...args: unknown[]) => void ) => unknown;
off: (event: string, listener: (...args: unknown[]) => void ) => unknown;
};
export function getSocketEmitter(app: unknown): EmitterLike | null {
const receiver = (app as { receiver?: unknown }).receiver;
const client =
receiver && typeof receiver === "object"
? (receiver as { client?: unknown }).client
: undefined;
if (!client || typeof client !== "object" ) {
return null ;
}
const on = (client as { on?: unknown }).on;
const off = (client as { off?: unknown }).off;
if (typeof on !== "function" || typeof off !== "function" ) {
return null ;
}
return {
on: (event, listener) =>
(
on as (this : unknown, event: string, listener: (...args: unknown[]) => void ) => unknown
).call(client, event, listener),
off: (event, listener) =>
(
off as (this : unknown, event: string, listener: (...args: unknown[]) => void ) => unknown
).call(client, event, listener),
};
}
export function waitForSlackSocketDisconnect(
app: unknown,
abortSignal?: AbortSignal,
): Promise<{
event: SlackSocketDisconnectEvent;
error?: unknown;
}> {
return new Promise((resolve) => {
const emitter = getSocketEmitter(app);
if (!emitter) {
abortSignal?.addEventListener("abort" , () => resolve({ event: "disconnect" }), {
once: true ,
});
return ;
}
const disconnectListener = () => resolveOnce({ event: "disconnect" });
const startFailListener = (error?: unknown) =>
resolveOnce({ event: "unable_to_socket_mode_start" , error });
const errorListener = (error: unknown) => resolveOnce({ event: "error" , error });
const abortListener = () => resolveOnce({ event: "disconnect" });
const cleanup = () => {
emitter.off("disconnected" , disconnectListener);
emitter.off("unable_to_socket_mode_start" , startFailListener);
emitter.off("error" , errorListener);
abortSignal?.removeEventListener("abort" , abortListener);
};
const resolveOnce = (value: { event: SlackSocketDisconnectEvent; error?: unknown }) => {
cleanup();
resolve(value);
};
emitter.on("disconnected" , disconnectListener);
emitter.on("unable_to_socket_mode_start" , startFailListener);
emitter.on("error" , errorListener);
abortSignal?.addEventListener("abort" , abortListener, { once: true });
});
}
/**
* Detect non - recoverable Slack API / auth errors that should NOT be retried .
* These indicate permanent credential problems ( revoked bot , deactivated account , etc . )
* and retrying will never succeed — continuing to retry blocks the entire gateway .
*/
export function isNonRecoverableSlackAuthError(error: unknown): boolean {
const msg = error instanceof Error ? error.message : typeof error === "string" ? error : "" ;
return SLACK_AUTH_ERROR_RE.test(msg);
}
export function formatUnknownError(error: unknown): string {
if (error instanceof Error) {
return error.message;
}
if (typeof error === "string" ) {
return error;
}
try {
return JSON.stringify(error);
} catch {
return "unknown error" ;
}
}
Messung V0.5 in Prozent C=100 H=99 G=99
¤ Dauer der Verarbeitung: 0.12 Sekunden
(vorverarbeitet am 2026-06-10)
¤
*© Formatika GbR, Deutschland