* refactor: move Discord channel implementation to extensions/discord/src/ Move all Discord source files from src/discord/ to extensions/discord/src/, following the extension migration pattern. Source files in src/discord/ are replaced with re-export shims. Channel-plugin files from src/channels/plugins/*/discord* are similarly moved and shimmed. - Copy all .ts source files preserving subdirectory structure (monitor/, voice/) - Move channel-plugin files (actions, normalize, onboarding, outbound, status-issues) - Fix all relative imports to use correct paths from new location - Create re-export shims at original locations for backward compatibility - Delete test files from shim locations (tests live in extension now) - Update tsconfig.plugin-sdk.dts.json rootDir from "src" to "." to accommodate extension files outside src/ - Update write-plugin-sdk-entry-dts.ts to match new declaration output paths * fix: add importOriginal to thread-bindings session-meta mock for extensions test * style: fix formatting in thread-bindings lifecycle test
79 lines
2.0 KiB
TypeScript
79 lines
2.0 KiB
TypeScript
import type { EventEmitter } from "node:events";
|
|
|
|
export type DiscordGatewayHandle = {
|
|
emitter?: Pick<EventEmitter, "on" | "removeListener">;
|
|
disconnect?: () => void;
|
|
};
|
|
|
|
export type WaitForDiscordGatewayStopParams = {
|
|
gateway?: DiscordGatewayHandle;
|
|
abortSignal?: AbortSignal;
|
|
onGatewayError?: (err: unknown) => void;
|
|
shouldStopOnError?: (err: unknown) => boolean;
|
|
registerForceStop?: (forceStop: (err: unknown) => void) => void;
|
|
};
|
|
|
|
export function getDiscordGatewayEmitter(gateway?: unknown): EventEmitter | undefined {
|
|
return (gateway as { emitter?: EventEmitter } | undefined)?.emitter;
|
|
}
|
|
|
|
export async function waitForDiscordGatewayStop(
|
|
params: WaitForDiscordGatewayStopParams,
|
|
): Promise<void> {
|
|
const { gateway, abortSignal, onGatewayError, shouldStopOnError } = params;
|
|
const emitter = gateway?.emitter;
|
|
return await new Promise<void>((resolve, reject) => {
|
|
let settled = false;
|
|
const cleanup = () => {
|
|
abortSignal?.removeEventListener("abort", onAbort);
|
|
emitter?.removeListener("error", onGatewayErrorEvent);
|
|
};
|
|
const finishResolve = () => {
|
|
if (settled) {
|
|
return;
|
|
}
|
|
settled = true;
|
|
cleanup();
|
|
try {
|
|
gateway?.disconnect?.();
|
|
} finally {
|
|
resolve();
|
|
}
|
|
};
|
|
const finishReject = (err: unknown) => {
|
|
if (settled) {
|
|
return;
|
|
}
|
|
settled = true;
|
|
cleanup();
|
|
try {
|
|
gateway?.disconnect?.();
|
|
} finally {
|
|
reject(err);
|
|
}
|
|
};
|
|
const onAbort = () => {
|
|
finishResolve();
|
|
};
|
|
const onGatewayErrorEvent = (err: unknown) => {
|
|
onGatewayError?.(err);
|
|
const shouldStop = shouldStopOnError?.(err) ?? true;
|
|
if (shouldStop) {
|
|
finishReject(err);
|
|
}
|
|
};
|
|
const onForceStop = (err: unknown) => {
|
|
finishReject(err);
|
|
};
|
|
|
|
if (abortSignal?.aborted) {
|
|
onAbort();
|
|
return;
|
|
}
|
|
|
|
abortSignal?.addEventListener("abort", onAbort, { once: true });
|
|
emitter?.on("error", onGatewayErrorEvent);
|
|
params.registerForceStop?.(onForceStop);
|
|
});
|
|
}
|