lunibee 0.3.1 → 0.3.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/builders/attachment.d.ts +1 -0
- package/dist/builders/clone.d.ts +10 -0
- package/dist/builders/commands.d.ts +1 -1
- package/dist/builders/components.d.ts +24 -5
- package/dist/builders/index.js +64 -17
- package/dist/builders/index.js.map +10 -9
- package/dist/collection/index.d.ts +1 -0
- package/dist/collection/index.js +297 -19
- package/dist/collection/index.js.map +5 -4
- package/dist/collection/redis.d.ts +98 -0
- package/dist/core/collector.d.ts +8 -0
- package/dist/core/events.d.ts +1 -0
- package/dist/core/index.d.ts +3 -0
- package/dist/core/index.js +6213 -4716
- package/dist/core/index.js.map +46 -39
- package/dist/core/permissions.d.ts +3 -402
- package/dist/core/redis-cache.d.ts +85 -0
- package/dist/formatters/index.d.ts +5 -1
- package/dist/formatters/index.js +7 -3
- package/dist/formatters/index.js.map +3 -3
- package/dist/handlers/index.js +11 -2
- package/dist/handlers/index.js.map +3 -3
- package/dist/index.js +8272 -6085
- package/dist/index.js.map +64 -58
- package/dist/managers/application.d.ts +10 -6
- package/dist/managers/base.d.ts +4 -1
- package/dist/managers/guild.d.ts +3 -1
- package/dist/managers/index.d.ts +6 -1
- package/dist/managers/index.js +365 -103
- package/dist/managers/index.js.map +19 -22
- package/dist/managers/member.d.ts +3 -1
- package/dist/managers/message.d.ts +13 -4
- package/dist/managers/thread.d.ts +3 -3
- package/dist/rest/decoder.d.ts +2 -0
- package/dist/rest/errors.d.ts +5 -2
- package/dist/rest/headers.d.ts +31 -0
- package/dist/rest/identity.d.ts +31 -0
- package/dist/rest/index.d.ts +97 -6
- package/dist/rest/index.js +1350 -220
- package/dist/rest/index.js.map +16 -13
- package/dist/rest/limiter.d.ts +75 -10
- package/dist/rest/redis.d.ts +103 -22
- package/dist/rest/scheduler.d.ts +13 -0
- package/dist/rest/store.d.ts +135 -12
- package/dist/rest/telemetry.d.ts +151 -0
- package/dist/rest/webhook.d.ts +18 -6
- package/dist/sharding/bus.d.ts +4 -2
- package/dist/sharding/cluster.d.ts +1 -1
- package/dist/sharding/health.d.ts +1 -1
- package/dist/sharding/host.d.ts +6 -0
- package/dist/sharding/identify.d.ts +2 -0
- package/dist/sharding/index.d.ts +6 -0
- package/dist/sharding/index.js +205 -39
- package/dist/sharding/index.js.map +14 -14
- package/dist/sharding/remote.d.ts +18 -2
- package/dist/sharding/supervisor.d.ts +1 -1
- package/dist/sharding/transport.d.ts +6 -5
- package/dist/sharding/worker.d.ts +11 -1
- package/dist/structures/base.d.ts +16 -5
- package/dist/structures/index.d.ts +1 -1
- package/dist/structures/index.js +57 -91
- package/dist/structures/index.js.map +10 -18
- package/dist/structures/options.d.ts +3 -2
- package/dist/structures/permissions.d.ts +409 -0
- package/dist/structures/resources.d.ts +4 -1
- package/dist/types/gateway.d.ts +4 -0
- package/dist/types/index.d.ts +14 -4
- package/dist/types/index.js.map +3 -3
- package/dist/utils/args.d.ts +3 -1
- package/dist/utils/index.d.ts +6 -1
- package/dist/utils/index.js +17 -10
- package/dist/utils/index.js.map +4 -4
- package/dist/voice/index.d.ts +7 -1
- package/dist/voice/index.js +33 -10
- package/dist/voice/index.js.map +3 -3
- package/dist/ws/index.d.ts +2 -0
- package/dist/ws/index.js +32 -4
- package/dist/ws/index.js.map +5 -5
- package/package.json +19 -16
|
@@ -8,30 +8,30 @@
|
|
|
8
8
|
"/**\n * Discord Gateway close codes.\n *\n * Discord.js-familiar names and numeric values, matching the Discord Gateway\n * protocol. Exposed so consumers can branch on named codes instead of magic\n * numbers; {@link Gateway} uses them internally to decide resume/identify/stop.\n */\nexport const GatewayCloseCodes = {\n UnknownError: 4000,\n UnknownOpcode: 4001,\n DecodeError: 4002,\n NotAuthenticated: 4003,\n AuthenticationFailed: 4004,\n AlreadyAuthenticated: 4005,\n InvalidSeq: 4007,\n RateLimited: 4008,\n SessionTimedOut: 4009,\n InvalidShard: 4010,\n ShardingRequired: 4011,\n InvalidAPIVersion: 4012,\n InvalidIntents: 4013,\n DisallowedIntents: 4014,\n} as const;\n",
|
|
9
9
|
"/**\n * Reconnect policy and scheduling for one Gateway connection.\n *\n * Owns how a close code is classified, whether another attempt is allowed, how\n * long to wait, and the coordination that keeps at most one logical connection\n * attempt in flight. It owns no socket, no session state, no heartbeat and no\n * event emission: it is told a close code and whether a resume is possible, and\n * answers with a decision.\n */\nimport { GatewayCloseCodes } from \"./close-codes.js\";\n\n/** What a closed connection should do next. */\nexport type CloseAction =\n /** Reconnect and RESUME the existing session. */\n | \"resume\"\n /** Reconnect, but IDENTIFY afresh: the session cannot be resumed. */\n | \"identify\"\n /** Do not reconnect; the condition will not fix itself. */\n | \"stop\";\n\n/**\n * Close codes that no reconnect can recover from: bad token, bad shard\n * configuration, bad API version, or intents the application is not approved\n * for. Retrying these hammers Discord with a request that cannot succeed.\n */\nexport const FATAL_CLOSE_CODES: readonly number[] = [\n GatewayCloseCodes.AuthenticationFailed,\n GatewayCloseCodes.InvalidShard,\n GatewayCloseCodes.ShardingRequired,\n GatewayCloseCodes.InvalidAPIVersion,\n GatewayCloseCodes.InvalidIntents,\n GatewayCloseCodes.DisallowedIntents,\n];\n\n/**\n * Close codes that keep the connection recoverable but destroy the session, so\n * the next connection must IDENTIFY rather than RESUME.\n */\nexport const IDENTIFY_CLOSE_CODES: readonly number[] = [\n GatewayCloseCodes.InvalidSeq,\n GatewayCloseCodes.SessionTimedOut,\n];\n\n/**\n * Classifies a close code.\n *\n * Any code outside the two tables above is **reconnectable**: transport-level\n * closes (`1006`), server restarts (`1001`), Discord's own `4000`, and the\n * `1000` the Gateway itself sends after a non-resumable `op 9` all belong here.\n * Whether that reconnect resumes or identifies is not a property of the code —\n * it is whether a session survives, which is why `canResume` is a parameter\n * rather than something this module works out for itself.\n *\n * @param code WebSocket close code.\n * @param canResume Whether the session can still be resumed.\n */\nexport function classifyCloseCode(\n code: number,\n canResume: boolean,\n): CloseAction {\n if (FATAL_CLOSE_CODES.includes(code)) return \"stop\";\n if (IDENTIFY_CLOSE_CODES.includes(code)) return \"identify\";\n return canResume ? \"resume\" : \"identify\";\n}\n\n/** Why a reconnect was not scheduled. */\nexport type ScheduleRefusal =\n /** Automatic reconnect is disabled for this Gateway. */\n | \"disabled\"\n /** A reconnect is already armed; a second would open a second socket. */\n | \"pending\"\n /** The attempt budget is spent. */\n | \"exhausted\";\n\n/** Outcome of asking for a reconnect. */\nexport type ScheduleResult =\n | {\n scheduled: true;\n delayMs: number;\n attempt: number;\n /** Set when reconnects are coming too fast: the delay was raised. */\n unstable?: { reconnects: number };\n }\n | { scheduled: false; reason: ScheduleRefusal };\n\n/** Configuration for {@link GatewayReconnect}. */\nexport interface ReconnectOptions {\n /** Whether automatic reconnect is permitted at all. */\n enabled: boolean;\n /** Maximum consecutive attempts before giving up. */\n maxAttempts: number;\n /** Delay for the first attempt, doubled per attempt. */\n baseDelay: number;\n /** Ceiling for the backoff. */\n maxDelay: number;\n /** Randomness source for jitter; injected in tests. */\n random?: () => number;\n /** Reconnects inside `stormWindow` before the link counts as unstable (default 5). */\n stormLimit?: number;\n /** The span over which reconnects are counted, in ms (default 60,000). */\n stormWindow?: number;\n /** Clock in ms; injected in tests. */\n now?: () => number;\n}\n\n/** Reconnect scheduling, backoff and attempt coordination. */\nexport class GatewayReconnect {\n readonly #options: ReconnectOptions;\n readonly #random: () => number;\n #attempt = 0;\n /** When recent reconnects were scheduled. A successful connection does not clear it, so a link that connects and drops again keeps backing off. */\n readonly #recent: number[] = [];\n #timer?: ReturnType<typeof setTimeout>;\n #promise?: Promise<void>;\n #resolve?: () => void;\n #reject?: (error: Error) => void;\n\n public constructor(options: ReconnectOptions) {\n this.#options = options;\n this.#random = options.random ?? Math.random;\n }\n\n /** Whether automatic reconnect is permitted. */\n public get enabled(): boolean {\n return this.#options.enabled;\n }\n\n /** Consecutive attempts made since the last {@link reset}. */\n public get attempts(): number {\n return this.#attempt;\n }\n\n /** Whether a reconnect timer is currently armed. */\n public get pending(): boolean {\n return this.#timer !== undefined;\n }\n\n /** Whether the attempt budget is spent. */\n public get exhausted(): boolean {\n return this.#attempt >= this.#options.maxAttempts;\n }\n\n /** Whether a connection attempt is currently in flight. */\n public get connecting(): boolean {\n return this.#promise !== undefined;\n }\n\n /** The in-flight attempt, or undefined when none is active. */\n public get attemptPromise(): Promise<void> | undefined {\n return this.#promise;\n }\n\n /** Classifies a close code against this Gateway's policy. */\n public classifyClose(code: number, canResume: boolean): CloseAction {\n return classifyCloseCode(code, canResume);\n }\n\n /** Whether another reconnect is currently allowed. */\n public canReconnect(): boolean {\n return this.#options.enabled && !this.exhausted;\n }\n\n /**\n * Arms a reconnect, unless one is already armed or no attempt is allowed.\n *\n * Refusing while `pending` is what keeps repeated closes from stacking\n * timers: several closes in a row produce one reconnect, not several\n * competing sockets.\n */\n public schedule(run: () => void): ScheduleResult {\n if (!this.#options.enabled)\n return { scheduled: false, reason: \"disabled\" };\n if (this.#timer) return { scheduled: false, reason: \"pending\" };\n if (this.exhausted) return { scheduled: false, reason: \"exhausted\" };\n\n const attempt = this.#attempt;\n const now = (this.#options.now ?? Date.now)();\n const window = this.#options.stormWindow ?? 60_000;\n const limit = this.#options.stormLimit ?? 5;\n while (this.#recent.length && now - this.#recent[0]! >= window)\n this.#recent.shift();\n const over = this.#recent.length - limit;\n this.#recent.push(now);\n let delay = Math.min(\n this.#options.maxDelay,\n this.#options.baseDelay * 2 ** this.#attempt++,\n );\n const unstable = over >= 0;\n // Past the limit, every further reconnect in the window doubles the\n // wait, even when each connection succeeded and reset the attempt count.\n if (unstable)\n delay = Math.min(\n this.#options.maxDelay,\n Math.max(delay, this.#options.baseDelay * 2 ** (over + 1)),\n );\n // Jitter must scale with the delay: a fixed ceiling would reconnect\n // every shard inside the same narrow window after a gateway-wide\n // restart, which is exactly when decorrelation matters.\n const jitter = this.#random() * Math.max(1, delay * 0.25);\n const delayMs = delay + jitter;\n this.#timer = setTimeout(() => {\n this.#timer = undefined;\n run();\n }, delayMs);\n return {\n scheduled: true,\n delayMs,\n attempt,\n ...(unstable\n ? { unstable: { reconnects: this.#recent.length } }\n : {}),\n };\n }\n\n /** Cancels an armed reconnect. Safe to call when none is armed. */\n public cancel(): void {\n if (this.#timer) clearTimeout(this.#timer);\n this.#timer = undefined;\n }\n\n /** Clears the attempt counter after a connection succeeds. */\n public reset(): void {\n this.#attempt = 0;\n }\n\n /**\n * Coordinates one logical connection attempt.\n *\n * Callers that ask while an attempt is in flight join it instead of\n * starting a second: three `connect()` calls produce one socket attempt and\n * one shared promise.\n *\n * @param start Invoked exactly once, when this call begins a new attempt.\n */\n public attempt(start: () => void): Promise<void> {\n if (this.#promise) return this.#promise;\n this.#promise = new Promise<void>((resolve, reject) => {\n this.#resolve = resolve;\n this.#reject = reject;\n start();\n });\n return this.#promise;\n }\n\n /** Settles the in-flight attempt, resolving it or rejecting with `error`. */\n public settle(error?: Error): void {\n const resolve = this.#resolve;\n const reject = this.#reject;\n this.#resolve = undefined;\n this.#reject = undefined;\n this.#promise = undefined;\n if (error) reject?.(error);\n else resolve?.();\n }\n}\n",
|
|
10
10
|
"/**\n * Turns WebSocket data into complete Gateway text frames.\n *\n * The contract is deliberately boring: bytes in, whole frames out. A decoder\n * knows nothing about opcodes, sessions, heartbeats or JSON validity — it only\n * knows when a frame is *complete*. Parsing the text is the protocol layer's\n * job, so malformed JSON is not a decoding failure; malformed *compressed\n * bytes* are.\n */\nimport {\n createInflate,\n constants as zlibConstants,\n type Inflate,\n} from \"node:zlib\";\n\n/** Decodes transport data into complete frames. */\nexport interface GatewayDecoder {\n /**\n * Feeds one WebSocket message.\n *\n * A decoder that can answer synchronously must do so: delivery timing is\n * observable to consumers, and an uncompressed frame has no reason to cost\n * a microtask.\n *\n * @returns Every frame completed by this chunk; empty while a frame is\n * still arriving.\n */\n push(data: unknown): string[] | Promise<string[]>;\n /** Discards buffered state. Call when a connection is replaced. */\n reset(): void;\n}\n\n/** Normalises a binary WebSocket payload into bytes. */\nasync function toBytes(data: unknown): Promise<Uint8Array> {\n if (data instanceof Uint8Array) return data;\n if (data instanceof ArrayBuffer) return new Uint8Array(data);\n if (typeof Blob !== \"undefined\" && data instanceof Blob)\n return new Uint8Array(await data.arrayBuffer());\n throw new TypeError(\n \"Gateway returned an unsupported compressed frame type.\",\n );\n}\n\n/** Passes text frames through unchanged, for an uncompressed connection. */\nexport class PlainTextDecoder implements GatewayDecoder {\n public push(data: unknown): string[] {\n return [String(data)];\n }\n public reset(): void {\n // Stateless: nothing to discard.\n }\n}\n\n/**\n * Decodes Discord's `zlib-stream` transport compression.\n *\n * Discord sends one zlib stream whose logical payloads are separated by the\n * `Z_SYNC_FLUSH` marker `00 00 FF FF`. A payload may span several WebSocket\n * messages, so output is produced only when that marker arrives: a partial\n * payload is retained rather than guessed at.\n *\n * The inflater carries state across frames, so `push` calls must not\n * interleave. {@link WebSocketTransport} serialises them by construction.\n */\nexport class ZlibStreamDecoder implements GatewayDecoder {\n readonly #create: () => Inflate;\n readonly #maxFrameBytes: number;\n #inflate?: Inflate;\n #chunks: Uint8Array[] = [];\n /** Bytes inflated for the frame being assembled. */\n #size = 0;\n #failure?: Error;\n /**\n * Rejector for the operation currently awaiting the inflater.\n *\n * zlib does **not** invoke a `write` or `flush` callback once the stream\n * has errored — it emits `error` and abandons the callback — so an\n * awaited callback alone hangs forever on corrupt input. The error handler\n * settles the waiter instead.\n */\n #pendingReject?: (error: Error) => void;\n\n /**\n * @param create Inflater factory; injected in tests.\n * @param maxFrameBytes Largest inflated frame accepted (default 64 MiB);\n * past it the stream fails instead of growing until memory runs out.\n */\n public constructor(\n create: () => Inflate = () => createInflate(),\n maxFrameBytes = 64 * 1024 * 1024,\n ) {\n this.#create = create;\n this.#maxFrameBytes = maxFrameBytes;\n }\n\n /**\n * A text frame on a compressed connection is already a complete frame, so\n * it is returned synchronously and never touches the inflater. Discord\n * sends everything binary under `zlib-stream`; tolerating a string costs\n * nothing, and treating one as a decode failure — or delaying it by a\n * microtask — would both change observable behaviour.\n */\n public push(data: unknown): string[] | Promise<string[]> {\n if (typeof data === \"string\") return [data];\n return this.#inflateFrame(data);\n }\n\n async #inflateFrame(data: unknown): Promise<string[]> {\n const chunk = await toBytes(data);\n const inflate = (this.#inflate ??= this.#open());\n // A stream that has already failed cannot be trusted for any later\n // frame: its dictionary state is undefined from the error onwards.\n if (this.#failure) throw this.#failure;\n const complete =\n chunk.length >= 4 &&\n chunk[chunk.length - 4] === 0x00 &&\n chunk[chunk.length - 3] === 0x00 &&\n chunk[chunk.length - 2] === 0xff &&\n chunk[chunk.length - 1] === 0xff;\n\n await this.#awaiting((settle) =>\n inflate.write(chunk, (error) => (error ? settle(error) : settle())),\n );\n if (!complete) return [];\n\n await this.#awaiting((settle) =>\n inflate.flush(zlibConstants.Z_SYNC_FLUSH, () => settle()),\n );\n\n const parts = this.#chunks;\n this.#chunks = [];\n this.#size = 0;\n if (parts.length === 0) return [];\n const total = parts.reduce((size, part) => size + part.length, 0);\n const merged = new Uint8Array(total);\n let offset = 0;\n for (const part of parts) {\n merged.set(part, offset);\n offset += part.length;\n }\n return [new TextDecoder().decode(merged)];\n }\n\n public reset(): void {\n const inflate = this.#inflate;\n this.#inflate = undefined;\n this.#chunks = [];\n this.#size = 0;\n this.#failure = undefined;\n this.#pendingReject = undefined;\n if (!inflate) return;\n inflate.removeAllListeners();\n try {\n inflate.close();\n } catch {\n // Closing a stream that already failed is not itself a failure.\n }\n }\n\n /**\n * Runs one inflater operation, failing it if the stream errors meanwhile.\n */\n #awaiting(\n run: (settle: (error?: Error | null) => void) => void,\n ): Promise<void> {\n if (this.#failure) return Promise.reject(this.#failure);\n return new Promise<void>((resolve, reject) => {\n this.#pendingReject = reject;\n run((error) => {\n this.#pendingReject = undefined;\n if (error) reject(error);\n else if (this.#failure) reject(this.#failure);\n else resolve();\n });\n });\n }\n\n #open(): Inflate {\n const inflate = this.#create();\n inflate.on(\"data\", (chunk: Uint8Array) => {\n if (this.#failure) return;\n this.#size += chunk.length;\n if (this.#size > this.#maxFrameBytes) {\n // Same path as a corrupt stream: the waiter rejects and the\n // connection is reset, so nothing keeps accumulating.\n this.#chunks = [];\n inflate.emit(\n \"error\",\n new Error(\n `Gateway frame inflated past ${this.#maxFrameBytes} bytes.`,\n ),\n );\n return;\n }\n this.#chunks.push(chunk);\n });\n inflate.on(\"error\", (error: Error) => {\n this.#failure = error;\n // Settle whatever is waiting: zlib will not call its callback.\n const reject = this.#pendingReject;\n this.#pendingReject = undefined;\n reject?.(error);\n });\n return inflate;\n }\n}\n\n/** Creates the decoder a connection needs. */\nexport function createDecoder(compress: boolean | undefined): GatewayDecoder {\n return compress ? new ZlibStreamDecoder() : new PlainTextDecoder();\n}\n",
|
|
11
|
-
"/**\n * The WebSocket a Gateway connection runs on.\n *\n * Owns socket construction, listener wiring, sending, closing, and replacement.\n * It is infrastructure, not Discord semantics: it never inspects an opcode, a\n * session, or a heartbeat. Frames go out as text and come back as text.\n */\nimport { createDecoder, type GatewayDecoder } from \"./decoder.js\";\n\n/** Creates the underlying socket. Injected to support alternative runtimes. */\nexport type SocketFactory = (url: string) => WebSocket;\n\n/** Callbacks a transport reports to its owner. */\nexport interface TransportHandlers {\n /** The socket opened and is ready to send. */\n onOpen: () => void;\n /** A complete frame arrived, already decompressed. */\n onFrame: (raw: string) => void;\n /** The socket closed. */\n onClose: (code: number, reason: string) => void;\n /** A socket or decoding failure. Not a close on its own. */\n onError: (error: Error) => void;\n}\n\n/** Outcome of opening a connection. */\nexport type ConnectResult = { ok: true } | { ok: false; error: Error };\n\n/** Configuration for {@link WebSocketTransport}. */\nexport interface TransportOptions {\n /** Handlers for this transport's lifetime. */\n handlers: TransportHandlers;\n /** Frame decoder. Defaults to plain text, or zlib-stream when compressing. */\n decoder?: GatewayDecoder;\n /** Whether the connection negotiates `zlib-stream` compression. */\n compress?: boolean;\n /** Socket constructor. Defaults to the ambient `WebSocket`. */\n createSocket?: SocketFactory;\n}\n\n/** WebSocket states. Compared numerically so a stub needs no static constants. */\nconst OPEN = 1;\n\n/**\n * A replaceable WebSocket connection.\n *\n * **Generations.** Each {@link connect} takes a generation, and every event is\n * checked against the current one. A socket that has been replaced still holds\n * its listeners and can still fire — especially a frame that finishes\n * decompressing after the swap — and every such event is dropped. The owner\n * therefore never has to ask \"is this still my socket?\".\n */\nexport class WebSocketTransport {\n readonly #handlers: TransportHandlers;\n readonly #decoder: GatewayDecoder;\n readonly #createSocket: SocketFactory;\n readonly #compress: boolean;\n #socket?: WebSocket;\n #generation = 0;\n /** Serialises decoding so stateful decoders never see interleaved chunks. */\n #queue: Promise<void> = Promise.resolve();\n\n public constructor(options: TransportOptions) {\n this.#handlers = options.handlers;\n this.#compress = options.compress ?? false;\n this.#decoder = options.decoder ?? createDecoder(options.compress);\n this.#createSocket =\n options.createSocket ?? ((url: string) => new WebSocket(url));\n }\n\n /** Whether a socket is open and able to send. */\n public get connected(): boolean {\n return this.#socket?.readyState === OPEN;\n }\n\n /** Whether a socket is connecting or open, and therefore still in play. */\n public get live(): boolean {\n return this.#socket !== undefined && this.#socket.readyState <= OPEN;\n }\n\n /** Current connection generation; increments on every {@link connect}. */\n public get generation(): number {\n return this.#generation;\n }\n\n /**\n * Opens a connection, replacing any existing one.\n *\n * A construction failure is returned rather than reported through\n * `onError`: the caller is mid-`connect` and must decide what the failure\n * means for the attempt it is running.\n */\n public connect(url: string): ConnectResult {\n // Never leave a predecessor behind: an orphan keeps its listeners and\n // would go on feeding this transport's owner.\n this.#discard(1000, \"Superseded by a new connection\");\n const generation = ++this.#generation;\n // A replaced connection's decoder state is meaningless to the new one.\n this.#decoder.reset();\n this.#queue = Promise.resolve();\n\n let socket: WebSocket;\n try {\n socket = this.#createSocket(url);\n } catch (error) {\n return { ok: false, error: this.#asError(error) };\n }\n if (this.#compress) {\n try {\n (socket as { binaryType?: string }).binaryType = \"arraybuffer\";\n } catch {\n // Runtimes that pin binaryType are handled by the decoder.\n }\n }\n this.#socket = socket;\n\n socket.addEventListener(\"open\", () => {\n if (!this.#owns(generation)) return;\n this.#handlers.onOpen();\n });\n socket.addEventListener(\"message\", (event) => {\n if (!this.#owns(generation)) return;\n // A decoder that answers synchronously is delivered synchronously:\n // an uncompressed frame must not become a microtask, because\n // dispatch timing is observable to consumers.\n let decoded: string[] | Promise<string[]>;\n try {\n decoded = this.#decoder.push(event.data);\n } catch (error) {\n this.#handlers.onError(this.#asError(error));\n return;\n }\n if (!(decoded instanceof Promise)) {\n for (const frame of decoded) this.#handlers.onFrame(frame);\n return;\n }\n // A stateful decoder must never see interleaved chunks, and the\n // generation is re-checked once the chunk is actually decoded: the\n // socket may have been replaced while this frame was in flight.\n this.#queue = this.#queue\n .then(async () => {\n const frames = await decoded;\n if (!this.#owns(generation)) return;\n for (const frame of frames) this.#handlers.onFrame(frame);\n })\n .catch((error) => {\n if (!this.#owns(generation)) return;\n this.#handlers.onError(this.#asError(error));\n });\n });\n socket.addEventListener(\"close\", (event) => {\n if (!this.#owns(generation)) return;\n this.#socket = undefined;\n // A closed socket owns nothing further. Retiring its generation\n // here is what makes every later event from it — a late frame, a\n // second close, an error — stale by construction, including a\n // frame that was still decompressing when the socket went away.\n this.#generation++;\n this.#decoder.reset();\n this.#handlers.onClose(event.code, event.reason ?? \"\");\n });\n socket.addEventListener(\"error\", () => {\n if (!this.#owns(generation)) return;\n this.#handlers.onError(new Error(\"Gateway WebSocket error\"));\n });\n return { ok: true };\n }\n\n /**\n * Writes a frame.\n * @returns Whether it reached the socket.\n */\n public send(data: string): boolean {\n if (!this.connected) return false;\n try {\n this.#socket!.send(data);\n return true;\n } catch (error) {\n this.#handlers.onError(this.#asError(error));\n return false;\n }\n }\n\n /** Closes the current socket, if any. Its `close` event still fires. */\n public close(code = 1000, reason = \"\"): void {\n const socket = this.#socket;\n if (!socket) return;\n try {\n socket.close(code, reason);\n } catch (error) {\n this.#handlers.onError(this.#asError(error));\n }\n }\n\n /**\n * Abandons the current socket without reporting its close.\n *\n * Used for a deliberate shutdown, where the owner has already decided the\n * connection is over and must not be woken by the resulting close event.\n */\n public destroy(code = 1000, reason = \"\"): void {\n this.#discard(code, reason);\n this.#decoder.reset();\n }\n\n /** Whether `generation` still owns this transport. */\n #owns(generation: number): boolean {\n return generation === this.#generation;\n }\n\n /** Detaches and closes the current socket without emitting its close. */\n #discard(code: number, reason: string): void {\n const socket = this.#socket;\n if (!socket) return;\n // Bumping the generation first makes every event this socket may still\n // fire — including a close caused by the call below — a stale event.\n this.#generation++;\n this.#socket = undefined;\n try {\n socket.close(code, reason);\n } catch (error) {\n this.#handlers.onError(this.#asError(error));\n }\n }\n\n #asError(error: unknown): Error {\n return error instanceof Error ? error : new Error(String(error));\n }\n}\n",
|
|
11
|
+
"/**\n * The WebSocket a Gateway connection runs on.\n *\n * Owns socket construction, listener wiring, sending, closing, and replacement.\n * It is infrastructure, not Discord semantics: it never inspects an opcode, a\n * session, or a heartbeat. Frames go out as text and come back as text.\n */\nimport { createDecoder, type GatewayDecoder } from \"./decoder.js\";\n\n/** Creates the underlying socket. Injected to support alternative runtimes. */\nexport type SocketFactory = (url: string) => WebSocket;\n\n/** Callbacks a transport reports to its owner. */\nexport interface TransportHandlers {\n /** The socket opened and is ready to send. */\n onOpen: () => void;\n /** A complete frame arrived, already decompressed. */\n onFrame: (raw: string) => void;\n /** The socket closed. */\n onClose: (code: number, reason: string) => void;\n /** A socket or decoding failure. Not a close on its own. */\n onError: (error: Error) => void;\n}\n\n/** Outcome of opening a connection. */\nexport type ConnectResult = { ok: true } | { ok: false; error: Error };\n\n/** Configuration for {@link WebSocketTransport}. */\nexport interface TransportOptions {\n /** Handlers for this transport's lifetime. */\n handlers: TransportHandlers;\n /** Frame decoder. Defaults to plain text, or zlib-stream when compressing. */\n decoder?: GatewayDecoder;\n /** Whether the connection negotiates `zlib-stream` compression. */\n compress?: boolean;\n /** Socket constructor. Defaults to the ambient `WebSocket`. */\n createSocket?: SocketFactory;\n}\n\n/** WebSocket states. Compared numerically so a stub needs no static constants. */\nconst OPEN = 1;\n\n/**\n * A replaceable WebSocket connection.\n *\n * **Generations.** Each {@link connect} takes a generation, and every event is\n * checked against the current one. A socket that has been replaced still holds\n * its listeners and can still fire — especially a frame that finishes\n * decompressing after the swap — and every such event is dropped. The owner\n * therefore never has to ask \"is this still my socket?\".\n */\nexport class WebSocketTransport {\n readonly #handlers: TransportHandlers;\n readonly #decoder: GatewayDecoder;\n readonly #createSocket: SocketFactory;\n readonly #compress: boolean;\n #socket?: WebSocket;\n #generation = 0;\n /** Serialises decoding so stateful decoders never see interleaved chunks. */\n #queue: Promise<void> = Promise.resolve();\n\n public constructor(options: TransportOptions) {\n this.#handlers = options.handlers;\n this.#compress = options.compress ?? false;\n this.#decoder = options.decoder ?? createDecoder(options.compress);\n this.#createSocket =\n options.createSocket ?? ((url: string) => new WebSocket(url));\n }\n\n /** Whether a socket is open and able to send. */\n public get connected(): boolean {\n return this.#socket?.readyState === OPEN;\n }\n\n /** Whether a socket is connecting or open, and therefore still in play. */\n public get live(): boolean {\n return this.#socket !== undefined && this.#socket.readyState <= OPEN;\n }\n\n /** Current connection generation; increments on every {@link connect}. */\n public get generation(): number {\n return this.#generation;\n }\n\n /**\n * Opens a connection, replacing any existing one.\n *\n * A construction failure is returned rather than reported through\n * `onError`: the caller is mid-`connect` and must decide what the failure\n * means for the attempt it is running.\n */\n public connect(url: string): ConnectResult {\n // Never leave a predecessor behind: an orphan keeps its listeners and\n // would go on feeding this transport's owner.\n this.#discard(1000, \"Superseded by a new connection\");\n const generation = ++this.#generation;\n // A replaced connection's decoder state is meaningless to the new one.\n this.#decoder.reset();\n this.#queue = Promise.resolve();\n\n let socket: WebSocket;\n try {\n socket = this.#createSocket(url);\n } catch (error) {\n return { ok: false, error: this.#asError(error) };\n }\n if (this.#compress) {\n try {\n (socket as { binaryType?: string }).binaryType = \"arraybuffer\";\n } catch {\n // Runtimes that pin binaryType are handled by the decoder.\n }\n }\n this.#socket = socket;\n\n socket.addEventListener(\"open\", () => {\n if (!this.#owns(generation)) return;\n this.#handlers.onOpen();\n });\n socket.addEventListener(\"message\", (event) => {\n if (!this.#owns(generation)) return;\n // A decoder that answers synchronously is delivered synchronously:\n // an uncompressed frame must not become a microtask, because\n // dispatch timing is observable to consumers.\n let decoded: string[] | Promise<string[]>;\n try {\n decoded = this.#decoder.push(event.data);\n } catch (error) {\n this.#handlers.onError(this.#asError(error));\n this.#failDecoding(generation);\n return;\n }\n if (!(decoded instanceof Promise)) {\n for (const frame of decoded) this.#handlers.onFrame(frame);\n return;\n }\n // A stateful decoder must never see interleaved chunks, and the\n // generation is re-checked once the chunk is actually decoded: the\n // socket may have been replaced while this frame was in flight.\n this.#queue = this.#queue\n .then(async () => {\n const frames = await decoded;\n if (!this.#owns(generation)) return;\n for (const frame of frames) this.#handlers.onFrame(frame);\n })\n .catch((error) => {\n if (!this.#owns(generation)) return;\n this.#handlers.onError(this.#asError(error));\n this.#failDecoding(generation);\n });\n });\n socket.addEventListener(\"close\", (event) => {\n if (!this.#owns(generation)) return;\n this.#socket = undefined;\n // A closed socket owns nothing further. Retiring its generation\n // here is what makes every later event from it — a late frame, a\n // second close, an error — stale by construction, including a\n // frame that was still decompressing when the socket went away.\n this.#generation++;\n this.#decoder.reset();\n this.#handlers.onClose(event.code, event.reason ?? \"\");\n });\n socket.addEventListener(\"error\", () => {\n if (!this.#owns(generation)) return;\n this.#handlers.onError(new Error(\"Gateway WebSocket error\"));\n });\n return { ok: true };\n }\n\n /**\n * Writes a frame.\n * @returns Whether it reached the socket.\n */\n public send(data: string): boolean {\n if (!this.connected) return false;\n try {\n this.#socket!.send(data);\n return true;\n } catch (error) {\n this.#handlers.onError(this.#asError(error));\n return false;\n }\n }\n\n /**\n * A decoder that failed cannot be trusted for any later frame, so the\n * socket is closed (4000, resumable) and the close path resets the decoder.\n */\n #failDecoding(generation: number): void {\n if (this.#owns(generation)) this.close(4000, \"decoder failure\");\n }\n\n /** Closes the current socket, if any. Its `close` event still fires. */\n public close(code = 1000, reason = \"\"): void {\n const socket = this.#socket;\n if (!socket) return;\n try {\n socket.close(code, reason);\n } catch (error) {\n this.#handlers.onError(this.#asError(error));\n }\n }\n\n /**\n * Abandons the current socket without reporting its close.\n *\n * Used for a deliberate shutdown, where the owner has already decided the\n * connection is over and must not be woken by the resulting close event.\n */\n public destroy(code = 1000, reason = \"\"): void {\n this.#discard(code, reason);\n this.#decoder.reset();\n }\n\n /** Whether `generation` still owns this transport. */\n #owns(generation: number): boolean {\n return generation === this.#generation;\n }\n\n /** Detaches and closes the current socket without emitting its close. */\n #discard(code: number, reason: string): void {\n const socket = this.#socket;\n if (!socket) return;\n // Bumping the generation first makes every event this socket may still\n // fire — including a close caused by the call below — a stale event.\n this.#generation++;\n this.#socket = undefined;\n try {\n socket.close(code, reason);\n } catch (error) {\n this.#handlers.onError(this.#asError(error));\n }\n }\n\n #asError(error: unknown): Error {\n return error instanceof Error ? error : new Error(String(error));\n }\n}\n",
|
|
12
12
|
"/** Discord Gateway opcodes. */\nexport const GatewayOpcodes = {\n Dispatch: 0,\n Heartbeat: 1,\n Identify: 2,\n PresenceUpdate: 3,\n VoiceStateUpdate: 4,\n Resume: 6,\n Reconnect: 7,\n RequestGuildMembers: 8,\n InvalidSession: 9,\n Hello: 10,\n HeartbeatAck: 11,\n} as const;\n",
|
|
13
13
|
"/**\n * Discord Gateway protocol: what a payload *means*.\n *\n * Pure translation from a received frame to an action, plus construction of\n * the payloads Lunibee sends. It performs nothing: no socket, no timers, no\n * session mutation, no event emission, no sending. The caller decides what to\n * do with an action, which is what lets the whole protocol be tested without a\n * connection.\n */\nimport type { GatewayPayload, GatewayPresence } from \"@lunibee/types\";\nimport { GatewayOpcodes } from \"./opcodes.js\";\nimport type { ResumeInfo } from \"./session.js\";\n\n/** Why a frame could not be understood. */\nexport type ProtocolViolation =\n /** The frame was not valid JSON. */\n | \"invalid-json\"\n /** The frame decoded to something other than a payload object. */\n | \"invalid-payload\"\n /** `HELLO` carried no usable heartbeat interval. */\n | \"invalid-hello\";\n\n/** What a received frame means. */\nexport type GatewayAction =\n /** `op 10`: begin heartbeating and hand shake. */\n | { type: \"hello\"; heartbeatInterval: number }\n /** `op 0`: an event. `event` is null only if Discord omitted `t`. */\n | { type: \"dispatch\"; event: string | null; data: unknown }\n /** `op 1`: Discord asks for a heartbeat now. */\n | { type: \"heartbeat\" }\n /** `op 11`: the last heartbeat was acknowledged. */\n | { type: \"heartbeat-ack\"; data: unknown }\n /** `op 7`: Discord asks the client to reconnect. */\n | { type: \"reconnect\" }\n /** `op 9`: the session is gone. `resumable` decides RESUME vs IDENTIFY. */\n | { type: \"invalid-session\"; resumable: boolean }\n /**\n * An opcode this version does not handle.\n *\n * Ignored rather than treated as an error: Discord adds opcodes, and a\n * client that fails on an unfamiliar one breaks itself on every addition.\n */\n | { type: \"unknown\"; opcode: number }\n /** The frame was malformed. Never silently swallowed. */\n | {\n type: \"invalid\";\n violation: ProtocolViolation;\n message: string;\n cause?: unknown;\n };\n\n/** A classified frame: its sequence, if any, and what it means. */\nexport interface ProtocolResult {\n /**\n * Sequence carried by the frame, or null when it carried none.\n *\n * Reported for every payload type, not just dispatches: recording it is\n * the caller's decision, and the caller records before acting so a RESUME\n * cannot be built from a sequence that was never seen.\n */\n sequence: number | null;\n /** What the frame means. */\n action: GatewayAction;\n}\n\n/**\n * Classifies one received frame.\n *\n * @param raw The frame text, already decompressed by the transport.\n */\nexport function classifyFrame(raw: string): ProtocolResult {\n let payload: GatewayPayload;\n try {\n payload = JSON.parse(raw) as GatewayPayload;\n } catch (error) {\n return {\n sequence: null,\n action: {\n type: \"invalid\",\n violation: \"invalid-json\",\n message: \"Gateway returned invalid JSON\",\n cause: error,\n },\n };\n }\n return classifyPayload(payload);\n}\n\n/** Classifies an already-parsed payload. */\nexport function classifyPayload(payload: unknown): ProtocolResult {\n if (\n payload === null ||\n typeof payload !== \"object\" ||\n typeof (payload as GatewayPayload).op !== \"number\"\n )\n return {\n sequence: null,\n action: {\n type: \"invalid\",\n violation: \"invalid-payload\",\n message: \"Gateway returned an invalid payload\",\n },\n };\n\n const frame = payload as GatewayPayload;\n const sequence = typeof frame.s === \"number\" ? frame.s : null;\n\n switch (frame.op) {\n case GatewayOpcodes.Dispatch:\n return {\n sequence,\n action: {\n type: \"dispatch\",\n event: typeof frame.t === \"string\" ? frame.t : null,\n data: frame.d,\n },\n };\n case GatewayOpcodes.Hello: {\n const interval = (frame.d as { heartbeat_interval?: unknown })\n ?.heartbeat_interval;\n if (\n typeof interval !== \"number\" ||\n !Number.isFinite(interval) ||\n interval <= 0\n )\n return {\n sequence,\n action: {\n type: \"invalid\",\n violation: \"invalid-hello\",\n message:\n \"Gateway HELLO payload contains an invalid heartbeat interval\",\n },\n };\n return {\n sequence,\n action: { type: \"hello\", heartbeatInterval: interval },\n };\n }\n case GatewayOpcodes.Heartbeat:\n return { sequence, action: { type: \"heartbeat\" } };\n case GatewayOpcodes.HeartbeatAck:\n return {\n sequence,\n action: { type: \"heartbeat-ack\", data: frame.d },\n };\n case GatewayOpcodes.Reconnect:\n return { sequence, action: { type: \"reconnect\" } };\n case GatewayOpcodes.InvalidSession:\n // `d` is a boolean: true means the session may still be resumed.\n // Anything else is treated as non-resumable, which is the safe\n // reading — a wrongly-attempted RESUME costs another round trip.\n return {\n sequence,\n action: {\n type: \"invalid-session\",\n resumable: frame.d === true,\n },\n };\n default:\n return { sequence, action: { type: \"unknown\", opcode: frame.op } };\n }\n}\n\n/** Identification properties sent with IDENTIFY. */\nexport interface IdentifyProperties {\n os?: string;\n browser?: string;\n device?: string;\n [key: string]: string | undefined;\n}\n\n/** Everything IDENTIFY needs. */\nexport interface IdentifyOptions {\n token: string;\n intents: number;\n shardId: number;\n shardCount: number;\n properties?: IdentifyProperties;\n presence?: GatewayPresence;\n}\n\n/** The `d` field of an IDENTIFY payload. */\nexport interface IdentifyData {\n token: string;\n intents: number;\n properties: IdentifyProperties;\n presence: Required<GatewayPresence>;\n shard: [shardId: number, shardCount: number];\n}\n\n/** Builds an IDENTIFY payload. */\nexport function identifyPayload(\n options: IdentifyOptions,\n): GatewayPayload<IdentifyData> {\n const props = options.properties ?? {};\n const os = props.os ?? process.platform;\n const browser = props.browser ?? \"Lunibee\";\n const device = props.device ?? \"Lunibee\";\n return {\n op: GatewayOpcodes.Identify,\n d: {\n token: options.token,\n intents: options.intents,\n properties: {\n os,\n browser,\n device,\n // Discord accepted `$`-prefixed property names historically and\n // still does; both spellings are sent so the payload works\n // against either expectation.\n $os: os,\n $browser: browser,\n $device: device,\n ...props,\n },\n presence: {\n since: options.presence?.since ?? null,\n activities: options.presence?.activities ?? [],\n status: options.presence?.status ?? \"online\",\n afk: Boolean(options.presence?.afk),\n },\n shard: [options.shardId, options.shardCount],\n },\n s: null,\n t: null,\n };\n}\n\n/** Builds a RESUME payload from the session's resume material. */\nexport function resumePayload(\n token: string,\n resume: ResumeInfo,\n): GatewayPayload {\n return {\n op: GatewayOpcodes.Resume,\n d: {\n token,\n session_id: resume.sessionId,\n seq: resume.sequence,\n },\n s: null,\n t: null,\n };\n}\n\n/** Builds a heartbeat payload carrying the last sequence seen. */\nexport function heartbeatPayload(sequence: number | null): GatewayPayload {\n return { op: GatewayOpcodes.Heartbeat, d: sequence, s: null, t: null };\n}\n",
|
|
14
14
|
"/**\n * Channel kinds: a short name for what a channel is, in place of the numeric\n * `type` Discord sends. Threads share one kind; `type` still tells the three\n * apart.\n */\nexport const ChannelKinds = {\n text: [0],\n dm: [1],\n voice: [2],\n \"group-dm\": [3],\n category: [4],\n announcement: [5],\n thread: [10, 11, 12],\n stage: [13],\n directory: [14],\n forum: [15],\n media: [16],\n} as const;\n\n/** The name of a kind of channel, such as `\"text\"` or `\"voice\"`. */\nexport type ChannelKind = keyof typeof ChannelKinds;\n\n/** A channel type Discord has no kind for yet. */\nexport type UnknownChannelKind = \"unknown\";\n\nconst KIND_OF_TYPE = new Map<number, ChannelKind>(\n (Object.keys(ChannelKinds) as ChannelKind[]).flatMap((kind) =>\n ChannelKinds[kind].map((type): [number, ChannelKind] => [type, kind]),\n ),\n);\n\n/** The kind of a Discord channel `type`, or `\"unknown\"` for a type this version does not know. */\nexport function channelKindOf(type: number): ChannelKind | UnknownChannelKind {\n return KIND_OF_TYPE.get(type) ?? \"unknown\";\n}\n\n/** The Discord channel `type` numbers a kind covers (three for `\"thread\"`, one for the rest). */\nexport function channelTypesOf(kind: ChannelKind): readonly number[] {\n return ChannelKinds[kind];\n}\n",
|
|
15
|
-
"// ─── Gateway Intents ─────────────────────────────────────────────────────────\n\n/** Gateway intent bit flags supported by Discord (PascalCase). */\nexport const GatewayIntentBits = {\n Guilds: 1 << 0,\n GuildMembers: 1 << 1,\n GuildModeration: 1 << 2,\n GuildExpressions: 1 << 3,\n GuildIntegrations: 1 << 4,\n GuildWebhooks: 1 << 5,\n GuildInvites: 1 << 6,\n GuildVoiceStates: 1 << 7,\n GuildPresences: 1 << 8,\n GuildMessages: 1 << 9,\n GuildMessageReactions: 1 << 10,\n GuildMessageTyping: 1 << 11,\n DirectMessages: 1 << 12,\n DirectMessageReactions: 1 << 13,\n DirectMessageTyping: 1 << 14,\n MessageContent: 1 << 15,\n GuildScheduledEvents: 1 << 16,\n AutoModerationConfiguration: 1 << 20,\n AutoModerationExecution: 1 << 21,\n GuildMessagePolls: 1 << 24,\n DirectMessagePolls: 1 << 25,\n} as const;\n\n/** Idiomatic camelCase Gateway intent bit flags supported by Discord. */\nexport const IntentBits = {\n guild: 1 << 0,\n guilds: 1 << 0,\n guildMembers: 1 << 1,\n guildModeration: 1 << 2,\n guildBans: 1 << 2,\n guildExpressions: 1 << 3,\n guildEmojis: 1 << 3,\n guildEmojisAndStickers: 1 << 3,\n guildIntegrations: 1 << 4,\n guildWebhooks: 1 << 5,\n guildInvites: 1 << 6,\n guildVoiceStates: 1 << 7,\n guildPresences: 1 << 8,\n guildMessages: 1 << 9,\n guildMessage: 1 << 9,\n guildMessageReactions: 1 << 10,\n guildMessageTyping: 1 << 11,\n directMessages: 1 << 12,\n directMessageReactions: 1 << 13,\n directMessageTyping: 1 << 14,\n messageContent: 1 << 15,\n guildScheduledEvents: 1 << 16,\n autoModerationConfiguration: 1 << 20,\n autoModerationExecution: 1 << 21,\n guildMessagePolls: 1 << 24,\n directMessagePolls: 1 << 25,\n} as const;\n\n/** Alias for IntentBits. */\nexport const Intents = IntentBits;\n\n/** Gateway intents: an array of intent bits or names, e.g. `[GatewayIntentBits.Guilds, \"GuildMessages\"]`. A single number, name or OR-ed bitfield is not accepted. */\nexport type GatewayIntentResolvable = readonly (\n number | keyof typeof GatewayIntentBits | keyof typeof IntentBits | string\n)[];\n\n/**\n * Resolves an array of intents into a raw bitfield integer.\n * @throws {TypeError} If `intents` is not an array (an OR-ed number, a single name or a bitfield object).\n */\nexport function resolveGatewayIntents(\n intents: GatewayIntentResolvable,\n): number {\n if (!Array.isArray(intents))\n throw new TypeError(\n \"Gateway intents must be an array, e.g. [GatewayIntentBits.Guilds, GatewayIntentBits.GuildMessages].\",\n );\n return intents.reduce<number>(\n (acc, intent) => acc | resolveIntent(intent),\n 0,\n );\n}\n\n/** Resolves one intent: a bitfield, an intent name in either casing, or a numeric string. */\nfunction resolveIntent(intent: number | string): number {\n if (typeof intent === \"number\") return intent;\n const bits: Record<string, number> = {\n ...IntentBits,\n ...GatewayIntentBits,\n };\n const lower = intent.charAt(0).toLowerCase() + intent.slice(1);\n if (Object.hasOwn(bits, intent)) return bits[intent]!;\n if (Object.hasOwn(bits, lower)) return bits[lower]!;\n const num = Number(intent);\n return Number.isNaN(num) ? 0 : num;\n}\n\n/** What `IntentsBitField` methods take: one intent or an array of them. Clients and gateways take only arrays. */\ntype IntentsInput = GatewayIntentResolvable[number] | GatewayIntentResolvable;\n\nfunction resolveInput(bits: IntentsInput): number {\n return resolveGatewayIntents(Array.isArray(bits) ? bits : [bits as number]);\n}\n\n/**\n * Discord.js-familiar intent bitfield.\n * @example new IntentsBitField([IntentsBitField.Flags.Guilds, \"GuildMessages\"])\n */\nexport class IntentsBitField {\n /** Intent flag values by name. */\n public static readonly Flags = GatewayIntentBits;\n /** Raw intent bitfield. */\n public bitfield: number;\n\n public constructor(bits: IntentsInput = []) {\n this.bitfield = resolveInput(bits);\n }\n\n /** Whether every given intent is set. */\n public has(bits: IntentsInput): boolean {\n const resolved = resolveInput(bits);\n return (this.bitfield & resolved) === resolved;\n }\n\n /** Whether any given intent is set. */\n public any(bits: IntentsInput): boolean {\n return (this.bitfield & resolveInput(bits)) !== 0;\n }\n\n /** Adds intents in place. @returns This bitfield. */\n public add(...bits: IntentsInput[]): this {\n for (const bit of bits) this.bitfield |= resolveInput(bit);\n return this;\n }\n\n /** Removes intents in place. @returns This bitfield. */\n public remove(...bits: IntentsInput[]): this {\n for (const bit of bits) this.bitfield &= ~resolveInput(bit);\n return this;\n }\n\n /** Names of the set intents. */\n public toArray(): (keyof typeof GatewayIntentBits)[] {\n return (\n Object.keys(GatewayIntentBits) as (keyof typeof GatewayIntentBits)[]\n ).filter((name) => this.has(GatewayIntentBits[name]));\n }\n\n /** Whether this equals another bitfield. */\n public equals(bits: IntentsInput): boolean {\n return this.bitfield === resolveInput(bits);\n }\n\n public valueOf(): number {\n return this.bitfield;\n }\n\n public toJSON(): number {\n return this.bitfield;\n }\n}\n\n// ─── Gateway Config ───────────────────────────────────────────────────────────\n\n/** Gateway connection properties (OS, browser, device). */\nexport interface GatewayProperties {\n os?: string;\n browser?: string;\n device?: string;\n [key: string]: string | undefined;\n}\n/** Gateway presence activity. */\nexport interface GatewayPresenceActivity {\n name: string;\n type?: number;\n url?: string;\n state?: string;\n}\n/** Gateway presence data. */\nexport interface GatewayPresence {\n status?: \"online\" | \"dnd\" | \"idle\" | \"invisible\" | \"offline\";\n activities?: GatewayPresenceActivity[];\n afk?: boolean;\n since?: number | null;\n}\n/** Client configuration. */\nexport interface ClientOptions {\n token: string;\n intents: GatewayIntentResolvable;\n gateway?: GatewayOptions;\n /**\n * zlib-stream transport compression for the Gateway connection, about a\n * tenth of the inbound bandwidth. Shorthand for `gateway: { compress }`,\n * which wins when both are set. Off by default.\n */\n compress?: boolean;\n rest?: RESTOptions;\n /** Opt-in bounded per-channel message cache. Messages are not cached by default. */\n messageCache?: { maxSize?: number; ttl?: number };\n /**\n * Which resources the client keeps from Gateway events. Everything is\n * cached by default; large bots can turn off what they do not read.\n * Events are still emitted either way.\n */\n cache?: {\n users?: boolean;\n members?: boolean;\n roles?: boolean;\n emojis?: boolean;\n /** Keep each member's latest presence (needs the `GuildPresences` intent). Off by default: it is a lot of data. */\n presences?: boolean;\n };\n /**\n * Default `allowed_mentions` for every message the client sends (channel\n * send/edit, replies, interaction replies, edits and follow-ups), e.g.\n * `{ parse: [] }` so nothing pings unless a message says so. A message's\n * own `allowed_mentions` always wins.\n */\n allowedMentions?: AllowedMentions;\n}\n/** Discord's `allowed_mentions` object. */\nexport interface AllowedMentions {\n parse?: (\"users\" | \"roles\" | \"everyone\")[];\n users?: string[];\n roles?: string[];\n replied_user?: boolean;\n}\n/** Gateway connection configuration. */\nexport interface GatewayOptions {\n intents?: GatewayIntentResolvable;\n reconnect?: boolean;\n maxReconnectAttempts?: number;\n reconnectBaseDelay?: number;\n reconnectMaxDelay?: number;\n shardId?: number;\n shardCount?: number;\n properties?: GatewayProperties;\n presence?: GatewayPresence;\n}\n\n/** REST request configuration. */\nexport interface RESTOptions {\n timeout?: number;\n retries?: number;\n /** Where requests go. Must be `https:` (or `http:` on this machine) with no credentials, query or fragment. */\n baseURL?: string;\n /** Allow a cleartext `http://` `baseURL` on a network you trust, such as a REST proxy. Default false. */\n allowInsecureBaseURL?: boolean;\n}\n",
|
|
15
|
+
"// ─── Gateway Intents ─────────────────────────────────────────────────────────\n\n/** Gateway intent bit flags supported by Discord (PascalCase). */\nexport const GatewayIntentBits = {\n Guilds: 1 << 0,\n GuildMembers: 1 << 1,\n GuildModeration: 1 << 2,\n GuildExpressions: 1 << 3,\n GuildIntegrations: 1 << 4,\n GuildWebhooks: 1 << 5,\n GuildInvites: 1 << 6,\n GuildVoiceStates: 1 << 7,\n GuildPresences: 1 << 8,\n GuildMessages: 1 << 9,\n GuildMessageReactions: 1 << 10,\n GuildMessageTyping: 1 << 11,\n DirectMessages: 1 << 12,\n DirectMessageReactions: 1 << 13,\n DirectMessageTyping: 1 << 14,\n MessageContent: 1 << 15,\n GuildScheduledEvents: 1 << 16,\n AutoModerationConfiguration: 1 << 20,\n AutoModerationExecution: 1 << 21,\n GuildMessagePolls: 1 << 24,\n DirectMessagePolls: 1 << 25,\n} as const;\n\n/** Idiomatic camelCase Gateway intent bit flags supported by Discord. */\nexport const IntentBits = {\n guild: 1 << 0,\n guilds: 1 << 0,\n guildMembers: 1 << 1,\n guildModeration: 1 << 2,\n guildBans: 1 << 2,\n guildExpressions: 1 << 3,\n guildEmojis: 1 << 3,\n guildEmojisAndStickers: 1 << 3,\n guildIntegrations: 1 << 4,\n guildWebhooks: 1 << 5,\n guildInvites: 1 << 6,\n guildVoiceStates: 1 << 7,\n guildPresences: 1 << 8,\n guildMessages: 1 << 9,\n guildMessage: 1 << 9,\n guildMessageReactions: 1 << 10,\n guildMessageTyping: 1 << 11,\n directMessages: 1 << 12,\n directMessageReactions: 1 << 13,\n directMessageTyping: 1 << 14,\n messageContent: 1 << 15,\n guildScheduledEvents: 1 << 16,\n autoModerationConfiguration: 1 << 20,\n autoModerationExecution: 1 << 21,\n guildMessagePolls: 1 << 24,\n directMessagePolls: 1 << 25,\n} as const;\n\n/** Alias for IntentBits. */\nexport const Intents = IntentBits;\n\n/** Gateway intents: an array of intent bits or names, e.g. `[GatewayIntentBits.Guilds, \"GuildMessages\"]`. A single number, name or OR-ed bitfield is not accepted. */\nexport type GatewayIntentResolvable = readonly (\n number | keyof typeof GatewayIntentBits | keyof typeof IntentBits | string\n)[];\n\n/**\n * Resolves an array of intents into a raw bitfield integer.\n * @throws {TypeError} If `intents` is not an array (an OR-ed number, a single name or a bitfield object).\n */\nexport function resolveGatewayIntents(\n intents: GatewayIntentResolvable,\n): number {\n if (!Array.isArray(intents))\n throw new TypeError(\n \"Gateway intents must be an array, e.g. [GatewayIntentBits.Guilds, GatewayIntentBits.GuildMessages].\",\n );\n return intents.reduce<number>(\n (acc, intent) => acc | resolveIntent(intent),\n 0,\n );\n}\n\n/** Resolves one intent: a bitfield, an intent name in either casing, or a numeric string. */\nfunction resolveIntent(intent: number | string): number {\n if (typeof intent === \"number\") return intent;\n const bits: Record<string, number> = {\n ...IntentBits,\n ...GatewayIntentBits,\n };\n const lower = intent.charAt(0).toLowerCase() + intent.slice(1);\n if (Object.hasOwn(bits, intent)) return bits[intent]!;\n if (Object.hasOwn(bits, lower)) return bits[lower]!;\n const num = Number(intent);\n return Number.isNaN(num) ? 0 : num;\n}\n\n/** What `IntentsBitField` methods take: one intent or an array of them. Clients and gateways take only arrays. */\ntype IntentsInput = GatewayIntentResolvable[number] | GatewayIntentResolvable;\n\nfunction resolveInput(bits: IntentsInput): number {\n return resolveGatewayIntents(Array.isArray(bits) ? bits : [bits as number]);\n}\n\n/**\n * Discord.js-familiar intent bitfield.\n * @example new IntentsBitField([IntentsBitField.Flags.Guilds, \"GuildMessages\"])\n */\nexport class IntentsBitField {\n /** Intent flag values by name. */\n public static readonly Flags = GatewayIntentBits;\n /** Raw intent bitfield. */\n public bitfield: number;\n\n public constructor(bits: IntentsInput = []) {\n this.bitfield = resolveInput(bits);\n }\n\n /** Whether every given intent is set. */\n public has(bits: IntentsInput): boolean {\n const resolved = resolveInput(bits);\n return (this.bitfield & resolved) === resolved;\n }\n\n /** Whether any given intent is set. */\n public any(bits: IntentsInput): boolean {\n return (this.bitfield & resolveInput(bits)) !== 0;\n }\n\n /** Adds intents in place. @returns This bitfield. */\n public add(...bits: IntentsInput[]): this {\n for (const bit of bits) this.bitfield |= resolveInput(bit);\n return this;\n }\n\n /** Removes intents in place. @returns This bitfield. */\n public remove(...bits: IntentsInput[]): this {\n for (const bit of bits) this.bitfield &= ~resolveInput(bit);\n return this;\n }\n\n /** Names of the set intents. */\n public toArray(): (keyof typeof GatewayIntentBits)[] {\n return (\n Object.keys(GatewayIntentBits) as (keyof typeof GatewayIntentBits)[]\n ).filter((name) => this.has(GatewayIntentBits[name]));\n }\n\n /** Whether this equals another bitfield. */\n public equals(bits: IntentsInput): boolean {\n return this.bitfield === resolveInput(bits);\n }\n\n public valueOf(): number {\n return this.bitfield;\n }\n\n public toJSON(): number {\n return this.bitfield;\n }\n}\n\n// ─── Gateway Config ───────────────────────────────────────────────────────────\n\n/** Gateway connection properties (OS, browser, device). */\nexport interface GatewayProperties {\n os?: string;\n browser?: string;\n device?: string;\n [key: string]: string | undefined;\n}\n/** Gateway presence activity. */\nexport interface GatewayPresenceActivity {\n name: string;\n type?: number;\n url?: string;\n state?: string;\n}\n/** Gateway presence data. */\nexport interface GatewayPresence {\n status?: \"online\" | \"dnd\" | \"idle\" | \"invisible\" | \"offline\";\n activities?: GatewayPresenceActivity[];\n afk?: boolean;\n since?: number | null;\n}\n/** Client configuration. */\nexport interface ClientOptions {\n token: string;\n intents: GatewayIntentResolvable;\n gateway?: GatewayOptions;\n /**\n * zlib-stream transport compression for the Gateway connection, about a\n * tenth of the inbound bandwidth. Shorthand for `gateway: { compress }`,\n * which wins when both are set. Off by default.\n */\n compress?: boolean;\n rest?: RESTOptions;\n /** Opt-in bounded per-channel message cache. Messages are not cached by default. */\n messageCache?: { maxSize?: number; ttl?: number };\n /**\n * Which resources the client keeps from Gateway events. Everything is\n * cached by default; large bots can turn off what they do not read.\n * Events are still emitted either way.\n */\n cache?: {\n users?: boolean;\n members?: boolean;\n roles?: boolean;\n emojis?: boolean;\n /** Keep each member's latest presence (needs the `GuildPresences` intent). Off by default: it is a lot of data. */\n presences?: boolean;\n /** Most members kept per guild (least recently used leave first). Default: unbounded, so a big guild's member list stays whole. */\n maxMembers?: number;\n };\n /**\n * Default `allowed_mentions` for every message the client sends (channel\n * send/edit, replies, interaction replies, edits and follow-ups), e.g.\n * `{ parse: [] }` so nothing pings unless a message says so. A message's\n * own `allowed_mentions` always wins.\n */\n allowedMentions?: AllowedMentions;\n}\n/** Discord's `allowed_mentions` object. */\nexport interface AllowedMentions {\n parse?: (\"users\" | \"roles\" | \"everyone\")[];\n users?: string[];\n roles?: string[];\n replied_user?: boolean;\n}\n/** Gateway connection configuration. */\nexport interface GatewayOptions {\n intents?: GatewayIntentResolvable;\n /** zlib-stream transport compression; `ClientOptions.compress` is shorthand for this. */\n compress?: boolean;\n reconnect?: boolean;\n maxReconnectAttempts?: number;\n reconnectBaseDelay?: number;\n reconnectMaxDelay?: number;\n shardId?: number;\n shardCount?: number;\n properties?: GatewayProperties;\n presence?: GatewayPresence;\n}\n\n/** REST request configuration. */\nexport interface RESTOptions {\n timeout?: number;\n retries?: number;\n /** Where requests go. Must be `https:` (or `http:` on this machine) with no credentials, query or fragment. */\n baseURL?: string;\n /** Allow a cleartext `http://` `baseURL` on a network you trust, such as a REST proxy. Default false. */\n allowInsecureBaseURL?: boolean;\n}\n",
|
|
16
16
|
"/** Gateway event listener. */\nexport type GatewayListener = (data: unknown) => unknown;\n\n/**\n * Listener registry and emission for one Gateway.\n *\n * A listener that throws, or returns a promise that rejects, is isolated from\n * the others and reported through `onFailure`, except for `error` listeners:\n * a failing error listener must not report itself again.\n */\nexport class GatewayDispatcher {\n readonly #listeners = new Map<string, Set<GatewayListener>>();\n readonly #onFailure: (error: unknown) => void;\n\n public constructor(onFailure: (error: unknown) => void) {\n this.#onFailure = onFailure;\n }\n\n /** Adds a listener. */\n public on(event: string, listener: GatewayListener): void {\n let listeners = this.#listeners.get(event);\n if (!listeners) this.#listeners.set(event, (listeners = new Set()));\n listeners.add(listener);\n }\n\n /** Removes a listener. */\n public off(event: string, listener: GatewayListener): void {\n this.#listeners.get(event)?.delete(listener);\n }\n\n /** Calls the listeners of `event` with `data`. */\n public emit(event: string, data: unknown): void {\n for (const listener of this.#listeners.get(event) ?? []) {\n try {\n const result = listener(data);\n if (\n result &&\n typeof (result as PromiseLike<unknown>).then === \"function\"\n )\n void Promise.resolve(result).catch((error) => {\n if (event !== \"error\") this.#onFailure(error);\n });\n } catch (error) {\n if (event !== \"error\") this.#onFailure(error);\n }\n }\n }\n}\n",
|
|
17
17
|
"/**\n * Sliding-window budget for outgoing Gateway payloads. Heartbeats, IDENTIFY and\n * RESUME are privileged: they always go out (and are still counted), so\n * application traffic can never starve them.\n *\n * Discord allows 120 sends per 60 seconds. Application traffic is capped at\n * 115 so the remaining headroom is reserved for privileged sends (heartbeats),\n * which are still recorded so the true total stays under Discord's limit.\n */\nexport class GatewaySendLimiter {\n readonly #timestamps: number[] = [];\n readonly #limit: number;\n readonly #windowMs: number;\n\n public constructor(limit = 115, windowMs = 60_000) {\n this.#limit = limit;\n this.#windowMs = windowMs;\n }\n\n /** Sends recorded within the current window. */\n public get used(): number {\n return this.#timestamps.length;\n }\n\n /** Whether a send may go out now. Privileged sends always may. */\n public allows(privileged: boolean, now = Date.now()): boolean {\n while (\n this.#timestamps.length &&\n now - this.#timestamps[0]! >= this.#windowMs\n )\n this.#timestamps.shift();\n return privileged || this.#timestamps.length < this.#limit;\n }\n\n /** Records a send that went out. */\n public record(now = Date.now()): void {\n this.#timestamps.push(now);\n }\n}\n\n/** The limiter's name before 0.3.1. */\nexport const SendBudget = GatewaySendLimiter;\n",
|
|
18
18
|
"/** Gateway connection lifecycle states. */\nexport enum GatewayState {\n /** Initial connection state. */\n Connect = \"CONNECT\",\n /** Gateway HELLO received state. */\n Hello = \"HELLO\",\n /** IDENTIFY operation in progress. */\n Identify = \"IDENTIFY\",\n /** RESUME operation in progress. */\n Resume = \"RESUME\",\n /** Gateway READY state. */\n Ready = \"READY\",\n /** Gateway dispatch processing state. */\n Dispatch = \"DISPATCH\",\n /** Heartbeat processing state. */\n Heartbeat = \"HEARTBEAT\",\n /** Reconnect in progress. */\n Reconnect = \"RECONNECT\",\n /** Gateway is permanently closed. */\n Closed = \"CLOSED\",\n}\n/**\n * Discord.js-familiar alias for {@link GatewayState}.\n *\n * Discord.js exposes connection status via a `Status` enum. Lunibee's canonical\n * name is {@link GatewayState}; this is an additive alias so `discord.js` users\n * find the expected name. Note the *values* remain Lunibee's string states\n * (e.g. `\"READY\"`), not Discord.js's numeric `Status` members — an intentional\n * divergence documented in the compatibility matrix.\n */\nexport { GatewayState as Status };\n/** Gateway protocol error. */\nexport class GatewayError extends Error {\n /** Gateway close/error code. */\n public readonly code?: number;\n /** Creates a Gateway error. @param message Error message. @param code Optional Gateway code. @param options Optional error metadata. */\n public constructor(message: string, code?: number, options?: ErrorOptions) {\n super(message, options);\n this.name = \"GatewayError\";\n this.code = code;\n }\n}\n",
|
|
19
|
-
"import { GatewaySession } from \"./session.js\";\nimport { GatewayHeartbeat, type HeartbeatTimeout } from \"./heartbeat.js\";\nimport { GatewayCloseCodes } from \"./close-codes.js\";\nimport { GatewayReconnect, type CloseAction } from \"./reconnect.js\";\nimport { WebSocketTransport, type SocketFactory } from \"./transport.js\";\nimport { GatewayOpcodes } from \"./opcodes.js\";\nimport {\n classifyFrame,\n identifyPayload,\n resumePayload,\n heartbeatPayload,\n type GatewayAction,\n} from \"./protocol.js\";\nimport {\n resolveGatewayIntents,\n type GatewayPayload,\n type GatewayProperties,\n type GatewayPresence,\n type GatewayIntentResolvable,\n} from \"@lunibee/types\";\n\nexport {\n classifyFrame,\n classifyPayload,\n identifyPayload,\n resumePayload,\n heartbeatPayload,\n type GatewayAction,\n type ProtocolResult,\n type ProtocolViolation,\n type IdentifyOptions,\n type IdentifyProperties,\n type IdentifyData,\n} from \"./protocol.js\";\n\nexport {\n WebSocketTransport,\n type TransportHandlers,\n type TransportOptions,\n type SocketFactory,\n} from \"./transport.js\";\n\nexport {\n createDecoder,\n PlainTextDecoder,\n ZlibStreamDecoder,\n type GatewayDecoder,\n} from \"./decoder.js\";\n\nexport {\n GatewayReconnect,\n classifyCloseCode,\n FATAL_CLOSE_CODES,\n IDENTIFY_CLOSE_CODES,\n type CloseAction,\n type ReconnectOptions,\n type ScheduleResult,\n type ScheduleRefusal,\n} from \"./reconnect.js\";\n\nexport {\n GatewayHeartbeat,\n type HeartbeatOptions,\n type HeartbeatTimeout,\n} from \"./heartbeat.js\";\n\nexport {\n GatewaySession,\n type ResumeInfo,\n type HandshakeIntent,\n type InvalidationReason,\n} from \"./session.js\";\n\nexport { GatewayDispatcher, type GatewayListener } from \"./dispatcher.js\";\nexport { GatewaySendLimiter, SendBudget } from \"./send-budget.js\";\nexport { GatewayOpcodes } from \"./opcodes.js\";\n\nexport { GatewayCloseCodes } from \"./close-codes.js\";\n\nexport { GatewayState, Status, GatewayError } from \"./state.js\";\nimport { GatewayState, GatewayError } from \"./state.js\";\nimport { GatewayDispatcher, type GatewayListener } from \"./dispatcher.js\";\nimport { GatewaySendLimiter } from \"./send-budget.js\";\n/** Gateway connection configuration. */\nexport interface GatewayOptions {\n /** Authentication token. */\n token: string;\n /** Gateway intent bitfield or resolvable. */\n intents: GatewayIntentResolvable;\n /** Shard identifier. */\n shardId?: number;\n /** Total shard count. */\n shardCount?: number;\n /** Whether automatic reconnect is enabled. */\n reconnect?: boolean;\n /** Maximum reconnect attempts. */\n maxReconnectAttempts?: number;\n /** Initial reconnect delay. */\n reconnectBaseDelay?: number;\n /** Maximum reconnect delay. */\n reconnectMaxDelay?: number;\n /** Heartbeat acknowledgement timeout. */\n heartbeatAckTimeout?: number;\n /** Zombie connection timeout. */\n zombieTimeout?: number;\n /** Reconnects inside `reconnectStormWindow` after which the link counts as unstable: `unstable` is emitted and the delay keeps doubling. Default 5. */\n reconnectStormLimit?: number;\n /** The span over which reconnects are counted for storm detection, in ms. Default 60,000. */\n reconnectStormWindow?: number;\n /** Identification properties. */\n properties?: GatewayProperties;\n /** Presence data. */\n presence?: GatewayPresence;\n /**\n * Whether to enable zlib-stream transport compression.\n * Decoded with a persistent `node:zlib` inflate stream, matching\n * Discord's zlib-wrapped stream framing (`Z_SYNC_FLUSH` boundaries).\n * When enabled, appends `&compress=zlib-stream` to the Gateway URL.\n */\n compress?: boolean;\n /**\n * Constructs the underlying socket. Defaults to the ambient `WebSocket`.\n * Injected to run the Gateway on an alternative transport, or on a stub.\n */\n createSocket?: SocketFactory;\n}\n/** Discord's main Gateway endpoint, used when no resume host is known. */\n/**\n * Close code for connections Lunibee means to RESUME. Discord invalidates the\n * session when a client closes with 1000 or 1001, so a reconnect that should\n * keep its session must close with anything else.\n */\nconst RESUMABLE_CLOSE_CODE = 4900;\nconst DEFAULT_GATEWAY_URL = \"wss://gateway.discord.gg/?v=10&encoding=json\";\n\n/** What {@link Gateway.health} reports. */\nexport interface GatewayHealth {\n /** Connection lifecycle state. */\n state: GatewayState;\n /** Round trip of the last acknowledged heartbeat in ms, or -1 before the first. */\n latency: number;\n /** Milliseconds since the last message from Discord, or 0 before any. */\n silentFor: number;\n /** Reconnects scheduled since this Gateway was created. */\n reconnects: number;\n}\n/** Manages a Discord Gateway connection. */\nexport class Gateway {\n /** Current Gateway lifecycle state. */\n public state: GatewayState = GatewayState.Connect;\n #options: Required<\n Omit<\n GatewayOptions,\n | \"properties\"\n | \"presence\"\n | \"compress\"\n | \"createSocket\"\n | \"intents\"\n | \"reconnectStormLimit\"\n | \"reconnectStormWindow\"\n >\n > & {\n /** Resolved intent bitfield, never the resolvable form. */\n intents: number;\n properties?: GatewayProperties;\n presence?: GatewayPresence;\n compress?: boolean;\n };\n /**\n * The socket this connection runs on.\n *\n * The Gateway never touches a `WebSocket` directly: construction, listener\n * wiring, replacement and decoding all belong to the transport.\n */\n readonly #transport: WebSocketTransport;\n /**\n * Session identity and the IDENTIFY-vs-RESUME decision.\n *\n * The Gateway holds no session fields of its own: every read and write goes\n * through the session, which rejects mutations from a superseded socket.\n */\n readonly #session = new GatewaySession();\n /** Token of the socket this Gateway currently owns. */\n #token = 0;\n /**\n * Heartbeat, acknowledgement deadline and staleness watch.\n *\n * The Gateway holds no heartbeat timers of its own; it supplies the socket\n * and decides what to close when liveness fails.\n */\n readonly #heartbeat: GatewayHeartbeat;\n #closed = false;\n /**\n * Close classification, backoff, scheduling, and the coordination that\n * keeps at most one logical connection attempt in flight.\n */\n readonly #reconnect: GatewayReconnect;\n /** Listener registry and emission. */\n readonly #events = new GatewayDispatcher((error) => this.#emitError(error));\n /** Application send budget under Discord's 120-per-60s limit. */\n readonly #sendBudget = new GatewaySendLimiter();\n /** How many reconnects were scheduled over this Gateway's life. */\n #reconnects = 0;\n public ping: number = -1;\n /** Creates a Gateway connection manager. @param options Gateway configuration. @throws {TypeError|RangeError} If configuration is invalid. */\n public constructor(options: GatewayOptions) {\n if (!options.token?.trim())\n throw new TypeError(\"A Gateway token is required.\");\n const resolvedIntents = resolveGatewayIntents(options.intents);\n if (!Number.isInteger(resolvedIntents) || resolvedIntents < 0)\n throw new TypeError(\n \"Gateway intents must be a non-negative integer.\",\n );\n this.#options = {\n shardId: 0,\n shardCount: 1,\n reconnect: true,\n maxReconnectAttempts: Infinity,\n reconnectBaseDelay: 1000,\n reconnectMaxDelay: 30000,\n heartbeatAckTimeout: 10000,\n zombieTimeout: 30000,\n // Lunibee identifies as itself, not as an official Discord client.\n properties: {\n os: process.platform,\n browser: \"Lunibee\",\n device: \"Lunibee\",\n },\n presence: {\n status: \"online\",\n activities: [],\n afk: false,\n since: null,\n },\n ...options,\n // Store the *resolved* bitfield, not the resolvable the caller\n // passed. IDENTIFY must carry a number: sending the array or\n // string form verbatim makes Discord answer 4013 (invalid\n // intents), which is a fatal close — so the documented array form\n // would never connect.\n intents: resolvedIntents,\n };\n if (\n this.#options.shardId < 0 ||\n this.#options.shardId >= this.#options.shardCount\n )\n throw new RangeError(\n \"Gateway shardId must be within the configured shard count.\",\n );\n if (this.#options.zombieTimeout <= this.#options.heartbeatAckTimeout)\n throw new RangeError(\n \"Gateway zombieTimeout must be greater than heartbeatAckTimeout\",\n );\n this.#transport = new WebSocketTransport({\n compress: this.#options.compress,\n createSocket: options.createSocket,\n handlers: {\n onOpen: () => this.#onOpen(),\n onFrame: (raw) => this.#message(raw, this.#token),\n onClose: (code) => this.#close(code, this.#token),\n // Transport failures reach consumers as GatewayError, the\n // type every other Gateway error path already uses.\n onError: (error) =>\n this.#emitError(\n error instanceof GatewayError\n ? error\n : new GatewayError(error.message, undefined, {\n cause: error,\n }),\n ),\n },\n });\n this.#reconnect = new GatewayReconnect({\n enabled: this.#options.reconnect,\n maxAttempts: this.#options.maxReconnectAttempts,\n baseDelay: this.#options.reconnectBaseDelay,\n maxDelay: this.#options.reconnectMaxDelay,\n stormLimit: options.reconnectStormLimit,\n stormWindow: options.reconnectStormWindow,\n });\n this.#heartbeat = new GatewayHeartbeat({\n ackTimeout: this.#options.heartbeatAckTimeout,\n zombieTimeout: this.#options.zombieTimeout,\n // Heartbeats are privileged: application traffic must never starve\n // the one payload that keeps the connection alive.\n send: (sequence) =>\n this.#dispatch(heartbeatPayload(sequence), true),\n sequence: () => this.#session.sequence,\n isConnected: () => !this.#closed && this.#transport.connected,\n onTimeout: (timeout) => this.#onHeartbeatTimeout(timeout),\n onError: (error) => this.#emitError(error),\n });\n }\n\n /**\n * Closes a connection that failed a liveness check.\n *\n * The heartbeat decides *that* the connection is dead; the Gateway decides\n * what to do about it, because only the Gateway owns the socket.\n */\n #onHeartbeatTimeout(timeout: HeartbeatTimeout): void {\n if (timeout.type === \"zombie\") {\n this.#emit(\"zombie\", {\n silentFor: timeout.silentFor,\n timeout: timeout.deadline,\n });\n this.#emitError(\n new GatewayError(\n `Gateway connection appears stale after ${timeout.silentFor}ms without traffic.`,\n ),\n );\n } else {\n this.#emitError(\n new GatewayError(\n `Gateway heartbeat acknowledgement timed out after ${timeout.elapsedMs}ms.`,\n ),\n );\n }\n this.#transport.close(\n RESUMABLE_CLOSE_CODE,\n timeout.type === \"zombie\"\n ? \"Zombie Gateway connection\"\n : \"Heartbeat timeout\",\n );\n }\n\n /** Opens the Gateway connection. @param url Gateway WebSocket URL. @returns Promise fulfilled when the socket opens; it rejects with a GatewayError if the Gateway was permanently closed or cannot connect. */\n public connect(url = DEFAULT_GATEWAY_URL): Promise<void> {\n if (this.state === GatewayState.Closed)\n return Promise.reject(\n new GatewayError(\"Gateway has been permanently closed.\"),\n );\n // At most one logical connection attempt is active: concurrent callers\n // join the in-flight attempt rather than opening competing sockets.\n const inFlight = this.#reconnect.attemptPromise;\n if (inFlight) return inFlight;\n // Connecting an already-live Gateway must be a no-op. Opening a second\n // socket leaves the first one unmanaged but still dispatching, which\n // interleaves two sequence streams (corrupting a later RESUME) and\n // sends a second IDENTIFY on the same session.\n if (this.#transport.live) return Promise.resolve();\n // A manual connect supersedes a scheduled reconnect; leaving the timer\n // armed would open a second socket once it fires.\n this.#reconnect.cancel();\n this.#closed = false;\n this.#setState(GatewayState.Connect);\n // Append compression parameter if enabled\n const connectURL = this.#options.compress\n ? url.includes(\"compress=\")\n ? url\n : `${url}&compress=zlib-stream`\n : url;\n return this.#reconnect.attempt(() => this.#open(connectURL));\n }\n /** Permanently closes the Gateway connection. */\n public close(): void {\n this.#closed = true;\n this.#clearTimers();\n this.#settleConnect(\n new GatewayError(\"Gateway connection closed before socket open.\"),\n );\n // Abandon rather than close-and-listen: the Gateway has already decided\n // the connection is over and must not be woken by its close event.\n this.#transport.destroy(1000, \"Client closed connection\");\n this.#setState(GatewayState.Closed);\n }\n /** Registers a Gateway event listener. @param event Event name. @param listener Event callback. @returns This Gateway. */\n /** Emits an event to all registered listeners. @param event Event name. @param data Event payload. */\n public emit(event: string, data?: unknown): void {\n this.#emit(event, data);\n }\n public on(event: string, listener: GatewayListener): this {\n if (!event || typeof listener !== \"function\")\n throw new TypeError(\"Gateway event and listener are required.\");\n this.#events.on(event, listener);\n return this;\n }\n /** Removes a Gateway event listener. @param event Event name. @param listener Event callback. @returns This Gateway. */\n public off(event: string, listener: GatewayListener): this {\n this.#events.off(event, listener);\n return this;\n }\n /**\n * A snapshot for dashboards and health checks: connection state, last\n * heartbeat round trip, how long the link has been silent, and how many\n * times it has reconnected.\n */\n public health(): GatewayHealth {\n return {\n state: this.state,\n latency: this.#heartbeat.latency,\n silentFor: this.#heartbeat.silentFor,\n reconnects: this.#reconnects,\n };\n }\n /** Sends a Gateway payload. @param payload Gateway payload. @returns Whether it was sent. */\n public send(payload: GatewayPayload): boolean {\n if (!payload || !Number.isInteger(payload.op))\n throw new TypeError(\n \"Gateway payload must contain an integer opcode.\",\n );\n return this.#dispatch(payload, false);\n }\n /**\n * Writes a payload to the socket. Heartbeats pass `privileged` so they are\n * never starved by application traffic: the 115 budget sits below\n * Discord's real 120/60s limit precisely to leave room for them, so\n * spending that headroom on heartbeats is what it is reserved for. They\n * are still recorded, keeping the true total under Discord's limit.\n */\n #dispatch(payload: GatewayPayload, privileged: boolean): boolean {\n if (!this.#transport.connected) return false;\n const now = Date.now();\n if (!this.#sendBudget.allows(privileged, now)) {\n this.#emitError(\n new GatewayError(\"Gateway send rate budget exhausted.\"),\n );\n return false;\n }\n if (!this.#transport.send(JSON.stringify(payload))) return false;\n this.#sendBudget.record(now);\n return true;\n }\n /** Sends a presence update. @param data Presence payload. @returns Whether it was sent. */\n public setPresence(data: GatewayPresence): boolean {\n this.#options.presence = { ...this.#options.presence, ...data };\n const payload = {\n since: this.#options.presence.since ?? null,\n activities: this.#options.presence.activities ?? [],\n status: this.#options.presence.status ?? \"online\",\n afk: Boolean(this.#options.presence.afk),\n };\n return this.send({\n op: GatewayOpcodes.PresenceUpdate,\n d: payload,\n s: null,\n t: null,\n });\n }\n /** Sends a voice state update. @param data Voice state payload. @returns Whether it was sent. */\n public setVoiceState(data: Record<string, unknown>): boolean {\n return this.send({\n op: GatewayOpcodes.VoiceStateUpdate,\n d: data,\n s: null,\n t: null,\n });\n }\n /**\n * Joins, moves or leaves a voice channel (op 4). Returns `false` without\n * sending when the guild belongs to another shard, or when disconnected.\n * @param guildId Guild snowflake. @param channelId Channel to join, or `null` to leave.\n * @param options `selfMute` and `selfDeaf`, both default `false`. @returns Whether it was sent.\n */\n public sendVoiceState(\n guildId: string,\n channelId: string | null,\n options: { selfMute?: boolean; selfDeaf?: boolean } = {},\n ): boolean {\n if (!this.ownsGuild(guildId)) return false;\n return this.setVoiceState({\n guild_id: guildId,\n channel_id: channelId,\n self_mute: options.selfMute ?? false,\n self_deaf: options.selfDeaf ?? false,\n });\n }\n /** Whether `guildId` maps to this Gateway's shard (`(id >> 22) % shardCount === shardId`). */\n public ownsGuild(guildId: string): boolean {\n const count = this.#options.shardCount;\n if (count <= 1) return true;\n return (\n Number((BigInt(guildId) >> 22n) % BigInt(count)) ===\n this.#options.shardId\n );\n }\n /** Requests guild members. @param data Guild member request payload. @returns Whether it was sent. */\n public requestGuildMembers(data: Record<string, unknown>): boolean {\n return this.send({\n op: GatewayOpcodes.RequestGuildMembers,\n d: data,\n s: null,\n t: null,\n });\n }\n /**\n * Starts one connection attempt.\n *\n * Ownership of the session moves to this attempt before the socket exists,\n * so a frame from the socket being replaced can never be mistaken for one\n * belonging to this connection.\n */\n #open(url: string): void {\n if (this.#closed) return;\n this.#token = this.#session.beginConnection();\n const result = this.#transport.connect(url);\n if (result.ok) return;\n // The socket could not be constructed. The caller's attempt fails with\n // the underlying reason, so a thrown GatewayError keeps its message.\n const failure = this.#normalizeError(result.error);\n this.#emitError(failure);\n if (this.#options.reconnect) {\n this.#setState(GatewayState.Reconnect);\n this.#scheduleReconnect();\n } else {\n this.#settleConnect(failure);\n }\n }\n #onOpen(): void {\n // Watch for silence from the moment the socket opens: a connection\n // that never reaches HELLO must still be detected as dead.\n this.#heartbeat.watch();\n this.#emit(\"open\", undefined);\n this.#settleConnect();\n }\n #message(raw: string, token: number): void {\n // The transport already drops frames from a replaced socket; this\n // second check keeps the session authoritative about who may mutate it.\n if (!this.#session.owns(token)) return;\n // Any frame from the current socket proves it is alive. Without this the\n // silence clock only moved on open, so every healthy connection was\n // declared a zombie once the staleness deadline passed.\n this.#heartbeat.receivedMessage();\n const { sequence, action } = classifyFrame(raw);\n // Record before acting: a RESUME must never be built from a sequence\n // the connection has not actually observed. The session ignores a\n // sequence from a superseded connection.\n if (sequence !== null) this.#session.recordSequence(sequence, token);\n this.#apply(action, token);\n }\n /**\n * Performs what the protocol says a frame meant.\n *\n * Every branch is an operation the protocol layer deliberately cannot do\n * for itself: closing a socket, mutating the session, running timers,\n * emitting events.\n */\n #apply(action: GatewayAction, token: number): void {\n switch (action.type) {\n case \"dispatch\":\n this.#setState(GatewayState.Dispatch);\n this.#handleDispatch(action.event, action.data, token);\n return;\n case \"hello\":\n this.#handleHello(action.heartbeatInterval);\n return;\n case \"heartbeat\":\n this.#setState(GatewayState.Heartbeat);\n this.#heartbeat.sendHeartbeat();\n return;\n case \"heartbeat-ack\":\n this.#heartbeat.acknowledge();\n this.ping = this.#heartbeat.latency;\n this.#emit(\"heartbeatAck\", action.data);\n return;\n case \"reconnect\":\n this.#transport.close(\n RESUMABLE_CLOSE_CODE,\n \"Server requested reconnect\",\n );\n return;\n case \"invalid-session\": {\n // A resumable invalidation keeps the session; a non-resumable\n // one destroys it, so the next connection must IDENTIFY.\n if (!action.resumable)\n this.#session.invalidate(token, \"invalid-session\");\n this.#emit(\"invalidSession\", action.resumable);\n this.#transport.close(\n action.resumable ? GatewayCloseCodes.UnknownError : 1000,\n action.resumable\n ? \"Invalid session (resumable)\"\n : \"Invalid session\",\n );\n return;\n }\n case \"invalid\":\n this.#emitError(\n new GatewayError(\n action.message,\n undefined,\n action.cause === undefined\n ? undefined\n : { cause: action.cause },\n ),\n );\n this.#transport.close(\n 1002,\n action.violation === \"invalid-json\"\n ? \"Invalid JSON\"\n : action.violation === \"invalid-hello\"\n ? \"Invalid heartbeat interval\"\n : \"Invalid payload\",\n );\n return;\n case \"unknown\":\n // Forward compatibility: Discord may add opcodes, and an\n // unfamiliar one is not a failure.\n return;\n }\n }\n #handleDispatch(event: string | null, data: unknown, token: number): void {\n if (event === \"READY\") {\n if (!this.#session.activate(data, token)) {\n // Either the payload lacked session information or this socket\n // no longer owns the session; only the former is an error.\n if (this.#session.owns(token))\n this.#emitError(\n new GatewayError(\n \"Gateway READY payload is missing session information\",\n ),\n );\n return;\n }\n this.#reconnect.reset();\n this.#setState(GatewayState.Ready);\n this.#emit(\"ready\", data);\n } else if (event === \"RESUMED\") {\n // A successful RESUME ends the reconnect cycle just like READY:\n // reset the backoff counter so a later disconnect starts from the\n // base delay, mark the connection READY, and surface a Discord.js-\n // familiar `resumed` event.\n this.#reconnect.reset();\n this.#setState(GatewayState.Ready);\n this.#emit(\"resumed\", data);\n }\n this.#emit(\"RAW\", { event: event ?? \"UNKNOWN\", data });\n if (this.#closed) return;\n this.#emit(event ?? \"dispatch\", data);\n }\n #handleHello(interval: number): void {\n this.#setState(\n this.#session.canResume ? GatewayState.Resume : GatewayState.Hello,\n );\n this.#heartbeat.start(interval);\n this.#identifyOrResume();\n }\n #identifyOrResume(): void {\n const intent = this.#session.handshake();\n // IDENTIFY/RESUME are privileged for the same reason heartbeats are:\n // dropping one leaves the shard connected but never READY, recoverable\n // only via the zombie timeout. Application traffic must never starve\n // the handshake out of the send budget.\n if (intent.type === \"resume\") {\n this.#setState(GatewayState.Resume);\n this.#dispatch(resumePayload(this.#options.token, intent), true);\n return;\n }\n this.#setState(GatewayState.Identify);\n this.#dispatch(\n identifyPayload({\n token: this.#options.token,\n intents: this.#options.intents,\n shardId: this.#options.shardId,\n shardCount: this.#options.shardCount,\n properties: this.#options.properties,\n presence: this.#options.presence,\n }),\n true,\n );\n }\n #close(code: number, token: number): void {\n this.#clearTimers();\n const action: CloseAction = this.#reconnect.classifyClose(\n code,\n this.#session.canResume,\n );\n this.#emit(\"close\", { code, action });\n if (this.#closed || !this.#reconnect.enabled || action === \"stop\") {\n // A fatal close is terminal: the condition behind 4004/4013/4014\n // cannot fix itself, so the Gateway settles as CLOSED rather than\n // sitting in CONNECT looking like it is about to try again (WS-003).\n if (action === \"stop\") this.#closed = true;\n this.#setState(\n this.#closed ? GatewayState.Closed : GatewayState.Connect,\n );\n if (this.#reconnect.connecting)\n this.#reconnect.settle(\n new GatewayError(\n `Gateway closed before READY (code ${code}).`,\n code,\n ),\n );\n return;\n }\n if (action === \"identify\")\n // Fresh IDENTIFY: drop the session AND its resume host so the\n // reconnect targets the main Gateway, not a stale resume URL.\n this.#session.invalidate(token, \"session-timeout\");\n this.#setState(GatewayState.Reconnect);\n this.#scheduleReconnect();\n }\n #scheduleReconnect(): void {\n if (this.#closed) return;\n const result = this.#reconnect.schedule(() =>\n this.#open(this.#session.connectURL(DEFAULT_GATEWAY_URL)),\n );\n if (result.scheduled) {\n this.#reconnects++;\n if (result.unstable)\n this.#emit(\"unstable\", {\n reconnects: result.unstable.reconnects,\n delay: result.delayMs,\n });\n return;\n }\n if (result.reason !== \"exhausted\") return;\n this.#reconnect.settle(\n new GatewayError(\"Gateway reconnect attempts exhausted.\"),\n );\n this.#setState(GatewayState.Connect);\n }\n #settleConnect(error?: GatewayError): void {\n this.#reconnect.settle(error);\n }\n #clearTimers(): void {\n this.#reconnect.cancel();\n this.#heartbeat.stop();\n }\n #normalizeError(error: unknown): GatewayError {\n return error instanceof GatewayError\n ? error\n : new GatewayError(\"Gateway connection failed.\", undefined, {\n cause: error,\n });\n }\n #emitError(error: unknown): void {\n this.#emit(\n \"error\",\n error instanceof Error ? error : new GatewayError(String(error)),\n );\n }\n #emit(event: string, data: unknown): void {\n this.#events.emit(event, data);\n }\n /** Changes the Gateway lifecycle state. @param next Next lifecycle state. */\n #setState(next: GatewayState): void {\n if (this.state === next) return;\n const previous = this.state;\n this.state = next;\n this.#emit(\"stateChange\", { previous, next });\n }\n}\n",
|
|
19
|
+
"import { GatewaySession } from \"./session.js\";\nimport { GatewayHeartbeat, type HeartbeatTimeout } from \"./heartbeat.js\";\nimport { GatewayCloseCodes } from \"./close-codes.js\";\nimport { GatewayReconnect, type CloseAction } from \"./reconnect.js\";\nimport { WebSocketTransport, type SocketFactory } from \"./transport.js\";\nimport { GatewayOpcodes } from \"./opcodes.js\";\nimport {\n classifyFrame,\n identifyPayload,\n resumePayload,\n heartbeatPayload,\n type GatewayAction,\n} from \"./protocol.js\";\nimport {\n resolveGatewayIntents,\n type GatewayPayload,\n type GatewayProperties,\n type GatewayPresence,\n type GatewayIntentResolvable,\n} from \"@lunibee/types\";\n\nexport {\n classifyFrame,\n classifyPayload,\n identifyPayload,\n resumePayload,\n heartbeatPayload,\n type GatewayAction,\n type ProtocolResult,\n type ProtocolViolation,\n type IdentifyOptions,\n type IdentifyProperties,\n type IdentifyData,\n} from \"./protocol.js\";\n\nexport {\n WebSocketTransport,\n type TransportHandlers,\n type TransportOptions,\n type SocketFactory,\n} from \"./transport.js\";\n\nexport {\n createDecoder,\n PlainTextDecoder,\n ZlibStreamDecoder,\n type GatewayDecoder,\n} from \"./decoder.js\";\n\nexport {\n GatewayReconnect,\n classifyCloseCode,\n FATAL_CLOSE_CODES,\n IDENTIFY_CLOSE_CODES,\n type CloseAction,\n type ReconnectOptions,\n type ScheduleResult,\n type ScheduleRefusal,\n} from \"./reconnect.js\";\n\nexport {\n GatewayHeartbeat,\n type HeartbeatOptions,\n type HeartbeatTimeout,\n} from \"./heartbeat.js\";\n\nexport {\n GatewaySession,\n type ResumeInfo,\n type HandshakeIntent,\n type InvalidationReason,\n} from \"./session.js\";\n\nexport { GatewayDispatcher, type GatewayListener } from \"./dispatcher.js\";\nexport { GatewaySendLimiter, SendBudget } from \"./send-budget.js\";\nexport { GatewayOpcodes } from \"./opcodes.js\";\n\nexport { GatewayCloseCodes } from \"./close-codes.js\";\n\nexport { GatewayState, Status, GatewayError } from \"./state.js\";\nimport { GatewayState, GatewayError } from \"./state.js\";\nimport { GatewayDispatcher, type GatewayListener } from \"./dispatcher.js\";\nimport { GatewaySendLimiter } from \"./send-budget.js\";\n/** Gateway connection configuration. */\nexport interface GatewayOptions {\n /** Authentication token. */\n token: string;\n /** Gateway intent bitfield or resolvable. */\n intents: GatewayIntentResolvable;\n /** Shard identifier. */\n shardId?: number;\n /** Total shard count. */\n shardCount?: number;\n /** Whether automatic reconnect is enabled. */\n reconnect?: boolean;\n /** Maximum reconnect attempts. */\n maxReconnectAttempts?: number;\n /** How long a socket may stay connecting before it is closed and retried, in ms. Default 30000. */\n connectTimeout?: number;\n /** Initial reconnect delay. */\n reconnectBaseDelay?: number;\n /** Maximum reconnect delay. */\n reconnectMaxDelay?: number;\n /** Heartbeat acknowledgement timeout. */\n heartbeatAckTimeout?: number;\n /** Zombie connection timeout. */\n zombieTimeout?: number;\n /** Reconnects inside `reconnectStormWindow` after which the link counts as unstable: `unstable` is emitted and the delay keeps doubling. Default 5. */\n reconnectStormLimit?: number;\n /** The span over which reconnects are counted for storm detection, in ms. Default 60,000. */\n reconnectStormWindow?: number;\n /** Identification properties. */\n properties?: GatewayProperties;\n /** Presence data. */\n presence?: GatewayPresence;\n /**\n * Whether to enable zlib-stream transport compression.\n * Decoded with a persistent `node:zlib` inflate stream, matching\n * Discord's zlib-wrapped stream framing (`Z_SYNC_FLUSH` boundaries).\n * When enabled, appends `&compress=zlib-stream` to the Gateway URL.\n */\n compress?: boolean;\n /**\n * Constructs the underlying socket. Defaults to the ambient `WebSocket`.\n * Injected to run the Gateway on an alternative transport, or on a stub.\n */\n createSocket?: SocketFactory;\n}\n/** Discord's main Gateway endpoint, used when no resume host is known. */\n/**\n * Close code for connections Lunibee means to RESUME. Discord invalidates the\n * session when a client closes with 1000 or 1001, so a reconnect that should\n * keep its session must close with anything else.\n */\nconst RESUMABLE_CLOSE_CODE = 4900;\nconst DEFAULT_GATEWAY_URL = \"wss://gateway.discord.gg/?v=10&encoding=json\";\n\n/** What {@link Gateway.health} reports. */\nexport interface GatewayHealth {\n /** Connection lifecycle state. */\n state: GatewayState;\n /** Round trip of the last acknowledged heartbeat in ms, or -1 before the first. */\n latency: number;\n /** Milliseconds since the last message from Discord, or 0 before any. */\n silentFor: number;\n /** Reconnects scheduled since this Gateway was created. */\n reconnects: number;\n}\n/** Manages a Discord Gateway connection. */\nexport class Gateway {\n /** Current Gateway lifecycle state. */\n public state: GatewayState = GatewayState.Connect;\n #options: Required<\n Omit<\n GatewayOptions,\n | \"properties\"\n | \"presence\"\n | \"compress\"\n | \"createSocket\"\n | \"intents\"\n | \"reconnectStormLimit\"\n | \"reconnectStormWindow\"\n >\n > & {\n /** Resolved intent bitfield, never the resolvable form. */\n intents: number;\n properties?: GatewayProperties;\n presence?: GatewayPresence;\n compress?: boolean;\n };\n /**\n * The socket this connection runs on.\n *\n * The Gateway never touches a `WebSocket` directly: construction, listener\n * wiring, replacement and decoding all belong to the transport.\n */\n readonly #transport: WebSocketTransport;\n /**\n * Session identity and the IDENTIFY-vs-RESUME decision.\n *\n * The Gateway holds no session fields of its own: every read and write goes\n * through the session, which rejects mutations from a superseded socket.\n */\n readonly #session = new GatewaySession();\n /** Token of the socket this Gateway currently owns. */\n #token = 0;\n /**\n * Heartbeat, acknowledgement deadline and staleness watch.\n *\n * The Gateway holds no heartbeat timers of its own; it supplies the socket\n * and decides what to close when liveness fails.\n */\n readonly #heartbeat: GatewayHeartbeat;\n #closed = false;\n /**\n * Close classification, backoff, scheduling, and the coordination that\n * keeps at most one logical connection attempt in flight.\n */\n readonly #reconnect: GatewayReconnect;\n /** Listener registry and emission. */\n readonly #events = new GatewayDispatcher((error) => this.#emitError(error));\n /** Application send budget under Discord's 120-per-60s limit. */\n readonly #sendBudget = new GatewaySendLimiter();\n /** How many reconnects were scheduled over this Gateway's life. */\n #reconnects = 0;\n public ping: number = -1;\n /** Creates a Gateway connection manager. @param options Gateway configuration. @throws {TypeError|RangeError} If configuration is invalid. */\n public constructor(options: GatewayOptions) {\n if (!options.token?.trim())\n throw new TypeError(\"A Gateway token is required.\");\n for (const key of [\n \"reconnectBaseDelay\",\n \"reconnectMaxDelay\",\n \"connectTimeout\",\n ] as const)\n if (options[key] !== undefined && !(options[key]! > 0))\n throw new RangeError(`Gateway ${key} must be greater than 0.`);\n const resolvedIntents = resolveGatewayIntents(options.intents);\n if (!Number.isInteger(resolvedIntents) || resolvedIntents < 0)\n throw new TypeError(\n \"Gateway intents must be a non-negative integer.\",\n );\n this.#options = {\n shardId: 0,\n shardCount: 1,\n reconnect: true,\n maxReconnectAttempts: Infinity,\n connectTimeout: 30000,\n reconnectBaseDelay: 1000,\n reconnectMaxDelay: 30000,\n heartbeatAckTimeout: 10000,\n zombieTimeout: 30000,\n // Lunibee identifies as itself, not as an official Discord client.\n properties: {\n os: process.platform,\n browser: \"Lunibee\",\n device: \"Lunibee\",\n },\n presence: {\n status: \"online\",\n activities: [],\n afk: false,\n since: null,\n },\n ...options,\n // Store the *resolved* bitfield, not the resolvable the caller\n // passed. IDENTIFY must carry a number: sending the array or\n // string form verbatim makes Discord answer 4013 (invalid\n // intents), which is a fatal close — so the documented array form\n // would never connect.\n intents: resolvedIntents,\n };\n if (\n this.#options.shardId < 0 ||\n this.#options.shardId >= this.#options.shardCount\n )\n throw new RangeError(\n \"Gateway shardId must be within the configured shard count.\",\n );\n if (this.#options.zombieTimeout <= this.#options.heartbeatAckTimeout)\n throw new RangeError(\n \"Gateway zombieTimeout must be greater than heartbeatAckTimeout\",\n );\n this.#transport = new WebSocketTransport({\n compress: this.#options.compress,\n createSocket: options.createSocket,\n handlers: {\n onOpen: () => this.#onOpen(),\n onFrame: (raw) => this.#message(raw, this.#token),\n onClose: (code) => this.#close(code, this.#token),\n // Transport failures reach consumers as GatewayError, the\n // type every other Gateway error path already uses.\n onError: (error) =>\n this.#emitError(\n error instanceof GatewayError\n ? error\n : new GatewayError(error.message, undefined, {\n cause: error,\n }),\n ),\n },\n });\n this.#reconnect = new GatewayReconnect({\n enabled: this.#options.reconnect,\n maxAttempts: this.#options.maxReconnectAttempts,\n baseDelay: this.#options.reconnectBaseDelay,\n maxDelay: this.#options.reconnectMaxDelay,\n stormLimit: options.reconnectStormLimit,\n stormWindow: options.reconnectStormWindow,\n });\n this.#heartbeat = new GatewayHeartbeat({\n ackTimeout: this.#options.heartbeatAckTimeout,\n zombieTimeout: this.#options.zombieTimeout,\n // Heartbeats are privileged: application traffic must never starve\n // the one payload that keeps the connection alive.\n send: (sequence) =>\n this.#dispatch(heartbeatPayload(sequence), true),\n sequence: () => this.#session.sequence,\n isConnected: () => !this.#closed && this.#transport.connected,\n onTimeout: (timeout) => this.#onHeartbeatTimeout(timeout),\n onError: (error) => this.#emitError(error),\n });\n }\n\n /**\n * Closes a connection that failed a liveness check.\n *\n * The heartbeat decides *that* the connection is dead; the Gateway decides\n * what to do about it, because only the Gateway owns the socket.\n */\n #onHeartbeatTimeout(timeout: HeartbeatTimeout): void {\n if (timeout.type === \"zombie\") {\n this.#emit(\"zombie\", {\n silentFor: timeout.silentFor,\n timeout: timeout.deadline,\n });\n this.#emitError(\n new GatewayError(\n `Gateway connection appears stale after ${timeout.silentFor}ms without traffic.`,\n ),\n );\n } else {\n this.#emitError(\n new GatewayError(\n `Gateway heartbeat acknowledgement timed out after ${timeout.elapsedMs}ms.`,\n ),\n );\n }\n this.#transport.close(\n RESUMABLE_CLOSE_CODE,\n timeout.type === \"zombie\"\n ? \"Zombie Gateway connection\"\n : \"Heartbeat timeout\",\n );\n }\n\n /** Opens the Gateway connection. @param url Gateway WebSocket URL. @returns Promise fulfilled when the socket opens; it rejects with a GatewayError if the Gateway was permanently closed or cannot connect. */\n public connect(url = DEFAULT_GATEWAY_URL): Promise<void> {\n if (this.state === GatewayState.Closed)\n return Promise.reject(\n new GatewayError(\"Gateway has been permanently closed.\"),\n );\n // At most one logical connection attempt is active: concurrent callers\n // join the in-flight attempt rather than opening competing sockets.\n const inFlight = this.#reconnect.attemptPromise;\n if (inFlight) return inFlight;\n // Connecting an already-live Gateway must be a no-op. Opening a second\n // socket leaves the first one unmanaged but still dispatching, which\n // interleaves two sequence streams (corrupting a later RESUME) and\n // sends a second IDENTIFY on the same session.\n if (this.#transport.live) return Promise.resolve();\n // A manual connect supersedes a scheduled reconnect; leaving the timer\n // armed would open a second socket once it fires.\n this.#reconnect.cancel();\n this.#closed = false;\n this.#setState(GatewayState.Connect);\n // Append compression parameter if enabled\n const connectURL = this.#options.compress\n ? url.includes(\"compress=\")\n ? url\n : `${url}${url.includes(\"?\") ? \"&\" : \"?\"}compress=zlib-stream`\n : url;\n return this.#reconnect.attempt(() => this.#open(connectURL));\n }\n /** Permanently closes the Gateway connection. */\n public close(): void {\n this.#closed = true;\n this.#clearTimers();\n this.#settleConnect(\n new GatewayError(\"Gateway connection closed before socket open.\"),\n );\n // Abandon rather than close-and-listen: the Gateway has already decided\n // the connection is over and must not be woken by its close event.\n this.#transport.destroy(1000, \"Client closed connection\");\n this.#setState(GatewayState.Closed);\n }\n /** Registers a Gateway event listener. @param event Event name. @param listener Event callback. @returns This Gateway. */\n /** Emits an event to all registered listeners. @param event Event name. @param data Event payload. */\n public emit(event: string, data?: unknown): void {\n this.#emit(event, data);\n }\n public on(event: string, listener: GatewayListener): this {\n if (!event || typeof listener !== \"function\")\n throw new TypeError(\"Gateway event and listener are required.\");\n this.#events.on(event, listener);\n return this;\n }\n /** Removes a Gateway event listener. @param event Event name. @param listener Event callback. @returns This Gateway. */\n public off(event: string, listener: GatewayListener): this {\n this.#events.off(event, listener);\n return this;\n }\n /**\n * A snapshot for dashboards and health checks: connection state, last\n * heartbeat round trip, how long the link has been silent, and how many\n * times it has reconnected.\n */\n public health(): GatewayHealth {\n return {\n state: this.state,\n latency: this.#heartbeat.latency,\n silentFor: this.#heartbeat.silentFor,\n reconnects: this.#reconnects,\n };\n }\n /** Sends a Gateway payload. @param payload Gateway payload. @returns Whether it was sent. */\n public send(payload: GatewayPayload): boolean {\n if (!payload || !Number.isInteger(payload.op))\n throw new TypeError(\n \"Gateway payload must contain an integer opcode.\",\n );\n return this.#dispatch(payload, false);\n }\n /**\n * Writes a payload to the socket. Heartbeats pass `privileged` so they are\n * never starved by application traffic: the 115 budget sits below\n * Discord's real 120/60s limit precisely to leave room for them, so\n * spending that headroom on heartbeats is what it is reserved for. They\n * are still recorded, keeping the true total under Discord's limit.\n */\n #dispatch(payload: GatewayPayload, privileged: boolean): boolean {\n if (!this.#transport.connected) return false;\n const now = Date.now();\n if (!this.#sendBudget.allows(privileged, now)) {\n this.#emitError(\n new GatewayError(\"Gateway send rate budget exhausted.\"),\n );\n return false;\n }\n if (!this.#transport.send(JSON.stringify(payload))) return false;\n this.#sendBudget.record(now);\n return true;\n }\n /** Sends a presence update. @param data Presence payload. @returns Whether it was sent. */\n public setPresence(data: GatewayPresence): boolean {\n this.#options.presence = { ...this.#options.presence, ...data };\n const payload = {\n since: this.#options.presence.since ?? null,\n activities: this.#options.presence.activities ?? [],\n status: this.#options.presence.status ?? \"online\",\n afk: Boolean(this.#options.presence.afk),\n };\n return this.send({\n op: GatewayOpcodes.PresenceUpdate,\n d: payload,\n s: null,\n t: null,\n });\n }\n /** Sends a voice state update. @param data Voice state payload. @returns Whether it was sent. */\n public setVoiceState(data: Record<string, unknown>): boolean {\n return this.send({\n op: GatewayOpcodes.VoiceStateUpdate,\n d: data,\n s: null,\n t: null,\n });\n }\n /**\n * Joins, moves or leaves a voice channel (op 4). Returns `false` without\n * sending when the guild belongs to another shard, or when disconnected.\n * @param guildId Guild snowflake. @param channelId Channel to join, or `null` to leave.\n * @param options `selfMute` and `selfDeaf`, both default `false`. @returns Whether it was sent.\n */\n public sendVoiceState(\n guildId: string,\n channelId: string | null,\n options: { selfMute?: boolean; selfDeaf?: boolean } = {},\n ): boolean {\n if (!this.ownsGuild(guildId)) return false;\n return this.setVoiceState({\n guild_id: guildId,\n channel_id: channelId,\n self_mute: options.selfMute ?? false,\n self_deaf: options.selfDeaf ?? false,\n });\n }\n /** Whether `guildId` maps to this Gateway's shard (`(id >> 22) % shardCount === shardId`). */\n public ownsGuild(guildId: string): boolean {\n const count = this.#options.shardCount;\n if (count <= 1) return true;\n if (!/^\\d{1,20}$/.test(guildId)) return false;\n return (\n Number((BigInt(guildId) >> 22n) % BigInt(count)) ===\n this.#options.shardId\n );\n }\n /** Requests guild members. @param data Guild member request payload. @returns Whether it was sent. */\n public requestGuildMembers(data: Record<string, unknown>): boolean {\n return this.send({\n op: GatewayOpcodes.RequestGuildMembers,\n d: data,\n s: null,\n t: null,\n });\n }\n /**\n * Starts one connection attempt.\n *\n * Ownership of the session moves to this attempt before the socket exists,\n * so a frame from the socket being replaced can never be mistaken for one\n * belonging to this connection.\n */\n #open(url: string): void {\n if (this.#closed) return;\n this.#token = this.#session.beginConnection();\n const result = this.#transport.connect(url);\n if (result.ok) {\n // A black-holed TLS handshake never opens or closes on its own.\n const token = this.#token;\n clearTimeout(this.#connectTimer);\n this.#connectTimer = setTimeout(() => {\n if (token === this.#token && !this.#closed)\n this.#transport.close(4000, \"connect timeout\");\n }, this.#options.connectTimeout);\n this.#connectTimer.unref?.();\n return;\n }\n // The socket could not be constructed. The caller's attempt fails with\n // the underlying reason, so a thrown GatewayError keeps its message.\n const failure = this.#normalizeError(result.error);\n this.#emitError(failure);\n if (this.#options.reconnect) {\n this.#setState(GatewayState.Reconnect);\n this.#scheduleReconnect();\n } else {\n this.#settleConnect(failure);\n }\n }\n #connectTimer?: ReturnType<typeof setTimeout>;\n #onOpen(): void {\n clearTimeout(this.#connectTimer);\n // Watch for silence from the moment the socket opens: a connection\n // that never reaches HELLO must still be detected as dead.\n this.#heartbeat.watch();\n this.#emit(\"open\", undefined);\n this.#settleConnect();\n }\n #message(raw: string, token: number): void {\n // The transport already drops frames from a replaced socket; this\n // second check keeps the session authoritative about who may mutate it.\n if (!this.#session.owns(token)) return;\n // Any frame from the current socket proves it is alive. Without this the\n // silence clock only moved on open, so every healthy connection was\n // declared a zombie once the staleness deadline passed.\n this.#heartbeat.receivedMessage();\n const { sequence, action } = classifyFrame(raw);\n // Record before acting: a RESUME must never be built from a sequence\n // the connection has not actually observed. The session ignores a\n // sequence from a superseded connection.\n if (sequence !== null) this.#session.recordSequence(sequence, token);\n this.#apply(action, token);\n }\n /**\n * Performs what the protocol says a frame meant.\n *\n * Every branch is an operation the protocol layer deliberately cannot do\n * for itself: closing a socket, mutating the session, running timers,\n * emitting events.\n */\n #apply(action: GatewayAction, token: number): void {\n switch (action.type) {\n case \"dispatch\":\n this.#setState(GatewayState.Dispatch);\n this.#handleDispatch(action.event, action.data, token);\n return;\n case \"hello\":\n this.#handleHello(action.heartbeatInterval);\n return;\n case \"heartbeat\":\n this.#setState(GatewayState.Heartbeat);\n this.#heartbeat.sendHeartbeat();\n return;\n case \"heartbeat-ack\":\n this.#heartbeat.acknowledge();\n this.ping = this.#heartbeat.latency;\n this.#emit(\"heartbeatAck\", action.data);\n return;\n case \"reconnect\":\n this.#transport.close(\n RESUMABLE_CLOSE_CODE,\n \"Server requested reconnect\",\n );\n return;\n case \"invalid-session\": {\n // A resumable invalidation keeps the session; a non-resumable\n // one destroys it, so the next connection must IDENTIFY.\n if (!action.resumable)\n this.#session.invalidate(token, \"invalid-session\");\n this.#emit(\"invalidSession\", action.resumable);\n this.#transport.close(\n action.resumable ? GatewayCloseCodes.UnknownError : 1000,\n action.resumable\n ? \"Invalid session (resumable)\"\n : \"Invalid session\",\n );\n return;\n }\n case \"invalid\":\n this.#emitError(\n new GatewayError(\n action.message,\n undefined,\n action.cause === undefined\n ? undefined\n : { cause: action.cause },\n ),\n );\n this.#transport.close(\n 1002,\n action.violation === \"invalid-json\"\n ? \"Invalid JSON\"\n : action.violation === \"invalid-hello\"\n ? \"Invalid heartbeat interval\"\n : \"Invalid payload\",\n );\n return;\n case \"unknown\":\n // Forward compatibility: Discord may add opcodes, and an\n // unfamiliar one is not a failure.\n return;\n }\n }\n #handleDispatch(event: string | null, data: unknown, token: number): void {\n if (event === \"READY\") {\n if (!this.#session.activate(data, token)) {\n // Either the payload lacked session information or this socket\n // no longer owns the session; only the former is an error.\n if (this.#session.owns(token))\n this.#emitError(\n new GatewayError(\n \"Gateway READY payload is missing session information\",\n ),\n );\n return;\n }\n this.#reconnect.reset();\n this.#setState(GatewayState.Ready);\n this.#emit(\"ready\", data);\n } else if (event === \"RESUMED\") {\n // A successful RESUME ends the reconnect cycle just like READY:\n // reset the backoff counter so a later disconnect starts from the\n // base delay, mark the connection READY, and surface a Discord.js-\n // familiar `resumed` event.\n this.#reconnect.reset();\n this.#setState(GatewayState.Ready);\n this.#emit(\"resumed\", data);\n }\n this.#emit(\"RAW\", { event: event ?? \"UNKNOWN\", data });\n if (this.#closed) return;\n // Real Gateway events are UPPER_SNAKE_CASE; anything else could\n // collide with a lifecycle event (`close`, `ready`, `error`).\n if (event == null || /^[A-Z][A-Z0-9_]*$/.test(event))\n this.#emit(event ?? \"dispatch\", data);\n }\n #handleHello(interval: number): void {\n this.#setState(\n this.#session.canResume ? GatewayState.Resume : GatewayState.Hello,\n );\n this.#heartbeat.start(interval);\n this.#identifyOrResume();\n }\n #identifyOrResume(): void {\n const intent = this.#session.handshake();\n // IDENTIFY/RESUME are privileged for the same reason heartbeats are:\n // dropping one leaves the shard connected but never READY, recoverable\n // only via the zombie timeout. Application traffic must never starve\n // the handshake out of the send budget.\n if (intent.type === \"resume\") {\n this.#setState(GatewayState.Resume);\n this.#dispatch(resumePayload(this.#options.token, intent), true);\n return;\n }\n this.#setState(GatewayState.Identify);\n this.#dispatch(\n identifyPayload({\n token: this.#options.token,\n intents: this.#options.intents,\n shardId: this.#options.shardId,\n shardCount: this.#options.shardCount,\n properties: this.#options.properties,\n presence: this.#options.presence,\n }),\n true,\n );\n }\n #close(code: number, token: number): void {\n this.#clearTimers();\n const action: CloseAction = this.#reconnect.classifyClose(\n code,\n this.#session.canResume,\n );\n this.#emit(\"close\", { code, action });\n if (this.#closed || !this.#reconnect.enabled || action === \"stop\") {\n // A fatal close is terminal: the condition behind 4004/4013/4014\n // cannot fix itself, so the Gateway settles as CLOSED rather than\n // sitting in CONNECT looking like it is about to try again (WS-003).\n if (action === \"stop\") this.#closed = true;\n this.#setState(\n this.#closed ? GatewayState.Closed : GatewayState.Connect,\n );\n if (this.#reconnect.connecting)\n this.#reconnect.settle(\n new GatewayError(\n `Gateway closed before READY (code ${code}).`,\n code,\n ),\n );\n return;\n }\n if (action === \"identify\")\n // Fresh IDENTIFY: drop the session AND its resume host so the\n // reconnect targets the main Gateway, not a stale resume URL.\n this.#session.invalidate(token, \"session-timeout\");\n this.#setState(GatewayState.Reconnect);\n this.#scheduleReconnect();\n }\n #scheduleReconnect(): void {\n if (this.#closed) return;\n const result = this.#reconnect.schedule(() =>\n this.#open(this.#session.connectURL(DEFAULT_GATEWAY_URL)),\n );\n if (result.scheduled) {\n this.#reconnects++;\n if (result.unstable)\n this.#emit(\"unstable\", {\n reconnects: result.unstable.reconnects,\n delay: result.delayMs,\n });\n return;\n }\n if (result.reason !== \"exhausted\") return;\n this.#reconnect.settle(\n new GatewayError(\"Gateway reconnect attempts exhausted.\"),\n );\n this.#setState(GatewayState.Connect);\n }\n #settleConnect(error?: GatewayError): void {\n this.#reconnect.settle(error);\n }\n #clearTimers(): void {\n clearTimeout(this.#connectTimer);\n this.#reconnect.cancel();\n this.#heartbeat.stop();\n }\n #normalizeError(error: unknown): GatewayError {\n return error instanceof GatewayError\n ? error\n : new GatewayError(\"Gateway connection failed.\", undefined, {\n cause: error,\n });\n }\n #emitError(error: unknown): void {\n this.#emit(\n \"error\",\n error instanceof Error ? error : new GatewayError(String(error)),\n );\n }\n #emit(event: string, data: unknown): void {\n this.#events.emit(event, data);\n }\n /** Changes the Gateway lifecycle state. @param next Next lifecycle state. */\n #setState(next: GatewayState): void {\n if (this.state === next) return;\n const previous = this.state;\n this.state = next;\n this.#emit(\"stateChange\", { previous, next });\n }\n}\n",
|
|
20
20
|
"/**\n * What a cluster worker knows about itself. `ClusterManager` sets it through\n * environment variables; {@link readClusterContext} reads it back.\n */\nexport interface ClusterContext {\n /** This cluster's ID. */\n clusterId: number;\n /** How many clusters the deployment started. */\n clusterCount: number;\n /** Total shards in the deployment (what Discord calls `num_shards`). */\n shardCount: number;\n /** Shards this cluster owns. */\n shardIds: number[];\n /** Whether the parent hands out IDENTIFY slots for this cluster. */\n coordinatedIdentify: boolean;\n}\n\n/** Set to `1` in every process `ClusterManager` forks, so a worker can tell it is one. */\nexport const WORKER_MARKER = \"LUNIBEE_CLUSTER_WORKER\";\n\n/** Whether this process was forked by a `ClusterManager`. */\nexport function isClusterWorker(\n env: Record<string, string | undefined> = process.env,\n): boolean {\n return env[WORKER_MARKER] === \"1\";\n}\n\n/** Checks that shard IDs are unique integers in `[0, total)`. @throws {RangeError} Otherwise. */\nexport function validateShardIds(\n shardIds: readonly number[],\n total: number,\n): void {\n if (!Number.isInteger(total) || total < 1)\n throw new RangeError(\"Shard count must be a positive integer.\");\n const seen = new Set<number>();\n for (const id of shardIds) {\n if (!Number.isInteger(id) || id < 0 || id >= total)\n throw new RangeError(\n `Shard ID ${String(id)} is outside 0-${total - 1}.`,\n );\n if (seen.has(id))\n throw new RangeError(`Shard ID ${id} is listed twice.`);\n seen.add(id);\n }\n}\n\n/**\n * Splits shard IDs across clusters round-robin: the i-th ID goes to cluster\n * `i % clusterCount`. The same input always gives the same output.\n * @returns One list per cluster (some may be empty when there are more clusters than shards).\n * @throws {RangeError} If `clusterCount` is not a positive integer.\n */\nexport function allocateShards(\n shardIds: readonly number[],\n clusterCount: number,\n): number[][] {\n if (!Number.isInteger(clusterCount) || clusterCount < 1)\n throw new RangeError(\"clusterCount must be a positive integer.\");\n const chunks = Array.from({ length: clusterCount }, () => [] as number[]);\n shardIds.forEach((id, index) => chunks[index % clusterCount]!.push(id));\n return chunks;\n}\n\n/**\n * Proves an allocation covers `expected` exactly once.\n * @throws {Error} Naming the missing, duplicate or unexpected shard IDs.\n */\nexport function assertCompleteAllocation(\n chunks: readonly (readonly number[])[],\n expected: readonly number[],\n): void {\n const owners = new Map<number, number>();\n const duplicate: number[] = [];\n chunks.forEach((chunk, cluster) => {\n for (const id of chunk) {\n if (owners.has(id)) duplicate.push(id);\n owners.set(id, cluster);\n }\n });\n const want = new Set(expected);\n const missing = expected.filter((id) => !owners.has(id));\n const extra = [...owners.keys()].filter((id) => !want.has(id));\n if (duplicate.length || missing.length || extra.length)\n throw new Error(\n `Shard allocation is not exact: duplicate [${duplicate}], missing [${missing}], unexpected [${extra}].`,\n );\n}\n\nfunction parseInteger(name: string, value: string | undefined): number {\n if (value === undefined || !/^\\d+$/.test(value))\n throw new RangeError(`${name} must be a non-negative integer.`);\n return Number(value);\n}\n\n/**\n * Reads the worker contract from the environment: `CLUSTER_ID`, `CLUSTER_COUNT`,\n * `SHARD_COUNT`, `SHARD_LIST` (comma separated) and `LUNIBEE_IDENTIFY`.\n * @returns The context, or `null` when this process is not a cluster worker.\n * @throws {RangeError} If the variables are present but malformed.\n */\nexport function readClusterContext(\n env: Record<string, string | undefined> = process.env,\n): ClusterContext | null {\n if (!isClusterWorker(env)) return null;\n const shardCount = parseInteger(\"SHARD_COUNT\", env.SHARD_COUNT);\n const shardIds = (env.SHARD_LIST ?? \"\")\n .split(\",\")\n .filter((part) => part !== \"\")\n .map((part) => parseInteger(\"SHARD_LIST\", part));\n validateShardIds(shardIds, shardCount);\n return {\n clusterId: parseInteger(\"CLUSTER_ID\", env.CLUSTER_ID),\n clusterCount: parseInteger(\"CLUSTER_COUNT\", env.CLUSTER_COUNT),\n shardCount,\n shardIds,\n coordinatedIdentify: env.LUNIBEE_IDENTIFY === \"coordinated\",\n };\n}\n",
|
|
21
21
|
"import type { WorkerReport } from \"./health.js\";\n\n/** Marks a cluster control frame on the IPC channel, next to the shard-bus frames. */\nexport const CLUSTER_FRAME = \"__lunibeeCluster\";\n/** The `source` and reply `target` of the parent process on the shard bus. */\nexport const PARENT_ID = -1;\n/** Built-in cluster request: the worker answers with a {@link WorkerReport}. */\nexport const REQUEST_HEALTH = \"__lunibee:health\";\n/** Built-in cluster request: the worker starts the given shards (recovery). */\nexport const REQUEST_ASSIGN = \"__lunibee:assign\";\n\n/** Frames a worker sends to the parent. */\nexport type ChildFrame =\n | { kind: \"ready\"; shardIds: number[] }\n | { kind: \"identify\"; id: number; shardId: number }\n | { kind: \"identify-done\"; shardId: number }\n | { kind: \"reply\"; id: string; result?: unknown; error?: string }\n | { kind: \"health\"; report: WorkerReport };\n\n/** Frames the parent sends to a worker. */\nexport type ParentFrame =\n | { kind: \"identify-grant\"; id: number }\n | { kind: \"identify-deny\"; id: number; error: string }\n | { kind: \"request\"; id: string; type: string; data: unknown };\n\nconst isRecord = (value: unknown): value is Record<string, unknown> =>\n typeof value === \"object\" && value !== null;\nconst isShardId = (value: unknown): value is number =>\n typeof value === \"number\" && Number.isInteger(value) && value >= 0;\n\n/** Validates the body of a control frame from a worker; `null` when it is malformed. */\nexport function parseChildFrame(value: unknown): ChildFrame | null {\n if (!isRecord(value)) return null;\n switch (value.kind) {\n case \"ready\":\n return Array.isArray(value.shardIds) &&\n value.shardIds.every(isShardId)\n ? { kind: \"ready\", shardIds: value.shardIds }\n : null;\n case \"identify\":\n return typeof value.id === \"number\" && isShardId(value.shardId)\n ? { kind: \"identify\", id: value.id, shardId: value.shardId }\n : null;\n case \"identify-done\":\n return isShardId(value.shardId)\n ? { kind: \"identify-done\", shardId: value.shardId }\n : null;\n case \"reply\":\n return typeof value.id === \"string\" &&\n (value.error === undefined || typeof value.error === \"string\")\n ? {\n kind: \"reply\",\n id: value.id,\n ...(value.error !== undefined\n ? { error: value.error }\n : { result: value.result }),\n }\n : null;\n case \"health\":\n return isRecord(value.report) &&\n Array.isArray(value.report.shards) &&\n typeof value.report.reportedAt === \"number\"\n ? { kind: \"health\", report: value.report as never }\n : null;\n default:\n return null;\n }\n}\n\n/** Validates the body of a control frame from the parent; `null` when it is malformed. */\nexport function parseParentFrame(value: unknown): ParentFrame | null {\n if (!isRecord(value)) return null;\n switch (value.kind) {\n case \"identify-grant\":\n return typeof value.id === \"number\"\n ? { kind: \"identify-grant\", id: value.id }\n : null;\n case \"identify-deny\":\n return typeof value.id === \"number\" &&\n typeof value.error === \"string\"\n ? { kind: \"identify-deny\", id: value.id, error: value.error }\n : null;\n case \"request\":\n return typeof value.id === \"string\" &&\n typeof value.type === \"string\"\n ? {\n kind: \"request\",\n id: value.id,\n type: value.type,\n data: value.data,\n }\n : null;\n default:\n return null;\n }\n}\n",
|
|
22
|
-
"import { readClusterContext, type ClusterContext } from \"./context.js\";\nimport type { WorkerReport } from \"./health.js\";\nimport { CLUSTER_FRAME, parseParentFrame } from \"./protocol.js\";\n\n/** Asks for the right to IDENTIFY, and gives it back. */\nexport interface IdentifyGate {\n /** Resolves when `shardId` may IDENTIFY now. Rejects if the parent refuses or goes away. */\n acquire(shardId: number): Promise<void>;\n /** `shardId` has sent its IDENTIFY. */\n release(shardId: number): void;\n}\n\n/** The part of `process` a worker uses. */\nexport interface WorkerEndpoint {\n send?(message: unknown): unknown;\n on(event: string, listener: (...args: never[]) => void): unknown;\n off(event: string, listener: (...args: never[]) => void): unknown;\n}\n\ntype RequestHandler = (data: unknown) => unknown;\n\n/**\n * The worker side of a cluster. It tells the parent when the process is\n * ready, answers the parent's requests, and asks the parent for IDENTIFY\n * slots. `ShardManager` uses it automatically inside a cluster; use it\n * directly when you run your own Gateways.\n */\nexport class ClusterWorker {\n static #current: ClusterWorker | undefined;\n\n /** The worker of this process, or `undefined` when it was not forked by a `ClusterManager`. */\n public static current(): ClusterWorker | undefined {\n if (!ClusterWorker.#current) {\n const context = readClusterContext();\n if (context && typeof process.send === \"function\")\n ClusterWorker.#current = new ClusterWorker(\n context,\n process as unknown as WorkerEndpoint,\n );\n }\n return ClusterWorker.#current;\n }\n\n /** Forgets the process-wide worker (tests). */\n public static reset(): void {\n ClusterWorker.#current?.close();\n ClusterWorker.#current = undefined;\n }\n\n public readonly context: ClusterContext;\n readonly #endpoint: WorkerEndpoint;\n readonly #handlers = new Map<string, RequestHandler>();\n readonly #waiting = new Map<\n number,\n { resolve: () => void; reject: (error: Error) => void }\n >();\n readonly #onMessage: (frame: unknown) => void;\n readonly #onDisconnect: () => void;\n #counter = 0;\n #closed = false;\n\n public constructor(\n context: ClusterContext,\n endpoint: WorkerEndpoint = process as unknown as WorkerEndpoint,\n ) {\n if (typeof endpoint.send !== \"function\")\n throw new Error(\n \"ClusterWorker needs an IPC channel: run it in a process forked by ClusterManager.\",\n );\n this.context = context;\n this.#endpoint = endpoint;\n this.#onMessage = (frame) => this.#handle(frame);\n this.#onDisconnect = () => this.#failAll(\"The parent went away.\");\n endpoint.on(\"message\", this.#onMessage);\n endpoint.on(\"disconnect\", this.#onDisconnect);\n }\n\n /** The gate to use when the parent coordinates IDENTIFY, otherwise `undefined`. */\n public get identifyGate(): IdentifyGate | undefined {\n return this.context.coordinatedIdentify ? this.#gate : undefined;\n }\n\n readonly #gate: IdentifyGate = {\n acquire: (shardId) => {\n if (this.#closed)\n return Promise.reject(\n new Error(\"The cluster worker is closed.\"),\n );\n const id = ++this.#counter;\n return new Promise<void>((resolve, reject) => {\n this.#waiting.set(id, { resolve, reject });\n this.#send({ kind: \"identify\", id, shardId });\n });\n },\n release: (shardId) => this.#send({ kind: \"identify-done\", shardId }),\n };\n\n /** Tells the parent every shard of this process finished its handshake. */\n public ready(shardIds: readonly number[] = this.context.shardIds): void {\n this.#send({ kind: \"ready\", shardIds: [...shardIds] });\n }\n\n /** Pushes a health report to the parent without being asked. */\n public reportHealth(report: WorkerReport): void {\n this.#send({ kind: \"health\", report });\n }\n\n /**\n * Answers requests of `type` sent with `ClusterManager.requestCluster()`.\n * The return value (or thrown error) goes back to the caller.\n */\n public onRequest(type: string, handler: RequestHandler): this {\n if (!type.trim() || typeof handler !== \"function\")\n throw new TypeError(\"A request type and handler are required.\");\n this.#handlers.set(type, handler);\n return this;\n }\n\n /** Stops listening and rejects pending IDENTIFY requests. */\n public close(): void {\n this.#closed = true;\n this.#endpoint.off(\"message\", this.#onMessage);\n this.#endpoint.off(\"disconnect\", this.#onDisconnect);\n this.#failAll(\"The cluster worker closed.\");\n }\n\n #send(body: Record<string, unknown>): void {\n try {\n this.#endpoint.send!({ [CLUSTER_FRAME]: body });\n } catch (error) {\n this.#failAll(\n error instanceof Error ? error.message : String(error),\n );\n }\n }\n\n #failAll(reason: string): void {\n for (const waiter of this.#waiting.values())\n waiter.reject(new Error(reason));\n this.#waiting.clear();\n }\n\n #handle(frame: unknown): void {\n if (!frame || typeof frame !== \"object\" || !(CLUSTER_FRAME in frame))\n return;\n const parsed = parseParentFrame(\n (frame as Record<string, unknown>)[CLUSTER_FRAME],\n );\n if (!parsed) return;\n if (\n parsed.kind === \"identify-grant\" ||\n parsed.kind === \"identify-deny\"\n ) {\n const waiter = this.#waiting.get(parsed.id);\n if (!waiter) return;\n this.#waiting.delete(parsed.id);\n if (parsed.kind === \"identify-grant\") waiter.resolve();\n else waiter.reject(new Error(parsed.error));\n return;\n }\n const handler = this.#handlers.get(parsed.type);\n const id = parsed.id;\n if (!handler) {\n this.#send({\n kind: \"reply\",\n id,\n error: `No handler for \"${parsed.type}\".`,\n });\n return;\n }\n void (async () => {\n try {\n this.#send({\n kind: \"reply\",\n id,\n result: await handler(parsed.data),\n });\n } catch (error) {\n this.#send({\n kind: \"reply\",\n id,\n error:\n error instanceof Error ? error.message : String(error),\n });\n }\n })();\n }\n}\n",
|
|
23
|
-
"import type { ShardMessage } from \"./bus.js\";\n\n/** Carries {@link ShardMessage}s between the processes or threads that run shards. */\nexport interface ShardBusTransport {\n /** Sends a message to the other shards. Delivery to the sender itself is not required. */\n post(message: ShardMessage): void;\n /** Listens for messages from other shards. */\n onMessage(listener: (message: ShardMessage) => void): void;\n /** Listens for the shards of a cluster that died, so pending requests to them can fail early. */\n onPeerDown?(listener: (shardIds: readonly number[]) => void): void;\n /** Stops listening and releases the channel. */\n close(): void;\n}\n\n/** One process: shards in the same process (or worker threads of it) talk over a `BroadcastChannel`. */\nexport class BroadcastChannelTransport implements ShardBusTransport {\n readonly #channel: BroadcastChannel;\n\n public constructor(name: string) {\n this.#channel = new BroadcastChannel(name);\n }\n\n public post(message: ShardMessage): void {\n this.#channel.postMessage(message);\n }\n\n public onMessage(listener: (message: ShardMessage) => void): void {\n this.#channel.addEventListener(\"message\", (event) =>\n listener(event.data as ShardMessage),\n );\n }\n\n public close(): void {\n this.#channel.close();\n }\n}\n\n/** Marks a bus message on the IPC channel, so it cannot be confused with the bot's own `process.send()` traffic. */\nexport const BUS_FRAME = \"__lunibeeBus\";\n/** Marks the parent's notice that a cluster's shards are gone. */\nexport const PEER_DOWN_FRAME = \"__lunibeePeerDown\";\n\n/** The part of `process` (or a child process handle) the IPC transport uses. */\nexport interface IpcEndpoint {\n send?(message: unknown): unknown;\n on(event: \"message\", listener: (message: unknown) => void): unknown;\n off(event: \"message\", listener: (message: unknown) => void): unknown;\n}\n\n/**\n * Forked clusters: a worker `send()`s to its parent, and the parent\n * ({@link ClusterManager}) relays to every other worker. Selected\n * automatically in a worker forked by `ClusterManager`.\n */\nexport class IpcTransport implements ShardBusTransport {\n readonly #endpoint: IpcEndpoint;\n readonly #listener: (frame: unknown) => void;\n #message?: (message: ShardMessage) => void;\n #down?: (shardIds: readonly number[]) => void;\n\n public constructor(endpoint: IpcEndpoint = process as IpcEndpoint) {\n if (typeof endpoint.send !== \"function\")\n throw new Error(\n \"IpcTransport needs an IPC channel: run the shard in a process forked by ClusterManager.\",\n );\n this.#endpoint = endpoint;\n this.#listener = (frame) => {\n const record = frame as Record<string, unknown> | null;\n if (!record || typeof record !== \"object\") return;\n if (BUS_FRAME in record)\n this.#message?.(record[BUS_FRAME] as ShardMessage);\n else if (PEER_DOWN_FRAME in record)\n this.#down?.(record[PEER_DOWN_FRAME] as number[]);\n };\n endpoint.on(\"message\", this.#listener);\n }\n\n public post(message: ShardMessage): void {\n this.#endpoint.send!({ [BUS_FRAME]: message });\n }\n\n public onMessage(listener: (message: ShardMessage) => void): void {\n this.#message = listener;\n }\n\n public onPeerDown(listener: (shardIds: readonly number[]) => void): void {\n this.#down = listener;\n }\n\n public close(): void {\n this.#endpoint.off(\"message\", this.#listener);\n }\n}\n\n/** Picks the transport for a bus: IPC inside a forked cluster, `BroadcastChannel` otherwise. */\nexport function defaultTransport(name: string): ShardBusTransport {\n if (\n process.env.LUNIBEE_SHARD_BUS === \"ipc\" &&\n typeof process.send === \"function\"\n )\n return new IpcTransport();\n return new BroadcastChannelTransport(name);\n}\n",
|
|
24
|
-
"import { defaultTransport, type ShardBusTransport } from \"./transport.js\";\n\n/** Message envelope exchanged between Lunibee shards. */\nexport interface ShardMessage<T = unknown> {\n /** Sending shard ID. */\n source: number;\n /** Target shard ID, or null for broadcast. */\n target: number | null;\n /** Application message type. */\n type: string;\n /** Message payload. */\n data: T;\n /** Unique message ID. */\n id: string;\n /** Set on requests that expect a reply (see {@link ShardBus.request}). */\n expectsReply?: boolean;\n}\n/** A reply collected by {@link ShardBus.broadcastRequest}. */\nexport interface ShardReply<R = unknown> {\n /** Replying shard ID. */\n shardId: number;\n /** Handler result, when it succeeded. */\n result?: R;\n /** Handler error message, when it failed. */\n error?: string;\n}\n/** Internal reply envelope type. */\nexport const REPLY_TYPE = \"__lunibee:reply\";\n/** Payload carried by a reply envelope. */\nexport type ReplyPayload = {\n requestId: string;\n result?: unknown;\n error?: string;\n};\n/** Handler invoked for shard messages. @typeParam T Message payload type. */\nexport type ShardMessageHandler<T = unknown> = (\n message: ShardMessage<T>,\n) => unknown;\n/** Listener for errors thrown or rejected by shard message handlers. */\nexport type ShardBusErrorHandler = (\n error: unknown,\n message: ShardMessage,\n) => void;\n/** Options for a {@link ShardBus}. */\nexport interface ShardBusOptions {\n /**\n * How messages travel. Defaults to IPC inside a cluster forked by\n * `ClusterManager`, and to a `BroadcastChannel` otherwise.\n */\n transport?: ShardBusTransport;\n /** Most requests waiting for a reply at once (default 10,000); more are rejected. */\n maxPending?: number;\n}\n/** Longest message type or ID a bus accepts. */\nconst MAX_NAME_LENGTH = 256;\n\n/** Checks the envelope of a message that arrived from a transport, which is untrusted input. */\nfunction isShardMessage(message: unknown): message is ShardMessage {\n if (!message || typeof message !== \"object\") return false;\n const m = message as Record<string, unknown>;\n return (\n typeof m.type === \"string\" &&\n m.type.length > 0 &&\n m.type.length <= MAX_NAME_LENGTH &&\n typeof m.id === \"string\" &&\n m.id.length <= MAX_NAME_LENGTH &&\n // -1 is the parent process (see ClusterManager.request()).\n Number.isInteger(m.source) &&\n (m.source as number) >= -1 &&\n (m.target === null ||\n (Number.isInteger(m.target) && (m.target as number) >= -1))\n );\n}\n\n/** Cross-shard messaging: send, broadcast, and request/reply over a {@link ShardBusTransport}. */\nexport class ShardBus {\n /** Underlying transport. */\n readonly #transport: ShardBusTransport;\n /** Most requests waiting for a reply at once. */\n readonly #maxPending: number;\n /** Message handlers by message type. */\n readonly #handlers = new Map<string, Set<ShardMessageHandler>>();\n /** Handler error listeners. */\n readonly #errorHandlers = new Set<ShardBusErrorHandler>();\n /** Current shard ID. */\n readonly #shardId: number;\n /** Application-specific channel namespace. */\n readonly #namespace: string;\n /** BroadcastChannel name used by this bus. */\n public readonly channelName: string;\n /** Monotonic message counter. */\n #counter = 0;\n /** Reply collectors for in-flight requests, by request message ID. */\n readonly #pending = new Map<\n string,\n {\n target: number | null;\n /** Shards that already answered; a shard counts once. */\n seen: Set<number>;\n reply: (reply: ShardReply) => void;\n fail: (error: Error) => void;\n /** Ends the wait now: a request rejects, a broadcast resolves with what it has. */\n cancel: (error: Error) => void;\n }\n >();\n #closed = false;\n /** Creates a shard bus. @param shardId Shard ID. @param channelName Application-specific channel name. @throws {RangeError} If shard ID is invalid. @throws {TypeError} If channel name is empty. */\n public constructor(\n shardId: number,\n channelName: string,\n options: ShardBusOptions = {},\n ) {\n if (!Number.isInteger(shardId) || shardId < 0)\n throw new RangeError(\"Shard ID must be a non-negative integer.\");\n if (!channelName?.trim())\n throw new TypeError(\n \"An application-specific shard channel name is required.\",\n );\n this.#shardId = shardId;\n this.#namespace = channelName.trim();\n this.channelName = this.#namespace;\n this.#maxPending = options.maxPending ?? 10_000;\n this.#transport =\n options.transport ?? defaultTransport(this.channelName);\n this.#transport.onMessage((message) => this.#dispatch(message));\n this.#transport.onPeerDown?.((shardIds) => this.#peerDown(shardIds));\n }\n /** Registers a message handler. @param type Message type. @param handler Handler callback. @returns This bus. @throws {TypeError} If type or handler is invalid. */\n public on<T>(type: string, handler: ShardMessageHandler<T>): this {\n if (!type.trim() || typeof handler !== \"function\")\n throw new TypeError(\"Shard message type and handler are required.\");\n let handlers = this.#handlers.get(type);\n if (!handlers) this.#handlers.set(type, (handlers = new Set()));\n handlers.add(handler as ShardMessageHandler);\n return this;\n }\n /** Removes a message handler. @param type Message type. @param handler Handler callback. @returns This bus. */\n public off<T>(type: string, handler: ShardMessageHandler<T>): this {\n this.#handlers.get(type)?.delete(handler as ShardMessageHandler);\n return this;\n }\n /** Registers a listener for errors thrown or rejected by message handlers. Without one, handler errors are isolated and dropped. @param handler Error listener. @returns This bus. @throws {TypeError} If handler is invalid. */\n public onError(handler: ShardBusErrorHandler): this {\n if (typeof handler !== \"function\")\n throw new TypeError(\"Shard bus error handler is required.\");\n this.#errorHandlers.add(handler);\n return this;\n }\n /** Removes a handler error listener. @param handler Error listener. @returns This bus. */\n public offError(handler: ShardBusErrorHandler): this {\n this.#errorHandlers.delete(handler);\n return this;\n }\n /** Sends a targeted shard message. @param target Target shard ID. @param type Message type. @param data Payload. @returns Unique message ID. */\n public send<T>(target: number, type: string, data: T): string {\n if (!Number.isInteger(target) || target < 0)\n throw new RangeError(\n \"Shard target must be a non-negative integer.\",\n );\n return this.#publish(target, type, data);\n }\n /** Broadcasts to all other shards. @param type Message type. @param data Payload. @returns Unique message ID. */\n public broadcast<T>(type: string, data: T): string {\n return this.#publish(null, type, data);\n }\n /**\n * Registers a handler that answers requests of `type`. Its return value (or\n * thrown error) is sent back to the requesting shard. Discord.js-familiar\n * replacement for `broadcastEval` that never evaluates received code.\n * @returns This bus.\n */\n public respond<T, R>(\n type: string,\n handler: (data: T, message: ShardMessage<T>) => R | Promise<R>,\n ): this {\n return this.on<T>(type, async (message) => {\n if (!message.expectsReply) return;\n const reply: ReplyPayload = { requestId: message.id };\n try {\n reply.result = await handler(message.data, message);\n } catch (error) {\n reply.error =\n error instanceof Error ? error.message : String(error);\n }\n this.#publish(message.source, REPLY_TYPE, reply);\n });\n }\n /**\n * Sends a request to one shard and resolves with its handler's result.\n * @throws {Error} If the handler failed or no reply arrives in time.\n */\n public async request<R = unknown, T = unknown>(\n target: number,\n type: string,\n data: T,\n timeoutMs = 5000,\n ): Promise<R> {\n if (!Number.isInteger(target) || target < 0)\n throw new RangeError(\n \"Shard target must be a non-negative integer.\",\n );\n const [reply] = await this.#collect<R>(\n target,\n type,\n data,\n timeoutMs,\n 1,\n true,\n );\n if (!reply)\n throw new Error(\n `Shard ${target} did not reply to \"${type}\" within ${timeoutMs}ms.`,\n );\n if (reply.error !== undefined) throw new Error(reply.error);\n return reply.result as R;\n }\n /**\n * Sends a request to every other shard and collects replies until\n * `expected` have arrived or `timeoutMs` elapses (never rejects).\n */\n public broadcastRequest<R = unknown, T = unknown>(\n type: string,\n data: T,\n options: { timeoutMs?: number; expected?: number } = {},\n ): Promise<ShardReply<R>[]> {\n return this.#collect<R>(\n null,\n type,\n data,\n options.timeoutMs ?? 5000,\n options.expected ?? Number.POSITIVE_INFINITY,\n );\n }\n /** Publishes a request and gathers replies. */\n #collect<R>(\n target: number | null,\n type: string,\n data: unknown,\n timeoutMs: number,\n expected: number,\n rejectOnPeerDown = false,\n ): Promise<ShardReply<R>[]> {\n const replies: ShardReply<R>[] = [];\n if (this.#closed)\n return Promise.reject(new Error(\"ShardBus is closed.\"));\n if (this.#pending.size >= this.#maxPending)\n return Promise.reject(\n new Error(\n `Too many shard requests waiting for a reply (${this.#maxPending}).`,\n ),\n );\n return new Promise((resolve, reject) => {\n let requestId = \"\";\n const finish = (): void => {\n clearTimeout(timer);\n this.#pending.delete(requestId);\n resolve(replies);\n };\n const timer = setTimeout(finish, timeoutMs);\n requestId = this.#publish(target, type, data, true);\n const fail = (error: Error): void => {\n if (!rejectOnPeerDown) return;\n clearTimeout(timer);\n this.#pending.delete(requestId);\n reject(error);\n };\n this.#pending.set(requestId, {\n target,\n seen: new Set(),\n reply: (reply) => {\n replies.push(reply as ShardReply<R>);\n if (replies.length >= expected) finish();\n },\n fail,\n cancel: (error) => (rejectOnPeerDown ? fail(error) : finish()),\n });\n });\n }\n /** Closes the transport. @returns Nothing. */\n public close(): void {\n if (this.#closed) return;\n this.#closed = true;\n // Nothing can answer once the transport is gone, so end the waits now\n // instead of leaving their timers to fire later.\n for (const pending of [...this.#pending.values()])\n pending.cancel(new Error(\"ShardBus is closed.\"));\n this.#pending.clear();\n this.#transport.close();\n this.#handlers.clear();\n this.#errorHandlers.clear();\n }\n /** Publishes a message. @param target Target shard ID or null. @param type Message type. @param data Payload. @returns Unique message ID. */\n #publish<T>(\n target: number | null,\n type: string,\n data: T,\n expectsReply = false,\n ): string {\n if (!type.trim())\n throw new TypeError(\"Shard message type is required.\");\n if (this.#closed) throw new Error(\"ShardBus is closed.\");\n const id = `${this.#namespace}:${this.#shardId}:${++this.#counter}`;\n this.#transport.post({\n source: this.#shardId,\n target,\n type,\n data,\n id,\n ...(expectsReply ? { expectsReply } : {}),\n } satisfies ShardMessage<T>);\n return id;\n }\n /** Routes an incoming message to matching handlers. @param message Message envelope. @returns Nothing. */\n #dispatch(message: ShardMessage): void {\n if (this.#closed || !isShardMessage(message)) return;\n if (\n message.source === this.#shardId ||\n (message.target !== null && message.target !== this.#shardId)\n )\n return;\n if (message.type === REPLY_TYPE) {\n const reply = message.data as ReplyPayload | null;\n if (\n !reply ||\n typeof reply !== \"object\" ||\n typeof reply.requestId !== \"string\"\n )\n return;\n const pending = this.#pending.get(reply.requestId);\n // Late, unknown and duplicate answers are dropped, and so is an\n // answer from a shard other than the one that was asked: nobody\n // can resolve a request by guessing its ID.\n if (\n !pending ||\n pending.seen.has(message.source) ||\n (pending.target !== null && pending.target !== message.source)\n )\n return;\n pending.seen.add(message.source);\n pending.reply({\n shardId: message.source,\n ...(reply.error !== undefined\n ? { error: String(reply.error) }\n : { result: reply.result }),\n });\n return;\n }\n for (const handler of this.#handlers.get(message.type) ?? []) {\n try {\n const result = handler(message);\n if (\n result &&\n typeof (result as PromiseLike<unknown>).then === \"function\"\n )\n void Promise.resolve(result).catch((error) =>\n this.#reportError(error, message),\n );\n } catch (error) {\n this.#reportError(error, message);\n }\n }\n }\n /** Fails requests waiting on shards that no longer exist. */\n #peerDown(shardIds: readonly number[]): void {\n const down = new Set(shardIds);\n for (const [id, pending] of this.#pending)\n if (pending.target !== null && down.has(pending.target)) {\n pending.fail(\n new Error(\n `Shard ${pending.target} went away before it replied.`,\n ),\n );\n this.#pending.delete(id);\n }\n }\n /** Forwards a handler error to error listeners, isolating listener failures. Without a listener it is raised as a process warning, never dropped. @param error Handler error. @param message Message being handled. @returns Nothing. */\n #reportError(error: unknown, message: ShardMessage): void {\n if (this.#errorHandlers.size === 0) {\n process.emitWarning(\n error instanceof Error ? error : new Error(String(error)),\n { code: \"LUNIBEE_SHARD_BUS_ERROR\" },\n );\n return;\n }\n for (const handler of this.#errorHandlers) {\n try {\n handler(error, message);\n } catch {\n /* Error listener failures are isolated. */\n }\n }\n }\n}\n",
|
|
25
|
-
"/** How a crashed cluster is restarted. The defaults restart every crash after a fixed delay. */\nexport interface SupervisorOptions {\n /** Delay before the first restart, in milliseconds (default 5000). */\n restartDelay?: number;\n /** Each further crash in the window multiplies the delay by this (default 1: a fixed delay). */\n backoff?: number;\n /** The longest delay (default 5 minutes). */\n maxDelay?: number;\n /** Random spread added to each delay, as a fraction of it (0-1, default 0). */\n jitter?: number;\n /** Give up after this many restarts inside `window` (default unlimited). */\n maxRestarts?: number;\n /** The time over which restarts are counted, and after which a stable cluster is forgiven (default 60,000). */\n window?: number;\n}\n\n/**\n * Decides when, and whether, a crashed cluster comes back. It only does\n * arithmetic on timestamps (pass `now` in tests), so `ClusterManager` stays in\n * charge of forking.\n */\nexport class ShardSupervisor {\n readonly #options: Required<SupervisorOptions>;\n readonly #restarts = new Map<number, number[]>();\n readonly #random: () => number;\n\n public constructor(\n options: SupervisorOptions = {},\n random: () => number = Math.random,\n ) {\n this.#options = {\n restartDelay: options.restartDelay ?? 5000,\n backoff: options.backoff ?? 1,\n maxDelay: options.maxDelay ?? 300_000,\n jitter: options.jitter ?? 0,\n maxRestarts: options.maxRestarts ?? Number.POSITIVE_INFINITY,\n window: options.window ?? 60_000,\n };\n const { restartDelay, backoff, jitter, maxDelay, window, maxRestarts } =\n this.#options;\n if (!(restartDelay >= 0) || !(maxDelay >= 0) || !(window > 0))\n throw new RangeError(\n \"restartDelay and maxDelay cannot be negative, and window must be positive.\",\n );\n if (!(backoff >= 1))\n throw new RangeError(\"backoff must be at least 1.\");\n if (!(jitter >= 0 && jitter <= 1))\n throw new RangeError(\"jitter must be between 0 and 1.\");\n if (!(maxRestarts >= 0))\n throw new RangeError(\"maxRestarts cannot be negative.\");\n this.#random = random;\n }\n\n /**\n * A cluster exited. @returns How many milliseconds to wait before\n * restarting it, or `null` to give up because it crashed more than\n * `maxRestarts` times inside `window`.\n */\n public next(id: number, now = Date.now()): number | null {\n const { window, maxRestarts, restartDelay, backoff, maxDelay, jitter } =\n this.#options;\n const recent = (this.#restarts.get(id) ?? []).filter(\n (at) => now - at < window,\n );\n if (recent.length >= maxRestarts) {\n this.#restarts.delete(id);\n return null;\n }\n recent.push(now);\n this.#restarts.set(id, recent);\n const base = Math.min(\n maxDelay,\n restartDelay * backoff ** (recent.length - 1),\n );\n return Math.round(base * (1 + jitter * this.#random()));\n }\n\n /** Forgets a cluster's crashes, e.g. after a deliberate respawn. */\n public reset(id?: number): void {\n if (id === undefined) this.#restarts.clear();\n else this.#restarts.delete(id);\n }\n}\n",
|
|
26
|
-
"import {\n assertGuildsPerShard,\n fetchGatewayBot,\n resolveShardCount,\n type GatewayBotInfo,\n} from \"./gateway-bot.js\";\nimport { fork, type ChildProcess } from \"node:child_process\";\nimport { cpus } from \"node:os\";\nimport { REPLY_TYPE, type ReplyPayload, type ShardReply } from \"./bus.js\";\nimport {\n allocateShards,\n assertCompleteAllocation,\n isClusterWorker,\n validateShardIds,\n WORKER_MARKER,\n} from \"./context.js\";\nimport type {\n ClusterHealth,\n ClusterManagerHealth,\n ClusterState,\n ShardHealth,\n WorkerMetrics,\n WorkerReport,\n} from \"./health.js\";\nimport { IdentifyCoordinator, type IdentifyBudget } from \"./identify.js\";\nimport {\n CLUSTER_FRAME,\n PARENT_ID,\n REQUEST_ASSIGN,\n REQUEST_HEALTH,\n parseChildFrame,\n type ChildFrame,\n} from \"./protocol.js\";\nimport { RemoteHosts, type HostStatus, type RemoteOptions } from \"./remote.js\";\nimport { RequestTracker } from \"./requests.js\";\nimport { ShardSupervisor, type SupervisorOptions } from \"./supervisor.js\";\nimport { BUS_FRAME, PEER_DOWN_FRAME } from \"./transport.js\";\n\n/** Configuration for managing multiple shard clusters. */\nexport interface ClusterManagerOptions {\n /** Discord bot token. */\n token: string;\n /** Path to the worker script to execute for each cluster. */\n script: string;\n /** Total number of shards. Use \"auto\" to request Discord's recommended count. */\n shardCount?: number | \"auto\";\n /**\n * With `shardCount: \"auto\"`, size the deployment by guilds instead of\n * Discord's recommendation: `ceil(guilds / guildsPerShard)` shards, at\n * least one. 1 to 2500 (Discord's limit); Discord recommends about 1000.\n * Guilds are assigned to shards by their ID, so this sets the average,\n * not an exact split.\n */\n guildsPerShard?: number;\n /** The bot's guild count for `guildsPerShard`, instead of asking Discord for it. */\n guildCount?: number;\n /**\n * The shards this deployment runs, out of `shardCount`. Use it to split one\n * bot across hosts: each host runs a `ClusterManager` with its own range.\n * Defaults to every shard.\n */\n shardIds?: number[];\n /** Number of clusters to spawn. Defaults to the number of logical CPUs. Not combinable with `shardsPerCluster`. */\n clusterCount?: number;\n /** Shards per cluster; the cluster count follows. Not combinable with `clusterCount`. */\n shardsPerCluster?: number;\n /**\n * Discord's `max_concurrency` for IDENTIFY pacing. Fetched from `/gateway/bot`\n * when `shardCount` is \"auto\" (this value may only lower it); otherwise defaults to 1.\n */\n maxConcurrency?: number;\n /** Ask `/gateway/bot` for `max_concurrency` and the session budget even with a fixed `shardCount`. */\n fetchLimits?: boolean;\n /** IDENTIFY coordination across clusters. On by default. */\n identify?: {\n /** Set `false` to let every cluster pace its own IDENTIFYs. */\n coordinate?: boolean;\n /** Milliseconds between IDENTIFYs on one key. Default 5000. */\n interval?: number;\n /** A slot a worker never releases is released after this many ms. Default 60,000. */\n holdTimeout?: number;\n };\n /** Milliseconds between forking two clusters, to spread the CPU cost of starting. Default 500. IDENTIFY pacing does not depend on it. */\n spawnInterval?: number;\n /**\n * A cluster that has not reported ready this many ms after it was forked is\n * killed and restarted like a crash. Off by default, because a worker that\n * never reports ready (no `ShardManager`, no `ClusterWorker`) would be killed.\n */\n startTimeout?: number;\n /** Make `spawn()` wait until every cluster reported ready. Default false. */\n waitForReady?: boolean;\n /**\n * When a cluster crashes more often than `supervisor.maxRestarts` allows,\n * move its shards to the healthiest ready cluster instead of leaving them\n * offline. Default false.\n */\n reassignOnGiveUp?: boolean;\n /**\n * Largest shard-bus message, request payload or worker reply the manager\n * handles, in UTF-8 bytes (a string as it is, anything else as its JSON text). Bigger ones from a worker are\n * dropped before they are relayed (an `error` event says so; a request\n * waiting for the answer times out as usual), and bigger ones passed to\n * `request()`, `broadcast()` or `requestCluster()` throw a `RangeError`.\n * Default 8 MiB, the same limit as the host link.\n */\n maxMessageBytes?: number;\n /** Default timeout for `request()`, `requestCluster()` and `broadcast()`. Default 5000. */\n requestTimeout?: number;\n /** Most requests waiting for answers at once. Default 10,000. */\n maxPendingRequests?: number;\n /**\n * Accept hosts on other machines. The coordinator listens for\n * `ClusterHost` agents, places clusters on them and supervises those\n * workers like local ones. See {@link RemoteOptions}.\n */\n remote?: RemoteOptions;\n /** Starts a worker process. Defaults to `child_process.fork()`; replace it for tests or custom launchers. */\n fork?: (\n script: string,\n env: Record<string, string | undefined>,\n ) => ChildProcess;\n /** Interval in milliseconds to automatically check for recommended shard count and re-scale if needed. */\n autoScaleInterval?: number;\n /** Optional handler invoked when a background auto-scale check fails. Receives the thrown error. */\n onAutoScaleError?: (error: Error) => void;\n /** Grace period in milliseconds to await a cluster's clean exit after SIGTERM before force-killing. Defaults to 5000. */\n shutdownTimeout?: number;\n /**\n * Whether a cluster that exits unexpectedly is re-forked with the same\n * shard assignment. Defaults to true: without it a crashed child leaves\n * its shards permanently offline and nothing reports it.\n */\n restartOnExit?: boolean;\n /** Delay in milliseconds before re-forking a crashed cluster. Defaults to 5000. */\n restartDelay?: number;\n /**\n * Backoff, jitter and a give-up limit for restarts. `restartDelay` is its\n * first delay unless it sets its own. The default restarts every crash\n * after a fixed delay, as before.\n */\n supervisor?: Omit<SupervisorOptions, \"restartDelay\"> & {\n restartDelay?: number;\n };\n /** Called when a cluster crashed more often than `supervisor.maxRestarts` allows and will not be restarted. */\n onGiveUp?(cluster: ClusterInfo): void;\n /** Called whenever a cluster process exits, before any restart. */\n onClusterExit?(\n cluster: ClusterInfo,\n code: number | null,\n signal: NodeJS.Signals | null,\n ): void;\n}\n\n/** Information about a cluster. The object is live: it is updated as the cluster changes. */\nexport interface ClusterInfo {\n /** Cluster identifier. */\n id: number;\n /** The current child process (the last one while a restart is pending). For a cluster on another host this is a proxy; `pid` is the pid on that host. */\n process: ChildProcess;\n /** Where it runs: `\"local\"` or a host ID. */\n host: string;\n /** The shard IDs managed by this cluster. */\n shards: number[];\n /** Where the cluster is in its life. */\n state: ClusterState;\n /** How many times it was restarted after a crash. */\n restarts: number;\n /** When the current process was forked (epoch ms). */\n startedAt: number;\n /** When the current process reported ready. */\n readyAt?: number;\n /** When any process of this cluster last reported ready. */\n lastReadyAt?: number;\n /** The worker's last health report. */\n report?: WorkerReport;\n}\n\n/** What `ClusterManager` emits. */\nexport interface ClusterManagerEvents {\n /** A cluster process was forked. */\n clusterSpawn: [cluster: ClusterInfo];\n /** A cluster's shards all finished their handshake. */\n clusterReady: [cluster: ClusterInfo];\n /** Every cluster is ready. Emitted again after a recovery. */\n ready: [];\n /** A cluster process exited. `expected` is true for a deliberate shutdown. */\n clusterExit: [\n cluster: ClusterInfo,\n code: number | null,\n signal: NodeJS.Signals | null,\n expected: boolean,\n ];\n /** A restart was scheduled after a crash. */\n clusterRestart: [\n cluster: ClusterInfo,\n info: { delay: number; attempt: number },\n ];\n /** A cluster keeps crashing: three or more restarts are pending or recent. */\n clusterUnstable: [\n cluster: ClusterInfo,\n info: { attempt: number; delay: number },\n ];\n /** A cluster did not report ready within `startTimeout` and was killed. */\n clusterStartTimeout: [cluster: ClusterInfo];\n /** A cluster crashed too often and will not be restarted. */\n clusterGiveUp: [cluster: ClusterInfo];\n /** A failed cluster's shards were moved to another cluster. */\n shardsReassigned: [info: { shardIds: number[]; from: number; to: number }];\n /** A host joined the deployment. */\n hostJoin: [hostId: string];\n /** A host left or was lost. Its clusters are treated as crashed. */\n hostLeave: [hostId: string, reason: string];\n /** An IDENTIFY is waiting for the session-start budget to reset. */\n identifyWait: [info: { shardId: number; waitMs: number }];\n /** Something went wrong that did not stop the deployment (a bad frame, a failed recovery). */\n error: [error: Error];\n}\n\n/** Options for a request to a shard or cluster. */\nexport interface RequestOptions {\n /** Milliseconds to wait. Defaults to `requestTimeout`. */\n timeoutMs?: number;\n /** Cancels the wait. */\n signal?: AbortSignal;\n}\n\n/** What a broadcast collected. It never rejects for a slow or failed shard. */\nexport interface BroadcastResult<R = unknown> {\n /** One reply per answering shard: its result, or the error its handler threw. */\n replies: ShardReply<R>[];\n /** Shards that did not answer: down, restarting, or too slow. */\n missing: number[];\n}\n\ntype LiveState = \"starting\" | \"ready\";\n\n/** Default for `maxMessageBytes`. */\nconst DEFAULT_MAX_MESSAGE_BYTES = 8 * 1024 * 1024;\n\n/**\n * The size of a payload in UTF-8 bytes: a string as it is, anything else as\n * its JSON text. `Infinity` for one that cannot be serialized (a cycle).\n */\nfunction payloadSize(value: unknown): number {\n if (value === undefined || value === null) return 0;\n if (typeof value === \"string\") return Buffer.byteLength(value, \"utf8\");\n if (typeof value !== \"object\") return String(value).length;\n try {\n const text = JSON.stringify(value);\n return text === undefined ? 0 : Buffer.byteLength(text, \"utf8\");\n } catch {\n return Number.POSITIVE_INFINITY;\n }\n}\n\nconst isLive = (state: ClusterState): state is LiveState =>\n state === \"starting\" || state === \"ready\";\n\n/**\n * Orchestrates cluster processes: forks one worker per cluster, gives each a\n * fixed set of shards, restarts crashed clusters with backoff, hands out\n * IDENTIFY slots across all of them, routes shard-bus messages to the cluster\n * that owns the target shard, and answers requests, broadcasts and health\n * queries for the whole deployment.\n *\n * Ownership: the `ClusterManager` decides which cluster owns which shard and\n * keeps processes alive; the `ShardManager` inside each worker runs those\n * shards' Gateways. A shard is never owned by two clusters at once.\n *\n * Works under Node.js and Bun (`child_process.fork()` with IPC).\n */\nexport class ClusterManager {\n /** Live clusters (starting or ready) indexed by cluster ID. A crashed cluster leaves this map until it is restarted. */\n public readonly clusters = new Map<number, ClusterInfo>();\n /** Total number of shards in the deployment. */\n public shardCount = 0;\n\n readonly #options: ClusterManagerOptions;\n /** Whether the manager was created in auto shard-count mode. Preserved across respawns so auto-scaling keeps running. */\n readonly #auto: boolean;\n #autoScaleTimer?: ReturnType<typeof setInterval>;\n /** Every cluster this deployment knows, whatever its state. */\n readonly #infos = new Map<number, ClusterInfo>();\n /** Which cluster owns each shard. */\n readonly #owner = new Map<number, number>();\n #startPromise?: Promise<void>;\n #stopPromise?: Promise<void>;\n #wasStarted = false;\n /** Set while a deliberate shutdown is in progress so exits are not restarted. */\n #stopping = false;\n /** Pending restart timers, kept so shutdown can cancel them. */\n readonly #restartTimers = new Set<ReturnType<typeof setTimeout>>();\n readonly #startTimers = new Map<number, ReturnType<typeof setTimeout>>();\n readonly #pauses = new Set<() => void>();\n readonly #readyWaiters = new Set<{\n resolve: () => void;\n reject: (error: Error) => void;\n timer?: ReturnType<typeof setTimeout>;\n }>();\n readonly #supervisor: ShardSupervisor;\n readonly #tracker: RequestTracker;\n #coordinator?: IdentifyCoordinator;\n #allReady = false;\n #clusterCount = 0;\n /** Shards of each cluster that are waiting for an IDENTIFY slot. */\n readonly #identifyWaiting = new Map<number, Set<number>>();\n readonly #remote?: RemoteHosts;\n /** Which host runs each cluster. Fixed for the life of a spawn, so a cluster returns to its host. */\n readonly #hostOf = new Map<number, string>();\n #lastWait = \"\";\n readonly #listeners = new Map<\n keyof ClusterManagerEvents,\n Set<(...args: never[]) => void>\n >();\n\n /** Creates a cluster manager. @param options Clustering configuration. @throws {TypeError} If token or script is missing. @throws {RangeError} If a numeric option is invalid. */\n public constructor(options: ClusterManagerOptions) {\n if (!options.token?.trim())\n throw new TypeError(\"A Discord bot token is required.\");\n if (!options.script?.trim())\n throw new TypeError(\"A script path is required for clustering.\");\n if (\n options.autoScaleInterval !== undefined &&\n (!Number.isInteger(options.autoScaleInterval) ||\n options.autoScaleInterval < 1000)\n ) {\n throw new RangeError(\n \"autoScaleInterval must be an integer of at least 1000 milliseconds.\",\n );\n }\n if (\n options.clusterCount !== undefined &&\n options.shardsPerCluster !== undefined\n )\n throw new RangeError(\n \"Set either clusterCount or shardsPerCluster, not both.\",\n );\n for (const [name, value] of [\n [\"clusterCount\", options.clusterCount],\n [\"shardsPerCluster\", options.shardsPerCluster],\n [\"maxConcurrency\", options.maxConcurrency],\n ] as const)\n if (value !== undefined && (!Number.isInteger(value) || value < 1))\n throw new RangeError(`${name} must be a positive integer.`);\n if (\n options.maxMessageBytes !== undefined &&\n (!Number.isInteger(options.maxMessageBytes) ||\n options.maxMessageBytes < 1)\n )\n throw new RangeError(\"maxMessageBytes must be a positive integer.\");\n for (const [name, value] of [\n [\"spawnInterval\", options.spawnInterval],\n [\"startTimeout\", options.startTimeout],\n [\"requestTimeout\", options.requestTimeout],\n ] as const)\n if (value !== undefined && !(value >= 0))\n throw new RangeError(`${name} cannot be negative.`);\n if (options.guildsPerShard !== undefined) {\n assertGuildsPerShard(options.guildsPerShard);\n if (typeof options.shardCount === \"number\")\n throw new RangeError(\n 'guildsPerShard needs shardCount: \"auto\".',\n );\n }\n if (options.shardIds && typeof options.shardCount === \"number\")\n validateShardIds(options.shardIds, options.shardCount);\n\n this.#auto = options.shardCount === \"auto\";\n this.#options = { ...options };\n this.#supervisor = new ShardSupervisor({\n restartDelay: options.restartDelay,\n ...options.supervisor,\n });\n this.#tracker = new RequestTracker(\n \"parent\",\n options.maxPendingRequests,\n );\n if (options.remote)\n this.#remote = new RemoteHosts(options.remote, {\n join: (id) => this.#emit(\"hostJoin\", id),\n leave: (id, reason) => this.#emit(\"hostLeave\", id, reason),\n error: (error) => this.#emit(\"error\", error),\n });\n }\n\n /** Listens for a deployment event. @returns This manager. */\n public on<E extends keyof ClusterManagerEvents>(\n event: E,\n listener: (...args: ClusterManagerEvents[E]) => void,\n ): this {\n let set = this.#listeners.get(event);\n if (!set) this.#listeners.set(event, (set = new Set()));\n set.add(listener as never);\n return this;\n }\n\n /** Stops listening for a deployment event. @returns This manager. */\n public off<E extends keyof ClusterManagerEvents>(\n event: E,\n listener: (...args: ClusterManagerEvents[E]) => void,\n ): this {\n this.#listeners.get(event)?.delete(listener as never);\n return this;\n }\n\n #emit<E extends keyof ClusterManagerEvents>(\n event: E,\n ...args: ClusterManagerEvents[E]\n ): void {\n for (const listener of this.#listeners.get(event) ?? [])\n try {\n (listener as (...a: ClusterManagerEvents[E]) => void)(...args);\n } catch {\n // A faulty listener must not break supervision.\n }\n }\n\n /** Retrieves Discord's recommended shard count. @returns Recommended shard count. @throws {Error} If discovery fails or returns invalid data. */\n public async fetchRecommendedShardCount(): Promise<number> {\n return resolveShardCount(\n this.#options.token,\n await fetchGatewayBot(this.#options.token),\n this.#options,\n );\n }\n\n /** The cluster that owns a shard, or `undefined` when the shard is not part of this deployment. */\n public clusterForShard(shardId: number): ClusterInfo | undefined {\n const id = this.#owner.get(shardId);\n return id === undefined ? undefined : this.#infos.get(id);\n }\n\n /** Which cluster owns each shard. A copy: change ownership through recovery, not by writing to it. */\n public assignments(): ReadonlyMap<number, number> {\n return new Map(this.#owner);\n }\n\n /**\n * Spawns all clusters. Calling it again while clusters run, or while the\n * first call is still starting them, returns the same promise.\n * @throws {Error} If called from a cluster worker (a worker must not start the fleet again).\n */\n public spawn(): Promise<void> {\n if (isClusterWorker())\n return Promise.reject(\n new Error(\n \"A cluster worker cannot start a ClusterManager: it would fork the whole fleet again.\",\n ),\n );\n if (this.#stopPromise)\n return this.#stopPromise.then(() => this.spawn());\n if (!this.#startPromise) {\n const attempt = this.#start();\n this.#startPromise = attempt;\n attempt.catch(() => {\n if (this.#startPromise === attempt)\n this.#startPromise = undefined;\n });\n }\n return this.#startPromise;\n }\n\n async #resolveGateway(): Promise<{\n count: number;\n maxConcurrency: number;\n budget?: IdentifyBudget;\n }> {\n const { shardCount, maxConcurrency, fetchLimits } = this.#options;\n let info: GatewayBotInfo | undefined;\n let count: number | undefined;\n if (shardCount === \"auto\" || shardCount === undefined) {\n info = await fetchGatewayBot(this.#options.token);\n count = await resolveShardCount(\n this.#options.token,\n info,\n this.#options,\n );\n } else if (fetchLimits)\n try {\n info = await fetchGatewayBot(this.#options.token);\n } catch (error) {\n this.#emit(\n \"error\",\n error instanceof Error ? error : new Error(String(error)),\n );\n }\n const limit = info?.sessionStartLimit;\n return {\n count: count ?? (shardCount as number),\n maxConcurrency: Math.min(\n maxConcurrency ?? limit?.maxConcurrency ?? 1,\n limit?.maxConcurrency ?? Number.POSITIVE_INFINITY,\n ),\n ...(limit\n ? {\n budget: {\n total: limit.total,\n remaining: limit.remaining,\n resetAfter: limit.resetAfter,\n },\n }\n : {}),\n };\n }\n\n async #start(): Promise<void> {\n const { count, maxConcurrency, budget } = await this.#resolveGateway();\n if (this.#stopping) return;\n\n const ids =\n this.#options.shardIds ??\n Array.from({ length: count }, (_, i) => i);\n validateShardIds(ids, count);\n const clusterCount =\n this.#options.clusterCount ??\n (this.#options.shardsPerCluster\n ? Math.ceil(ids.length / this.#options.shardsPerCluster)\n : cpus().length);\n const chunks = allocateShards(ids, clusterCount);\n assertCompleteAllocation(chunks, ids);\n\n const placement = await this.#placeHosts().catch((error: unknown) => {\n // A shutdown that interrupts the wait is not a failure of the start.\n if (this.#stopping) return undefined;\n throw error;\n });\n if (!placement || this.#stopping) return;\n\n this.shardCount = count;\n this.#wasStarted = true;\n this.#owner.clear();\n this.#infos.clear();\n const identify = this.#options.identify;\n this.#coordinator =\n identify?.coordinate === false\n ? undefined\n : new IdentifyCoordinator({\n maxConcurrency,\n interval: identify?.interval,\n holdTimeout: identify?.holdTimeout,\n budget,\n onBudgetWait: (wait) => {\n const key = `${wait.shardId}:${Math.round(wait.waitMs / 1000)}`;\n if (key === this.#lastWait) return;\n this.#lastWait = key;\n this.#emit(\"identifyWait\", wait);\n },\n });\n\n const active = chunks\n .map((shards, id) => ({ id, shards }))\n .filter((chunk) => chunk.shards.length > 0);\n this.#clusterCount = active.length;\n this.#hostOf.clear();\n for (const chunk of active)\n this.#hostOf.set(chunk.id, placement[chunk.id % placement.length]!);\n for (const [index, chunk] of active.entries()) {\n if (this.#stopping) return;\n for (const shard of chunk.shards) this.#owner.set(shard, chunk.id);\n this.#launch(chunk.id, chunk.shards);\n // Spread the CPU cost of starting processes. IDENTIFY pacing is\n // the coordinator's job and does not depend on this.\n const gap = this.#options.spawnInterval ?? 500;\n if (index < active.length - 1 && gap > 0) await this.#pause(gap);\n }\n if (this.#stopping) return;\n\n if (this.#options.waitForReady) await this.ready();\n if (\n this.#options.autoScaleInterval &&\n this.#options.autoScaleInterval > 0\n ) {\n this.#autoScaleTimer = setInterval(() => {\n void this.checkAutoScale();\n }, this.#options.autoScaleInterval);\n }\n }\n\n /** Opens the listener for hosts and waits for them. @returns The places clusters run, in placement order. */\n async #placeHosts(): Promise<string[]> {\n const remote = this.#remote;\n const options = this.#options.remote;\n if (!remote || !options) return [\"local\"];\n remote.start();\n try {\n await remote.waitFor(\n options.hosts,\n options.minHosts ?? 1,\n options.joinTimeout ?? 60_000,\n );\n } catch (error) {\n remote.stop();\n throw error;\n }\n const ids = options.hosts ?? remote.connected();\n return options.local ? [\"local\", ...ids] : ids;\n }\n\n /** The port hosts connect to, once the listener is open (useful with `remote.port: 0`). */\n public get remotePort(): number | undefined {\n return this.#remote?.port;\n }\n\n /** The hosts of this deployment: connected or not, and the clusters on each. Empty without `remote`. */\n public hosts(): HostStatus[] {\n return this.#remote?.status(this.#options.remote?.hosts) ?? [];\n }\n\n /** Waits `ms`, or less when a shutdown starts. */\n #pause(ms: number): Promise<void> {\n return new Promise((resolve) => {\n const wake = (): void => {\n clearTimeout(timer);\n this.#pauses.delete(wake);\n resolve();\n };\n const timer = setTimeout(wake, ms);\n this.#pauses.add(wake);\n });\n }\n\n /**\n * Forks one cluster child and supervises its exit.\n *\n * A child that dies (crash, OOM kill, uncaught rejection) takes its shards\n * offline for good, so unless the exit came from a deliberate shutdown the\n * same shard assignment is re-forked after the supervisor's delay.\n */\n #launch(id: number, shards: number[]): ClusterInfo {\n // Only these cross to a remote host; the rest of a worker's environment is the host's own.\n const contract: Record<string, string> = {\n SHARD_LIST: shards.join(\",\"),\n SHARD_COUNT: this.shardCount.toString(),\n CLUSTER_ID: id.toString(),\n CLUSTER_COUNT: this.#clusterCount.toString(),\n LUNIBEE_SHARD_BUS: \"ipc\",\n [WORKER_MARKER]: \"1\",\n LUNIBEE_IDENTIFY: this.#coordinator ? \"coordinated\" : \"local\",\n };\n const host = this.#hostOf.get(id) ?? \"local\";\n const env: Record<string, string | undefined> = {\n ...process.env,\n ...contract,\n };\n const child =\n host !== \"local\" && this.#remote\n ? this.#remote.createChild(host, id, contract)\n : this.#options.fork\n ? this.#options.fork(this.#options.script, env)\n : fork(this.#options.script, [], {\n env: env as NodeJS.ProcessEnv,\n });\n const known = this.#infos.get(id);\n const info: ClusterInfo = known ?? {\n id,\n host,\n process: child,\n shards,\n state: \"starting\",\n restarts: 0,\n startedAt: Date.now(),\n };\n info.process = child;\n info.state = \"starting\";\n info.startedAt = Date.now();\n info.readyAt = undefined;\n this.#infos.set(id, info);\n this.clusters.set(id, info);\n this.#allReady = false;\n\n child.on(\"message\", (frame) => this.#onFrame(info, child, frame));\n child.once(\"exit\", (code, signal) =>\n this.#onExit(info, child, code, signal),\n );\n const timeout = this.#options.startTimeout;\n if (timeout) {\n const timer = setTimeout(() => {\n this.#startTimers.delete(id);\n if (info.process !== child || info.state !== \"starting\") return;\n this.#emit(\"clusterStartTimeout\", info);\n try {\n child.kill(\"SIGKILL\");\n } catch {\n // Already gone; the exit handler takes over.\n }\n }, timeout);\n (timer as { unref?: () => void }).unref?.();\n this.#startTimers.set(id, timer);\n }\n this.#emit(\"clusterSpawn\", info);\n return info;\n }\n\n #onExit(\n info: ClusterInfo,\n child: ChildProcess,\n code: number | null,\n signal: NodeJS.Signals | null,\n ): void {\n // Only act on the process still registered as this cluster: a\n // restart or respawn may already have replaced it.\n if (info.process !== child) return;\n child.removeAllListeners(\"message\");\n this.#clearStartTimer(info.id);\n this.#coordinator?.drop(info.id, `Cluster ${info.id} exited.`);\n this.#identifyWaiting.delete(info.id);\n this.#tracker.failOwner(info.id, `Cluster ${info.id} exited.`);\n this.clusters.delete(info.id);\n this.#allReady = false;\n try {\n this.#options.onClusterExit?.(info, code, signal);\n } catch {\n // A faulty consumer callback must not break supervision.\n }\n // A deliberate shutdown (graceful or `killAll()`) is never restarted.\n const expected =\n this.#stopping ||\n info.state === \"stopping\" ||\n info.state === \"stopped\";\n this.#emit(\"clusterExit\", info, code, signal, expected);\n if (expected) {\n info.state = \"stopped\";\n return;\n }\n this.#announceDown(info.id, info.shards);\n if (this.#options.restartOnExit === false) {\n info.state = \"failed\";\n this.#checkReady();\n return;\n }\n const delay = this.#supervisor.next(info.id);\n if (delay === null) {\n info.state = \"failed\";\n try {\n this.#options.onGiveUp?.(info);\n } catch {\n // A faulty consumer callback must not break supervision.\n }\n this.#emit(\"clusterGiveUp\", info);\n this.#checkReady();\n if (this.#options.reassignOnGiveUp) void this.#reassign(info);\n return;\n }\n info.state = \"restarting\";\n info.restarts++;\n this.#emit(\"clusterRestart\", info, { delay, attempt: info.restarts });\n if (info.restarts >= 3)\n this.#emit(\"clusterUnstable\", info, {\n attempt: info.restarts,\n delay,\n });\n const timer = setTimeout(() => {\n this.#restartTimers.delete(timer);\n if (this.#stopping || info.state !== \"restarting\") return;\n this.#launch(info.id, info.shards);\n }, delay);\n // Do not hold the event loop open purely for a pending restart.\n (timer as { unref?: () => void }).unref?.();\n this.#restartTimers.add(timer);\n }\n\n /**\n * Moves a failed cluster's shards to the ready cluster that owns the\n * fewest. Ownership changes only after that worker confirmed it started\n * them, and the failed cluster's process is already dead, so no shard\n * ever has two owners.\n */\n async #reassign(failed: ClusterInfo): Promise<void> {\n const shardIds = [...failed.shards];\n const target = [...this.#infos.values()]\n .filter((cluster) => cluster.state === \"ready\")\n .sort((a, b) => a.shards.length - b.shards.length)[0];\n if (!target) {\n this.#emit(\n \"error\",\n new Error(\n `No ready cluster can take over shards [${shardIds}] of cluster ${failed.id}.`,\n ),\n );\n return;\n }\n try {\n await this.requestCluster(\n target.id,\n REQUEST_ASSIGN,\n { shardIds },\n { timeoutMs: this.#options.startTimeout ?? 120_000 },\n );\n } catch (error) {\n this.#emit(\n \"error\",\n new Error(\n `Cluster ${target.id} could not take over shards [${shardIds}]: ${error instanceof Error ? error.message : String(error)}`,\n ),\n );\n return;\n }\n if (this.#stopping || failed.state !== \"failed\") return;\n for (const shard of shardIds) this.#owner.set(shard, target.id);\n target.shards.push(...shardIds);\n failed.shards = [];\n this.#emit(\"shardsReassigned\", {\n shardIds,\n from: failed.id,\n to: target.id,\n });\n }\n\n #clearStartTimer(id: number): void {\n const timer = this.#startTimers.get(id);\n if (timer) clearTimeout(timer);\n this.#startTimers.delete(id);\n }\n\n #sendFrame(cluster: ClusterInfo, frame: unknown): boolean {\n const child = cluster.process;\n if (\n !isLive(cluster.state) ||\n typeof child.send !== \"function\" ||\n !child.connected\n )\n return false;\n try {\n child.send(frame as never);\n return true;\n } catch {\n // The child is exiting; its own exit handler takes over.\n return false;\n }\n }\n\n /** Tells the surviving clusters that these shards are gone, so requests to them fail now instead of timing out. */\n #announceDown(exited: number, shards: number[]): void {\n for (const [id, cluster] of this.clusters)\n if (id !== exited)\n this.#sendFrame(cluster, { [PEER_DOWN_FRAME]: shards });\n }\n\n #onFrame(info: ClusterInfo, child: ChildProcess, frame: unknown): void {\n if (info.process !== child) return;\n if (!frame || typeof frame !== \"object\") return;\n if (BUS_FRAME in frame) {\n this.#onBus(info, (frame as Record<string, unknown>)[BUS_FRAME]);\n return;\n }\n if (CLUSTER_FRAME in frame) {\n const body = parseChildFrame(\n (frame as Record<string, unknown>)[CLUSTER_FRAME],\n );\n if (body) this.#onControl(info, child, body);\n else\n this.#emit(\n \"error\",\n new Error(\n `Cluster ${info.id} sent a malformed control frame.`,\n ),\n );\n }\n // Anything else is the bot's own process.send() traffic.\n }\n\n #onControl(\n info: ClusterInfo,\n child: ChildProcess,\n frame: ChildFrame,\n ): void {\n switch (frame.kind) {\n case \"ready\": {\n if (!frame.shardIds.every((id) => info.shards.includes(id))) {\n this.#emit(\n \"error\",\n new Error(\n `Cluster ${info.id} reported shards it does not own: [${frame.shardIds}].`,\n ),\n );\n return;\n }\n this.#clearStartTimer(info.id);\n info.state = \"ready\";\n info.readyAt = info.lastReadyAt = Date.now();\n this.#emit(\"clusterReady\", info);\n this.#checkReady();\n return;\n }\n case \"identify\": {\n const reply = (body: Record<string, unknown>): void => {\n if (info.process === child)\n this.#sendFrame(info, { [CLUSTER_FRAME]: body });\n };\n if (!this.#coordinator) {\n reply({\n kind: \"identify-deny\",\n id: frame.id,\n error: \"IDENTIFY coordination is off.\",\n });\n return;\n }\n if (!info.shards.includes(frame.shardId)) {\n reply({\n kind: \"identify-deny\",\n id: frame.id,\n error: `Shard ${frame.shardId} does not belong to cluster ${info.id}.`,\n });\n return;\n }\n // A shard asks for one slot at a time, so a worker cannot queue\n // more requests than it has shards.\n let waiting = this.#identifyWaiting.get(info.id);\n if (!waiting)\n this.#identifyWaiting.set(info.id, (waiting = new Set()));\n if (waiting.has(frame.shardId)) {\n reply({\n kind: \"identify-deny\",\n id: frame.id,\n error: `Shard ${frame.shardId} is already waiting for a slot.`,\n });\n return;\n }\n waiting.add(frame.shardId);\n const settled = (): void => {\n waiting.delete(frame.shardId);\n };\n this.#coordinator.acquire(info.id, frame.shardId).then(\n () => {\n settled();\n reply({ kind: \"identify-grant\", id: frame.id });\n },\n (error: unknown) => {\n settled();\n reply({\n kind: \"identify-deny\",\n id: frame.id,\n error:\n error instanceof Error\n ? error.message\n : String(error),\n });\n },\n );\n return;\n }\n case \"identify-done\":\n this.#coordinator?.release(info.id, frame.shardId);\n return;\n case \"reply\": {\n // An oversized answer settles the request with an error now\n // instead of being held until its timeout.\n const tooBig =\n frame.error === undefined && this.#tooBig(frame.result);\n this.#tracker.answer(frame.id, `c:${info.id}`, info.id, {\n ...(frame.error !== undefined\n ? { error: frame.error }\n : tooBig\n ? {\n error: `Cluster ${info.id} answered with more than ${this.#maxBytes} bytes.`,\n }\n : { result: frame.result }),\n });\n return;\n }\n case \"health\":\n if (this.#tooBig(frame.report)) {\n this.#emit(\n \"error\",\n new Error(\n `Cluster ${info.id} sent a health report of more than ${this.#maxBytes} bytes.`,\n ),\n );\n return;\n }\n info.report = frame.report;\n return;\n }\n }\n\n get #maxBytes(): number {\n return this.#options.maxMessageBytes ?? DEFAULT_MAX_MESSAGE_BYTES;\n }\n\n #tooBig(value: unknown): boolean {\n return payloadSize(value) > this.#maxBytes;\n }\n\n /** @throws {RangeError} If a payload this process is about to send is over the limit. */\n #assertSize(data: unknown, what: string): void {\n if (this.#tooBig(data))\n throw new RangeError(\n `${what} is larger than maxMessageBytes (${this.#maxBytes}).`,\n );\n }\n\n /** Routes one shard-bus message: replies for the parent are consumed, shard-targeted messages go to the owning cluster only, broadcasts to every other cluster. */\n #onBus(from: ClusterInfo, raw: unknown): void {\n const message = raw as {\n source?: unknown;\n target?: unknown;\n type?: unknown;\n data?: unknown;\n } | null;\n if (\n !message ||\n typeof message !== \"object\" ||\n typeof message.type !== \"string\" ||\n typeof message.source !== \"number\" ||\n (message.target !== null && !Number.isInteger(message.target))\n )\n return;\n if (this.#tooBig(message.data)) {\n this.#emit(\n \"error\",\n new Error(\n `Cluster ${from.id} sent a shard-bus message of more than ${this.#maxBytes} bytes; it was not relayed.`,\n ),\n );\n return;\n }\n const frame = { [BUS_FRAME]: message };\n if (message.target === PARENT_ID) {\n const reply = message.data as ReplyPayload | undefined;\n if (\n message.type === REPLY_TYPE &&\n reply &&\n typeof reply.requestId === \"string\"\n )\n this.#tracker.answer(\n reply.requestId,\n `s:${message.source}`,\n from.id,\n reply.error !== undefined\n ? { error: String(reply.error) }\n : { result: reply.result },\n );\n return;\n }\n if (message.target === null) {\n for (const [id, cluster] of this.clusters)\n if (id !== from.id) this.#sendFrame(cluster, frame);\n return;\n }\n const owner = this.clusterForShard(message.target as number);\n if (!owner || owner.id === from.id) return;\n // Fail fast: a request to a shard whose cluster is down rejects now.\n if (!this.#sendFrame(owner, frame))\n this.#sendFrame(from, {\n [PEER_DOWN_FRAME]: [message.target as number],\n });\n }\n\n /**\n * Sends a request to one shard and resolves with its handler's result.\n * @throws {RangeError} If the shard is not part of this deployment.\n * @throws {Error} If its cluster is down, the handler failed, or no answer arrives in time.\n */\n public async request<R = unknown, T = unknown>(\n shardId: number,\n type: string,\n data: T,\n options: RequestOptions = {},\n ): Promise<R> {\n this.#assertSize(data, \"The request data\");\n const owner = this.clusterForShard(shardId);\n if (!owner)\n throw new RangeError(\n `Shard ${shardId} is not part of this deployment.`,\n );\n const { id, done } = this.#tracker.begin<R>({\n keys: new Map([[`s:${shardId}`, owner.id]]),\n timeoutMs:\n options.timeoutMs ?? this.#options.requestTimeout ?? 5000,\n signal: options.signal,\n });\n const sent = this.#sendFrame(owner, {\n [BUS_FRAME]: {\n source: PARENT_ID,\n target: shardId,\n type,\n data,\n id,\n expectsReply: true,\n },\n });\n if (!sent) {\n this.#tracker.failOwner(\n owner.id,\n `Shard ${shardId} is down: cluster ${owner.id} is ${owner.state}.`,\n );\n }\n const { replies } = await done;\n const reply = replies[0];\n if (!reply)\n throw new Error(\n `Shard ${shardId} did not reply to \"${type}\" in time.`,\n );\n if (reply.error !== undefined) throw new Error(reply.error);\n return reply.result as R;\n }\n\n /**\n * Asks every shard of every live cluster and collects the answers. Shards\n * whose cluster is down, or that do not answer in time, are listed in\n * `missing`; it does not reject for them.\n */\n public async broadcast<R = unknown, T = unknown>(\n type: string,\n data: T,\n options: RequestOptions = {},\n ): Promise<BroadcastResult<R>> {\n this.#assertSize(data, \"The request data\");\n const keys = new Map<string, number>();\n const down: number[] = [];\n for (const [shard, clusterId] of this.#owner) {\n const cluster = this.#infos.get(clusterId);\n if (cluster && isLive(cluster.state))\n keys.set(`s:${shard}`, clusterId);\n else down.push(shard);\n }\n const { id, done } = this.#tracker.begin<R>({\n keys,\n timeoutMs:\n options.timeoutMs ?? this.#options.requestTimeout ?? 5000,\n signal: options.signal,\n });\n const frame = {\n [BUS_FRAME]: {\n source: PARENT_ID,\n target: null,\n type,\n data,\n id,\n expectsReply: true,\n },\n };\n for (const cluster of this.clusters.values())\n if (!this.#sendFrame(cluster, frame))\n this.#tracker.failOwner(\n cluster.id,\n `Cluster ${cluster.id} is ${cluster.state}.`,\n );\n const { replies, missing } = await done;\n return {\n replies: replies.map((reply) => ({\n shardId: Number(reply.key.slice(2)),\n ...(reply.error !== undefined\n ? { error: reply.error }\n : { result: reply.result as R }),\n })),\n missing: [\n ...down,\n ...missing.map((key) => Number(key.slice(2))),\n ].sort((a, b) => a - b),\n };\n }\n\n /**\n * Sends a request to a cluster's worker (see `ClusterWorker.onRequest()`).\n * @throws {Error} If the cluster is unknown or down, the handler failed, or no answer arrives in time.\n */\n public async requestCluster<R = unknown, T = unknown>(\n clusterId: number,\n type: string,\n data: T,\n options: RequestOptions = {},\n ): Promise<R> {\n this.#assertSize(data, \"The request data\");\n const cluster = this.#infos.get(clusterId);\n if (!cluster)\n throw new RangeError(`Cluster ${clusterId} does not exist.`);\n const { id, done } = this.#tracker.begin<R>({\n keys: new Map([[`c:${clusterId}`, clusterId]]),\n timeoutMs:\n options.timeoutMs ?? this.#options.requestTimeout ?? 5000,\n signal: options.signal,\n });\n if (\n !this.#sendFrame(cluster, {\n [CLUSTER_FRAME]: { kind: \"request\", id, type, data },\n })\n )\n this.#tracker.failOwner(\n clusterId,\n `Cluster ${clusterId} is ${cluster.state}.`,\n );\n const { replies } = await done;\n const reply = replies[0];\n if (!reply)\n throw new Error(\n `Cluster ${clusterId} did not reply to \"${type}\" in time.`,\n );\n if (reply.error !== undefined) throw new Error(reply.error);\n return reply.result as R;\n }\n\n /** Resolves when every cluster is ready. @throws {Error} On timeout, or if the manager stops first. */\n public ready(timeoutMs?: number): Promise<void> {\n if (this.#isReady()) return Promise.resolve();\n return new Promise<void>((resolve, reject) => {\n const waiter: {\n resolve: () => void;\n reject: (error: Error) => void;\n timer?: ReturnType<typeof setTimeout>;\n } = {\n resolve: () => {\n if (waiter.timer) clearTimeout(waiter.timer);\n this.#readyWaiters.delete(waiter);\n resolve();\n },\n reject: (error) => {\n if (waiter.timer) clearTimeout(waiter.timer);\n this.#readyWaiters.delete(waiter);\n reject(error);\n },\n };\n if (timeoutMs !== undefined)\n waiter.timer = setTimeout(\n () =>\n waiter.reject(\n new Error(\n `Clusters were not all ready within ${timeoutMs}ms.`,\n ),\n ),\n timeoutMs,\n );\n this.#readyWaiters.add(waiter);\n });\n }\n\n #isReady(): boolean {\n if (this.#infos.size === 0) return false;\n for (const cluster of this.#infos.values())\n if (cluster.state !== \"ready\") return false;\n return true;\n }\n\n #checkReady(): void {\n if (!this.#isReady()) return;\n for (const waiter of [...this.#readyWaiters]) waiter.resolve();\n if (!this.#allReady) {\n this.#allReady = true;\n this.#emit(\"ready\");\n }\n }\n\n /**\n * The last known health of every cluster and shard. Reads what the\n * workers last reported; nothing is sent. Use {@link refreshHealth} for fresh numbers.\n */\n public health(): ClusterManagerHealth {\n const now = Date.now();\n const clusters: ClusterHealth[] = [...this.#infos.values()]\n .sort((a, b) => a.id - b.id)\n .map((cluster) => ({\n id: cluster.id,\n state: cluster.state,\n ...(cluster.process.pid !== undefined && isLive(cluster.state)\n ? { pid: cluster.process.pid }\n : {}),\n host: cluster.host,\n shardIds: [...cluster.shards],\n uptimeMs: isLive(cluster.state) ? now - cluster.startedAt : 0,\n restarts: cluster.restarts,\n startedAt: cluster.startedAt,\n ...(cluster.readyAt !== undefined\n ? { readyAt: cluster.readyAt }\n : {}),\n ...(cluster.lastReadyAt !== undefined\n ? { lastReadyAt: cluster.lastReadyAt }\n : {}),\n shards: cluster.report?.shards ?? ([] as ShardHealth[]),\n ...(cluster.report?.metrics\n ? { metrics: cluster.report.metrics as WorkerMetrics }\n : {}),\n ...(cluster.report\n ? { reportedAt: cluster.report.reportedAt }\n : {}),\n }));\n const missingShards = [...this.#owner]\n .filter(([, id]) => this.#infos.get(id)?.state !== \"ready\")\n .map(([shard]) => shard)\n .sort((a, b) => a - b);\n const states = clusters.map((cluster) => cluster.state);\n const state: ClusterManagerHealth[\"state\"] = this.#stopping\n ? \"stopping\"\n : clusters.length === 0\n ? this.#wasStarted\n ? \"stopped\"\n : \"idle\"\n : states.every((s) => s === \"stopped\")\n ? \"stopped\"\n : states.every((s) => s === \"ready\")\n ? \"ready\"\n : states.some((s) => s === \"restarting\" || s === \"failed\")\n ? \"degraded\"\n : \"starting\";\n return {\n state,\n totalShards: this.shardCount,\n clusters,\n readyClusters: states.filter((s) => s === \"ready\").length,\n missingShards,\n };\n }\n\n /**\n * Asks every live cluster for a fresh report, then returns {@link health}.\n * A cluster that does not answer in time keeps its last report; it is not\n * treated as failed (only its process exiting does that).\n */\n public async refreshHealth(\n options: { metrics?: boolean; timeoutMs?: number } = {},\n ): Promise<ClusterManagerHealth> {\n await Promise.all(\n [...this.clusters.values()].map(async (cluster) => {\n try {\n cluster.report = await this.requestCluster<WorkerReport>(\n cluster.id,\n REQUEST_HEALTH,\n { metrics: options.metrics === true },\n { timeoutMs: options.timeoutMs ?? 2000 },\n );\n } catch {\n // Keep the previous report.\n }\n }),\n );\n return this.health();\n }\n\n /** Checks if the recommended shard count has changed and respawns if so. */\n public async checkAutoScale(): Promise<void> {\n if (!this.#auto) return; // Only auto-scale in auto mode\n try {\n const recommended = await this.fetchRecommendedShardCount();\n if (recommended !== this.shardCount) {\n await this.respawn(recommended);\n }\n } catch (error) {\n // Surface the failure instead of silently swallowing it; the next\n // interval tick retries.\n const normalized =\n error instanceof Error ? error : new Error(String(error));\n this.#options.onAutoScaleError?.(normalized);\n }\n }\n\n /** Gracefully shuts the existing clusters down and respawns them with the new shard count. @param newShardCount The new total shard count. */\n public async respawn(newShardCount: number): Promise<void> {\n this.#options.shardCount = newShardCount;\n // Hosts stay connected across a respawn, so they need not rejoin.\n await this.#stop(this.#options.shutdownTimeout ?? 5000, true);\n this.#supervisor.reset();\n await this.spawn();\n }\n\n /**\n * Gracefully stops all active clusters: sends SIGTERM, awaits each child's\n * clean exit, and force-kills (SIGKILL) only children that outlast the\n * shutdown timeout. Safe to call twice or concurrently; cancels restarts,\n * timers and pending requests.\n * @param timeoutMs Grace period per child before force-kill. Defaults to the configured `shutdownTimeout` (5000 ms).\n */\n public shutdownAll(\n timeoutMs = this.#options.shutdownTimeout ?? 5000,\n ): Promise<void> {\n return this.#stop(timeoutMs, false);\n }\n\n #stop(timeoutMs: number, keepRemote: boolean): Promise<void> {\n this.#stopPromise ??= this.#shutdown(timeoutMs, keepRemote).finally(\n () => {\n this.#stopPromise = undefined;\n },\n );\n return this.#stopPromise;\n }\n\n async #shutdown(timeoutMs: number, keepRemote: boolean): Promise<void> {\n this.#stopping = true;\n this.#halt();\n // A spawn that is still forking sees `#stopping` and stops on its own.\n await this.#startPromise?.catch(() => undefined);\n await Promise.all(\n [...this.clusters.values()].map((cluster) =>\n this.#shutdownCluster(cluster, timeoutMs),\n ),\n );\n for (const cluster of this.#infos.values()) cluster.state = \"stopped\";\n this.clusters.clear();\n this.#startPromise = undefined;\n if (!keepRemote) this.#remote?.stop();\n this.#stopping = false;\n }\n\n /** Cancels everything that could start work or keep the loop alive. */\n #halt(): void {\n for (const timer of this.#restartTimers) clearTimeout(timer);\n this.#restartTimers.clear();\n for (const id of [...this.#startTimers.keys()])\n this.#clearStartTimer(id);\n for (const wake of [...this.#pauses]) wake();\n if (this.#autoScaleTimer) {\n clearInterval(this.#autoScaleTimer);\n this.#autoScaleTimer = undefined;\n }\n this.#coordinator?.close();\n this.#coordinator = undefined;\n this.#remote?.abortWaits();\n this.#tracker.close();\n for (const waiter of [...this.#readyWaiters])\n waiter.reject(new Error(\"The cluster manager is stopping.\"));\n }\n\n /** Gracefully terminates a single cluster child, escalating to SIGKILL after the timeout. */\n async #shutdownCluster(\n cluster: ClusterInfo,\n timeoutMs: number,\n ): Promise<void> {\n const child = cluster.process;\n cluster.state = \"stopping\";\n if (child.exitCode !== null || child.signalCode !== null) return;\n await new Promise<void>((resolve) => {\n let timer: ReturnType<typeof setTimeout> | undefined;\n const finish = (): void => {\n if (timer) clearTimeout(timer);\n resolve();\n };\n child.once(\"exit\", finish);\n try {\n child.kill(\"SIGTERM\");\n } catch {\n child.off(\"exit\", finish);\n finish();\n return;\n }\n timer = setTimeout(() => {\n try {\n child.kill(\"SIGKILL\");\n } catch {\n // Ignore force-kill errors.\n }\n }, timeoutMs);\n });\n }\n\n /** Immediately force-kills all active cluster processes and clears the cluster map. Prefer {@link shutdownAll} for a graceful stop. */\n public killAll(): void {\n this.#stopping = true;\n this.#halt();\n for (const cluster of this.#infos.values()) {\n cluster.state = \"stopped\";\n try {\n cluster.process.kill();\n } catch {\n // Ignore kill errors\n }\n }\n this.clusters.clear();\n this.#startPromise = undefined;\n this.#remote?.stop();\n this.#stopping = false;\n }\n}\n",
|
|
27
|
-
"/** Discord allows one IDENTIFY per rate-limit key every five seconds. */\nexport const IDENTIFY_INTERVAL = 5000;\n\n/** What `/gateway/bot` says about the session budget. */\nexport interface IdentifyBudget {\n total: number;\n remaining: number;\n /** Milliseconds until `remaining` resets, counted from construction. */\n resetAfter: number;\n}\n\n/** Options for an {@link IdentifyCoordinator}. */\nexport interface IdentifyCoordinatorOptions {\n /** Discord's `max_concurrency`: shards with the same `id % maxConcurrency` share a key. Default 1. */\n maxConcurrency?: number;\n /** Milliseconds between two IDENTIFYs on one key. Default 5000. */\n interval?: number;\n /** The session-start budget, when known. Without it the coordinator paces but never blocks. */\n budget?: IdentifyBudget;\n /** A granted slot that is not released within this many ms is released for the holder. Default 60,000. */\n holdTimeout?: number;\n /** Clock, injectable for tests. */\n now?: () => number;\n /** Timer, injectable for tests. Must return a handle `cancel` understands. */\n schedule?: (callback: () => void, ms: number) => unknown;\n cancel?: (handle: unknown) => void;\n /** Called when a request has to wait for the budget to reset. */\n onBudgetWait?: (info: { shardId: number; waitMs: number }) => void;\n}\n\ninterface Waiter {\n owner: number;\n shardId: number;\n grant: () => void;\n fail: (error: Error) => void;\n}\n\ninterface Key {\n queue: Waiter[];\n /** The grant that has not been released yet. */\n holder?: { owner: number; shardId: number; timer: unknown };\n /** Earliest time the next IDENTIFY may start. */\n nextAt: number;\n}\n\n/**\n * Hands out IDENTIFY slots for a whole deployment. Every cluster asks the\n * parent, so the limit holds across processes: one IDENTIFY per rate-limit key\n * per interval, and no more IDENTIFYs than `session_start_limit.remaining`.\n * It only does arithmetic on timestamps and holds one timer, so it can be\n * tested with a fake clock.\n */\nexport class IdentifyCoordinator {\n readonly maxConcurrency: number;\n readonly #interval: number;\n readonly #holdTimeout: number;\n readonly #now: () => number;\n readonly #schedule: (callback: () => void, ms: number) => unknown;\n readonly #cancel: (handle: unknown) => void;\n readonly #onBudgetWait?: IdentifyCoordinatorOptions[\"onBudgetWait\"];\n readonly #keys = new Map<number, Key>();\n #remaining: number | undefined;\n #total: number | undefined;\n #resetAt = Number.POSITIVE_INFINITY;\n #timer: unknown;\n #timerAt = Number.POSITIVE_INFINITY;\n #closed = false;\n\n public constructor(options: IdentifyCoordinatorOptions = {}) {\n const concurrency = options.maxConcurrency ?? 1;\n if (!Number.isInteger(concurrency) || concurrency < 1)\n throw new RangeError(\"maxConcurrency must be a positive integer.\");\n const interval = options.interval ?? IDENTIFY_INTERVAL;\n const hold = options.holdTimeout ?? 60_000;\n if (!(interval >= 0) || !(hold > 0))\n throw new RangeError(\n \"interval cannot be negative and holdTimeout must be positive.\",\n );\n this.maxConcurrency = concurrency;\n this.#interval = interval;\n this.#holdTimeout = hold;\n this.#now = options.now ?? Date.now;\n this.#schedule =\n options.schedule ??\n ((callback, ms) => {\n const timer = setTimeout(callback, ms);\n (timer as { unref?: () => void }).unref?.();\n return timer;\n });\n this.#cancel =\n options.cancel ??\n ((handle) => clearTimeout(handle as ReturnType<typeof setTimeout>));\n this.#onBudgetWait = options.onBudgetWait;\n if (options.budget) {\n this.#total = options.budget.total;\n this.#remaining = options.budget.remaining;\n this.#resetAt = this.#now() + options.budget.resetAfter;\n }\n }\n\n /** Slots left in the session budget, or `undefined` when it is unknown. */\n public get remaining(): number | undefined {\n return this.#remaining;\n }\n\n /** Requests waiting for a slot. */\n public get waiting(): number {\n let count = 0;\n for (const key of this.#keys.values()) count += key.queue.length;\n return count;\n }\n\n /** Slots granted and not yet released. */\n public get holding(): number {\n let count = 0;\n for (const key of this.#keys.values()) if (key.holder) count++;\n return count;\n }\n\n /**\n * Waits for the shard's turn. Resolves when the shard may IDENTIFY now;\n * call {@link release} once it has. Rejects if `owner` is dropped or the\n * coordinator closes first.\n */\n public acquire(owner: number, shardId: number): Promise<void> {\n if (this.#closed)\n return Promise.reject(new Error(\"IDENTIFY coordinator is closed.\"));\n const index = shardId % this.maxConcurrency;\n let key = this.#keys.get(index);\n if (!key) this.#keys.set(index, (key = { queue: [], nextAt: 0 }));\n return new Promise<void>((grant, fail) => {\n key.queue.push({ owner, shardId, grant, fail });\n this.pump();\n });\n }\n\n /** The shard has sent its IDENTIFY: its key rests for one interval. Ignored unless `owner` holds the slot. */\n public release(owner: number, shardId: number): void {\n const key = this.#keys.get(shardId % this.maxConcurrency);\n const holder = key?.holder;\n if (\n !key ||\n !holder ||\n holder.owner !== owner ||\n holder.shardId !== shardId\n )\n return;\n this.#free(key);\n this.pump();\n }\n\n /** A cluster is gone: forget its waiting requests and free the slots it held. */\n public drop(owner: number, reason = \"The cluster went away.\"): void {\n for (const key of this.#keys.values()) {\n const keep: Waiter[] = [];\n for (const waiter of key.queue)\n if (waiter.owner === owner) waiter.fail(new Error(reason));\n else keep.push(waiter);\n key.queue = keep;\n if (key.holder?.owner === owner) this.#free(key);\n }\n this.pump();\n }\n\n /** Rejects every waiter and stops the timer. */\n public close(): void {\n this.#closed = true;\n if (this.#timer !== undefined) this.#cancel(this.#timer);\n this.#timer = undefined;\n this.#timerAt = Number.POSITIVE_INFINITY;\n for (const key of this.#keys.values()) {\n if (key.holder) this.#cancel(key.holder.timer);\n key.holder = undefined;\n for (const waiter of key.queue)\n waiter.fail(new Error(\"IDENTIFY coordinator is closed.\"));\n key.queue = [];\n }\n }\n\n /** Grants every slot that is due and arms one timer for the next. Called internally and by tests after moving the clock. */\n public pump(): void {\n if (this.#closed) return;\n const now = this.#now();\n if (this.#total !== undefined && now >= this.#resetAt) {\n this.#remaining = this.#total;\n this.#resetAt = Number.POSITIVE_INFINITY;\n }\n let next = Number.POSITIVE_INFINITY;\n for (const key of this.#keys.values()) {\n if (key.holder || key.queue.length === 0) continue;\n if (key.nextAt > now) {\n next = Math.min(next, key.nextAt);\n continue;\n }\n if (this.#remaining !== undefined && this.#remaining <= 0) {\n const waiter = key.queue[0]!;\n if (this.#resetAt === Number.POSITIVE_INFINITY) {\n // No reset time is known: nothing can be granted, fail loudly.\n key.queue.shift();\n waiter.fail(\n new Error(\"The session start limit is exhausted.\"),\n );\n continue;\n }\n this.#onBudgetWait?.({\n shardId: waiter.shardId,\n waitMs: this.#resetAt - now,\n });\n next = Math.min(next, this.#resetAt);\n continue;\n }\n const waiter = key.queue.shift()!;\n if (this.#remaining !== undefined) this.#remaining--;\n const timer = this.#schedule(() => {\n if (key.holder?.timer === timer) {\n this.#free(key);\n this.pump();\n }\n }, this.#holdTimeout);\n key.holder = {\n owner: waiter.owner,\n shardId: waiter.shardId,\n timer,\n };\n waiter.grant();\n }\n this.#arm(next);\n }\n\n #free(key: Key): void {\n if (key.holder) this.#cancel(key.holder.timer);\n key.holder = undefined;\n key.nextAt = this.#now() + this.#interval;\n }\n\n #arm(at: number): void {\n if (at === this.#timerAt && this.#timer !== undefined) return;\n if (this.#timer !== undefined) this.#cancel(this.#timer);\n this.#timer = undefined;\n this.#timerAt = Number.POSITIVE_INFINITY;\n if (at === Number.POSITIVE_INFINITY) return;\n this.#timerAt = at;\n this.#timer = this.#schedule(\n () => {\n this.#timer = undefined;\n this.#timerAt = Number.POSITIVE_INFINITY;\n this.pump();\n },\n Math.max(0, at - this.#now()),\n );\n }\n}\n",
|
|
28
|
-
"import { EventEmitter } from \"node:events\";\nimport { randomBytes } from \"node:crypto\";\nimport type { ChildProcess } from \"node:child_process\";\nimport type { Server, ServerWebSocket } from \"bun\";\nimport {\n HOST_ID,\n MAX_REMOTE_FRAME_BYTES,\n isLoopbackHost,\n REMOTE_PROTOCOL_VERSION,\n RemoteClose,\n parseToCoordinator,\n verifyChallenge,\n type ToHost,\n} from \"./remote-protocol.js\";\n\n/** Where, and how, a coordinator listens for hosts. */\nexport interface RemoteOptions {\n /** TCP port. `0` picks a free one (read it from `ClusterManager#remotePort`). */\n port: number;\n /**\n * Interface to bind. Default `127.0.0.1`, which only this machine can\n * reach. Any other address needs `tls` or `allowInsecure`.\n */\n hostname?: string;\n /** Shared secret, at least 16 characters. Hosts prove they know it; it never crosses the wire. */\n secret: string;\n /** TLS for `wss://`. Required to listen on a non-loopback address unless `allowInsecure` is set. */\n tls?: { cert: string; key: string };\n /**\n * Listen on a non-loopback address without TLS: frames (shard messages\n * and worker output) travel unencrypted and unsigned. Only for a network\n * you fully trust, or when a TLS proxy in front terminates the connection.\n * Default false.\n */\n allowInsecure?: boolean;\n /**\n * The host IDs that make up the deployment. Clusters are placed on them in\n * this order, and an unlisted host is refused. Without it, the hosts that\n * have joined when `spawn()` starts are used, sorted by ID.\n */\n hosts?: string[];\n /** How many hosts must join before clusters are placed when `hosts` is not set. Default 1. */\n minHosts?: number;\n /** Milliseconds to wait for the hosts to join before `spawn()` fails. Default 60,000. */\n joinTimeout?: number;\n /**\n * Fencing lease in milliseconds. A host that hears nothing from the\n * coordinator for this long kills its workers; the coordinator waits twice\n * as long before it treats the host as lost and restarts its clusters, so\n * a shard never runs in two places. Default 15,000.\n */\n lease?: number;\n /** Milliseconds a new connection has to authenticate before it is closed. Default 10,000. */\n helloTimeout?: number;\n /** Most connections that may be waiting to authenticate at once; more are closed. Default 16. */\n maxUnauthenticated?: number;\n /** Also run clusters on the coordinator's own machine. Default false: clusters run on the hosts only. */\n local?: boolean;\n}\n\n/** A host as the coordinator sees it. */\nexport interface HostStatus {\n id: string;\n /** Whether its socket is open and answering. */\n connected: boolean;\n /** When it last sent anything (epoch ms). */\n lastSeen: number;\n /** Clusters currently running on it. */\n clusters: number[];\n}\n\ninterface SocketData {\n nonce: string;\n id?: string;\n /** Whether this socket is counted in `#unauthenticated`. */\n counted?: boolean;\n helloTimer?: ReturnType<typeof setTimeout>;\n}\n\ninterface Link {\n id: string;\n ws: ServerWebSocket<SocketData>;\n lastSeen: number;\n children: Map<number, RemoteChild>;\n}\n\ntype Kill = (signal: string) => boolean;\n\n/** What the coordinator wants to hear about its hosts. */\nexport interface RemoteHooks {\n join(id: string): void;\n leave(id: string, reason: string): void;\n error(error: Error): void;\n}\n\n/**\n * A worker process on another host, shaped like the `ChildProcess` that\n * `ClusterManager` supervises. `send()` and `kill()` travel to the host's\n * agent; its messages and exit travel back, so the manager's restart,\n * IDENTIFY, routing and health logic needs no special case for it.\n */\nexport class RemoteChild extends EventEmitter {\n public pid: number | undefined;\n public exitCode: number | null = null;\n public signalCode: NodeJS.Signals | null = null;\n public connected = true;\n\n readonly #send: (frame: unknown) => boolean;\n readonly #kill: Kill;\n\n public constructor(\n public readonly host: string,\n public readonly cluster: number,\n public readonly epoch: number,\n send: (frame: unknown) => boolean,\n kill: Kill,\n ) {\n super();\n this.#send = send;\n this.#kill = kill;\n }\n\n /** Sends an IPC frame to the worker. */\n public send(frame: unknown): boolean {\n return this.connected &&\n this.exitCode === null &&\n this.signalCode === null\n ? this.#send(frame)\n : false;\n }\n\n /** Asks the host to signal the worker; the exit arrives as an event. */\n public kill(signal: string = \"SIGTERM\"): boolean {\n if (this.exitCode !== null || this.signalCode !== null) return false;\n return this.#kill(signal);\n }\n\n /** The agent says the worker started. */\n public spawned(pid: number | undefined): void {\n this.pid = pid;\n }\n\n /** The worker sent a message. */\n public deliver(frame: unknown): void {\n if (this.exitCode === null && this.signalCode === null)\n this.emit(\"message\", frame);\n }\n\n /** The worker exited, or its host was lost. Emits `exit` once. */\n public finish(code: number | null, signal: string | null): void {\n if (this.exitCode !== null || this.signalCode !== null) return;\n this.connected = false;\n // `exitCode` and `signalCode` must not both stay null once finished.\n this.exitCode = code ?? (signal === null ? 1 : null);\n this.signalCode = (signal as NodeJS.Signals | null) ?? null;\n this.emit(\"exit\", code, signal);\n }\n}\n\n/** The coordinator's listener and the hosts that joined it. */\nexport class RemoteHosts {\n readonly #options: Required<\n Pick<RemoteOptions, \"port\" | \"secret\" | \"lease\">\n > &\n RemoteOptions;\n readonly #links = new Map<string, Link>();\n readonly #waiters = new Set<{ check(): void; abort(error: Error): void }>();\n readonly #hooks: RemoteHooks;\n #server?: Server<SocketData>;\n #watchdog?: ReturnType<typeof setInterval>;\n #epoch = 0;\n #ping = 0;\n /** Sockets that have not said hello yet. */\n #unauthenticated = 0;\n\n public constructor(options: RemoteOptions, hooks: RemoteHooks) {\n if (typeof options.secret !== \"string\" || options.secret.length < 16)\n throw new RangeError(\n \"remote.secret must be a string of at least 16 characters.\",\n );\n if (\n !Number.isInteger(options.port) ||\n options.port < 0 ||\n options.port > 65535\n )\n throw new RangeError(\n \"remote.port must be an integer from 0 to 65535.\",\n );\n if (\n options.tls &&\n (typeof options.tls.cert !== \"string\" ||\n typeof options.tls.key !== \"string\" ||\n !options.tls.cert ||\n !options.tls.key)\n )\n throw new TypeError(\"remote.tls needs a non-empty cert and key.\");\n if (\n !options.tls &&\n !options.allowInsecure &&\n !isLoopbackHost(options.hostname ?? \"127.0.0.1\")\n )\n throw new RangeError(\n \"remote listens on a network address without TLS: set remote.tls, bind 127.0.0.1 behind a TLS proxy, or set remote.allowInsecure for a network you fully trust.\",\n );\n const lease = options.lease ?? 15_000;\n if (!Number.isFinite(lease) || lease < 30)\n throw new RangeError(\n \"remote.lease must be at least 30 milliseconds.\",\n );\n for (const id of options.hosts ?? [])\n if (!HOST_ID.test(id))\n throw new RangeError(`\"${id}\" is not a valid host ID.`);\n if (\n options.hosts &&\n (options.hosts.length === 0 ||\n new Set(options.hosts).size !== options.hosts.length)\n )\n throw new RangeError(\"remote.hosts must be unique and not empty.\");\n if (\n options.minHosts !== undefined &&\n (!Number.isInteger(options.minHosts) || options.minHosts < 1)\n )\n throw new RangeError(\"remote.minHosts must be a positive integer.\");\n this.#options = { ...options, lease };\n this.#hooks = hooks;\n }\n\n /** The port in use, once started. */\n public get port(): number | undefined {\n return this.#server?.port;\n }\n\n /** Host IDs that are connected now, sorted. */\n public connected(): string[] {\n return [...this.#links.keys()].sort();\n }\n\n /** Opens the listener. Safe to call twice. */\n public start(): void {\n if (this.#server) return;\n const { port, hostname, tls, lease } = this.#options;\n this.#server = Bun.serve<SocketData>({\n port,\n hostname: hostname ?? \"127.0.0.1\",\n ...(tls ? { tls } : {}),\n fetch: (request, server) => {\n if (\n server.upgrade(request, {\n data: { nonce: randomBytes(16).toString(\"hex\") },\n })\n )\n return undefined;\n return new Response(\"Lunibee cluster coordinator\", {\n status: 426,\n });\n },\n websocket: {\n maxPayloadLength: MAX_REMOTE_FRAME_BYTES,\n idleTimeout: 0,\n open: (ws) => this.#open(ws),\n message: (ws, raw) => this.#message(ws, raw),\n close: (ws, _code, reason) => this.#closed(ws, reason),\n },\n });\n const pingMs = Math.max(10, Math.floor(lease / 3));\n this.#watchdog = setInterval(() => this.#tick(), pingMs);\n (this.#watchdog as { unref?: () => void }).unref?.();\n }\n\n /** Closes the listener and every host connection. */\n public stop(): void {\n if (this.#watchdog) clearInterval(this.#watchdog);\n this.#watchdog = undefined;\n for (const link of [...this.#links.values()]) {\n this.#post(link, { t: \"bye\", reason: \"The coordinator stopped.\" });\n this.#drop(link, \"The coordinator stopped.\", RemoteClose.Shutdown);\n }\n void this.#server?.stop(true);\n this.#server = undefined;\n this.abortWaits();\n }\n\n /** Resolves when the given hosts (or `count` hosts) are connected. @throws {Error} On timeout. */\n public waitFor(\n hosts: readonly string[] | undefined,\n count: number,\n timeoutMs: number,\n ): Promise<void> {\n const done = (): boolean =>\n hosts\n ? hosts.every((id) => this.#links.has(id))\n : this.#links.size >= count;\n if (done()) return Promise.resolve();\n return new Promise<void>((resolve, reject) => {\n const waiter = {\n check: (): void => {\n if (!done()) return;\n clearTimeout(timer);\n this.#waiters.delete(waiter);\n resolve();\n },\n abort: (error: Error): void => {\n clearTimeout(timer);\n this.#waiters.delete(waiter);\n reject(error);\n },\n };\n const timer = setTimeout(() => {\n const missing = hosts?.filter((id) => !this.#links.has(id));\n waiter.abort(\n new Error(\n hosts\n ? `Hosts did not join within ${timeoutMs}ms: [${missing}].`\n : `Only ${this.#links.size} of ${count} hosts joined within ${timeoutMs}ms.`,\n ),\n );\n }, timeoutMs);\n this.#waiters.add(waiter);\n });\n }\n\n /** Fails everything waiting for hosts to join (a shutdown). */\n public abortWaits(): void {\n for (const waiter of [...this.#waiters])\n waiter.abort(new Error(\"The coordinator stopped.\"));\n }\n\n /** Hosts and the clusters running on them. */\n public status(declared: readonly string[] = []): HostStatus[] {\n const ids = new Set([...declared, ...this.#links.keys()]);\n return [...ids].sort().map((id) => {\n const link = this.#links.get(id);\n return {\n id,\n connected: link !== undefined,\n lastSeen: link?.lastSeen ?? 0,\n clusters: link\n ? [...link.children.keys()].sort((a, b) => a - b)\n : [],\n };\n });\n }\n\n /**\n * Starts a worker on `hostId`. If the host is not connected the returned\n * child exits at once, which `ClusterManager` handles like a crash.\n */\n public createChild(\n hostId: string,\n cluster: number,\n env: Record<string, string>,\n ): ChildProcess {\n const epoch = ++this.#epoch;\n const link = this.#links.get(hostId);\n const child = new RemoteChild(\n hostId,\n cluster,\n epoch,\n (frame) =>\n link !== undefined &&\n this.#post(link, { t: \"frame\", cluster, epoch, frame }),\n (signal) =>\n link !== undefined &&\n this.#post(link, { t: \"kill\", cluster, epoch, signal }),\n );\n if (!link) {\n queueMicrotask(() => child.finish(null, \"SIGHUP\"));\n return child as unknown as ChildProcess;\n }\n link.children.get(cluster)?.finish(null, \"SIGKILL\");\n link.children.set(cluster, child);\n if (!this.#post(link, { t: \"spawn\", cluster, epoch, env }))\n queueMicrotask(() => child.finish(null, \"SIGHUP\"));\n return child as unknown as ChildProcess;\n }\n\n #post(link: Link, message: ToHost): boolean {\n try {\n return link.ws.send(JSON.stringify(message)) > 0;\n } catch {\n return false;\n }\n }\n\n #open(ws: ServerWebSocket<SocketData>): void {\n if (this.#unauthenticated >= (this.#options.maxUnauthenticated ?? 16)) {\n ws.close(RemoteClose.TooManyConnections, \"Too many connections.\");\n return;\n }\n this.#unauthenticated++;\n ws.data.counted = true;\n ws.data.helloTimer = setTimeout(\n () => ws.close(RemoteClose.HelloTimeout, \"No hello.\"),\n this.#options.helloTimeout ?? 10_000,\n );\n ws.send(\n JSON.stringify({\n t: \"challenge\",\n nonce: ws.data.nonce,\n version: REMOTE_PROTOCOL_VERSION,\n } satisfies ToHost),\n );\n }\n\n #message(ws: ServerWebSocket<SocketData>, raw: string | Buffer): void {\n if (typeof raw !== \"string\") {\n ws.close(RemoteClose.BadRequest, \"Text frames only.\");\n return;\n }\n const message = parseToCoordinator(raw);\n if (!message) {\n ws.close(RemoteClose.BadRequest, \"Malformed frame.\");\n return;\n }\n if (ws.data.id === undefined) {\n this.#hello(ws, message);\n return;\n }\n const link = this.#links.get(ws.data.id);\n if (!link || link.ws !== ws) return;\n link.lastSeen = Date.now();\n if (message.t === \"pong\" || message.t === \"hello\") return;\n const child = link.children.get(message.cluster);\n // A frame from an earlier process of the same cluster is stale.\n if (!child || child.epoch !== message.epoch) return;\n switch (message.t) {\n case \"spawned\":\n child.spawned(message.pid);\n break;\n case \"frame\":\n child.deliver(message.frame);\n break;\n case \"exit\":\n link.children.delete(message.cluster);\n child.finish(message.code, message.signal);\n break;\n case \"error\":\n this.#hooks.error(\n new Error(\n `Host ${link.id} could not run cluster ${message.cluster}: ${message.message}`,\n ),\n );\n link.children.delete(message.cluster);\n child.finish(1, null);\n break;\n }\n }\n\n #hello(\n ws: ServerWebSocket<SocketData>,\n message: ReturnType<typeof parseToCoordinator>,\n ): void {\n if (message?.t !== \"hello\") {\n ws.close(RemoteClose.BadRequest, \"Expected hello.\");\n return;\n }\n const { id } = message;\n if (message.version !== REMOTE_PROTOCOL_VERSION) {\n ws.close(RemoteClose.VersionMismatch, \"Protocol version mismatch.\");\n return;\n }\n if (\n !HOST_ID.test(id) ||\n !verifyChallenge(\n this.#options.secret,\n ws.data.nonce,\n id,\n message.mac,\n )\n ) {\n ws.close(RemoteClose.AuthFailed, \"Authentication failed.\");\n return;\n }\n if (this.#options.hosts && !this.#options.hosts.includes(id)) {\n ws.close(\n RemoteClose.UnknownHost,\n \"Host is not part of this deployment.\",\n );\n return;\n }\n if (this.#links.has(id)) {\n ws.close(\n RemoteClose.DuplicateHost,\n \"A host with this ID is connected.\",\n );\n return;\n }\n clearTimeout(ws.data.helloTimer);\n this.#uncount(ws);\n ws.data.id = id;\n const link: Link = {\n id,\n ws,\n lastSeen: Date.now(),\n children: new Map(),\n };\n this.#links.set(id, link);\n const lease = this.#options.lease;\n this.#post(link, {\n t: \"welcome\",\n leaseMs: lease,\n pingMs: Math.max(10, Math.floor(lease / 3)),\n });\n this.#hooks.join(id);\n for (const waiter of [...this.#waiters]) waiter.check();\n }\n\n #uncount(ws: ServerWebSocket<SocketData>): void {\n if (ws.data.counted) {\n ws.data.counted = false;\n this.#unauthenticated--;\n }\n }\n\n #closed(ws: ServerWebSocket<SocketData>, reason: string): void {\n clearTimeout(ws.data.helloTimer);\n this.#uncount(ws);\n const link = ws.data.id ? this.#links.get(ws.data.id) : undefined;\n if (link && link.ws === ws)\n this.#drop(link, reason || \"The connection closed.\");\n }\n\n /** Forgets a host and ends every worker it ran. */\n #drop(link: Link, reason: string, code?: number): void {\n if (this.#links.get(link.id) !== link) return;\n this.#links.delete(link.id);\n if (code !== undefined)\n try {\n link.ws.close(code, reason.slice(0, 100));\n } catch {\n // Already closed.\n }\n for (const child of [...link.children.values()])\n child.finish(null, \"SIGHUP\");\n link.children.clear();\n this.#hooks.leave(link.id, reason);\n }\n\n #tick(): void {\n const now = Date.now();\n for (const link of [...this.#links.values()]) {\n // The host fences itself after one lease of silence; the\n // coordinator waits for a second before it lets anything restart.\n if (now - link.lastSeen > this.#options.lease * 2) {\n this.#drop(\n link,\n \"The host stopped answering.\",\n RemoteClose.HostLost,\n );\n continue;\n }\n this.#post(link, { t: \"ping\", n: ++this.#ping });\n }\n }\n}\n",
|
|
22
|
+
"import { readClusterContext, type ClusterContext } from \"./context.js\";\nimport type { WorkerReport } from \"./health.js\";\nimport { CLUSTER_FRAME, parseParentFrame } from \"./protocol.js\";\n\n/** Asks for the right to IDENTIFY, and gives it back. */\nexport interface IdentifyGate {\n /** Resolves when `shardId` may IDENTIFY now. Rejects if the parent refuses or goes away. */\n acquire(shardId: number): Promise<void>;\n /** `shardId` has sent its IDENTIFY. */\n release(shardId: number): void;\n}\n\n/** The part of `process` a worker uses. */\nexport interface WorkerEndpoint {\n send?(message: unknown): unknown;\n on(event: string, listener: (...args: never[]) => void): unknown;\n off(event: string, listener: (...args: never[]) => void): unknown;\n}\n\ntype RequestHandler = (data: unknown) => unknown;\n\n/**\n * The worker side of a cluster. It tells the parent when the process is\n * ready, answers the parent's requests, and asks the parent for IDENTIFY\n * slots. `ShardManager` uses it automatically inside a cluster; use it\n * directly when you run your own Gateways.\n */\nexport class ClusterWorker {\n static #current: ClusterWorker | undefined;\n\n /** The worker of this process, or `undefined` when it was not forked by a `ClusterManager`. */\n public static current(): ClusterWorker | undefined {\n if (!ClusterWorker.#current) {\n const context = readClusterContext();\n if (context && typeof process.send === \"function\")\n ClusterWorker.#current = new ClusterWorker(\n context,\n process as unknown as WorkerEndpoint,\n );\n }\n return ClusterWorker.#current;\n }\n\n /** Forgets the process-wide worker (tests). */\n public static reset(): void {\n ClusterWorker.#current?.close();\n ClusterWorker.#current = undefined;\n }\n\n public readonly context: ClusterContext;\n readonly #endpoint: WorkerEndpoint;\n readonly #handlers = new Map<string, RequestHandler>();\n readonly #waiting = new Map<\n number,\n { resolve: () => void; reject: (error: Error) => void }\n >();\n readonly #onMessage: (frame: unknown) => void;\n readonly #onDisconnect: () => void;\n #counter = 0;\n #closed = false;\n\n public constructor(\n context: ClusterContext,\n endpoint: WorkerEndpoint = process as unknown as WorkerEndpoint,\n options: {\n /**\n * Exit when the parent's IPC channel closes. Without a parent\n * nothing coordinates IDENTIFY or fences this process, and its\n * Gateway sessions would keep running beside a replacement.\n * Default: true for the real process, false for an injected endpoint.\n */\n exitOnParentLoss?: boolean;\n /** Replaces `process.exit`; for tests. */\n exit?: (code: number) => void;\n } = {},\n ) {\n if (typeof endpoint.send !== \"function\")\n throw new Error(\n \"ClusterWorker needs an IPC channel: run it in a process forked by ClusterManager.\",\n );\n this.context = context;\n this.#endpoint = endpoint;\n this.#onMessage = (frame) => this.#handle(frame);\n const exitOnLoss =\n options.exitOnParentLoss ??\n endpoint === (process as unknown as WorkerEndpoint);\n const exit = options.exit ?? ((code: number) => process.exit(code));\n this.#onDisconnect = () => {\n this.#failAll(\"The parent went away.\");\n if (exitOnLoss && !this.#closed) exit(1);\n };\n endpoint.on(\"message\", this.#onMessage);\n endpoint.on(\"disconnect\", this.#onDisconnect);\n }\n\n /** The gate to use when the parent coordinates IDENTIFY, otherwise `undefined`. */\n public get identifyGate(): IdentifyGate | undefined {\n return this.context.coordinatedIdentify ? this.#gate : undefined;\n }\n\n readonly #gate: IdentifyGate = {\n acquire: (shardId) => {\n if (this.#closed)\n return Promise.reject(\n new Error(\"The cluster worker is closed.\"),\n );\n const id = ++this.#counter;\n return new Promise<void>((resolve, reject) => {\n this.#waiting.set(id, { resolve, reject });\n this.#send({ kind: \"identify\", id, shardId });\n });\n },\n release: (shardId) => this.#send({ kind: \"identify-done\", shardId }),\n };\n\n /** Tells the parent every shard of this process finished its handshake. */\n public ready(shardIds: readonly number[] = this.context.shardIds): void {\n this.#send({ kind: \"ready\", shardIds: [...shardIds] });\n }\n\n /** Pushes a health report to the parent without being asked. */\n public reportHealth(report: WorkerReport): void {\n this.#send({ kind: \"health\", report });\n }\n\n /**\n * Answers requests of `type` sent with `ClusterManager.requestCluster()`.\n * The return value (or thrown error) goes back to the caller.\n */\n public onRequest(type: string, handler: RequestHandler): this {\n if (!type.trim() || typeof handler !== \"function\")\n throw new TypeError(\"A request type and handler are required.\");\n this.#handlers.set(type, handler);\n return this;\n }\n\n /** Stops listening and rejects pending IDENTIFY requests. */\n public close(): void {\n this.#closed = true;\n this.#endpoint.off(\"message\", this.#onMessage);\n this.#endpoint.off(\"disconnect\", this.#onDisconnect);\n this.#failAll(\"The cluster worker closed.\");\n }\n\n #send(body: Record<string, unknown>): void {\n try {\n this.#endpoint.send!({ [CLUSTER_FRAME]: body });\n } catch (error) {\n this.#failAll(\n error instanceof Error ? error.message : String(error),\n );\n }\n }\n\n #failAll(reason: string): void {\n for (const waiter of this.#waiting.values())\n waiter.reject(new Error(reason));\n this.#waiting.clear();\n }\n\n #handle(frame: unknown): void {\n if (!frame || typeof frame !== \"object\" || !(CLUSTER_FRAME in frame))\n return;\n const parsed = parseParentFrame(\n (frame as Record<string, unknown>)[CLUSTER_FRAME],\n );\n if (!parsed) return;\n if (\n parsed.kind === \"identify-grant\" ||\n parsed.kind === \"identify-deny\"\n ) {\n const waiter = this.#waiting.get(parsed.id);\n if (!waiter) return;\n this.#waiting.delete(parsed.id);\n if (parsed.kind === \"identify-grant\") waiter.resolve();\n else waiter.reject(new Error(parsed.error));\n return;\n }\n const handler = this.#handlers.get(parsed.type);\n const id = parsed.id;\n if (!handler) {\n this.#send({\n kind: \"reply\",\n id,\n error: `No handler for \"${parsed.type}\".`,\n });\n return;\n }\n void (async () => {\n try {\n this.#send({\n kind: \"reply\",\n id,\n result: await handler(parsed.data),\n });\n } catch (error) {\n this.#send({\n kind: \"reply\",\n id,\n error:\n error instanceof Error ? error.message : String(error),\n });\n }\n })();\n }\n}\n",
|
|
23
|
+
"import type { ShardMessage } from \"./bus.js\";\n\n/** Carries {@link ShardMessage}s between the processes or threads that run shards. */\nexport interface ShardBusTransport {\n /** Sends a message to the other shards. Delivery to the sender itself is not required. */\n post(message: ShardMessage): void;\n /** Listens for messages from other shards. */\n onMessage(listener: (message: ShardMessage) => void): void;\n /** Listens for the shards of a cluster that died, so pending requests to them can fail early. */\n onPeerDown?(listener: (shardIds: readonly number[]) => void): void;\n /** Stops listening and releases the channel. */\n close(): void;\n}\n\n/** One process: shards in the same process (or worker threads of it) talk over a `BroadcastChannel`. */\nexport class BroadcastChannelTransport implements ShardBusTransport {\n readonly #channel: BroadcastChannel;\n\n public constructor(name: string) {\n this.#channel = new BroadcastChannel(name);\n }\n\n public post(message: ShardMessage): void {\n this.#channel.postMessage(message);\n }\n\n public onMessage(listener: (message: ShardMessage) => void): void {\n this.#channel.addEventListener(\"message\", (event) =>\n listener(event.data as ShardMessage),\n );\n }\n\n public close(): void {\n this.#channel.close();\n }\n}\n\n/** Marks a bus message on the IPC channel, so it cannot be confused with the bot's own `process.send()` traffic. */\nexport const BUS_FRAME = \"__lunibeeBus\";\n/** Marks the parent's notice that a cluster's shards are gone. */\nexport const PEER_DOWN_FRAME = \"__lunibeePeerDown\";\n\n/** The part of `process` (or a child process handle) the IPC transport uses. */\nexport interface IpcEndpoint {\n send?(message: unknown): unknown;\n on(event: \"message\", listener: (message: unknown) => void): unknown;\n off(event: \"message\", listener: (message: unknown) => void): unknown;\n}\n\n/**\n * Forked clusters: a worker `send()`s to its parent, and the parent\n * ({@link ClusterManager}) relays to every other worker. Selected\n * automatically in a worker forked by `ClusterManager`.\n */\n/** The IPC transports open on each endpoint, so buses in one process can reach each other. */\nconst LOCAL = new WeakMap<IpcEndpoint, Set<IpcTransport>>();\n\nexport class IpcTransport implements ShardBusTransport {\n readonly #endpoint: IpcEndpoint;\n readonly #listener: (frame: unknown) => void;\n #message?: (message: ShardMessage) => void;\n #down?: (shardIds: readonly number[]) => void;\n\n public constructor(endpoint: IpcEndpoint = process as IpcEndpoint) {\n if (typeof endpoint.send !== \"function\")\n throw new Error(\n \"IpcTransport needs an IPC channel: run the shard in a process forked by ClusterManager.\",\n );\n this.#endpoint = endpoint;\n this.#listener = (frame) => {\n const record = frame as Record<string, unknown> | null;\n if (!record || typeof record !== \"object\") return;\n if (BUS_FRAME in record)\n this.#message?.(record[BUS_FRAME] as ShardMessage);\n else if (PEER_DOWN_FRAME in record)\n this.#down?.(record[PEER_DOWN_FRAME] as number[]);\n };\n endpoint.on(\"message\", this.#listener);\n let local = LOCAL.get(endpoint);\n if (!local) LOCAL.set(endpoint, (local = new Set()));\n local.add(this);\n }\n\n /**\n * Sends to the parent, which relays to other clusters, and delivers to the\n * other buses in this process: the parent never sends a message back to the\n * cluster it came from, so without this a shard could not reach a shard in\n * its own worker. Each bus drops what is not addressed to it.\n */\n public post(message: ShardMessage): void {\n this.#endpoint.send!({ [BUS_FRAME]: message });\n for (const peer of LOCAL.get(this.#endpoint) ?? [])\n if (peer !== this) {\n const copy = structuredClone(message);\n queueMicrotask(() => peer.#message?.(copy));\n }\n }\n\n public onMessage(listener: (message: ShardMessage) => void): void {\n this.#message = listener;\n }\n\n public onPeerDown(listener: (shardIds: readonly number[]) => void): void {\n this.#down = listener;\n }\n\n public close(): void {\n this.#endpoint.off(\"message\", this.#listener);\n LOCAL.get(this.#endpoint)?.delete(this);\n }\n}\n\n/** Picks the transport for a bus: IPC inside a forked cluster, `BroadcastChannel` otherwise. */\nexport function defaultTransport(name: string): ShardBusTransport {\n if (\n process.env.LUNIBEE_SHARD_BUS === \"ipc\" &&\n typeof process.send === \"function\"\n )\n return new IpcTransport();\n return new BroadcastChannelTransport(name);\n}\n",
|
|
24
|
+
"import { defaultTransport, type ShardBusTransport } from \"./transport.js\";\n\n/** Message envelope exchanged between Lunibee shards. */\nexport interface ShardMessage<T = unknown> {\n /** Sending shard ID. */\n source: number;\n /** Target shard ID, or null for broadcast. */\n target: number | null;\n /** Application message type. */\n type: string;\n /** Message payload. */\n data: T;\n /** Unique message ID. */\n id: string;\n /** Set on requests that expect a reply (see {@link ShardBus.request}). */\n expectsReply?: boolean;\n}\n/** A reply collected by {@link ShardBus.broadcastRequest}. */\nexport interface ShardReply<R = unknown> {\n /** Replying shard ID. */\n shardId: number;\n /** Handler result, when it succeeded. */\n result?: R;\n /** Handler error message, when it failed. */\n error?: string;\n}\n/** Internal reply envelope type. */\nexport const REPLY_TYPE = \"__lunibee:reply\";\n/** Payload carried by a reply envelope. */\nexport type ReplyPayload = {\n requestId: string;\n result?: unknown;\n error?: string;\n};\n/** Handler invoked for shard messages. @typeParam T Message payload type. */\nexport type ShardMessageHandler<T = unknown> = (\n message: ShardMessage<T>,\n) => unknown;\n/** Listener for errors thrown or rejected by shard message handlers. */\nexport type ShardBusErrorHandler = (\n error: unknown,\n message: ShardMessage,\n) => void;\n/** Options for a {@link ShardBus}. */\nexport interface ShardBusOptions {\n /**\n * How messages travel. Defaults to IPC inside a cluster forked by\n * `ClusterManager`, and to a `BroadcastChannel` otherwise.\n */\n transport?: ShardBusTransport;\n /** Most requests waiting for a reply at once (default 10,000); more are rejected. */\n maxPending?: number;\n}\n/** Longest message type or ID a bus accepts. */\nconst MAX_NAME_LENGTH = 256;\n\n/** Checks the envelope of a message that arrived from a transport, which is untrusted input. */\nfunction isShardMessage(message: unknown): message is ShardMessage {\n if (!message || typeof message !== \"object\") return false;\n const m = message as Record<string, unknown>;\n return (\n typeof m.type === \"string\" &&\n m.type.length > 0 &&\n m.type.length <= MAX_NAME_LENGTH &&\n typeof m.id === \"string\" &&\n m.id.length <= MAX_NAME_LENGTH &&\n // -1 is the parent process (see ClusterManager.request()).\n Number.isInteger(m.source) &&\n (m.source as number) >= -1 &&\n (m.target === null ||\n (Number.isInteger(m.target) && (m.target as number) >= -1))\n );\n}\n\n/** Cross-shard messaging: send, broadcast, and request/reply over a {@link ShardBusTransport}. */\nexport class ShardBus {\n /** Underlying transport. */\n readonly #transport: ShardBusTransport;\n /** Most requests waiting for a reply at once. */\n readonly #maxPending: number;\n /** Message handlers by message type. */\n readonly #handlers = new Map<string, Set<ShardMessageHandler>>();\n /** `respond()` handlers by type, mapped to the wrappers actually registered. */\n readonly #responders = new Map<string, Map<unknown, ShardMessageHandler>>();\n /** Handler error listeners. */\n readonly #errorHandlers = new Set<ShardBusErrorHandler>();\n /** Current shard ID. */\n readonly #shardId: number;\n /** Application-specific channel namespace. */\n readonly #namespace: string;\n /** BroadcastChannel name used by this bus. */\n public readonly channelName: string;\n /** Monotonic message counter. */\n #counter = 0;\n /** Reply collectors for in-flight requests, by request message ID. */\n readonly #pending = new Map<\n string,\n {\n target: number | null;\n /** Shards that already answered; a shard counts once. */\n seen: Set<number>;\n reply: (reply: ShardReply) => void;\n fail: (error: Error) => void;\n /** Ends the wait now: a request rejects, a broadcast resolves with what it has. */\n cancel: (error: Error) => void;\n }\n >();\n #closed = false;\n /** Creates a shard bus. @param shardId Shard ID. @param channelName Application-specific channel name. @throws {RangeError} If shard ID is invalid. @throws {TypeError} If channel name is empty. */\n public constructor(\n shardId: number,\n channelName: string,\n options: ShardBusOptions = {},\n ) {\n if (!Number.isInteger(shardId) || shardId < 0)\n throw new RangeError(\"Shard ID must be a non-negative integer.\");\n if (!channelName?.trim())\n throw new TypeError(\n \"An application-specific shard channel name is required.\",\n );\n this.#shardId = shardId;\n this.#namespace = channelName.trim();\n this.channelName = this.#namespace;\n this.#maxPending = options.maxPending ?? 10_000;\n this.#transport =\n options.transport ?? defaultTransport(this.channelName);\n this.#transport.onMessage((message) => this.#dispatch(message));\n this.#transport.onPeerDown?.((shardIds) => this.#peerDown(shardIds));\n }\n /** Registers a message handler. @param type Message type. @param handler Handler callback. @returns This bus. @throws {TypeError} If type or handler is invalid. */\n public on<T>(type: string, handler: ShardMessageHandler<T>): this {\n if (!type.trim() || typeof handler !== \"function\")\n throw new TypeError(\"Shard message type and handler are required.\");\n let handlers = this.#handlers.get(type);\n if (!handlers) this.#handlers.set(type, (handlers = new Set()));\n handlers.add(handler as ShardMessageHandler);\n return this;\n }\n /** Removes a message handler. @param type Message type. @param handler Handler callback. @returns This bus. */\n public off<T>(\n type: string,\n handler:\n | ShardMessageHandler<T>\n | ((data: T, message: ShardMessage<T>) => unknown),\n ): this {\n const handlers = this.#handlers.get(type);\n handlers?.delete(handler as ShardMessageHandler);\n // A `respond()` handler is registered as a wrapper; find it by the original.\n const responder = this.#responders.get(type)?.get(handler);\n if (responder) {\n handlers?.delete(responder);\n this.#responders.get(type)!.delete(handler);\n }\n return this;\n }\n /** Registers a listener for errors thrown or rejected by message handlers. Without one, handler errors are isolated and dropped. @param handler Error listener. @returns This bus. @throws {TypeError} If handler is invalid. */\n public onError(handler: ShardBusErrorHandler): this {\n if (typeof handler !== \"function\")\n throw new TypeError(\"Shard bus error handler is required.\");\n this.#errorHandlers.add(handler);\n return this;\n }\n /** Removes a handler error listener. @param handler Error listener. @returns This bus. */\n public offError(handler: ShardBusErrorHandler): this {\n this.#errorHandlers.delete(handler);\n return this;\n }\n /** Sends a targeted shard message. @param target Target shard ID. @param type Message type. @param data Payload. @returns Unique message ID. */\n public send<T>(target: number, type: string, data: T): string {\n if (!Number.isInteger(target) || target < 0)\n throw new RangeError(\n \"Shard target must be a non-negative integer.\",\n );\n return this.#publish(target, type, data);\n }\n /** Broadcasts to all other shards. @param type Message type. @param data Payload. @returns Unique message ID. */\n public broadcast<T>(type: string, data: T): string {\n return this.#publish(null, type, data);\n }\n /**\n * Registers a handler that answers requests of `type`. Its return value (or\n * thrown error) is sent back to the requesting shard. Discord.js-familiar\n * replacement for `broadcastEval` that never evaluates received code.\n * @returns This bus.\n */\n public respond<T, R>(\n type: string,\n handler: (data: T, message: ShardMessage<T>) => R | Promise<R>,\n ): this {\n const wrapper: ShardMessageHandler<T> = async (message) => {\n if (!message.expectsReply) return;\n const reply: ReplyPayload = { requestId: message.id };\n try {\n reply.result = await handler(message.data, message);\n } catch (error) {\n reply.error =\n error instanceof Error ? error.message : String(error);\n }\n this.#publish(message.source, REPLY_TYPE, reply);\n };\n let byType = this.#responders.get(type);\n if (!byType) this.#responders.set(type, (byType = new Map()));\n byType.set(handler, wrapper as ShardMessageHandler);\n return this.on<T>(type, wrapper);\n }\n /**\n * Sends a request to one shard and resolves with its handler's result.\n * @throws {Error} If the handler failed or no reply arrives in time.\n */\n public async request<R = unknown, T = unknown>(\n target: number,\n type: string,\n data: T,\n timeoutMs = 5000,\n ): Promise<R> {\n if (!Number.isInteger(target) || target < 0)\n throw new RangeError(\n \"Shard target must be a non-negative integer.\",\n );\n const [reply] = await this.#collect<R>(\n target,\n type,\n data,\n timeoutMs,\n 1,\n true,\n );\n if (!reply)\n throw new Error(\n `Shard ${target} did not reply to \"${type}\" within ${timeoutMs}ms.`,\n );\n if (reply.error !== undefined) throw new Error(reply.error);\n return reply.result as R;\n }\n /**\n * Sends a request to every other shard and collects replies until\n * `expected` have arrived or `timeoutMs` elapses. A timeout or a shard\n * going away does not reject; a closed bus, a blank type or too many\n * pending requests does.\n */\n public broadcastRequest<R = unknown, T = unknown>(\n type: string,\n data: T,\n options: { timeoutMs?: number; expected?: number } = {},\n ): Promise<ShardReply<R>[]> {\n return this.#collect<R>(\n null,\n type,\n data,\n options.timeoutMs ?? 5000,\n options.expected ?? Number.POSITIVE_INFINITY,\n );\n }\n /** Publishes a request and gathers replies. */\n #collect<R>(\n target: number | null,\n type: string,\n data: unknown,\n timeoutMs: number,\n expected: number,\n rejectOnPeerDown = false,\n ): Promise<ShardReply<R>[]> {\n const replies: ShardReply<R>[] = [];\n if (this.#closed)\n return Promise.reject(new Error(\"ShardBus is closed.\"));\n if (this.#pending.size >= this.#maxPending)\n return Promise.reject(\n new Error(\n `Too many shard requests waiting for a reply (${this.#maxPending}).`,\n ),\n );\n return new Promise((resolve, reject) => {\n let requestId = \"\";\n const finish = (): void => {\n clearTimeout(timer);\n this.#pending.delete(requestId);\n resolve(replies);\n };\n const timer = setTimeout(finish, timeoutMs);\n requestId = this.#publish(target, type, data, true);\n const fail = (error: Error): void => {\n if (!rejectOnPeerDown) return;\n clearTimeout(timer);\n this.#pending.delete(requestId);\n reject(error);\n };\n this.#pending.set(requestId, {\n target,\n seen: new Set(),\n reply: (reply) => {\n replies.push(reply as ShardReply<R>);\n if (replies.length >= expected) finish();\n },\n fail,\n cancel: (error) => (rejectOnPeerDown ? fail(error) : finish()),\n });\n });\n }\n /** Closes the transport. @returns Nothing. */\n public close(): void {\n if (this.#closed) return;\n this.#closed = true;\n // Nothing can answer once the transport is gone, so end the waits now\n // instead of leaving their timers to fire later.\n for (const pending of [...this.#pending.values()])\n pending.cancel(new Error(\"ShardBus is closed.\"));\n this.#pending.clear();\n this.#transport.close();\n this.#handlers.clear();\n this.#errorHandlers.clear();\n }\n /** Publishes a message. @param target Target shard ID or null. @param type Message type. @param data Payload. @returns Unique message ID. */\n #publish<T>(\n target: number | null,\n type: string,\n data: T,\n expectsReply = false,\n ): string {\n if (!type.trim())\n throw new TypeError(\"Shard message type is required.\");\n if (this.#closed) throw new Error(\"ShardBus is closed.\");\n const id = `${this.#namespace}:${this.#shardId}:${++this.#counter}`;\n this.#transport.post({\n source: this.#shardId,\n target,\n type,\n data,\n id,\n ...(expectsReply ? { expectsReply } : {}),\n } satisfies ShardMessage<T>);\n return id;\n }\n /** Routes an incoming message to matching handlers. @param message Message envelope. @returns Nothing. */\n #dispatch(message: ShardMessage): void {\n if (this.#closed || !isShardMessage(message)) return;\n if (\n message.source === this.#shardId ||\n (message.target !== null && message.target !== this.#shardId)\n )\n return;\n if (message.type === REPLY_TYPE) {\n const reply = message.data as ReplyPayload | null;\n if (\n !reply ||\n typeof reply !== \"object\" ||\n typeof reply.requestId !== \"string\"\n )\n return;\n const pending = this.#pending.get(reply.requestId);\n // Late, unknown and duplicate answers are dropped, and so is an\n // answer from a shard other than the one that was asked: nobody\n // can resolve a request by guessing its ID.\n if (\n !pending ||\n pending.seen.has(message.source) ||\n (pending.target !== null && pending.target !== message.source)\n )\n return;\n pending.seen.add(message.source);\n pending.reply({\n shardId: message.source,\n ...(reply.error !== undefined\n ? { error: String(reply.error) }\n : { result: reply.result }),\n });\n return;\n }\n for (const handler of this.#handlers.get(message.type) ?? []) {\n try {\n const result = handler(message);\n if (\n result &&\n typeof (result as PromiseLike<unknown>).then === \"function\"\n )\n void Promise.resolve(result).catch((error) =>\n this.#reportError(error, message),\n );\n } catch (error) {\n this.#reportError(error, message);\n }\n }\n }\n /** Fails requests waiting on shards that no longer exist. */\n #peerDown(shardIds: readonly number[]): void {\n const down = new Set(shardIds);\n for (const [id, pending] of this.#pending)\n if (pending.target !== null && down.has(pending.target)) {\n pending.fail(\n new Error(\n `Shard ${pending.target} went away before it replied.`,\n ),\n );\n this.#pending.delete(id);\n }\n }\n /** Forwards a handler error to error listeners, isolating listener failures. Without a listener it is raised as a process warning, never dropped. @param error Handler error. @param message Message being handled. @returns Nothing. */\n #reportError(error: unknown, message: ShardMessage): void {\n if (this.#errorHandlers.size === 0) {\n process.emitWarning(\n error instanceof Error ? error : new Error(String(error)),\n { code: \"LUNIBEE_SHARD_BUS_ERROR\" },\n );\n return;\n }\n for (const handler of this.#errorHandlers) {\n try {\n handler(error, message);\n } catch {\n /* Error listener failures are isolated. */\n }\n }\n }\n}\n",
|
|
25
|
+
"/** How a crashed cluster is restarted. The defaults restart every crash after a fixed delay. */\nexport interface SupervisorOptions {\n /** Delay before the first restart, in milliseconds (default 5000). */\n restartDelay?: number;\n /** Each further crash in the window multiplies the delay by this (default 1: a fixed delay). */\n backoff?: number;\n /** The longest delay, jitter included (default 5 minutes). */\n maxDelay?: number;\n /** Random spread added to each delay, as a fraction of it (0-1, default 0). */\n jitter?: number;\n /** Give up after this many restarts inside `window` (default unlimited). */\n maxRestarts?: number;\n /** The time over which restarts are counted, and after which a stable cluster is forgiven (default 60,000). */\n window?: number;\n}\n\n/**\n * Decides when, and whether, a crashed cluster comes back. It only does\n * arithmetic on timestamps (pass `now` in tests), so `ClusterManager` stays in\n * charge of forking.\n */\nexport class ShardSupervisor {\n readonly #options: Required<SupervisorOptions>;\n readonly #restarts = new Map<number, number[]>();\n readonly #random: () => number;\n\n public constructor(\n options: SupervisorOptions = {},\n random: () => number = Math.random,\n ) {\n this.#options = {\n restartDelay: options.restartDelay ?? 5000,\n backoff: options.backoff ?? 1,\n maxDelay: options.maxDelay ?? 300_000,\n jitter: options.jitter ?? 0,\n maxRestarts: options.maxRestarts ?? Number.POSITIVE_INFINITY,\n window: options.window ?? 60_000,\n };\n const { restartDelay, backoff, jitter, maxDelay, window, maxRestarts } =\n this.#options;\n if (!(restartDelay >= 0) || !(maxDelay >= 0) || !(window > 0))\n throw new RangeError(\n \"restartDelay and maxDelay cannot be negative, and window must be positive.\",\n );\n if (!(backoff >= 1))\n throw new RangeError(\"backoff must be at least 1.\");\n if (!(jitter >= 0 && jitter <= 1))\n throw new RangeError(\"jitter must be between 0 and 1.\");\n if (!(maxRestarts >= 0))\n throw new RangeError(\"maxRestarts cannot be negative.\");\n this.#random = random;\n }\n\n /**\n * A cluster exited. @returns How many milliseconds to wait before\n * restarting it, or `null` to give up because it crashed more than\n * `maxRestarts` times inside `window`.\n */\n public next(id: number, now = Date.now()): number | null {\n const { window, maxRestarts, restartDelay, backoff, maxDelay, jitter } =\n this.#options;\n const recent = (this.#restarts.get(id) ?? []).filter(\n (at) => now - at < window,\n );\n if (recent.length >= maxRestarts) {\n this.#restarts.delete(id);\n return null;\n }\n recent.push(now);\n this.#restarts.set(id, recent);\n const base = Math.min(\n maxDelay,\n restartDelay * backoff ** (recent.length - 1),\n );\n // The cap applies after jitter: `maxDelay` is the longest wait, always.\n return Math.min(\n maxDelay,\n Math.round(base * (1 + jitter * this.#random())),\n );\n }\n\n /** Forgets a cluster's crashes, e.g. after a deliberate respawn. */\n public reset(id?: number): void {\n if (id === undefined) this.#restarts.clear();\n else this.#restarts.delete(id);\n }\n}\n",
|
|
26
|
+
"import {\n assertGuildsPerShard,\n fetchGatewayBot,\n resolveShardCount,\n type GatewayBotInfo,\n} from \"./gateway-bot.js\";\nimport { fork, type ChildProcess } from \"node:child_process\";\nimport { cpus } from \"node:os\";\nimport { REPLY_TYPE, type ReplyPayload, type ShardReply } from \"./bus.js\";\nimport {\n allocateShards,\n assertCompleteAllocation,\n isClusterWorker,\n validateShardIds,\n WORKER_MARKER,\n} from \"./context.js\";\nimport type {\n ClusterHealth,\n ClusterManagerHealth,\n ClusterState,\n ShardHealth,\n WorkerMetrics,\n WorkerReport,\n} from \"./health.js\";\nimport { IdentifyCoordinator, type IdentifyBudget } from \"./identify.js\";\nimport {\n CLUSTER_FRAME,\n PARENT_ID,\n REQUEST_ASSIGN,\n REQUEST_HEALTH,\n parseChildFrame,\n type ChildFrame,\n} from \"./protocol.js\";\nimport { RemoteHosts, type HostStatus, type RemoteOptions } from \"./remote.js\";\nimport { RequestTracker } from \"./requests.js\";\nimport { ShardSupervisor, type SupervisorOptions } from \"./supervisor.js\";\nimport { BUS_FRAME, PEER_DOWN_FRAME } from \"./transport.js\";\n\n/** Configuration for managing multiple shard clusters. */\nexport interface ClusterManagerOptions {\n /** Discord bot token. */\n token: string;\n /** Path to the worker script to execute for each cluster. */\n script: string;\n /** Total number of shards. Use \"auto\" to request Discord's recommended count. */\n shardCount?: number | \"auto\";\n /**\n * With `shardCount: \"auto\"`, size the deployment by guilds instead of\n * Discord's recommendation: `ceil(guilds / guildsPerShard)` shards, at\n * least one. 1 to 2500 (Discord's limit); Discord recommends about 1000.\n * Guilds are assigned to shards by their ID, so this sets the average,\n * not an exact split.\n */\n guildsPerShard?: number;\n /** The bot's guild count for `guildsPerShard`, instead of asking Discord for it. */\n guildCount?: number;\n /**\n * The shards this deployment runs, out of `shardCount`. Use it to split one\n * bot across hosts: each host runs a `ClusterManager` with its own range.\n * Defaults to every shard.\n */\n shardIds?: number[];\n /** Number of clusters to spawn. Defaults to the number of logical CPUs. Not combinable with `shardsPerCluster`. */\n clusterCount?: number;\n /** Shards per cluster; the cluster count follows. Not combinable with `clusterCount`. */\n shardsPerCluster?: number;\n /**\n * Discord's `max_concurrency` for IDENTIFY pacing. Fetched from `/gateway/bot`\n * when `shardCount` is \"auto\" (this value may only lower it); otherwise defaults to 1.\n */\n maxConcurrency?: number;\n /** Ask `/gateway/bot` for `max_concurrency` and the session budget even with a fixed `shardCount`. */\n fetchLimits?: boolean;\n /** IDENTIFY coordination across clusters. On by default. */\n identify?: {\n /** Set `false` to let every cluster pace its own IDENTIFYs. */\n coordinate?: boolean;\n /** Milliseconds between IDENTIFYs on one key. Default 5000. */\n interval?: number;\n /** A slot a worker never releases is released after this many ms. Default 60,000. */\n holdTimeout?: number;\n };\n /** Milliseconds between forking two clusters, to spread the CPU cost of starting. Default 500. IDENTIFY pacing does not depend on it. */\n spawnInterval?: number;\n /**\n * A cluster that has not reported ready this many ms after it was forked is\n * killed and restarted like a crash. Off by default, because a worker that\n * never reports ready (no `ShardManager`, no `ClusterWorker`) would be killed.\n */\n startTimeout?: number;\n /** Make `spawn()` wait until every cluster reported ready. Default false. */\n waitForReady?: boolean;\n /**\n * When a cluster crashes more often than `supervisor.maxRestarts` allows,\n * move its shards to the healthiest ready cluster instead of leaving them\n * offline. Default false.\n */\n reassignOnGiveUp?: boolean;\n /**\n * Largest shard-bus message, request payload or worker reply the manager\n * handles, in UTF-8 bytes (a string as it is, anything else as its JSON text). Bigger ones from a worker are\n * dropped before they are relayed (an `error` event says so; a request\n * waiting for the answer times out as usual), and bigger ones passed to\n * `request()`, `broadcast()` or `requestCluster()` throw a `RangeError`.\n * Default 8 MiB, the same limit as the host link.\n */\n maxMessageBytes?: number;\n /** Default timeout for `request()`, `requestCluster()` and `broadcast()`. Default 5000. */\n requestTimeout?: number;\n /** Most requests waiting for answers at once. Default 10,000. */\n maxPendingRequests?: number;\n /**\n * Accept hosts on other machines. The coordinator listens for\n * `ClusterHost` agents, places clusters on them and supervises those\n * workers like local ones. See {@link RemoteOptions}.\n */\n remote?: RemoteOptions;\n /** Starts a worker process. Defaults to `child_process.fork()`; replace it for tests or custom launchers. */\n fork?: (\n script: string,\n env: Record<string, string | undefined>,\n ) => ChildProcess;\n /** Interval in milliseconds to automatically check for recommended shard count and re-scale if needed. */\n autoScaleInterval?: number;\n /** Optional handler invoked when a background auto-scale check fails. Receives the thrown error. */\n onAutoScaleError?: (error: Error) => void;\n /** Grace period in milliseconds to await a cluster's clean exit after SIGTERM before force-killing. Defaults to 5000. */\n shutdownTimeout?: number;\n /**\n * Whether a cluster that exits unexpectedly is re-forked with the same\n * shard assignment. Defaults to true: without it a crashed child leaves\n * its shards permanently offline and nothing reports it.\n */\n restartOnExit?: boolean;\n /** Delay in milliseconds before re-forking a crashed cluster. Defaults to 5000. */\n restartDelay?: number;\n /**\n * Backoff, jitter and a give-up limit for restarts. `restartDelay` is its\n * first delay unless it sets its own. The default restarts every crash\n * after a fixed delay, as before.\n */\n supervisor?: Omit<SupervisorOptions, \"restartDelay\"> & {\n restartDelay?: number;\n };\n /** Called when a cluster crashed more often than `supervisor.maxRestarts` allows and will not be restarted. */\n onGiveUp?(cluster: ClusterInfo): void;\n /** Called whenever a cluster process exits, before any restart. */\n onClusterExit?(\n cluster: ClusterInfo,\n code: number | null,\n signal: NodeJS.Signals | null,\n ): void;\n}\n\n/** Information about a cluster. The object is live: it is updated as the cluster changes. */\nexport interface ClusterInfo {\n /** Cluster identifier. */\n id: number;\n /** The current child process (the last one while a restart is pending). For a cluster on another host this is a proxy; `pid` is the pid on that host. */\n process: ChildProcess;\n /** Where it runs: `\"local\"` or a host ID. */\n host: string;\n /** The shard IDs managed by this cluster. */\n shards: number[];\n /** Where the cluster is in its life. */\n state: ClusterState;\n /** How many times it was restarted after a crash. */\n restarts: number;\n /** When the current process was forked (epoch ms). */\n startedAt: number;\n /** When the current process reported ready. */\n readyAt?: number;\n /** When any process of this cluster last reported ready. */\n lastReadyAt?: number;\n /** The worker's last health report. */\n report?: WorkerReport;\n}\n\n/** What `ClusterManager` emits. */\nexport interface ClusterManagerEvents {\n /** A cluster process was forked. */\n clusterSpawn: [cluster: ClusterInfo];\n /** A cluster's shards all finished their handshake. */\n clusterReady: [cluster: ClusterInfo];\n /** Every cluster is ready. Emitted again after a recovery. */\n ready: [];\n /** A cluster process exited. `expected` is true for a deliberate shutdown. */\n clusterExit: [\n cluster: ClusterInfo,\n code: number | null,\n signal: NodeJS.Signals | null,\n expected: boolean,\n ];\n /** A restart was scheduled after a crash. */\n clusterRestart: [\n cluster: ClusterInfo,\n info: { delay: number; attempt: number },\n ];\n /** A cluster keeps crashing: three or more restarts are pending or recent. */\n clusterUnstable: [\n cluster: ClusterInfo,\n info: { attempt: number; delay: number },\n ];\n /** A cluster did not report ready within `startTimeout` and was killed. */\n clusterStartTimeout: [cluster: ClusterInfo];\n /** A cluster crashed too often and will not be restarted. */\n clusterGiveUp: [cluster: ClusterInfo];\n /** A failed cluster's shards were moved to another cluster. */\n shardsReassigned: [info: { shardIds: number[]; from: number; to: number }];\n /** A host joined the deployment. */\n hostJoin: [hostId: string];\n /** A host left or was lost. Its clusters are treated as crashed. */\n hostLeave: [hostId: string, reason: string];\n /** An IDENTIFY is waiting for the session-start budget to reset. */\n identifyWait: [info: { shardId: number; waitMs: number }];\n /** Something went wrong that did not stop the deployment (a bad frame, a failed recovery). */\n error: [error: Error];\n}\n\n/** Options for a request to a shard or cluster. */\nexport interface RequestOptions {\n /** Milliseconds to wait. Defaults to `requestTimeout`. */\n timeoutMs?: number;\n /** Cancels the wait. */\n signal?: AbortSignal;\n}\n\n/** What a broadcast collected. It never rejects for a slow or failed shard. */\nexport interface BroadcastResult<R = unknown> {\n /** One reply per answering shard: its result, or the error its handler threw. */\n replies: ShardReply<R>[];\n /** Shards that did not answer: down, restarting, or too slow. */\n missing: number[];\n}\n\ntype LiveState = \"starting\" | \"ready\";\n\n/** Default for `maxMessageBytes`. */\nconst DEFAULT_MAX_MESSAGE_BYTES = 8 * 1024 * 1024;\n\n/**\n * The size of a payload in UTF-8 bytes: a string as it is, anything else as\n * its JSON text. `Infinity` for one that cannot be serialized (a cycle).\n */\nfunction payloadSize(value: unknown): number {\n if (value === undefined || value === null) return 0;\n if (typeof value === \"string\") return Buffer.byteLength(value, \"utf8\");\n if (typeof value !== \"object\") return String(value).length;\n try {\n const text = JSON.stringify(value);\n return text === undefined ? 0 : Buffer.byteLength(text, \"utf8\");\n } catch {\n return Number.POSITIVE_INFINITY;\n }\n}\n\nconst isLive = (state: ClusterState): state is LiveState =>\n state === \"starting\" || state === \"ready\";\n\n/**\n * Orchestrates cluster processes: forks one worker per cluster, gives each a\n * fixed set of shards, restarts crashed clusters with backoff, hands out\n * IDENTIFY slots across all of them, routes shard-bus messages to the cluster\n * that owns the target shard, and answers requests, broadcasts and health\n * queries for the whole deployment.\n *\n * Ownership: the `ClusterManager` decides which cluster owns which shard and\n * keeps processes alive; the `ShardManager` inside each worker runs those\n * shards' Gateways. A shard is never owned by two clusters at once.\n *\n * Works under Node.js and Bun (`child_process.fork()` with IPC).\n */\nexport class ClusterManager {\n /** Live clusters (starting or ready) indexed by cluster ID. A crashed cluster leaves this map until it is restarted. */\n public readonly clusters = new Map<number, ClusterInfo>();\n /** Total number of shards in the deployment. */\n public shardCount = 0;\n\n readonly #options: ClusterManagerOptions;\n /** Whether the manager was created in auto shard-count mode. Preserved across respawns so auto-scaling keeps running. */\n readonly #auto: boolean;\n #autoScaleTimer?: ReturnType<typeof setInterval>;\n /** Every cluster this deployment knows, whatever its state. */\n readonly #infos = new Map<number, ClusterInfo>();\n /** Which cluster owns each shard. */\n readonly #owner = new Map<number, number>();\n #startPromise?: Promise<void>;\n #stopPromise?: Promise<void>;\n #wasStarted = false;\n /** Set while a deliberate shutdown is in progress so exits are not restarted. */\n #stopping = false;\n /** Pending restart timers, kept so shutdown can cancel them. */\n readonly #restartTimers = new Set<ReturnType<typeof setTimeout>>();\n readonly #startTimers = new Map<number, ReturnType<typeof setTimeout>>();\n readonly #pauses = new Set<() => void>();\n readonly #readyWaiters = new Set<{\n resolve: () => void;\n reject: (error: Error) => void;\n timer?: ReturnType<typeof setTimeout>;\n }>();\n readonly #supervisor: ShardSupervisor;\n readonly #tracker: RequestTracker;\n #coordinator?: IdentifyCoordinator;\n #allReady = false;\n #clusterCount = 0;\n /** Shards of each cluster that are waiting for an IDENTIFY slot. */\n readonly #identifyWaiting = new Map<number, Set<number>>();\n readonly #remote?: RemoteHosts;\n /** Which host runs each cluster. Fixed for the life of a spawn, so a cluster returns to its host. */\n readonly #hostOf = new Map<number, string>();\n #lastWait = \"\";\n readonly #listeners = new Map<\n keyof ClusterManagerEvents,\n Set<(...args: never[]) => void>\n >();\n\n /** Creates a cluster manager. @param options Clustering configuration. @throws {TypeError} If token or script is missing. @throws {RangeError} If a numeric option is invalid. */\n public constructor(options: ClusterManagerOptions) {\n if (!options.token?.trim())\n throw new TypeError(\"A Discord bot token is required.\");\n if (!options.script?.trim())\n throw new TypeError(\"A script path is required for clustering.\");\n if (\n options.autoScaleInterval !== undefined &&\n (!Number.isInteger(options.autoScaleInterval) ||\n options.autoScaleInterval < 1000)\n ) {\n throw new RangeError(\n \"autoScaleInterval must be an integer of at least 1000 milliseconds.\",\n );\n }\n if (\n options.clusterCount !== undefined &&\n options.shardsPerCluster !== undefined\n )\n throw new RangeError(\n \"Set either clusterCount or shardsPerCluster, not both.\",\n );\n for (const [name, value] of [\n [\"clusterCount\", options.clusterCount],\n [\"shardsPerCluster\", options.shardsPerCluster],\n [\"maxConcurrency\", options.maxConcurrency],\n ] as const)\n if (value !== undefined && (!Number.isInteger(value) || value < 1))\n throw new RangeError(`${name} must be a positive integer.`);\n if (\n options.maxMessageBytes !== undefined &&\n (!Number.isInteger(options.maxMessageBytes) ||\n options.maxMessageBytes < 1)\n )\n throw new RangeError(\"maxMessageBytes must be a positive integer.\");\n for (const [name, value] of [\n [\"spawnInterval\", options.spawnInterval],\n [\"startTimeout\", options.startTimeout],\n [\"requestTimeout\", options.requestTimeout],\n ] as const)\n if (value !== undefined && !(value >= 0))\n throw new RangeError(`${name} cannot be negative.`);\n if (options.guildsPerShard !== undefined) {\n assertGuildsPerShard(options.guildsPerShard);\n if (typeof options.shardCount === \"number\")\n throw new RangeError(\n 'guildsPerShard needs shardCount: \"auto\".',\n );\n }\n if (options.shardIds && typeof options.shardCount === \"number\")\n validateShardIds(options.shardIds, options.shardCount);\n\n this.#auto = options.shardCount === \"auto\";\n this.#options = { ...options };\n this.#supervisor = new ShardSupervisor({\n restartDelay: options.restartDelay,\n ...options.supervisor,\n });\n this.#tracker = new RequestTracker(\n \"parent\",\n options.maxPendingRequests,\n );\n if (options.remote)\n this.#remote = new RemoteHosts(options.remote, {\n join: (id) => this.#emit(\"hostJoin\", id),\n leave: (id, reason) => this.#emit(\"hostLeave\", id, reason),\n error: (error) => this.#emit(\"error\", error),\n });\n }\n\n /** Listens for a deployment event. @returns This manager. */\n public on<E extends keyof ClusterManagerEvents>(\n event: E,\n listener: (...args: ClusterManagerEvents[E]) => void,\n ): this {\n let set = this.#listeners.get(event);\n if (!set) this.#listeners.set(event, (set = new Set()));\n set.add(listener as never);\n return this;\n }\n\n /** Stops listening for a deployment event. @returns This manager. */\n public off<E extends keyof ClusterManagerEvents>(\n event: E,\n listener: (...args: ClusterManagerEvents[E]) => void,\n ): this {\n this.#listeners.get(event)?.delete(listener as never);\n return this;\n }\n\n #emit<E extends keyof ClusterManagerEvents>(\n event: E,\n ...args: ClusterManagerEvents[E]\n ): void {\n for (const listener of this.#listeners.get(event) ?? [])\n try {\n (listener as (...a: ClusterManagerEvents[E]) => void)(...args);\n } catch {\n // A faulty listener must not break supervision.\n }\n }\n\n /** Retrieves Discord's recommended shard count. @returns Recommended shard count. @throws {Error} If discovery fails or returns invalid data. */\n public async fetchRecommendedShardCount(): Promise<number> {\n return resolveShardCount(\n this.#options.token,\n await fetchGatewayBot(this.#options.token),\n this.#options,\n );\n }\n\n /** The cluster that owns a shard, or `undefined` when the shard is not part of this deployment. */\n public clusterForShard(shardId: number): ClusterInfo | undefined {\n const id = this.#owner.get(shardId);\n return id === undefined ? undefined : this.#infos.get(id);\n }\n\n /** Which cluster owns each shard. A copy: change ownership through recovery, not by writing to it. */\n public assignments(): ReadonlyMap<number, number> {\n return new Map(this.#owner);\n }\n\n /**\n * Spawns all clusters. Calling it again while clusters run, or while the\n * first call is still starting them, returns the same promise.\n * @throws {Error} If called from a cluster worker (a worker must not start the fleet again).\n */\n public spawn(): Promise<void> {\n if (isClusterWorker())\n return Promise.reject(\n new Error(\n \"A cluster worker cannot start a ClusterManager: it would fork the whole fleet again.\",\n ),\n );\n if (this.#stopPromise)\n return this.#stopPromise.then(() => this.spawn());\n if (!this.#startPromise) {\n const attempt = this.#start();\n this.#startPromise = attempt;\n attempt.catch(() => {\n if (this.#startPromise === attempt)\n this.#startPromise = undefined;\n });\n }\n return this.#startPromise;\n }\n\n async #resolveGateway(): Promise<{\n count: number;\n maxConcurrency: number;\n budget?: IdentifyBudget;\n }> {\n const { shardCount, maxConcurrency, fetchLimits } = this.#options;\n let info: GatewayBotInfo | undefined;\n let count: number | undefined;\n if (shardCount === \"auto\" || shardCount === undefined) {\n info = await fetchGatewayBot(this.#options.token);\n count = await resolveShardCount(\n this.#options.token,\n info,\n this.#options,\n );\n } else if (fetchLimits)\n try {\n info = await fetchGatewayBot(this.#options.token);\n } catch (error) {\n this.#emit(\n \"error\",\n error instanceof Error ? error : new Error(String(error)),\n );\n }\n const limit = info?.sessionStartLimit;\n return {\n count: count ?? (shardCount as number),\n maxConcurrency: Math.min(\n maxConcurrency ?? limit?.maxConcurrency ?? 1,\n limit?.maxConcurrency ?? Number.POSITIVE_INFINITY,\n ),\n ...(limit\n ? {\n budget: {\n total: limit.total,\n remaining: limit.remaining,\n resetAfter: limit.resetAfter,\n },\n }\n : {}),\n };\n }\n\n async #start(): Promise<void> {\n const { count, maxConcurrency, budget } = await this.#resolveGateway();\n if (this.#stopping) return;\n\n const ids =\n this.#options.shardIds ??\n Array.from({ length: count }, (_, i) => i);\n validateShardIds(ids, count);\n const clusterCount =\n this.#options.clusterCount ??\n (this.#options.shardsPerCluster\n ? Math.ceil(ids.length / this.#options.shardsPerCluster)\n : cpus().length);\n const chunks = allocateShards(ids, clusterCount);\n assertCompleteAllocation(chunks, ids);\n\n const placement = await this.#placeHosts().catch((error: unknown) => {\n // A shutdown that interrupts the wait is not a failure of the start.\n if (this.#stopping) return undefined;\n throw error;\n });\n if (!placement || this.#stopping) return;\n\n this.shardCount = count;\n this.#wasStarted = true;\n this.#owner.clear();\n this.#infos.clear();\n const identify = this.#options.identify;\n this.#coordinator =\n identify?.coordinate === false\n ? undefined\n : new IdentifyCoordinator({\n maxConcurrency,\n interval: identify?.interval,\n holdTimeout: identify?.holdTimeout,\n budget,\n onBudgetWait: (wait) => {\n const key = `${wait.shardId}:${Math.round(wait.waitMs / 1000)}`;\n if (key === this.#lastWait) return;\n this.#lastWait = key;\n this.#emit(\"identifyWait\", wait);\n },\n });\n\n const active = chunks\n .map((shards, id) => ({ id, shards }))\n .filter((chunk) => chunk.shards.length > 0);\n this.#clusterCount = active.length;\n this.#hostOf.clear();\n for (const chunk of active)\n this.#hostOf.set(chunk.id, placement[chunk.id % placement.length]!);\n for (const [index, chunk] of active.entries()) {\n if (this.#stopping) return;\n for (const shard of chunk.shards) this.#owner.set(shard, chunk.id);\n this.#launch(chunk.id, chunk.shards);\n // Spread the CPU cost of starting processes. IDENTIFY pacing is\n // the coordinator's job and does not depend on this.\n const gap = this.#options.spawnInterval ?? 500;\n if (index < active.length - 1 && gap > 0) await this.#pause(gap);\n }\n if (this.#stopping) return;\n\n if (this.#options.waitForReady) await this.ready();\n if (\n this.#options.autoScaleInterval &&\n this.#options.autoScaleInterval > 0\n ) {\n this.#autoScaleTimer = setInterval(() => {\n void this.checkAutoScale();\n }, this.#options.autoScaleInterval);\n }\n }\n\n /** Opens the listener for hosts and waits for them. @returns The places clusters run, in placement order. */\n async #placeHosts(): Promise<string[]> {\n const remote = this.#remote;\n const options = this.#options.remote;\n if (!remote || !options) return [\"local\"];\n remote.start();\n try {\n await remote.waitFor(\n options.hosts,\n options.minHosts ?? 1,\n options.joinTimeout ?? 60_000,\n );\n } catch (error) {\n remote.stop();\n throw error;\n }\n const ids = options.hosts ?? remote.connected();\n return options.local ? [\"local\", ...ids] : ids;\n }\n\n /** The port hosts connect to, once the listener is open (useful with `remote.port: 0`). */\n public get remotePort(): number | undefined {\n return this.#remote?.port;\n }\n\n /** The hosts of this deployment: connected or not, and the clusters on each. Empty without `remote`. */\n public hosts(): HostStatus[] {\n return this.#remote?.status(this.#options.remote?.hosts) ?? [];\n }\n\n /** Waits `ms`, or less when a shutdown starts. */\n #pause(ms: number): Promise<void> {\n return new Promise((resolve) => {\n const wake = (): void => {\n clearTimeout(timer);\n this.#pauses.delete(wake);\n resolve();\n };\n const timer = setTimeout(wake, ms);\n this.#pauses.add(wake);\n });\n }\n\n /**\n * Forks one cluster child and supervises its exit.\n *\n * A child that dies (crash, OOM kill, uncaught rejection) takes its shards\n * offline for good, so unless the exit came from a deliberate shutdown the\n * same shard assignment is re-forked after the supervisor's delay.\n */\n #launch(id: number, shards: number[]): ClusterInfo {\n // Only these cross to a remote host; the rest of a worker's environment is the host's own.\n const contract: Record<string, string> = {\n SHARD_LIST: shards.join(\",\"),\n SHARD_COUNT: this.shardCount.toString(),\n CLUSTER_ID: id.toString(),\n CLUSTER_COUNT: this.#clusterCount.toString(),\n LUNIBEE_SHARD_BUS: \"ipc\",\n [WORKER_MARKER]: \"1\",\n LUNIBEE_IDENTIFY: this.#coordinator ? \"coordinated\" : \"local\",\n };\n const host = this.#hostOf.get(id) ?? \"local\";\n const env: Record<string, string | undefined> = {\n ...process.env,\n ...contract,\n };\n const child =\n host !== \"local\" && this.#remote\n ? this.#remote.createChild(host, id, contract)\n : this.#options.fork\n ? this.#options.fork(this.#options.script, env)\n : fork(this.#options.script, [], {\n env: env as NodeJS.ProcessEnv,\n });\n const known = this.#infos.get(id);\n const info: ClusterInfo = known ?? {\n id,\n host,\n process: child,\n shards,\n state: \"starting\",\n restarts: 0,\n startedAt: Date.now(),\n };\n info.process = child;\n info.state = \"starting\";\n info.startedAt = Date.now();\n info.readyAt = undefined;\n this.#infos.set(id, info);\n this.clusters.set(id, info);\n this.#allReady = false;\n\n // `send()` can fail after it returned: a worker that exits while a frame\n // is being written emits `error` (EPIPE, \"Channel closed\") later. With no\n // listener that is an uncaught exception, and it would end the manager.\n // The worker's `exit` event does the actual recovery.\n child.on(\"error\", (error: Error) =>\n this.#emit(\n \"error\",\n new Error(`Cluster ${id} process error: ${error.message}`, {\n cause: error,\n }),\n ),\n );\n child.on(\"message\", (frame) => this.#onFrame(info, child, frame));\n child.once(\"exit\", (code, signal) =>\n this.#onExit(info, child, code, signal),\n );\n const timeout = this.#options.startTimeout;\n if (timeout) {\n const timer = setTimeout(() => {\n this.#startTimers.delete(id);\n if (info.process !== child || info.state !== \"starting\") return;\n this.#emit(\"clusterStartTimeout\", info);\n try {\n child.kill(\"SIGKILL\");\n } catch {\n // Already gone; the exit handler takes over.\n }\n }, timeout);\n (timer as { unref?: () => void }).unref?.();\n this.#startTimers.set(id, timer);\n }\n this.#emit(\"clusterSpawn\", info);\n return info;\n }\n\n #onExit(\n info: ClusterInfo,\n child: ChildProcess,\n code: number | null,\n signal: NodeJS.Signals | null,\n ): void {\n // Only act on the process still registered as this cluster: a\n // restart or respawn may already have replaced it.\n if (info.process !== child) return;\n child.removeAllListeners(\"message\");\n this.#clearStartTimer(info.id);\n this.#coordinator?.drop(info.id, `Cluster ${info.id} exited.`);\n this.#identifyWaiting.delete(info.id);\n this.#tracker.failOwner(info.id, `Cluster ${info.id} exited.`);\n this.clusters.delete(info.id);\n this.#allReady = false;\n try {\n this.#options.onClusterExit?.(info, code, signal);\n } catch {\n // A faulty consumer callback must not break supervision.\n }\n // A deliberate shutdown (graceful or `killAll()`) is never restarted.\n const expected =\n this.#stopping ||\n info.state === \"stopping\" ||\n info.state === \"stopped\";\n this.#emit(\"clusterExit\", info, code, signal, expected);\n if (expected) {\n info.state = \"stopped\";\n return;\n }\n this.#announceDown(info.id, info.shards);\n if (this.#options.restartOnExit === false) {\n info.state = \"failed\";\n this.#rejectReady(info, \"restartOnExit is false\");\n return;\n }\n const delay = this.#supervisor.next(info.id);\n if (delay === null) {\n info.state = \"failed\";\n try {\n this.#options.onGiveUp?.(info);\n } catch {\n // A faulty consumer callback must not break supervision.\n }\n this.#emit(\"clusterGiveUp\", info);\n if (this.#options.reassignOnGiveUp) void this.#reassign(info);\n else this.#rejectReady(info, \"restarts exhausted\");\n return;\n }\n info.state = \"restarting\";\n info.restarts++;\n this.#emit(\"clusterRestart\", info, { delay, attempt: info.restarts });\n if (info.restarts >= 3)\n this.#emit(\"clusterUnstable\", info, {\n attempt: info.restarts,\n delay,\n });\n const timer = setTimeout(() => {\n this.#restartTimers.delete(timer);\n if (this.#stopping || info.state !== \"restarting\") return;\n this.#launch(info.id, info.shards);\n }, delay);\n // Do not hold the event loop open purely for a pending restart.\n (timer as { unref?: () => void }).unref?.();\n this.#restartTimers.add(timer);\n }\n\n /**\n * Moves a failed cluster's shards to the ready cluster that owns the\n * fewest. Ownership changes only after that worker confirmed it started\n * them, and the failed cluster's process is already dead, so no shard\n * ever has two owners.\n */\n async #reassign(failed: ClusterInfo): Promise<void> {\n const shardIds = [...failed.shards];\n const target = [...this.#infos.values()]\n .filter((cluster) => cluster.state === \"ready\")\n .sort((a, b) => a.shards.length - b.shards.length)[0];\n if (!target) {\n this.#emit(\n \"error\",\n new Error(\n `No ready cluster can take over shards [${shardIds}] of cluster ${failed.id}.`,\n ),\n );\n this.#rejectReady(failed, \"no ready cluster to take them\");\n return;\n }\n try {\n await this.requestCluster(\n target.id,\n REQUEST_ASSIGN,\n { shardIds },\n { timeoutMs: this.#options.startTimeout ?? 120_000 },\n );\n } catch (error) {\n this.#emit(\n \"error\",\n new Error(\n `Cluster ${target.id} could not take over shards [${shardIds}]: ${error instanceof Error ? error.message : String(error)}`,\n ),\n );\n this.#rejectReady(failed, \"reassignment failed\");\n return;\n }\n if (this.#stopping || failed.state !== \"failed\") return;\n for (const shard of shardIds) this.#owner.set(shard, target.id);\n target.shards.push(...shardIds);\n failed.shards = [];\n this.#emit(\"shardsReassigned\", {\n shardIds,\n from: failed.id,\n to: target.id,\n });\n this.#checkReady();\n }\n\n #clearStartTimer(id: number): void {\n const timer = this.#startTimers.get(id);\n if (timer) clearTimeout(timer);\n this.#startTimers.delete(id);\n }\n\n #sendFrame(cluster: ClusterInfo, frame: unknown): boolean {\n const child = cluster.process;\n if (\n !isLive(cluster.state) ||\n typeof child.send !== \"function\" ||\n !child.connected\n )\n return false;\n try {\n child.send(frame as never);\n return true;\n } catch {\n // The child is exiting; its own exit handler takes over.\n return false;\n }\n }\n\n /** Tells the surviving clusters that these shards are gone, so requests to them fail now instead of timing out. */\n #announceDown(exited: number, shards: number[]): void {\n for (const [id, cluster] of this.clusters)\n if (id !== exited)\n this.#sendFrame(cluster, { [PEER_DOWN_FRAME]: shards });\n }\n\n #onFrame(info: ClusterInfo, child: ChildProcess, frame: unknown): void {\n if (info.process !== child) return;\n if (!frame || typeof frame !== \"object\") return;\n if (BUS_FRAME in frame) {\n this.#onBus(info, (frame as Record<string, unknown>)[BUS_FRAME]);\n return;\n }\n if (CLUSTER_FRAME in frame) {\n const body = parseChildFrame(\n (frame as Record<string, unknown>)[CLUSTER_FRAME],\n );\n if (body) this.#onControl(info, child, body);\n else\n this.#emit(\n \"error\",\n new Error(\n `Cluster ${info.id} sent a malformed control frame.`,\n ),\n );\n }\n // Anything else is the bot's own process.send() traffic.\n }\n\n #onControl(\n info: ClusterInfo,\n child: ChildProcess,\n frame: ChildFrame,\n ): void {\n switch (frame.kind) {\n case \"ready\": {\n if (!frame.shardIds.every((id) => info.shards.includes(id))) {\n this.#emit(\n \"error\",\n new Error(\n `Cluster ${info.id} reported shards it does not own: [${frame.shardIds}].`,\n ),\n );\n return;\n }\n this.#clearStartTimer(info.id);\n info.state = \"ready\";\n info.readyAt = info.lastReadyAt = Date.now();\n this.#emit(\"clusterReady\", info);\n this.#checkReady();\n return;\n }\n case \"identify\": {\n const reply = (body: Record<string, unknown>): void => {\n if (info.process === child)\n this.#sendFrame(info, { [CLUSTER_FRAME]: body });\n };\n if (!this.#coordinator) {\n reply({\n kind: \"identify-deny\",\n id: frame.id,\n error: \"IDENTIFY coordination is off.\",\n });\n return;\n }\n if (!info.shards.includes(frame.shardId)) {\n reply({\n kind: \"identify-deny\",\n id: frame.id,\n error: `Shard ${frame.shardId} does not belong to cluster ${info.id}.`,\n });\n return;\n }\n // A shard asks for one slot at a time, so a worker cannot queue\n // more requests than it has shards.\n let waiting = this.#identifyWaiting.get(info.id);\n if (!waiting)\n this.#identifyWaiting.set(info.id, (waiting = new Set()));\n if (waiting.has(frame.shardId)) {\n reply({\n kind: \"identify-deny\",\n id: frame.id,\n error: `Shard ${frame.shardId} is already waiting for a slot.`,\n });\n return;\n }\n waiting.add(frame.shardId);\n const settled = (): void => {\n waiting.delete(frame.shardId);\n };\n this.#coordinator.acquire(info.id, frame.shardId).then(\n () => {\n settled();\n reply({ kind: \"identify-grant\", id: frame.id });\n },\n (error: unknown) => {\n settled();\n reply({\n kind: \"identify-deny\",\n id: frame.id,\n error:\n error instanceof Error\n ? error.message\n : String(error),\n });\n },\n );\n return;\n }\n case \"identify-done\":\n this.#coordinator?.release(info.id, frame.shardId);\n return;\n case \"reply\": {\n // An oversized answer settles the request with an error now\n // instead of being held until its timeout.\n const tooBig =\n frame.error === undefined && this.#tooBig(frame.result);\n this.#tracker.answer(frame.id, `c:${info.id}`, info.id, {\n ...(frame.error !== undefined\n ? { error: frame.error }\n : tooBig\n ? {\n error: `Cluster ${info.id} answered with more than ${this.#maxBytes} bytes.`,\n }\n : { result: frame.result }),\n });\n return;\n }\n case \"health\":\n if (this.#tooBig(frame.report)) {\n this.#emit(\n \"error\",\n new Error(\n `Cluster ${info.id} sent a health report of more than ${this.#maxBytes} bytes.`,\n ),\n );\n return;\n }\n info.report = frame.report;\n return;\n }\n }\n\n get #maxBytes(): number {\n return this.#options.maxMessageBytes ?? DEFAULT_MAX_MESSAGE_BYTES;\n }\n\n #tooBig(value: unknown): boolean {\n return payloadSize(value) > this.#maxBytes;\n }\n\n /** @throws {RangeError} If a payload this process is about to send is over the limit. */\n #assertSize(data: unknown, what: string): void {\n if (this.#tooBig(data))\n throw new RangeError(\n `${what} is larger than maxMessageBytes (${this.#maxBytes}).`,\n );\n }\n\n /** Routes one shard-bus message: replies for the parent are consumed, shard-targeted messages go to the owning cluster only, broadcasts to every other cluster. */\n #onBus(from: ClusterInfo, raw: unknown): void {\n let message = raw as {\n source?: unknown;\n target?: unknown;\n type?: unknown;\n data?: unknown;\n } | null;\n if (\n !message ||\n typeof message !== \"object\" ||\n typeof message.type !== \"string\" ||\n message.type.length === 0 ||\n message.type.length > 256 ||\n (message as { id?: unknown }).id === undefined ||\n typeof (message as { id?: unknown }).id !== \"string\" ||\n (message as { id: string }).id.length > 256 ||\n !Number.isInteger(message.source) ||\n (message.target !== null && !Number.isInteger(message.target))\n )\n return;\n // A cluster speaks only for its own shards: a `source` it does not\n // own would let it answer requests meant for another cluster's shard.\n if (!from.shards.includes(message.source as number)) return;\n // Relay only the documented envelope, so undeclared fields cannot\n // ride along to every other cluster.\n const { type, id, source, target, data, expectsReply } =\n message as Record<string, unknown>;\n message = {\n type,\n id,\n source,\n target,\n data,\n ...(expectsReply === undefined ? {} : { expectsReply }),\n } as typeof message;\n if (this.#tooBig(message.data)) {\n this.#emit(\n \"error\",\n new Error(\n `Cluster ${from.id} sent a shard-bus message of more than ${this.#maxBytes} bytes; it was not relayed.`,\n ),\n );\n return;\n }\n const frame = { [BUS_FRAME]: message };\n if (message.target === PARENT_ID) {\n const reply = message.data as ReplyPayload | undefined;\n if (\n message.type === REPLY_TYPE &&\n reply &&\n typeof reply.requestId === \"string\"\n )\n this.#tracker.answer(\n reply.requestId,\n `s:${message.source}`,\n from.id,\n reply.error !== undefined\n ? { error: String(reply.error) }\n : { result: reply.result },\n );\n return;\n }\n if (message.target === null) {\n for (const [id, cluster] of this.clusters)\n if (id !== from.id) this.#sendFrame(cluster, frame);\n return;\n }\n const owner = this.clusterForShard(message.target as number);\n if (!owner || owner.id === from.id) return;\n // Fail fast: a request to a shard whose cluster is down rejects now.\n if (!this.#sendFrame(owner, frame))\n this.#sendFrame(from, {\n [PEER_DOWN_FRAME]: [message.target as number],\n });\n }\n\n /**\n * Sends a request to one shard and resolves with its handler's result.\n * @throws {RangeError} If the shard is not part of this deployment.\n * @throws {Error} If its cluster is down, the handler failed, or no answer arrives in time.\n */\n public async request<R = unknown, T = unknown>(\n shardId: number,\n type: string,\n data: T,\n options: RequestOptions = {},\n ): Promise<R> {\n this.#assertSize(data, \"The request data\");\n const owner = this.clusterForShard(shardId);\n if (!owner)\n throw new RangeError(\n `Shard ${shardId} is not part of this deployment.`,\n );\n const { id, done } = this.#tracker.begin<R>({\n keys: new Map([[`s:${shardId}`, owner.id]]),\n timeoutMs:\n options.timeoutMs ?? this.#options.requestTimeout ?? 5000,\n signal: options.signal,\n });\n const sent = this.#sendFrame(owner, {\n [BUS_FRAME]: {\n source: PARENT_ID,\n target: shardId,\n type,\n data,\n id,\n expectsReply: true,\n },\n });\n if (!sent) {\n this.#tracker.failOwner(\n owner.id,\n `Shard ${shardId} is down: cluster ${owner.id} is ${owner.state}.`,\n );\n }\n const { replies } = await done;\n const reply = replies[0];\n if (!reply)\n throw new Error(\n `Shard ${shardId} did not reply to \"${type}\" in time.`,\n );\n if (reply.error !== undefined) throw new Error(reply.error);\n return reply.result as R;\n }\n\n /**\n * Asks every shard of every live cluster and collects the answers. Shards\n * whose cluster is down, or that do not answer in time, are listed in\n * `missing`; it does not reject for them.\n */\n public async broadcast<R = unknown, T = unknown>(\n type: string,\n data: T,\n options: RequestOptions = {},\n ): Promise<BroadcastResult<R>> {\n this.#assertSize(data, \"The request data\");\n const keys = new Map<string, number>();\n const down: number[] = [];\n for (const [shard, clusterId] of this.#owner) {\n const cluster = this.#infos.get(clusterId);\n if (cluster && isLive(cluster.state))\n keys.set(`s:${shard}`, clusterId);\n else down.push(shard);\n }\n const { id, done } = this.#tracker.begin<R>({\n keys,\n timeoutMs:\n options.timeoutMs ?? this.#options.requestTimeout ?? 5000,\n signal: options.signal,\n });\n const frame = {\n [BUS_FRAME]: {\n source: PARENT_ID,\n target: null,\n type,\n data,\n id,\n expectsReply: true,\n },\n };\n for (const cluster of this.clusters.values())\n if (!this.#sendFrame(cluster, frame))\n this.#tracker.failOwner(\n cluster.id,\n `Cluster ${cluster.id} is ${cluster.state}.`,\n );\n const { replies, missing } = await done;\n return {\n replies: replies.map((reply) => ({\n shardId: Number(reply.key.slice(2)),\n ...(reply.error !== undefined\n ? { error: reply.error }\n : { result: reply.result as R }),\n })),\n missing: [\n ...down,\n ...missing.map((key) => Number(key.slice(2))),\n ].sort((a, b) => a - b),\n };\n }\n\n /**\n * Sends a request to a cluster's worker (see `ClusterWorker.onRequest()`).\n * @throws {Error} If the cluster is unknown or down, the handler failed, or no answer arrives in time.\n */\n public async requestCluster<R = unknown, T = unknown>(\n clusterId: number,\n type: string,\n data: T,\n options: RequestOptions = {},\n ): Promise<R> {\n this.#assertSize(data, \"The request data\");\n const cluster = this.#infos.get(clusterId);\n if (!cluster)\n throw new RangeError(`Cluster ${clusterId} does not exist.`);\n const { id, done } = this.#tracker.begin<R>({\n keys: new Map([[`c:${clusterId}`, clusterId]]),\n timeoutMs:\n options.timeoutMs ?? this.#options.requestTimeout ?? 5000,\n signal: options.signal,\n });\n if (\n !this.#sendFrame(cluster, {\n [CLUSTER_FRAME]: { kind: \"request\", id, type, data },\n })\n )\n this.#tracker.failOwner(\n clusterId,\n `Cluster ${clusterId} is ${cluster.state}.`,\n );\n const { replies } = await done;\n const reply = replies[0];\n if (!reply)\n throw new Error(\n `Cluster ${clusterId} did not reply to \"${type}\" in time.`,\n );\n if (reply.error !== undefined) throw new Error(reply.error);\n return reply.result as R;\n }\n\n /** Resolves when every cluster is ready. @throws {Error} On timeout, or if the manager stops first. */\n public ready(timeoutMs?: number): Promise<void> {\n if (this.#isReady()) return Promise.resolve();\n return new Promise<void>((resolve, reject) => {\n const waiter: {\n resolve: () => void;\n reject: (error: Error) => void;\n timer?: ReturnType<typeof setTimeout>;\n } = {\n resolve: () => {\n if (waiter.timer) clearTimeout(waiter.timer);\n this.#readyWaiters.delete(waiter);\n resolve();\n },\n reject: (error) => {\n if (waiter.timer) clearTimeout(waiter.timer);\n this.#readyWaiters.delete(waiter);\n reject(error);\n },\n };\n if (timeoutMs !== undefined)\n waiter.timer = setTimeout(\n () =>\n waiter.reject(\n new Error(\n `Clusters were not all ready within ${timeoutMs}ms.`,\n ),\n ),\n timeoutMs,\n );\n this.#readyWaiters.add(waiter);\n });\n }\n\n /**\n * Every shard of the deployment is served by a ready cluster. A failed\n * cluster whose shards were reassigned no longer counts.\n */\n #isReady(): boolean {\n let ready = 0;\n for (const cluster of this.#infos.values()) {\n if (cluster.state === \"ready\") ready++;\n else if (!(\n cluster.state === \"failed\" && cluster.shards.length === 0\n ))\n return false;\n }\n return ready > 0;\n }\n\n /**\n * Rejects pending `ready()` calls: a failed cluster's shards have no owner\n * and nothing will give them one, so waiting longer would never end.\n */\n #rejectReady(failed: ClusterInfo, why: string): void {\n if (failed.shards.length === 0) return;\n const error = new Error(\n `Cluster ${failed.id} failed and shards [${failed.shards}] have no owner (${why}).`,\n );\n for (const waiter of [...this.#readyWaiters]) waiter.reject(error);\n }\n\n #checkReady(): void {\n if (!this.#isReady()) return;\n for (const waiter of [...this.#readyWaiters]) waiter.resolve();\n if (!this.#allReady) {\n this.#allReady = true;\n this.#emit(\"ready\");\n }\n }\n\n /**\n * The last known health of every cluster and shard. Reads what the\n * workers last reported; nothing is sent. Use {@link refreshHealth} for fresh numbers.\n */\n public health(): ClusterManagerHealth {\n const now = Date.now();\n const clusters: ClusterHealth[] = [...this.#infos.values()]\n .sort((a, b) => a.id - b.id)\n .map((cluster) => ({\n id: cluster.id,\n state: cluster.state,\n ...(cluster.process.pid !== undefined && isLive(cluster.state)\n ? { pid: cluster.process.pid }\n : {}),\n host: cluster.host,\n shardIds: [...cluster.shards],\n uptimeMs: isLive(cluster.state) ? now - cluster.startedAt : 0,\n restarts: cluster.restarts,\n startedAt: cluster.startedAt,\n ...(cluster.readyAt !== undefined\n ? { readyAt: cluster.readyAt }\n : {}),\n ...(cluster.lastReadyAt !== undefined\n ? { lastReadyAt: cluster.lastReadyAt }\n : {}),\n shards: cluster.report?.shards ?? ([] as ShardHealth[]),\n ...(cluster.report?.metrics\n ? { metrics: cluster.report.metrics as WorkerMetrics }\n : {}),\n ...(cluster.report\n ? { reportedAt: cluster.report.reportedAt }\n : {}),\n }));\n const missingShards = [...this.#owner]\n .filter(([, id]) => this.#infos.get(id)?.state !== \"ready\")\n .map(([shard]) => shard)\n .sort((a, b) => a - b);\n const states = clusters.map((cluster) => cluster.state);\n const state: ClusterManagerHealth[\"state\"] = this.#stopping\n ? \"stopping\"\n : clusters.length === 0\n ? this.#wasStarted\n ? \"stopped\"\n : \"idle\"\n : states.every((s) => s === \"stopped\")\n ? \"stopped\"\n : states.every((s) => s === \"ready\")\n ? \"ready\"\n : states.some((s) => s === \"restarting\" || s === \"failed\")\n ? \"degraded\"\n : \"starting\";\n return {\n state,\n totalShards: this.shardCount,\n clusters,\n readyClusters: states.filter((s) => s === \"ready\").length,\n missingShards,\n };\n }\n\n /**\n * Asks every live cluster for a fresh report, then returns {@link health}.\n * A cluster that does not answer in time keeps its last report; it is not\n * treated as failed (only its process exiting does that).\n */\n public async refreshHealth(\n options: { metrics?: boolean; timeoutMs?: number } = {},\n ): Promise<ClusterManagerHealth> {\n await Promise.all(\n [...this.clusters.values()].map(async (cluster) => {\n try {\n cluster.report = await this.requestCluster<WorkerReport>(\n cluster.id,\n REQUEST_HEALTH,\n { metrics: options.metrics === true },\n { timeoutMs: options.timeoutMs ?? 2000 },\n );\n } catch {\n // Keep the previous report.\n }\n }),\n );\n return this.health();\n }\n\n /** Checks if the recommended shard count has changed and respawns if so. */\n public async checkAutoScale(): Promise<void> {\n if (!this.#auto) return; // Only auto-scale in auto mode\n try {\n const recommended = await this.fetchRecommendedShardCount();\n if (recommended !== this.shardCount) {\n await this.respawn(recommended);\n }\n } catch (error) {\n // Surface the failure instead of silently swallowing it; the next\n // interval tick retries.\n const normalized =\n error instanceof Error ? error : new Error(String(error));\n this.#options.onAutoScaleError?.(normalized);\n }\n }\n\n /** Gracefully shuts the existing clusters down and respawns them with the new shard count. @param newShardCount The new total shard count. */\n public async respawn(newShardCount: number): Promise<void> {\n this.#options.shardCount = newShardCount;\n // Hosts stay connected across a respawn, so they need not rejoin.\n await this.#stop(this.#options.shutdownTimeout ?? 5000, true);\n this.#supervisor.reset();\n await this.spawn();\n }\n\n /**\n * Gracefully stops all active clusters: sends SIGTERM, awaits each child's\n * clean exit, and force-kills (SIGKILL) only children that outlast the\n * shutdown timeout. Safe to call twice or concurrently; cancels restarts,\n * timers and pending requests.\n * @param timeoutMs Grace period per child before force-kill. Defaults to the configured `shutdownTimeout` (5000 ms).\n */\n public shutdownAll(\n timeoutMs = this.#options.shutdownTimeout ?? 5000,\n ): Promise<void> {\n return this.#stop(timeoutMs, false);\n }\n\n #stop(timeoutMs: number, keepRemote: boolean): Promise<void> {\n this.#stopPromise ??= this.#shutdown(timeoutMs, keepRemote).finally(\n () => {\n this.#stopPromise = undefined;\n },\n );\n return this.#stopPromise;\n }\n\n async #shutdown(timeoutMs: number, keepRemote: boolean): Promise<void> {\n this.#stopping = true;\n this.#halt();\n // A spawn that is still forking sees `#stopping` and stops on its own.\n await this.#startPromise?.catch(() => undefined);\n await Promise.all(\n [...this.clusters.values()].map((cluster) =>\n this.#shutdownCluster(cluster, timeoutMs),\n ),\n );\n for (const cluster of this.#infos.values()) cluster.state = \"stopped\";\n this.clusters.clear();\n this.#startPromise = undefined;\n if (!keepRemote) this.#remote?.stop();\n this.#stopping = false;\n }\n\n /** Cancels everything that could start work or keep the loop alive. */\n #halt(): void {\n for (const timer of this.#restartTimers) clearTimeout(timer);\n this.#restartTimers.clear();\n for (const id of [...this.#startTimers.keys()])\n this.#clearStartTimer(id);\n for (const wake of [...this.#pauses]) wake();\n if (this.#autoScaleTimer) {\n clearInterval(this.#autoScaleTimer);\n this.#autoScaleTimer = undefined;\n }\n this.#coordinator?.close();\n this.#coordinator = undefined;\n this.#remote?.abortWaits();\n this.#tracker.close();\n for (const waiter of [...this.#readyWaiters])\n waiter.reject(new Error(\"The cluster manager is stopping.\"));\n }\n\n /** Gracefully terminates a single cluster child, escalating to SIGKILL after the timeout. */\n async #shutdownCluster(\n cluster: ClusterInfo,\n timeoutMs: number,\n ): Promise<void> {\n const child = cluster.process;\n cluster.state = \"stopping\";\n if (child.exitCode !== null || child.signalCode !== null) return;\n await new Promise<void>((resolve) => {\n let timer: ReturnType<typeof setTimeout> | undefined;\n const finish = (): void => {\n if (timer) clearTimeout(timer);\n resolve();\n };\n child.once(\"exit\", finish);\n try {\n child.kill(\"SIGTERM\");\n } catch {\n child.off(\"exit\", finish);\n finish();\n return;\n }\n timer = setTimeout(() => {\n try {\n child.kill(\"SIGKILL\");\n } catch {\n // Ignore force-kill errors.\n }\n }, timeoutMs);\n });\n }\n\n /** Immediately sends SIGTERM to every cluster process (no grace period, no SIGKILL escalation) and clears the cluster map. Prefer {@link shutdownAll} for a graceful stop. */\n public killAll(): void {\n this.#stopping = true;\n this.#halt();\n for (const cluster of this.#infos.values()) {\n cluster.state = \"stopped\";\n try {\n cluster.process.kill();\n } catch {\n // Ignore kill errors\n }\n }\n this.clusters.clear();\n this.#startPromise = undefined;\n this.#remote?.stop();\n this.#stopping = false;\n }\n}\n",
|
|
27
|
+
"/** Discord allows one IDENTIFY per rate-limit key every five seconds. */\nexport const IDENTIFY_INTERVAL = 5000;\n\n/** What `/gateway/bot` says about the session budget. */\nexport interface IdentifyBudget {\n total: number;\n remaining: number;\n /** Milliseconds until `remaining` resets, counted from construction. */\n resetAfter: number;\n /** Milliseconds between resets after that one. Discord resets the budget every 24 hours (the default). */\n period?: number;\n}\n\n/** Options for an {@link IdentifyCoordinator}. */\nexport interface IdentifyCoordinatorOptions {\n /** Discord's `max_concurrency`: shards with the same `id % maxConcurrency` share a key. Default 1. */\n maxConcurrency?: number;\n /** Milliseconds between two IDENTIFYs on one key. Default 5000. */\n interval?: number;\n /** The session-start budget, when known. Without it the coordinator paces but never blocks. */\n budget?: IdentifyBudget;\n /** A granted slot that is not released within this many ms is released for the holder. Default 60,000. */\n holdTimeout?: number;\n /** Clock, injectable for tests. */\n now?: () => number;\n /** Timer, injectable for tests. Must return a handle `cancel` understands. */\n schedule?: (callback: () => void, ms: number) => unknown;\n cancel?: (handle: unknown) => void;\n /** Called when a request has to wait for the budget to reset. */\n onBudgetWait?: (info: { shardId: number; waitMs: number }) => void;\n}\n\ninterface Waiter {\n owner: number;\n shardId: number;\n grant: () => void;\n fail: (error: Error) => void;\n}\n\ninterface Key {\n queue: Waiter[];\n /** The grant that has not been released yet. */\n holder?: { owner: number; shardId: number; timer: unknown };\n /** Earliest time the next IDENTIFY may start. */\n nextAt: number;\n}\n\n/**\n * Hands out IDENTIFY slots for a whole deployment. Every cluster asks the\n * parent, so the limit holds across processes: one IDENTIFY per rate-limit key\n * per interval, and no more IDENTIFYs than `session_start_limit.remaining`.\n * It only does arithmetic on timestamps and holds one timer, so it can be\n * tested with a fake clock.\n */\nexport class IdentifyCoordinator {\n readonly maxConcurrency: number;\n readonly #interval: number;\n readonly #holdTimeout: number;\n readonly #now: () => number;\n readonly #schedule: (callback: () => void, ms: number) => unknown;\n readonly #cancel: (handle: unknown) => void;\n readonly #onBudgetWait?: IdentifyCoordinatorOptions[\"onBudgetWait\"];\n readonly #keys = new Map<number, Key>();\n #remaining: number | undefined;\n #total: number | undefined;\n #resetAt = Number.POSITIVE_INFINITY;\n #period = 24 * 60 * 60 * 1000;\n #timer: unknown;\n #timerAt = Number.POSITIVE_INFINITY;\n #closed = false;\n\n public constructor(options: IdentifyCoordinatorOptions = {}) {\n const concurrency = options.maxConcurrency ?? 1;\n if (!Number.isInteger(concurrency) || concurrency < 1)\n throw new RangeError(\"maxConcurrency must be a positive integer.\");\n const interval = options.interval ?? IDENTIFY_INTERVAL;\n const hold = options.holdTimeout ?? 60_000;\n if (!(interval >= 0) || !(hold > 0))\n throw new RangeError(\n \"interval cannot be negative and holdTimeout must be positive.\",\n );\n this.maxConcurrency = concurrency;\n this.#interval = interval;\n this.#holdTimeout = hold;\n this.#now = options.now ?? Date.now;\n this.#schedule =\n options.schedule ??\n ((callback, ms) => {\n const timer = setTimeout(callback, ms);\n (timer as { unref?: () => void }).unref?.();\n return timer;\n });\n this.#cancel =\n options.cancel ??\n ((handle) => clearTimeout(handle as ReturnType<typeof setTimeout>));\n this.#onBudgetWait = options.onBudgetWait;\n if (options.budget) {\n this.#total = options.budget.total;\n this.#remaining = options.budget.remaining;\n this.#resetAt = this.#now() + options.budget.resetAfter;\n this.#period = options.budget.period ?? this.#period;\n if (!(this.#period > 0))\n throw new RangeError(\"budget.period must be positive.\");\n }\n }\n\n /** Slots left in the session budget, or `undefined` when it is unknown. */\n public get remaining(): number | undefined {\n return this.#remaining;\n }\n\n /** Requests waiting for a slot. */\n public get waiting(): number {\n let count = 0;\n for (const key of this.#keys.values()) count += key.queue.length;\n return count;\n }\n\n /** Slots granted and not yet released. */\n public get holding(): number {\n let count = 0;\n for (const key of this.#keys.values()) if (key.holder) count++;\n return count;\n }\n\n /**\n * Waits for the shard's turn. Resolves when the shard may IDENTIFY now;\n * call {@link release} once it has. Rejects if `owner` is dropped or the\n * coordinator closes first.\n */\n public acquire(owner: number, shardId: number): Promise<void> {\n if (this.#closed)\n return Promise.reject(new Error(\"IDENTIFY coordinator is closed.\"));\n const index = shardId % this.maxConcurrency;\n let key = this.#keys.get(index);\n if (!key) this.#keys.set(index, (key = { queue: [], nextAt: 0 }));\n return new Promise<void>((grant, fail) => {\n key.queue.push({ owner, shardId, grant, fail });\n this.pump();\n });\n }\n\n /** The shard has sent its IDENTIFY: its key rests for one interval. Ignored unless `owner` holds the slot. */\n public release(owner: number, shardId: number): void {\n const key = this.#keys.get(shardId % this.maxConcurrency);\n const holder = key?.holder;\n if (\n !key ||\n !holder ||\n holder.owner !== owner ||\n holder.shardId !== shardId\n )\n return;\n this.#free(key);\n this.pump();\n }\n\n /** A cluster is gone: forget its waiting requests and free the slots it held. */\n public drop(owner: number, reason = \"The cluster went away.\"): void {\n for (const key of this.#keys.values()) {\n const keep: Waiter[] = [];\n for (const waiter of key.queue)\n if (waiter.owner === owner) waiter.fail(new Error(reason));\n else keep.push(waiter);\n key.queue = keep;\n if (key.holder?.owner === owner) this.#free(key);\n }\n this.pump();\n }\n\n /** Rejects every waiter and stops the timer. */\n public close(): void {\n this.#closed = true;\n if (this.#timer !== undefined) this.#cancel(this.#timer);\n this.#timer = undefined;\n this.#timerAt = Number.POSITIVE_INFINITY;\n for (const key of this.#keys.values()) {\n if (key.holder) this.#cancel(key.holder.timer);\n key.holder = undefined;\n for (const waiter of key.queue)\n waiter.fail(new Error(\"IDENTIFY coordinator is closed.\"));\n key.queue = [];\n }\n }\n\n /** Grants every slot that is due and arms one timer for the next. Called internally and by tests after moving the clock. */\n public pump(): void {\n if (this.#closed) return;\n const now = this.#now();\n if (this.#total !== undefined && now >= this.#resetAt) {\n this.#remaining = this.#total;\n // The budget refills every period, so the next reset is known: a\n // later exhaustion waits for it instead of failing for good.\n while (now >= this.#resetAt) this.#resetAt += this.#period;\n }\n let next = Number.POSITIVE_INFINITY;\n for (const key of this.#keys.values()) {\n if (key.holder || key.queue.length === 0) continue;\n if (key.nextAt > now) {\n next = Math.min(next, key.nextAt);\n continue;\n }\n if (this.#remaining !== undefined && this.#remaining <= 0) {\n const waiter = key.queue[0]!;\n this.#onBudgetWait?.({\n shardId: waiter.shardId,\n waitMs: this.#resetAt - now,\n });\n next = Math.min(next, this.#resetAt);\n continue;\n }\n const waiter = key.queue.shift()!;\n if (this.#remaining !== undefined) this.#remaining--;\n const timer = this.#schedule(() => {\n if (key.holder?.timer === timer) {\n this.#free(key);\n this.pump();\n }\n }, this.#holdTimeout);\n key.holder = {\n owner: waiter.owner,\n shardId: waiter.shardId,\n timer,\n };\n waiter.grant();\n }\n this.#arm(next);\n }\n\n #free(key: Key): void {\n if (key.holder) this.#cancel(key.holder.timer);\n key.holder = undefined;\n key.nextAt = this.#now() + this.#interval;\n }\n\n #arm(at: number): void {\n if (at === this.#timerAt && this.#timer !== undefined) return;\n if (this.#timer !== undefined) this.#cancel(this.#timer);\n this.#timer = undefined;\n this.#timerAt = Number.POSITIVE_INFINITY;\n if (at === Number.POSITIVE_INFINITY) return;\n this.#timerAt = at;\n this.#timer = this.#schedule(\n () => {\n this.#timer = undefined;\n this.#timerAt = Number.POSITIVE_INFINITY;\n this.pump();\n },\n Math.max(0, at - this.#now()),\n );\n }\n}\n",
|
|
28
|
+
"import { EventEmitter } from \"node:events\";\nimport { randomBytes } from \"node:crypto\";\nimport type { ChildProcess } from \"node:child_process\";\nimport type { Server, ServerWebSocket } from \"bun\";\nimport {\n HOST_ID,\n MAX_REMOTE_FRAME_BYTES,\n isLoopbackHost,\n REMOTE_PROTOCOL_VERSION,\n RemoteClose,\n parseToCoordinator,\n verifyChallenge,\n type ToHost,\n} from \"./remote-protocol.js\";\n\n/** Where, and how, a coordinator listens for hosts. */\nexport interface RemoteOptions {\n /** TCP port. `0` picks a free one (read it from `ClusterManager#remotePort`). */\n port: number;\n /**\n * Interface to bind. Default `127.0.0.1`, which only this machine can\n * reach. Any other address needs `tls` or `allowInsecure`.\n */\n hostname?: string;\n /** Shared secret, at least 16 characters. Hosts prove they know it; it never crosses the wire. */\n secret: string;\n /** TLS for `wss://`. Required to listen on a non-loopback address unless `allowInsecure` is set. */\n tls?: { cert: string; key: string };\n /**\n * Listen on a non-loopback address without TLS: frames (shard messages\n * and worker output) travel unencrypted and unsigned. Only for a network\n * you fully trust, or when a TLS proxy in front terminates the connection.\n * Default false.\n */\n allowInsecure?: boolean;\n /**\n * The host IDs that make up the deployment. Clusters are placed on them in\n * this order, and an unlisted host is refused. Without it, the hosts that\n * have joined when `spawn()` starts are used, sorted by ID.\n */\n hosts?: string[];\n /** How many hosts must join before clusters are placed when `hosts` is not set. Default 1. */\n minHosts?: number;\n /** Milliseconds to wait for the hosts to join before `spawn()` fails. Default 60,000. */\n joinTimeout?: number;\n /**\n * Fencing lease in milliseconds. A host that hears nothing from the\n * coordinator for this long kills its workers; the coordinator waits twice\n * as long before it treats the host as lost and restarts its clusters, so\n * a shard never runs in two places. Default 15,000.\n */\n lease?: number;\n /** Milliseconds a new connection has to authenticate before it is closed. Default 3,000. */\n helloTimeout?: number;\n /**\n * Most connections that may be waiting to authenticate at once. When full,\n * the one that has waited longest is closed to make room: a host answers\n * the challenge within a round trip, a connection that holds a slot idle\n * does not. Default 16.\n */\n maxUnauthenticated?: number;\n /** The same limit for one client address (see `clientAddress`). Default 4. */\n maxUnauthenticatedPerAddress?: number;\n /** New connections one client address may open per second; more are refused. Default 10. */\n connectionsPerSecond?: number;\n /**\n * The client address the per-address limits count by. Default: the peer\n * address of the TCP connection. Behind a TLS proxy every peer is the\n * proxy, so return the client address the proxy forwards (only from a\n * header your proxy sets and overwrites).\n */\n clientAddress?: (request: Request, peer: string | undefined) => string;\n /** Also run clusters on the coordinator's own machine. Default false: clusters run on the hosts only. */\n local?: boolean;\n}\n\n/** A host as the coordinator sees it. */\nexport interface HostStatus {\n id: string;\n /** Whether its socket is open and answering. */\n connected: boolean;\n /** When it last sent anything (epoch ms). */\n lastSeen: number;\n /** Clusters currently running on it. */\n clusters: number[];\n}\n\ninterface SocketData {\n nonce: string;\n /** The client address the per-address limits count by. */\n address: string;\n id?: string;\n /** Whether this socket is counted among the pending (unauthenticated) ones. */\n counted?: boolean;\n helloTimer?: ReturnType<typeof setTimeout>;\n}\n\ninterface Link {\n id: string;\n ws: ServerWebSocket<SocketData>;\n lastSeen: number;\n children: Map<number, RemoteChild>;\n}\n\ntype Kill = (signal: string) => boolean;\n\n/** What the coordinator wants to hear about its hosts. */\nexport interface RemoteHooks {\n join(id: string): void;\n leave(id: string, reason: string): void;\n error(error: Error): void;\n}\n\n/**\n * A worker process on another host, shaped like the `ChildProcess` that\n * `ClusterManager` supervises. `send()` and `kill()` travel to the host's\n * agent; its messages and exit travel back, so the manager's restart,\n * IDENTIFY, routing and health logic needs no special case for it.\n */\nexport class RemoteChild extends EventEmitter {\n public pid: number | undefined;\n public exitCode: number | null = null;\n public signalCode: NodeJS.Signals | null = null;\n public connected = true;\n\n readonly #send: (frame: unknown) => boolean;\n readonly #kill: Kill;\n\n public constructor(\n public readonly host: string,\n public readonly cluster: number,\n public readonly epoch: number,\n send: (frame: unknown) => boolean,\n kill: Kill,\n ) {\n super();\n this.#send = send;\n this.#kill = kill;\n }\n\n /** Sends an IPC frame to the worker. */\n public send(frame: unknown): boolean {\n return this.connected &&\n this.exitCode === null &&\n this.signalCode === null\n ? this.#send(frame)\n : false;\n }\n\n /** Asks the host to signal the worker; the exit arrives as an event. */\n public kill(signal: string = \"SIGTERM\"): boolean {\n if (this.exitCode !== null || this.signalCode !== null) return false;\n return this.#kill(signal);\n }\n\n /** The agent says the worker started. */\n public spawned(pid: number | undefined): void {\n this.pid = pid;\n }\n\n /** The worker sent a message. */\n public deliver(frame: unknown): void {\n if (this.exitCode === null && this.signalCode === null)\n this.emit(\"message\", frame);\n }\n\n /** The worker exited, or its host was lost. Emits `exit` once. */\n public finish(code: number | null, signal: string | null): void {\n if (this.exitCode !== null || this.signalCode !== null) return;\n this.connected = false;\n // `exitCode` and `signalCode` must not both stay null once finished.\n this.exitCode = code ?? (signal === null ? 1 : null);\n this.signalCode = (signal as NodeJS.Signals | null) ?? null;\n this.emit(\"exit\", code, signal);\n }\n}\n\n/** The coordinator's listener and the hosts that joined it. */\nexport class RemoteHosts {\n readonly #options: Required<\n Pick<RemoteOptions, \"port\" | \"secret\" | \"lease\">\n > &\n RemoteOptions;\n readonly #links = new Map<string, Link>();\n readonly #waiters = new Set<{ check(): void; abort(error: Error): void }>();\n readonly #hooks: RemoteHooks;\n #server?: Server<SocketData>;\n #watchdog?: ReturnType<typeof setInterval>;\n #epoch = 0;\n #ping = 0;\n /** Sockets that have not said hello yet, oldest first. */\n readonly #pending = new Set<ServerWebSocket<SocketData>>();\n /** Pending sockets per client address. */\n readonly #pendingByAddress = new Map<string, number>();\n /** Connection allowance per client address (token buckets). */\n readonly #rates = new Map<string, { tokens: number; at: number }>();\n\n public constructor(options: RemoteOptions, hooks: RemoteHooks) {\n if (typeof options.secret !== \"string\" || options.secret.length < 16)\n throw new RangeError(\n \"remote.secret must be a string of at least 16 characters.\",\n );\n if (\n !Number.isInteger(options.port) ||\n options.port < 0 ||\n options.port > 65535\n )\n throw new RangeError(\n \"remote.port must be an integer from 0 to 65535.\",\n );\n if (\n options.tls &&\n (typeof options.tls.cert !== \"string\" ||\n typeof options.tls.key !== \"string\" ||\n !options.tls.cert ||\n !options.tls.key)\n )\n throw new TypeError(\"remote.tls needs a non-empty cert and key.\");\n if (\n !options.tls &&\n !options.allowInsecure &&\n !isLoopbackHost(options.hostname ?? \"127.0.0.1\")\n )\n throw new RangeError(\n \"remote listens on a network address without TLS: set remote.tls, bind 127.0.0.1 behind a TLS proxy, or set remote.allowInsecure for a network you fully trust.\",\n );\n const lease = options.lease ?? 15_000;\n if (!Number.isFinite(lease) || lease < 30)\n throw new RangeError(\n \"remote.lease must be at least 30 milliseconds.\",\n );\n for (const id of options.hosts ?? [])\n if (!HOST_ID.test(id))\n throw new RangeError(`\"${id}\" is not a valid host ID.`);\n if (\n options.hosts &&\n (options.hosts.length === 0 ||\n new Set(options.hosts).size !== options.hosts.length)\n )\n throw new RangeError(\"remote.hosts must be unique and not empty.\");\n if (\n options.minHosts !== undefined &&\n (!Number.isInteger(options.minHosts) || options.minHosts < 1)\n )\n throw new RangeError(\"remote.minHosts must be a positive integer.\");\n this.#options = { ...options, lease };\n this.#hooks = hooks;\n }\n\n /** The port in use, once started. */\n public get port(): number | undefined {\n return this.#server?.port;\n }\n\n /** Host IDs that are connected now, sorted. */\n public connected(): string[] {\n return [...this.#links.keys()].sort();\n }\n\n /** Opens the listener. Safe to call twice. */\n public start(): void {\n if (this.#server) return;\n const { port, hostname, tls, lease } = this.#options;\n this.#server = Bun.serve<SocketData>({\n port,\n hostname: hostname ?? \"127.0.0.1\",\n ...(tls ? { tls } : {}),\n fetch: (request, server) => {\n const peer = server.requestIP(request)?.address;\n const address =\n this.#options.clientAddress?.(request, peer) ??\n peer ??\n \"unknown\";\n // Refused before the upgrade: the cheapest answer to a flood.\n if (!this.#allowConnection(address))\n return new Response(\"Too many connections.\", {\n status: 429,\n });\n if (\n server.upgrade(request, {\n data: {\n nonce: randomBytes(16).toString(\"hex\"),\n address,\n },\n })\n )\n return undefined;\n return new Response(\"Lunibee cluster coordinator\", {\n status: 426,\n });\n },\n websocket: {\n maxPayloadLength: MAX_REMOTE_FRAME_BYTES,\n idleTimeout: 0,\n open: (ws) => this.#open(ws),\n message: (ws, raw) => this.#message(ws, raw),\n close: (ws, _code, reason) => this.#closed(ws, reason),\n },\n });\n const pingMs = Math.max(10, Math.floor(lease / 3));\n this.#watchdog = setInterval(() => this.#tick(), pingMs);\n (this.#watchdog as { unref?: () => void }).unref?.();\n }\n\n /** Closes the listener and every host connection. */\n public stop(): void {\n if (this.#watchdog) clearInterval(this.#watchdog);\n this.#watchdog = undefined;\n for (const link of [...this.#links.values()]) {\n this.#post(link, { t: \"bye\", reason: \"The coordinator stopped.\" });\n this.#drop(link, \"The coordinator stopped.\", RemoteClose.Shutdown);\n }\n void this.#server?.stop(true);\n this.#server = undefined;\n this.abortWaits();\n }\n\n /** Resolves when the given hosts (or `count` hosts) are connected. @throws {Error} On timeout. */\n public waitFor(\n hosts: readonly string[] | undefined,\n count: number,\n timeoutMs: number,\n ): Promise<void> {\n const done = (): boolean =>\n hosts\n ? hosts.every((id) => this.#links.has(id))\n : this.#links.size >= count;\n if (done()) return Promise.resolve();\n return new Promise<void>((resolve, reject) => {\n const waiter = {\n check: (): void => {\n if (!done()) return;\n clearTimeout(timer);\n this.#waiters.delete(waiter);\n resolve();\n },\n abort: (error: Error): void => {\n clearTimeout(timer);\n this.#waiters.delete(waiter);\n reject(error);\n },\n };\n const timer = setTimeout(() => {\n const missing = hosts?.filter((id) => !this.#links.has(id));\n waiter.abort(\n new Error(\n hosts\n ? `Hosts did not join within ${timeoutMs}ms: [${missing}].`\n : `Only ${this.#links.size} of ${count} hosts joined within ${timeoutMs}ms.`,\n ),\n );\n }, timeoutMs);\n this.#waiters.add(waiter);\n });\n }\n\n /** Fails everything waiting for hosts to join (a shutdown). */\n public abortWaits(): void {\n for (const waiter of [...this.#waiters])\n waiter.abort(new Error(\"The coordinator stopped.\"));\n }\n\n /** Hosts and the clusters running on them. */\n public status(declared: readonly string[] = []): HostStatus[] {\n const ids = new Set([...declared, ...this.#links.keys()]);\n return [...ids].sort().map((id) => {\n const link = this.#links.get(id);\n return {\n id,\n connected: link !== undefined,\n lastSeen: link?.lastSeen ?? 0,\n clusters: link\n ? [...link.children.keys()].sort((a, b) => a - b)\n : [],\n };\n });\n }\n\n /**\n * Starts a worker on `hostId`. If the host is not connected the returned\n * child exits at once, which `ClusterManager` handles like a crash.\n */\n public createChild(\n hostId: string,\n cluster: number,\n env: Record<string, string>,\n ): ChildProcess {\n const epoch = ++this.#epoch;\n const link = this.#links.get(hostId);\n const child = new RemoteChild(\n hostId,\n cluster,\n epoch,\n (frame) =>\n link !== undefined &&\n this.#post(link, { t: \"frame\", cluster, epoch, frame }),\n (signal) =>\n link !== undefined &&\n this.#post(link, { t: \"kill\", cluster, epoch, signal }),\n );\n if (!link) {\n queueMicrotask(() => child.finish(null, \"SIGHUP\"));\n return child as unknown as ChildProcess;\n }\n link.children.get(cluster)?.finish(null, \"SIGKILL\");\n link.children.set(cluster, child);\n if (!this.#post(link, { t: \"spawn\", cluster, epoch, env }))\n queueMicrotask(() => child.finish(null, \"SIGHUP\"));\n return child as unknown as ChildProcess;\n }\n\n #post(link: Link, message: ToHost): boolean {\n try {\n return link.ws.send(JSON.stringify(message)) > 0;\n } catch {\n return false;\n }\n }\n\n /** Takes one connection from the address's per-second allowance. */\n #allowConnection(address: string): boolean {\n const rate = this.#options.connectionsPerSecond ?? 10;\n const now = Date.now();\n let bucket = this.#rates.get(address);\n if (!bucket) {\n bucket = { tokens: rate, at: now };\n this.#rates.set(address, bucket);\n // A bounded map: forget the address idle longest.\n if (this.#rates.size > 10_000)\n this.#rates.delete(this.#rates.keys().next().value as string);\n }\n bucket.tokens = Math.min(\n rate,\n bucket.tokens + ((now - bucket.at) / 1000) * rate,\n );\n bucket.at = now;\n if (bucket.tokens < 1) return false;\n bucket.tokens--;\n return true;\n }\n\n /** Closes a pending socket to make room for a newer one. */\n #evict(victim: ServerWebSocket<SocketData>): void {\n this.#uncount(victim);\n clearTimeout(victim.data.helloTimer);\n victim.close(RemoteClose.TooManyConnections, \"Too many connections.\");\n }\n\n #open(ws: ServerWebSocket<SocketData>): void {\n // An address at its limit gives up its oldest pending socket, then\n // the whole listener does: a connection that holds a slot without\n // answering cannot keep a host that answers at once from joining.\n const address = ws.data.address;\n if (\n (this.#pendingByAddress.get(address) ?? 0) >=\n (this.#options.maxUnauthenticatedPerAddress ?? 4)\n )\n for (const other of this.#pending)\n if (other.data.address === address) {\n this.#evict(other);\n break;\n }\n if (this.#pending.size >= (this.#options.maxUnauthenticated ?? 16)) {\n const oldest = this.#pending.values().next().value;\n if (oldest) this.#evict(oldest);\n }\n this.#pending.add(ws);\n this.#pendingByAddress.set(\n address,\n (this.#pendingByAddress.get(address) ?? 0) + 1,\n );\n ws.data.counted = true;\n ws.data.helloTimer = setTimeout(\n () => ws.close(RemoteClose.HelloTimeout, \"No hello.\"),\n this.#options.helloTimeout ?? 3_000,\n );\n ws.send(\n JSON.stringify({\n t: \"challenge\",\n nonce: ws.data.nonce,\n version: REMOTE_PROTOCOL_VERSION,\n } satisfies ToHost),\n );\n }\n\n #message(ws: ServerWebSocket<SocketData>, raw: string | Buffer): void {\n if (typeof raw !== \"string\") {\n ws.close(RemoteClose.BadRequest, \"Text frames only.\");\n return;\n }\n const message = parseToCoordinator(raw);\n if (!message) {\n ws.close(RemoteClose.BadRequest, \"Malformed frame.\");\n return;\n }\n if (ws.data.id === undefined) {\n this.#hello(ws, message);\n return;\n }\n const link = this.#links.get(ws.data.id);\n if (!link || link.ws !== ws) return;\n link.lastSeen = Date.now();\n if (message.t === \"pong\" || message.t === \"hello\") return;\n const child = link.children.get(message.cluster);\n // A frame from an earlier process of the same cluster is stale.\n if (!child || child.epoch !== message.epoch) return;\n switch (message.t) {\n case \"spawned\":\n child.spawned(message.pid);\n break;\n case \"frame\":\n child.deliver(message.frame);\n break;\n case \"exit\":\n link.children.delete(message.cluster);\n child.finish(message.code, message.signal);\n break;\n case \"error\":\n this.#hooks.error(\n new Error(\n `Host ${link.id} could not run cluster ${message.cluster}: ${message.message}`,\n ),\n );\n link.children.delete(message.cluster);\n child.finish(1, null);\n break;\n }\n }\n\n #hello(\n ws: ServerWebSocket<SocketData>,\n message: ReturnType<typeof parseToCoordinator>,\n ): void {\n if (message?.t !== \"hello\") {\n ws.close(RemoteClose.BadRequest, \"Expected hello.\");\n return;\n }\n const { id } = message;\n if (message.version !== REMOTE_PROTOCOL_VERSION) {\n ws.close(RemoteClose.VersionMismatch, \"Protocol version mismatch.\");\n return;\n }\n if (\n !HOST_ID.test(id) ||\n !verifyChallenge(\n this.#options.secret,\n ws.data.nonce,\n id,\n message.mac,\n )\n ) {\n ws.close(RemoteClose.AuthFailed, \"Authentication failed.\");\n return;\n }\n if (this.#options.hosts && !this.#options.hosts.includes(id)) {\n ws.close(\n RemoteClose.UnknownHost,\n \"Host is not part of this deployment.\",\n );\n return;\n }\n if (this.#links.has(id)) {\n ws.close(\n RemoteClose.DuplicateHost,\n \"A host with this ID is connected.\",\n );\n return;\n }\n clearTimeout(ws.data.helloTimer);\n this.#uncount(ws);\n ws.data.id = id;\n const link: Link = {\n id,\n ws,\n lastSeen: Date.now(),\n children: new Map(),\n };\n this.#links.set(id, link);\n const lease = this.#options.lease;\n this.#post(link, {\n t: \"welcome\",\n leaseMs: lease,\n pingMs: Math.max(10, Math.floor(lease / 3)),\n });\n this.#hooks.join(id);\n for (const waiter of [...this.#waiters]) waiter.check();\n }\n\n #uncount(ws: ServerWebSocket<SocketData>): void {\n if (!ws.data.counted) return;\n ws.data.counted = false;\n this.#pending.delete(ws);\n const left = (this.#pendingByAddress.get(ws.data.address) ?? 1) - 1;\n if (left > 0) this.#pendingByAddress.set(ws.data.address, left);\n else this.#pendingByAddress.delete(ws.data.address);\n }\n\n #closed(ws: ServerWebSocket<SocketData>, reason: string): void {\n clearTimeout(ws.data.helloTimer);\n this.#uncount(ws);\n const link = ws.data.id ? this.#links.get(ws.data.id) : undefined;\n if (link && link.ws === ws)\n this.#drop(link, reason || \"The connection closed.\");\n }\n\n /** Forgets a host and ends every worker it ran. */\n #drop(link: Link, reason: string, code?: number): void {\n if (this.#links.get(link.id) !== link) return;\n this.#links.delete(link.id);\n if (code !== undefined)\n try {\n link.ws.close(code, reason.slice(0, 100));\n } catch {\n // Already closed.\n }\n for (const child of [...link.children.values()])\n child.finish(null, \"SIGHUP\");\n link.children.clear();\n this.#hooks.leave(link.id, reason);\n }\n\n #tick(): void {\n const now = Date.now();\n for (const link of [...this.#links.values()]) {\n // The host fences itself after one lease of silence; the\n // coordinator waits for a second before it lets anything restart.\n if (now - link.lastSeen > this.#options.lease * 2) {\n this.#drop(\n link,\n \"The host stopped answering.\",\n RemoteClose.HostLost,\n );\n continue;\n }\n this.#post(link, { t: \"ping\", n: ++this.#ping });\n }\n }\n}\n",
|
|
29
29
|
"import { createHmac, timingSafeEqual } from \"node:crypto\";\n\n/** Version of the coordinator ↔ host wire protocol. Both sides must agree. */\nexport const REMOTE_PROTOCOL_VERSION = 1;\n/** Largest text frame either side accepts, in UTF-8 bytes. */\nexport const MAX_REMOTE_FRAME_BYTES = 8 * 1024 * 1024;\n\n/** WebSocket close codes used between a coordinator and a host. */\nexport const RemoteClose = {\n BadRequest: 4400,\n AuthFailed: 4401,\n HelloTimeout: 4408,\n DuplicateHost: 4409,\n TooManyConnections: 4429,\n UnknownHost: 4410,\n VersionMismatch: 4426,\n HostLost: 4504,\n Shutdown: 4500,\n} as const;\n\n/**\n * The only environment variables a coordinator may set in a remote worker.\n * Everything else comes from the host's own environment, so a coordinator\n * cannot inject `NODE_OPTIONS`, `LD_PRELOAD` or a token.\n */\nexport const SPAWN_ENV_KEYS: ReadonlySet<string> = new Set([\n \"SHARD_LIST\",\n \"SHARD_COUNT\",\n \"CLUSTER_ID\",\n \"CLUSTER_COUNT\",\n \"LUNIBEE_SHARD_BUS\",\n \"LUNIBEE_CLUSTER_WORKER\",\n \"LUNIBEE_IDENTIFY\",\n]);\n\n/** Signals a coordinator may send to a remote worker. */\nexport const KILL_SIGNALS: ReadonlySet<string> = new Set([\n \"SIGTERM\",\n \"SIGKILL\",\n \"SIGINT\",\n]);\n\n/** Whether a host name is this machine, where an unencrypted link is acceptable. */\nexport function isLoopbackHost(hostname: string): boolean {\n return /^(?:localhost|127(?:\\.\\d{1,3}){3}|\\[::1\\]|::1)$/i.test(hostname);\n}\n\n/** A host ID: letters, digits, `.`, `_` and `-`, up to 64 characters. */\nexport const HOST_ID = /^[A-Za-z0-9._-]{1,64}$/;\n\n/** Frames the coordinator sends to a host. */\nexport type ToHost =\n | { t: \"challenge\"; nonce: string; version: number }\n | { t: \"welcome\"; leaseMs: number; pingMs: number }\n | { t: \"ping\"; n: number }\n | {\n t: \"spawn\";\n cluster: number;\n epoch: number;\n env: Record<string, string>;\n }\n | { t: \"frame\"; cluster: number; epoch: number; frame: unknown }\n | { t: \"kill\"; cluster: number; epoch: number; signal: string }\n | { t: \"bye\"; reason: string };\n\n/** Frames a host sends to the coordinator. */\nexport type ToCoordinator =\n | { t: \"hello\"; id: string; mac: string; version: number }\n | { t: \"pong\"; n: number }\n | { t: \"spawned\"; cluster: number; epoch: number; pid?: number }\n | { t: \"frame\"; cluster: number; epoch: number; frame: unknown }\n | {\n t: \"exit\";\n cluster: number;\n epoch: number;\n code: number | null;\n signal: string | null;\n }\n | { t: \"error\"; cluster: number; epoch: number; message: string };\n\nconst isRecord = (value: unknown): value is Record<string, unknown> =>\n typeof value === \"object\" && value !== null && !Array.isArray(value);\nconst isInt = (value: unknown): value is number =>\n typeof value === \"number\" && Number.isInteger(value) && value >= 0;\n\n/** The size of a string on the wire, in UTF-8 bytes. */\nexport function utf8Bytes(text: string): number {\n return Buffer.byteLength(text, \"utf8\");\n}\n\nfunction parseJson(\n text: unknown,\n maxBytes: number,\n): Record<string, unknown> | null {\n if (typeof text !== \"string\") return null;\n // A UTF-16 code unit takes at least one byte and at most three, so the\n // length decides most frames without counting bytes.\n if (text.length > maxBytes) return null;\n if (text.length * 3 > maxBytes && utf8Bytes(text) > maxBytes) return null;\n try {\n const value: unknown = JSON.parse(text);\n return isRecord(value) ? value : null;\n } catch {\n return null;\n }\n}\n\n/** The proof a host sends: an HMAC of the coordinator's nonce and the host ID, keyed by the shared secret. The secret itself never travels. */\nexport function signChallenge(\n secret: string,\n nonce: string,\n hostId: string,\n): string {\n return createHmac(\"sha256\", secret)\n .update(`${nonce}:${hostId}`)\n .digest(\"hex\");\n}\n\n/** Checks a host's proof in constant time. */\nexport function verifyChallenge(\n secret: string,\n nonce: string,\n hostId: string,\n mac: unknown,\n): boolean {\n if (typeof mac !== \"string\") return false;\n const expected = Buffer.from(signChallenge(secret, nonce, hostId), \"hex\");\n const given = Buffer.from(mac, \"hex\");\n return given.length === expected.length && timingSafeEqual(given, expected);\n}\n\n/** Validates a text frame from the coordinator; `null` when malformed. */\nexport function parseToHost(\n text: unknown,\n maxBytes = MAX_REMOTE_FRAME_BYTES,\n): ToHost | null {\n const v = parseJson(text, maxBytes);\n if (!v) return null;\n switch (v.t) {\n case \"challenge\":\n return typeof v.nonce === \"string\" && isInt(v.version)\n ? { t: \"challenge\", nonce: v.nonce, version: v.version }\n : null;\n case \"welcome\":\n return isInt(v.leaseMs) && isInt(v.pingMs)\n ? { t: \"welcome\", leaseMs: v.leaseMs, pingMs: v.pingMs }\n : null;\n case \"ping\":\n return isInt(v.n) ? { t: \"ping\", n: v.n } : null;\n case \"spawn\": {\n if (!isInt(v.cluster) || !isInt(v.epoch) || !isRecord(v.env))\n return null;\n const env: Record<string, string> = {};\n for (const [key, value] of Object.entries(v.env)) {\n if (!SPAWN_ENV_KEYS.has(key) || typeof value !== \"string\")\n return null;\n env[key] = value;\n }\n return { t: \"spawn\", cluster: v.cluster, epoch: v.epoch, env };\n }\n case \"frame\":\n return isInt(v.cluster) && isInt(v.epoch)\n ? {\n t: \"frame\",\n cluster: v.cluster,\n epoch: v.epoch,\n frame: v.frame,\n }\n : null;\n case \"kill\":\n return isInt(v.cluster) &&\n isInt(v.epoch) &&\n typeof v.signal === \"string\" &&\n KILL_SIGNALS.has(v.signal)\n ? {\n t: \"kill\",\n cluster: v.cluster,\n epoch: v.epoch,\n signal: v.signal,\n }\n : null;\n case \"bye\":\n return typeof v.reason === \"string\"\n ? { t: \"bye\", reason: v.reason }\n : null;\n default:\n return null;\n }\n}\n\n/** Validates a text frame from a host; `null` when malformed. */\nexport function parseToCoordinator(\n text: unknown,\n maxBytes = MAX_REMOTE_FRAME_BYTES,\n): ToCoordinator | null {\n const v = parseJson(text, maxBytes);\n if (!v) return null;\n switch (v.t) {\n case \"hello\":\n return typeof v.id === \"string\" &&\n typeof v.mac === \"string\" &&\n isInt(v.version)\n ? { t: \"hello\", id: v.id, mac: v.mac, version: v.version }\n : null;\n case \"pong\":\n return isInt(v.n) ? { t: \"pong\", n: v.n } : null;\n case \"spawned\":\n return isInt(v.cluster) &&\n isInt(v.epoch) &&\n (v.pid === undefined || isInt(v.pid))\n ? {\n t: \"spawned\",\n cluster: v.cluster,\n epoch: v.epoch,\n ...(v.pid !== undefined ? { pid: v.pid as number } : {}),\n }\n : null;\n case \"frame\":\n return isInt(v.cluster) && isInt(v.epoch)\n ? {\n t: \"frame\",\n cluster: v.cluster,\n epoch: v.epoch,\n frame: v.frame,\n }\n : null;\n case \"exit\":\n return isInt(v.cluster) &&\n isInt(v.epoch) &&\n (v.code === null || typeof v.code === \"number\") &&\n (v.signal === null || typeof v.signal === \"string\")\n ? {\n t: \"exit\",\n cluster: v.cluster,\n epoch: v.epoch,\n code: v.code,\n signal: v.signal,\n }\n : null;\n case \"error\":\n return isInt(v.cluster) &&\n isInt(v.epoch) &&\n typeof v.message === \"string\"\n ? {\n t: \"error\",\n cluster: v.cluster,\n epoch: v.epoch,\n message: v.message,\n }\n : null;\n default:\n return null;\n }\n}\n",
|
|
30
30
|
"/** One answer collected for a request. */\nexport interface Settled<R = unknown> {\n key: string;\n result?: R;\n error?: string;\n}\n\n/** What a finished request collected. */\nexport interface Collected<R = unknown> {\n /** Answers by key, in arrival order. */\n replies: Settled<R>[];\n /** Keys that never answered (timeout, abort or shutdown). */\n missing: string[];\n}\n\ninterface Entry {\n keys: Map<string, number>;\n replies: Settled[];\n /** Keys that already have an answer, for constant-time checks on large broadcasts. */\n answered: Set<string>;\n finish: () => void;\n timer: ReturnType<typeof setTimeout>;\n cleanup?: () => void;\n}\n\n/**\n * Pending requests of the parent process. Each request waits for answers from\n * a set of keys (a shard, a cluster, or every shard of the live clusters) and\n * settles when all arrived, the timeout passed, or the caller aborted.\n *\n * Answers are accepted only from the owner of the key, once per key; late,\n * duplicate and unknown answers are ignored. The number of waiting requests is\n * bounded, and a timer per request guarantees the entry is removed.\n */\nexport class RequestTracker {\n readonly #pending = new Map<string, Entry>();\n readonly #maxPending: number;\n readonly #prefix: string;\n #counter = 0;\n\n public constructor(prefix = \"parent\", maxPending = 10_000) {\n if (!Number.isInteger(maxPending) || maxPending < 1)\n throw new RangeError(\"maxPending must be a positive integer.\");\n this.#prefix = prefix;\n this.#maxPending = maxPending;\n }\n\n /** Requests waiting for answers. */\n public get size(): number {\n return this.#pending.size;\n }\n\n /**\n * Starts waiting for answers.\n * @param keys Each key that should answer, with the cluster that owns it.\n * @throws {Error} If `maxPending` requests are already waiting.\n */\n public begin<R>(options: {\n keys: ReadonlyMap<string, number>;\n timeoutMs: number;\n signal?: AbortSignal;\n }): { id: string; done: Promise<Collected<R>> } {\n if (this.#pending.size >= this.#maxPending)\n throw new Error(\n `Too many requests waiting for a reply (${this.#maxPending}).`,\n );\n const id = `${this.#prefix}:${++this.#counter}`;\n const done = new Promise<Collected<R>>((resolve) => {\n const finish = (): void => {\n const entry = this.#pending.get(id);\n if (!entry) return;\n this.#pending.delete(id);\n clearTimeout(entry.timer);\n entry.cleanup?.();\n resolve({\n replies: entry.replies as Settled<R>[],\n missing: [...entry.keys.keys()].filter(\n (key) => !entry.answered.has(key),\n ),\n });\n };\n const entry: Entry = {\n keys: new Map(options.keys),\n replies: [],\n answered: new Set(),\n finish,\n timer: setTimeout(finish, options.timeoutMs),\n };\n if (options.signal) {\n const signal = options.signal;\n if (signal.aborted) queueMicrotask(finish);\n else {\n signal.addEventListener(\"abort\", finish, { once: true });\n entry.cleanup = () =>\n signal.removeEventListener(\"abort\", finish);\n }\n }\n this.#pending.set(id, entry);\n if (entry.keys.size === 0) queueMicrotask(finish);\n });\n return { id, done };\n }\n\n /**\n * Records one answer.\n * @param from The cluster the answer came from; it must own `key`.\n * @returns Whether the answer was accepted.\n */\n public answer(\n id: string,\n key: string,\n from: number,\n value: { result?: unknown; error?: string },\n ): boolean {\n const entry = this.#pending.get(id);\n if (!entry || entry.keys.get(key) !== from) return false;\n if (entry.answered.has(key)) return false;\n entry.answered.add(key);\n entry.replies.push({ key, ...value });\n if (entry.replies.length >= entry.keys.size) entry.finish();\n return true;\n }\n\n /** A cluster went away: every key it owns that has not answered settles with `error`. */\n public failOwner(owner: number, error: string): void {\n for (const entry of [...this.#pending.values()]) {\n for (const [key, keyOwner] of entry.keys)\n if (keyOwner === owner && !entry.answered.has(key)) {\n entry.answered.add(key);\n entry.replies.push({ key, error });\n }\n if (entry.replies.length >= entry.keys.size) entry.finish();\n }\n }\n\n /** Settles every waiting request with what it has (shutdown). */\n public close(): void {\n for (const entry of [...this.#pending.values()]) entry.finish();\n }\n}\n",
|
|
31
|
-
"import type { ChildProcess } from \"node:child_process\";\nimport { fork } from \"node:child_process\";\nimport {\n KILL_SIGNALS,\n REMOTE_PROTOCOL_VERSION,\n RemoteClose,\n HOST_ID,\n isLoopbackHost,\n parseToHost,\n signChallenge,\n type ToCoordinator,\n} from \"./remote-protocol.js\";\n\n/** Options for a {@link ClusterHost}. */\nexport interface ClusterHostOptions {\n /** The coordinator's address: `wss://host:port`. `ws://` is accepted for this machine, or anywhere with `allowInsecure`. */\n url: string;\n /** Connect with `ws://` to a coordinator on another machine, over a network you fully trust. Default false. */\n allowInsecure?: boolean;\n /** The coordinator's shared secret. */\n secret: string;\n /** This host's ID: letters, digits, `.`, `_`, `-`. Must be unique in the deployment. */\n id: string;\n /**\n * The worker script this host runs for every cluster. A coordinator cannot\n * choose it: remote workers run only what the host owner configured.\n */\n script: string;\n /** Starts a worker process. Defaults to `child_process.fork()`. */\n fork?: (\n script: string,\n env: Record<string, string | undefined>,\n ) => ChildProcess;\n /** First delay before reconnecting, in ms. Default 1000. */\n reconnectDelay?: number;\n /** Longest delay between reconnect attempts, in ms. Default 30,000. */\n maxReconnectDelay?: number;\n}\n\n/** What a {@link ClusterHost} emits. */\nexport interface ClusterHostEvents {\n /** The coordinator accepted this host. */\n connected: [];\n /** The connection ended. Every worker was killed first. */\n disconnected: [info: { code: number; reason: string }];\n /** A worker started for a cluster. */\n clusterSpawn: [cluster: number];\n /** A worker exited. */\n clusterExit: [cluster: number, code: number | null, signal: string | null];\n /** A refused frame, a failed start or a rejected connection. */\n error: [error: Error];\n}\n\ninterface Running {\n epoch: number;\n child: ChildProcess;\n}\n\n/**\n * The agent that makes a machine part of a deployment. It connects to the\n * coordinator, starts the worker script for the clusters the coordinator\n * places here, and relays their IPC frames both ways. The coordinator's\n * `ClusterManager` supervises those workers exactly like local ones.\n *\n * Fencing: when the connection closes, or the coordinator stays silent for a\n * whole lease, the host kills every worker at once. The coordinator waits\n * longer than that before it restarts the clusters elsewhere, so a shard is\n * never owned in two places.\n */\nexport class ClusterHost {\n readonly #options: ClusterHostOptions;\n readonly #running = new Map<number, Running>();\n readonly #listeners = new Map<\n keyof ClusterHostEvents,\n Set<(...args: never[]) => void>\n >();\n #socket?: WebSocket;\n #lease = 15_000;\n #lastRx = 0;\n #watchdog?: ReturnType<typeof setInterval>;\n #retry?: ReturnType<typeof setTimeout>;\n #delay: number;\n #closed = true;\n #connected = false;\n #firstConnect?: { resolve: () => void; reject: (error: Error) => void };\n\n public constructor(options: ClusterHostOptions) {\n if (!HOST_ID.test(options.id))\n throw new RangeError(\n \"id must be 1-64 letters, digits, '.', '_' or '-'.\",\n );\n if (!options.secret || options.secret.length < 16)\n throw new RangeError(\"secret must be at least 16 characters.\");\n if (!options.script?.trim())\n throw new TypeError(\"A worker script path is required.\");\n let url: URL;\n try {\n url = new URL(options.url);\n } catch {\n throw new TypeError(\"url must be an absolute ws:// or wss:// URL.\");\n }\n if (url.protocol !== \"ws:\" && url.protocol !== \"wss:\")\n throw new TypeError(\"url must start with ws:// or wss://.\");\n if (url.username || url.password)\n throw new TypeError(\"url must not contain credentials.\");\n if (\n url.protocol === \"ws:\" &&\n !options.allowInsecure &&\n !isLoopbackHost(url.hostname)\n )\n throw new TypeError(\n \"url must use wss:// to reach another machine (allowInsecure: true overrides this for a network you fully trust).\",\n );\n this.#options = options;\n this.#delay = options.reconnectDelay ?? 1000;\n }\n\n /** Whether the coordinator has accepted this host and the link is open. */\n public get connected(): boolean {\n return this.#connected;\n }\n\n /** Cluster IDs running on this host. */\n public clusters(): number[] {\n return [...this.#running.keys()].sort((a, b) => a - b);\n }\n\n /** Listens for a host event. @returns This host. */\n public on<E extends keyof ClusterHostEvents>(\n event: E,\n listener: (...args: ClusterHostEvents[E]) => void,\n ): this {\n let set = this.#listeners.get(event);\n if (!set) this.#listeners.set(event, (set = new Set()));\n set.add(listener as never);\n return this;\n }\n\n /** Stops listening for a host event. @returns This host. */\n public off<E extends keyof ClusterHostEvents>(\n event: E,\n listener: (...args: ClusterHostEvents[E]) => void,\n ): this {\n this.#listeners.get(event)?.delete(listener as never);\n return this;\n }\n\n #emit<E extends keyof ClusterHostEvents>(\n event: E,\n ...args: ClusterHostEvents[E]\n ): void {\n for (const listener of this.#listeners.get(event) ?? [])\n try {\n (listener as (...a: ClusterHostEvents[E]) => void)(...args);\n } catch {\n // A faulty listener must not disturb the host.\n }\n }\n\n /**\n * Connects and resolves once the coordinator accepts this host. After\n * that, a lost connection is retried with backoff until {@link close}.\n * @throws {Error} If the first attempt is refused (bad secret, unknown or duplicate ID).\n */\n public connect(): Promise<void> {\n if (!this.#closed) return Promise.resolve();\n this.#closed = false;\n return new Promise<void>((resolve, reject) => {\n this.#firstConnect = { resolve, reject };\n this.#open();\n });\n }\n\n /** Kills every worker, closes the connection and stops reconnecting. */\n public close(): Promise<void> {\n this.#closed = true;\n if (this.#retry) clearTimeout(this.#retry);\n this.#retry = undefined;\n this.#fence();\n if (this.#watchdog) clearInterval(this.#watchdog);\n this.#watchdog = undefined;\n this.#connected = false;\n const socket = this.#socket;\n this.#socket = undefined;\n this.#firstConnect?.reject(new Error(\"The host was closed.\"));\n this.#firstConnect = undefined;\n if (!socket || socket.readyState === WebSocket.CLOSED)\n return Promise.resolve();\n return new Promise<void>((resolve) => {\n socket.addEventListener(\"close\", () => resolve(), { once: true });\n try {\n socket.close(1000, \"Host closed.\");\n } catch {\n resolve();\n }\n });\n }\n\n #open(): void {\n const socket = new WebSocket(this.#options.url);\n this.#socket = socket;\n socket.addEventListener(\"message\", (event) =>\n this.#message(socket, event.data),\n );\n socket.addEventListener(\"error\", () => {\n // The close event that follows carries the outcome.\n });\n socket.addEventListener(\"close\", (event) => {\n if (this.#socket === socket) this.#lost(event.code, event.reason);\n });\n }\n\n #send(socket: WebSocket, message: ToCoordinator): boolean {\n if (socket.readyState !== WebSocket.OPEN) return false;\n try {\n socket.send(JSON.stringify(message));\n return true;\n } catch {\n return false;\n }\n }\n\n #message(socket: WebSocket, data: unknown): void {\n if (this.#socket !== socket) return;\n const message = parseToHost(data);\n if (!message) {\n this.#emit(\n \"error\",\n new Error(\"The coordinator sent a malformed frame.\"),\n );\n return;\n }\n this.#lastRx = Date.now();\n switch (message.t) {\n case \"challenge\":\n if (message.version !== REMOTE_PROTOCOL_VERSION) {\n socket.close(\n RemoteClose.VersionMismatch,\n \"Protocol version mismatch.\",\n );\n return;\n }\n this.#send(socket, {\n t: \"hello\",\n id: this.#options.id,\n mac: signChallenge(\n this.#options.secret,\n message.nonce,\n this.#options.id,\n ),\n version: REMOTE_PROTOCOL_VERSION,\n });\n return;\n case \"welcome\":\n this.#lease = message.leaseMs;\n this.#connected = true;\n this.#delay = this.#options.reconnectDelay ?? 1000;\n this.#startWatchdog();\n this.#firstConnect?.resolve();\n this.#firstConnect = undefined;\n this.#emit(\"connected\");\n return;\n case \"ping\":\n this.#send(socket, { t: \"pong\", n: message.n });\n return;\n case \"bye\":\n this.#fence();\n return;\n case \"spawn\":\n this.#spawn(\n socket,\n message.cluster,\n message.epoch,\n message.env,\n );\n return;\n case \"frame\": {\n const run = this.#running.get(message.cluster);\n if (run?.epoch === message.epoch && run.child.connected)\n try {\n run.child.send(message.frame as never);\n } catch {\n // The worker is exiting; its exit event reports it.\n }\n return;\n }\n case \"kill\": {\n const run = this.#running.get(message.cluster);\n if (\n run?.epoch === message.epoch &&\n KILL_SIGNALS.has(message.signal)\n )\n try {\n run.child.kill(message.signal as NodeJS.Signals);\n } catch {\n // Already gone.\n }\n return;\n }\n }\n }\n\n #spawn(\n socket: WebSocket,\n cluster: number,\n epoch: number,\n env: Record<string, string>,\n ): void {\n const reply = (message: ToCoordinator): void => {\n if (this.#socket === socket) this.#send(socket, message);\n };\n if (this.#running.has(cluster)) {\n reply({\n t: \"error\",\n cluster,\n epoch,\n message: `Cluster ${cluster} is already running here.`,\n });\n return;\n }\n let child: ChildProcess;\n try {\n const forkWorker = this.#options.fork ?? defaultFork;\n child = forkWorker(this.#options.script, {\n ...process.env,\n ...env,\n });\n } catch (error) {\n reply({\n t: \"error\",\n cluster,\n epoch,\n message: error instanceof Error ? error.message : String(error),\n });\n return;\n }\n this.#running.set(cluster, { epoch, child });\n child.on(\"message\", (frame) =>\n reply({ t: \"frame\", cluster, epoch, frame }),\n );\n child.once(\"exit\", (code, signal) => {\n if (this.#running.get(cluster)?.child === child)\n this.#running.delete(cluster);\n this.#emit(\"clusterExit\", cluster, code, signal);\n reply({ t: \"exit\", cluster, epoch, code, signal });\n });\n child.on(\"error\", (error) =>\n this.#emit(\n \"error\",\n error instanceof Error ? error : new Error(String(error)),\n ),\n );\n reply({\n t: \"spawned\",\n cluster,\n epoch,\n ...(child.pid ? { pid: child.pid } : {}),\n });\n this.#emit(\"clusterSpawn\", cluster);\n }\n\n /** Kills every worker now. Used whenever the coordinator might consider the host gone. */\n #fence(): void {\n for (const { child } of [...this.#running.values()])\n try {\n child.kill(\"SIGKILL\");\n } catch {\n // Already gone.\n }\n }\n\n #startWatchdog(): void {\n if (this.#watchdog) clearInterval(this.#watchdog);\n this.#lastRx = Date.now();\n this.#watchdog = setInterval(\n () => {\n if (Date.now() - this.#lastRx > this.#lease) {\n this.#fence();\n // Closing triggers the reconnect path.\n this.#socket?.close(\n RemoteClose.HostLost,\n \"Coordinator silent.\",\n );\n }\n },\n Math.max(10, Math.floor(this.#lease / 3)),\n );\n (this.#watchdog as { unref?: () => void }).unref?.();\n }\n\n #lost(code: number, reason: string): void {\n if (this.#watchdog) clearInterval(this.#watchdog);\n this.#watchdog = undefined;\n this.#socket = undefined;\n const wasConnected = this.#connected;\n this.#connected = false;\n this.#fence();\n if (wasConnected) this.#emit(\"disconnected\", { code, reason });\n if (this.#closed) return;\n const refused =\n code === RemoteClose.AuthFailed ||\n code === RemoteClose.UnknownHost ||\n code === RemoteClose.DuplicateHost ||\n code === RemoteClose.VersionMismatch;\n const first = this.#firstConnect;\n if (first) {\n this.#firstConnect = undefined;\n this.#closed = true;\n first.reject(\n new Error(\n `The coordinator refused this host (${code}${reason ? `: ${reason}` : \"\"}).`,\n ),\n );\n return;\n }\n if (refused)\n this.#emit(\n \"error\",\n new Error(\n `The coordinator refused this host (${code}: ${reason}).`,\n ),\n );\n const wait = Math.round(this.#delay * (1 + Math.random() * 0.2));\n this.#delay = Math.min(\n this.#options.maxReconnectDelay ?? 30_000,\n this.#delay * 2,\n );\n this.#retry = setTimeout(() => {\n this.#retry = undefined;\n if (!this.#closed) this.#open();\n }, wait);\n }\n}\n\nfunction defaultFork(\n script: string,\n env: Record<string, string | undefined>,\n): ChildProcess {\n return fork(script, [], { env: env as NodeJS.ProcessEnv });\n}\n",
|
|
32
|
-
"import {\n assertGuildsPerShard,\n fetchGatewayBot,\n resolveShardCount,\n type GatewayBotInfo,\n} from \"./gateway-bot.js\";\nimport { Gateway, GatewayState } from \"@lunibee/ws\";\nimport {\n readClusterContext,\n validateShardIds,\n type ClusterContext,\n} from \"./context.js\";\nimport type {\n ShardHealth,\n ShardStatus,\n WorkerMetrics,\n WorkerReport,\n} from \"./health.js\";\nimport { REQUEST_ASSIGN, REQUEST_HEALTH } from \"./protocol.js\";\nimport { ClusterWorker, type IdentifyGate } from \"./worker.js\";\n\n/** Runtime-agnostic delay used between shard starts (works under Node and Bun). @param ms Milliseconds to wait. */\nconst sleep = (ms: number): Promise<void> =>\n new Promise<void>((resolve) => setTimeout(resolve, ms));\n\nexport {\n MAX_GUILDS_PER_SHARD,\n fetchGatewayBot,\n fetchGuildCount,\n shardsForGuilds,\n type GatewayBotInfo,\n} from \"./gateway-bot.js\";\nexport { ShardBus } from \"./bus.js\";\nexport {\n BroadcastChannelTransport,\n IpcTransport,\n type IpcEndpoint,\n type ShardBusTransport,\n} from \"./transport.js\";\nexport { ShardSupervisor, type SupervisorOptions } from \"./supervisor.js\";\nexport type {\n ShardBusErrorHandler,\n ShardBusOptions,\n ShardMessage,\n ShardMessageHandler,\n ShardReply,\n} from \"./bus.js\";\n\nexport { ClusterManager } from \"./cluster.js\";\nexport type {\n ClusterManagerOptions,\n ClusterManagerEvents,\n ClusterInfo,\n BroadcastResult,\n RequestOptions,\n} from \"./cluster.js\";\nexport {\n allocateShards,\n assertCompleteAllocation,\n isClusterWorker,\n readClusterContext,\n validateShardIds,\n type ClusterContext,\n} from \"./context.js\";\nexport { ClusterWorker, type IdentifyGate } from \"./worker.js\";\nexport {\n ClusterHost,\n type ClusterHostEvents,\n type ClusterHostOptions,\n} from \"./host.js\";\nexport type { HostStatus, RemoteOptions } from \"./remote.js\";\nexport { RemoteClose, REMOTE_PROTOCOL_VERSION } from \"./remote-protocol.js\";\nexport {\n IdentifyCoordinator,\n IDENTIFY_INTERVAL,\n type IdentifyBudget,\n type IdentifyCoordinatorOptions,\n} from \"./identify.js\";\nexport { RequestTracker } from \"./requests.js\";\nexport type {\n ClusterHealth,\n ClusterManagerHealth,\n ClusterState,\n ShardHealth,\n ShardStatus,\n WorkerMetrics,\n WorkerReport,\n} from \"./health.js\";\n\n/** Whether a shard has no live socket, so connecting it costs an IDENTIFY. */\nfunction needsConnection(gateway: Gateway): boolean {\n return (\n gateway.state === GatewayState.Connect ||\n gateway.state === GatewayState.Reconnect ||\n gateway.state === GatewayState.Closed\n );\n}\n\n/** States reached only after IDENTIFY or RESUME has been sent. */\nconst HANDSHAKE_SENT = new Set<string>([\n GatewayState.Identify,\n GatewayState.Resume,\n GatewayState.Ready,\n GatewayState.Dispatch,\n GatewayState.Heartbeat,\n GatewayState.Closed,\n]);\n\n/** Resolves once the shard has sent IDENTIFY/RESUME (or closed), or after `timeout` ms. */\nfunction handshakeSent(gateway: Gateway, timeout: number): Promise<void> {\n if (HANDSHAKE_SENT.has(gateway.state)) return Promise.resolve();\n return new Promise((resolve) => {\n const done = (): void => {\n clearTimeout(timer);\n gateway.off(\"stateChange\", onState);\n resolve();\n };\n const onState = (change: unknown): void => {\n if (HANDSHAKE_SENT.has((change as { next: string }).next)) done();\n };\n const timer = setTimeout(done, timeout);\n gateway.on(\"stateChange\", onState);\n });\n}\n\n/** Configuration for a sharded Gateway client. */\nexport interface ShardManagerOptions {\n /** Bot token. */\n token: string;\n /** Gateway intents as an array, e.g. `[GatewayIntentBits.Guilds]`. */\n intents: ConstructorParameters<typeof Gateway>[0][\"intents\"];\n /** Number of shards. Use `\"auto\"` to request Discord's recommended count. */\n shardCount?: number | \"auto\";\n /**\n * With `shardCount: \"auto\"`, size the deployment by guilds instead of\n * Discord's recommendation: `ceil(guilds / guildsPerShard)` shards, at\n * least one. 1 to 2500 (Discord's limit); Discord recommends about 1000.\n * Guilds are assigned to shards by their ID, so this sets the average,\n * not an exact split.\n */\n guildsPerShard?: number;\n /** The bot's guild count for `guildsPerShard`, instead of asking Discord for it. */\n guildCount?: number;\n /** Gateway reconnect behavior. */\n reconnect?: boolean;\n /**\n * Delay between shard starts in milliseconds. Defaults to 5000 to respect\n * Discord's IDENTIFY rate limit (one per 5s per rate-limit key); set 0 to\n * opt out when an external scheduler already paces the handshakes.\n */ spawnDelay?: number;\n /**\n * Shards that may IDENTIFY at once (Discord's `max_concurrency`). Defaults\n * to the value from `/gateway/bot` when it was fetched (auto shard count),\n * else 1.\n */ maxConcurrency?: number;\n /**\n * How long a startup round waits for its shards to send IDENTIFY/RESUME\n * before the next round's `spawnDelay` starts. Defaults to 15000 ms.\n */ handshakeTimeout?: number;\n /** Interval in milliseconds to automatically check for recommended shard count and re-scale if needed. Must be an integer >= 1000. */\n autoScaleInterval?: number;\n /** Optional handler invoked when a background auto-scale check fails. Receives the thrown error. */\n onAutoScaleError?: (error: unknown) => void;\n /**\n * The shards this manager runs, out of `shardCount`. Defaults to all of\n * them, or to the cluster's own list inside a worker forked by\n * `ClusterManager`. Must be unique integers in `[0, shardCount)`.\n */\n shardIds?: number[];\n /**\n * The worker contract. Read from the environment inside a worker forked by\n * `ClusterManager`; pass `false` to ignore it, or a context to override it.\n */\n cluster?: ClusterContext | false;\n /**\n * Where to get IDENTIFY slots. Inside a coordinated cluster this is the\n * parent, so the limit holds across all clusters. Pass `false` to pace\n * locally with `spawnDelay` instead.\n */\n identify?: IdentifyGate | false;\n /** The worker to report to. Defaults to this process's `ClusterWorker` inside a cluster. */\n worker?: ClusterWorker | false;\n}\n/** What `ShardManager` emits about its shards. */\nexport interface ShardManagerEvents {\n /** A shard finished its handshake and is receiving events. */\n shardReady: [shardId: number];\n /** A shard's connection closed (it may reconnect by itself). */\n shardDisconnect: [shardId: number, close: { code: number; action: string }];\n /** A shard resumed its session after a reconnect. */\n shardResume: [shardId: number];\n /** A shard reported an error. */\n shardError: [shardId: number, error: Error];\n /** Every shard this manager owns is ready. Emitted again after a shard drops and recovers. */\n ready: [];\n /** A shard reconnected too often too fast and is backing off (see `Gateway` `unstable`). */\n shardUnstable: [\n shardId: number,\n info: { reconnects: number; delay: number },\n ];\n}\n\n/** Runtime state for a managed shard. */\nexport interface ShardInfo {\n /** Shard identifier. */\n id: number;\n /** Gateway instance. */\n gateway: Gateway;\n}\n\n/** Manages independent Discord Gateway shards with explicit destruction and reinitialization semantics. */\nexport class ShardManager {\n /** Discord's minimum interval between IDENTIFY payloads, in milliseconds. */\n public static readonly IDENTIFY_INTERVAL = 5000;\n /** Active Gateway shards indexed by shard identifier. */\n public readonly shards = new Map<number, Gateway>();\n /** Total shards in the deployment once initialized, 0 before and after `destroy()`. */\n public get shardCount(): number {\n return this.#resolved ? this.#total : 0;\n }\n /** IDs of the shards this manager owns. */\n public get shardIds(): number[] {\n return [...this.shards.keys()].sort((a, b) => a - b);\n }\n readonly #options: ShardManagerOptions;\n /** Shards this manager was asked to own; every shard when `undefined`. */\n #ids: number[] | undefined;\n readonly #gate: IdentifyGate | undefined;\n readonly #worker: ClusterWorker | undefined;\n readonly #lifecycle = new Map<\n number,\n { status: ShardStatus; readyAt?: number; disconnectedAt?: number }\n >();\n /** Whether `ready` was announced since the last shard dropped. */\n #announced = false;\n /** Whether the shard set has been initialized. */\n #resolved = false;\n /** Whether this manager is currently destroyed. */\n #destroyed = false;\n /** Whether the manager was created in auto shard-count mode. Preserved across reshards so auto-scaling keeps running. */\n readonly #auto: boolean;\n #autoScaleTimer?: ReturnType<typeof setInterval>;\n /** The shard count of the current shard set; kept while a reshard has no shards so `getShardIdForGuild` stays right. */\n #total: number;\n readonly #listeners = new Map<\n keyof ShardManagerEvents,\n Set<(...args: unknown[]) => void>\n >();\n /** Last `/gateway/bot` answer, used for concurrency and the start limit. */\n #gatewayInfo?: GatewayBotInfo;\n /** Creates a shard manager. @param options Sharding configuration. @throws {TypeError} If token or intents are invalid. @throws {RangeError} If shard count is invalid. */\n public constructor(options: ShardManagerOptions) {\n if (!options.token?.trim())\n throw new TypeError(\"A Discord bot token is required.\");\n if (!Array.isArray(options.intents))\n throw new TypeError(\n \"Gateway intents must be an array, e.g. [GatewayIntentBits.Guilds].\",\n );\n const context =\n options.cluster === false\n ? null\n : (options.cluster ?? readClusterContext());\n if (context) {\n if (options.shardCount === \"auto\")\n throw new RangeError(\n 'shardCount \"auto\" is the ClusterManager\\'s decision inside a cluster worker.',\n );\n if (\n options.shardCount !== undefined &&\n options.shardCount !== context.shardCount\n )\n throw new RangeError(\n `shardCount ${options.shardCount} differs from the cluster's ${context.shardCount}.`,\n );\n if (options.autoScaleInterval !== undefined)\n throw new TypeError(\n \"autoScaleInterval belongs to ClusterManager inside a cluster worker.\",\n );\n if (\n options.shardIds &&\n !options.shardIds.every((id) => context.shardIds.includes(id))\n )\n throw new RangeError(\n \"shardIds must be a subset of the cluster's own shards.\",\n );\n }\n const count = context\n ? context.shardCount\n : options.shardCount === \"auto\" || options.shardCount === undefined\n ? 1\n : options.shardCount;\n if (!Number.isInteger(count) || count < 1)\n throw new RangeError(\"Shard count must be a positive integer.\");\n if (options.guildsPerShard !== undefined) {\n assertGuildsPerShard(options.guildsPerShard);\n if (options.shardCount !== \"auto\")\n throw new RangeError(\n 'guildsPerShard needs shardCount: \"auto\".',\n );\n }\n this.#ids = options.shardIds ?? context?.shardIds;\n if (this.#ids && options.shardCount !== \"auto\")\n validateShardIds(this.#ids, count);\n this.#worker =\n options.worker === false\n ? undefined\n : (options.worker ??\n (context && options.cluster === undefined\n ? ClusterWorker.current()\n : undefined));\n this.#gate =\n options.identify === false\n ? undefined\n : (options.identify ??\n (context?.coordinatedIdentify\n ? this.#worker?.identifyGate\n : undefined));\n if (\n options.autoScaleInterval !== undefined &&\n (!Number.isInteger(options.autoScaleInterval) ||\n options.autoScaleInterval < 1000)\n )\n throw new RangeError(\n \"autoScaleInterval must be an integer of at least 1000 milliseconds.\",\n );\n if (\n options.spawnDelay !== undefined &&\n (!Number.isFinite(options.spawnDelay) || options.spawnDelay < 0)\n )\n throw new RangeError(\n \"spawnDelay must be a non-negative finite number of milliseconds.\",\n );\n if (\n options.maxConcurrency !== undefined &&\n (!Number.isInteger(options.maxConcurrency) ||\n options.maxConcurrency < 1)\n )\n throw new RangeError(\"maxConcurrency must be a positive integer.\");\n if (\n options.handshakeTimeout !== undefined &&\n (!Number.isFinite(options.handshakeTimeout) ||\n options.handshakeTimeout < 0)\n )\n throw new RangeError(\n \"handshakeTimeout must be a non-negative number of milliseconds.\",\n );\n this.#total = count;\n this.#auto = options.shardCount === \"auto\";\n // Discord allows one IDENTIFY per 5s per rate-limit key. Spawning\n // shards back-to-back trips that limit and the gateway answers with\n // close 4008 / invalid session, so 5s is the default rather than an\n // opt-in.\n this.#options = {\n spawnDelay: ShardManager.IDENTIFY_INTERVAL,\n ...options,\n };\n this.#worker?.onRequest(REQUEST_HEALTH, (data) =>\n this.report(\n (data as { metrics?: boolean } | null | undefined)?.metrics ===\n true,\n ),\n );\n this.#worker?.onRequest(REQUEST_ASSIGN, async (data) => {\n const ids = (data as { shardIds?: unknown } | null)?.shardIds;\n if (!Array.isArray(ids))\n throw new TypeError(\"shardIds must be an array.\");\n await this.addShards(ids as number[]);\n return { shardIds: ids };\n });\n if (options.shardCount !== \"auto\") this.#initialize(count);\n }\n /** Retrieves Discord's recommended shard count. @returns Recommended shard count. @throws {Error} If discovery fails or returns invalid data. */\n public async fetchRecommendedShardCount(): Promise<number> {\n return resolveShardCount(\n this.#options.token,\n await this.fetchGatewayInfo(),\n this.#options,\n );\n }\n /** Retrieves `/gateway/bot`: recommended shards and the IDENTIFY budget. Remembered for pacing the next connect. @throws {Error} If discovery fails or returns invalid data. */\n public async fetchGatewayInfo(): Promise<GatewayBotInfo> {\n const info = await fetchGatewayBot(this.#options.token);\n this.#gatewayInfo = info;\n return info;\n }\n /** Connects all shards sequentially. A destroyed manager is reinitialized before connecting. @returns A promise fulfilled after all shards connect. @throws {Error} If a shard fails to connect. */\n public async connect(): Promise<void> {\n // Clear any timer from a previous connect() so a second call cannot leak one.\n if (this.#autoScaleTimer) {\n clearInterval(this.#autoScaleTimer);\n this.#autoScaleTimer = undefined;\n }\n await this.#ensureInitialized();\n // Only shards without a live socket spend an IDENTIFY; connect() is a\n // no-op for the rest, so they are neither counted nor paced.\n await this.#start(\n [...this.shards]\n .filter(([, gateway]) => needsConnection(gateway))\n .map(([id]) => id),\n );\n if (this.#options.autoScaleInterval && !this.#autoScaleTimer) {\n this.#autoScaleTimer = setInterval(() => {\n void this.checkAutoScale();\n }, this.#options.autoScaleInterval);\n }\n }\n /**\n * Takes over more shards of the same deployment (recovery after another\n * cluster gave up) and connects them through the same IDENTIFY path.\n * @throws {RangeError} If an ID is invalid, already owned, or the manager is not initialized.\n */\n public async addShards(shardIds: readonly number[]): Promise<void> {\n await this.#ensureInitialized();\n validateShardIds(shardIds, this.#total);\n for (const id of shardIds)\n if (this.shards.has(id))\n throw new RangeError(`Shard ${id} is already owned.`);\n for (const id of shardIds)\n this.shards.set(id, this.#createShard(id, this.#total));\n if (this.#ids) this.#ids = [...this.#ids, ...shardIds];\n this.#announced = false;\n try {\n // Only the new shards are torn down on failure: the ones that were\n // already running keep their sessions.\n await this.#start([...shardIds], false);\n } catch (error) {\n for (const id of shardIds) {\n this.shards.get(id)?.close();\n this.shards.delete(id);\n this.#lifecycle.delete(id);\n }\n if (this.#ids)\n this.#ids = this.#ids.filter((id) => !shardIds.includes(id));\n throw error;\n }\n }\n /** Starts the given shards: through the coordinated IDENTIFY gate when there is one, else in paced rounds. */\n async #start(ids: number[], destroyOnFailure = true): Promise<void> {\n const gate = this.#gate;\n if (gate) {\n const timeout = this.#options.handshakeTimeout ?? 15_000;\n const results = await Promise.allSettled(\n ids.map(async (id) => {\n const gateway = this.shards.get(id)!;\n this.#setStatus(id, \"connecting\");\n await gate.acquire(id);\n try {\n await gateway.connect();\n await handshakeSent(gateway, timeout);\n } finally {\n gate.release(id);\n }\n }),\n );\n const failed = results.findIndex(\n (result) => result.status === \"rejected\",\n );\n if (failed !== -1) {\n if (destroyOnFailure) this.destroy();\n throw new Error(`Failed to connect shard ${ids[failed]}.`, {\n cause: (results[failed] as PromiseRejectedResult).reason,\n });\n }\n return;\n }\n const limit = this.#gatewayInfo?.sessionStartLimit;\n if (limit && limit.remaining < ids.length)\n // Live shards are left running: the budget only blocks new ones.\n throw new Error(\n `Session start limit exhausted: ${limit.remaining} of ${limit.total} IDENTIFYs left for ${ids.length} shards; resets in ${Math.ceil(limit.resetAfter / 1000)}s.`,\n );\n // Shards whose IDs differ modulo max_concurrency use different\n // IDENTIFY rate-limit keys, so each round of consecutive IDs may start\n // together; rounds are spaced by spawnDelay. An override may lower the\n // concurrency but never exceed what Discord reported.\n const concurrency = Math.min(\n this.#options.maxConcurrency ?? limit?.maxConcurrency ?? 1,\n limit?.maxConcurrency ?? Infinity,\n );\n // Discord's IDENTIFY rate-limit key is `shard_id % max_concurrency`: one\n // IDENTIFY per key per 5 seconds. Each round starts the next shard of\n // every bucket, so a round never puts two shards on one key.\n const buckets = new Map<number, number[]>();\n for (const id of ids) {\n const key = id % concurrency;\n const bucket = buckets.get(key);\n if (bucket) bucket.push(id);\n else buckets.set(key, [id]);\n }\n const rounds = Math.max(\n 0,\n ...[...buckets.values()].map((bucket) => bucket.length),\n );\n for (let index = 0; index < rounds; index++) {\n const round = [...buckets.values()]\n .map((bucket) => bucket[index])\n .filter((id): id is number => id !== undefined);\n for (const id of round) this.#setStatus(id, \"connecting\");\n const results = await Promise.allSettled(\n round.map((id) => this.shards.get(id)!.connect()),\n );\n const failed = results.findIndex(\n (result) => result.status === \"rejected\",\n );\n if (failed !== -1) {\n if (destroyOnFailure) this.destroy();\n throw new Error(`Failed to connect shard ${round[failed]}.`, {\n cause: (results[failed] as PromiseRejectedResult).reason,\n });\n }\n if (\n index + 1 < rounds &&\n this.#options.spawnDelay &&\n this.#options.spawnDelay > 0\n ) {\n // connect() resolves when the socket opens; IDENTIFY follows\n // HELLO. Start the interval from the handshake itself so a late\n // HELLO cannot squeeze two rounds' IDENTIFYs together.\n await Promise.all(\n round.map((id) =>\n handshakeSent(\n this.shards.get(id)!,\n this.#options.handshakeTimeout ?? 15_000,\n ),\n ),\n );\n await sleep(this.#options.spawnDelay);\n }\n }\n }\n /** Checks if the recommended shard count has changed and reconnects if so. */\n public async checkAutoScale(): Promise<void> {\n if (!this.#auto) return;\n try {\n const recommended = await this.fetchRecommendedShardCount();\n if (recommended !== this.shardCount) {\n await this.reshard(recommended);\n }\n } catch (error) {\n // Surface the failure instead of silently swallowing it; the next\n // interval tick retries.\n this.#options.onAutoScaleError?.(error);\n }\n }\n /** Forces a reshard to a new shard count, replacing all active shards. @param count New shard count. */\n public async reshard(count: number): Promise<void> {\n this.#options.shardCount = count;\n this.destroy();\n await this.connect();\n }\n /** Permanently closes the current shard set and releases Gateway resources. The next connect recreates them. @returns Nothing. */\n public destroy(): void {\n for (const shard of this.shards.values()) shard.close();\n for (const id of this.shards.keys()) this.#setStatus(id, \"destroyed\");\n this.shards.clear();\n this.#lifecycle.clear();\n this.#announced = false;\n this.#resolved = false;\n this.#destroyed = true;\n if (this.#autoScaleTimer) {\n clearInterval(this.#autoScaleTimer);\n this.#autoScaleTimer = undefined;\n }\n }\n /** Listens for an aggregated shard event. @returns This manager. */\n public on<E extends keyof ShardManagerEvents>(\n event: E,\n listener: (...args: ShardManagerEvents[E]) => void,\n ): this {\n let set = this.#listeners.get(event);\n if (!set) this.#listeners.set(event, (set = new Set()));\n set.add(listener as unknown as (...args: unknown[]) => void);\n return this;\n }\n /** Stops listening for a shard event. @returns This manager. */\n public off<E extends keyof ShardManagerEvents>(\n event: E,\n listener: (...args: ShardManagerEvents[E]) => void,\n ): this {\n this.#listeners\n .get(event)\n ?.delete(listener as unknown as (...args: unknown[]) => void);\n return this;\n }\n #emit<E extends keyof ShardManagerEvents>(\n event: E,\n ...args: ShardManagerEvents[E]\n ): void {\n for (const listener of this.#listeners.get(event) ?? [])\n try {\n listener(...args);\n } catch {\n // A faulty listener must not disturb the shard.\n }\n }\n /** Calculates the target shard ID for a Discord guild snowflake. @param guildId Guild snowflake. @returns Shard identifier. */\n public getShardIdForGuild(guildId: string): number {\n const id = BigInt(guildId);\n return Number((id >> 22n) % BigInt(this.#total));\n }\n /** Sends a voice state update (op 4) on the shard that owns the guild. @returns Whether it was sent; `false` if that shard is absent or disconnected. */\n public sendVoiceState(\n guildId: string,\n channelId: string | null,\n options: { selfMute?: boolean; selfDeaf?: boolean } = {},\n ): boolean {\n return (\n this.shards\n .get(this.getShardIdForGuild(guildId))\n ?.sendVoiceState(guildId, channelId, options) ?? false\n );\n }\n /** Gets a shard by ID. @param id Shard identifier. @returns Gateway instance or undefined. */\n public get(id: number): Gateway | undefined {\n return this.shards.get(id);\n }\n /** Returns each shard's connection state, lifecycle status and heartbeat latency. */\n public health(): ShardHealth[] {\n return [...this.shards].map(([id, gateway]) => {\n const life = this.#lifecycle.get(id);\n return {\n id,\n state: gateway.state,\n ping: gateway.ping,\n status: life?.status ?? \"idle\",\n ...(life?.readyAt !== undefined\n ? { readyAt: life.readyAt }\n : {}),\n ...(life?.disconnectedAt !== undefined\n ? { disconnectedAt: life.disconnectedAt }\n : {}),\n };\n });\n }\n /** A health report for the parent: shard health and, on request, process metrics. */\n public report(metrics = false): WorkerReport {\n let numbers: WorkerMetrics | undefined;\n if (metrics) {\n const memory = process.memoryUsage();\n const cpu = process.cpuUsage();\n numbers = {\n rssBytes: memory.rss,\n heapUsedBytes: memory.heapUsed,\n cpuUserMicros: cpu.user,\n cpuSystemMicros: cpu.system,\n };\n }\n return {\n shards: this.health(),\n ...(numbers ? { metrics: numbers } : {}),\n reportedAt: Date.now(),\n };\n }\n #setStatus(id: number, status: ShardStatus): void {\n const life = this.#lifecycle.get(id) ?? { status };\n life.status = status;\n const now = Date.now();\n if (status === \"ready\") life.readyAt = now;\n if (status === \"disconnected\") life.disconnectedAt = now;\n this.#lifecycle.set(id, life);\n if (status !== \"ready\") this.#announced = false;\n else this.#maybeAnnounce();\n }\n /** Emits `ready` and tells the parent once every owned shard is ready. */\n #maybeAnnounce(): void {\n if (this.#announced || this.shards.size === 0) return;\n for (const id of this.shards.keys())\n if (this.#lifecycle.get(id)?.status !== \"ready\") return;\n this.#announced = true;\n this.#emit(\"ready\");\n this.#worker?.ready(this.shardIds);\n this.#worker?.reportHealth(this.report());\n }\n /** Returns information for all managed shards. @returns Shard information snapshots. */\n public values(): ShardInfo[] {\n return [...this.shards].map(([id, gateway]) => ({ id, gateway }));\n }\n /** Ensures a live shard set exists. @returns A promise fulfilled after initialization. @throws {Error} If initialization fails. */\n async #ensureInitialized(): Promise<void> {\n if (this.#resolved) return;\n if (this.#destroyed) this.#destroyed = false;\n const count =\n this.#options.shardCount === \"auto\"\n ? await this.fetchRecommendedShardCount()\n : (this.#options.shardCount ?? 1);\n this.#initialize(count);\n }\n /** Creates every shard for a resolved shard count. @param count Number of shards. @returns Nothing. */\n #initialize(count: number): void {\n if (this.#resolved) return;\n this.#total = count;\n const ids = this.#ids ?? Array.from({ length: count }, (_, i) => i);\n validateShardIds(ids, count);\n for (const id of ids) this.shards.set(id, this.#createShard(id, count));\n this.#resolved = true;\n }\n /** Creates one Gateway instance with its shard identity. @param id Shard identifier. @param count Total shard count. @returns Configured Gateway. */\n #createShard(id: number, count: number): Gateway {\n const gateway = new Gateway({\n token: this.#options.token,\n intents: this.#options.intents,\n shardId: id,\n shardCount: count,\n reconnect: this.#options.reconnect ?? true,\n });\n gateway.on(\"ready\", () => {\n this.#setStatus(id, \"ready\");\n this.#emit(\"shardReady\", id);\n });\n gateway.on(\"resumed\", () => {\n this.#setStatus(id, \"ready\");\n this.#emit(\"shardResume\", id);\n });\n gateway.on(\"close\", (close) => {\n this.#setStatus(id, \"disconnected\");\n this.#emit(\n \"shardDisconnect\",\n id,\n close as { code: number; action: string },\n );\n });\n gateway.on(\"error\", (error) =>\n this.#emit(\"shardError\", id, error as Error),\n );\n gateway.on(\"unstable\", (info) => {\n this.#setStatus(id, \"unstable\");\n this.#emit(\n \"shardUnstable\",\n id,\n info as { reconnects: number; delay: number },\n );\n });\n return gateway;\n }\n}\n\n/** Discord.js-familiar alias for {@link ShardManager}. */\nexport { ShardManager as ShardingManager };\n"
|
|
31
|
+
"import type { ChildProcess } from \"node:child_process\";\nimport { fork } from \"node:child_process\";\nimport {\n KILL_SIGNALS,\n REMOTE_PROTOCOL_VERSION,\n RemoteClose,\n HOST_ID,\n isLoopbackHost,\n parseToHost,\n signChallenge,\n type ToCoordinator,\n} from \"./remote-protocol.js\";\n\n/** Options for a {@link ClusterHost}. */\nexport interface ClusterHostOptions {\n /** The coordinator's address: `wss://host:port`. `ws://` is accepted for this machine, or anywhere with `allowInsecure`. */\n url: string;\n /** Connect with `ws://` to a coordinator on another machine, over a network you fully trust. Default false. */\n allowInsecure?: boolean;\n /** The coordinator's shared secret. */\n secret: string;\n /** This host's ID: letters, digits, `.`, `_`, `-`. Must be unique in the deployment. */\n id: string;\n /**\n * The worker script this host runs for every cluster. A coordinator cannot\n * choose it: remote workers run only what the host owner configured.\n */\n script: string;\n /** Starts a worker process. Defaults to `child_process.fork()`. */\n fork?: (\n script: string,\n env: Record<string, string | undefined>,\n ) => ChildProcess;\n /** First delay before reconnecting, in ms. Default 1000. */\n reconnectDelay?: number;\n /** Longest delay between reconnect attempts, in ms. Default 30,000. */\n maxReconnectDelay?: number;\n /**\n * Attempts `connect()` makes while the coordinator is busy (it answers\n * \"too many connections\" or the hello timed out) before it gives up.\n * Default 10. A refusal (bad secret, unknown or duplicate id) is final.\n */\n connectAttempts?: number;\n}\n\n/** What a {@link ClusterHost} emits. */\nexport interface ClusterHostEvents {\n /** The coordinator accepted this host. */\n connected: [];\n /** The connection ended. Every worker was killed first. */\n disconnected: [info: { code: number; reason: string }];\n /** A worker started for a cluster. */\n clusterSpawn: [cluster: number];\n /** A worker exited. */\n clusterExit: [cluster: number, code: number | null, signal: string | null];\n /** A refused frame, a failed start or a rejected connection. */\n error: [error: Error];\n}\n\ninterface Running {\n epoch: number;\n child: ChildProcess;\n}\n\n/**\n * The agent that makes a machine part of a deployment. It connects to the\n * coordinator, starts the worker script for the clusters the coordinator\n * places here, and relays their IPC frames both ways. The coordinator's\n * `ClusterManager` supervises those workers exactly like local ones.\n *\n * Fencing: when the connection closes, or the coordinator stays silent for a\n * whole lease, the host kills every worker at once. The coordinator waits\n * longer than that before it restarts the clusters elsewhere, so a shard is\n * never owned in two places.\n */\nexport class ClusterHost {\n readonly #options: ClusterHostOptions;\n readonly #running = new Map<number, Running>();\n readonly #listeners = new Map<\n keyof ClusterHostEvents,\n Set<(...args: never[]) => void>\n >();\n #socket?: WebSocket;\n #lease = 15_000;\n #lastRx = 0;\n #watchdog?: ReturnType<typeof setInterval>;\n #retry?: ReturnType<typeof setTimeout>;\n #delay: number;\n /** Busy answers to the first connect so far. */\n #busyAttempts = 0;\n #closed = true;\n #connected = false;\n #firstConnect?: { resolve: () => void; reject: (error: Error) => void };\n\n public constructor(options: ClusterHostOptions) {\n if (!HOST_ID.test(options.id))\n throw new RangeError(\n \"id must be 1-64 letters, digits, '.', '_' or '-'.\",\n );\n if (!options.secret || options.secret.length < 16)\n throw new RangeError(\"secret must be at least 16 characters.\");\n if (!options.script?.trim())\n throw new TypeError(\"A worker script path is required.\");\n let url: URL;\n try {\n url = new URL(options.url);\n } catch {\n throw new TypeError(\"url must be an absolute ws:// or wss:// URL.\");\n }\n if (url.protocol !== \"ws:\" && url.protocol !== \"wss:\")\n throw new TypeError(\"url must start with ws:// or wss://.\");\n if (url.username || url.password)\n throw new TypeError(\"url must not contain credentials.\");\n if (\n url.protocol === \"ws:\" &&\n !options.allowInsecure &&\n !isLoopbackHost(url.hostname)\n )\n throw new TypeError(\n \"url must use wss:// to reach another machine (allowInsecure: true overrides this for a network you fully trust).\",\n );\n this.#options = options;\n this.#delay = options.reconnectDelay ?? 1000;\n }\n\n /** Whether the coordinator has accepted this host and the link is open. */\n public get connected(): boolean {\n return this.#connected;\n }\n\n /** Cluster IDs running on this host. */\n public clusters(): number[] {\n return [...this.#running.keys()].sort((a, b) => a - b);\n }\n\n /** Listens for a host event. @returns This host. */\n public on<E extends keyof ClusterHostEvents>(\n event: E,\n listener: (...args: ClusterHostEvents[E]) => void,\n ): this {\n let set = this.#listeners.get(event);\n if (!set) this.#listeners.set(event, (set = new Set()));\n set.add(listener as never);\n return this;\n }\n\n /** Stops listening for a host event. @returns This host. */\n public off<E extends keyof ClusterHostEvents>(\n event: E,\n listener: (...args: ClusterHostEvents[E]) => void,\n ): this {\n this.#listeners.get(event)?.delete(listener as never);\n return this;\n }\n\n #emit<E extends keyof ClusterHostEvents>(\n event: E,\n ...args: ClusterHostEvents[E]\n ): void {\n for (const listener of this.#listeners.get(event) ?? [])\n try {\n (listener as (...a: ClusterHostEvents[E]) => void)(...args);\n } catch {\n // A faulty listener must not disturb the host.\n }\n }\n\n /**\n * Connects and resolves once the coordinator accepts this host. After\n * that, a lost connection is retried with backoff until {@link close}.\n * @throws {Error} If the first attempt is refused (bad secret, unknown or duplicate ID).\n */\n public connect(): Promise<void> {\n if (!this.#closed) return Promise.resolve();\n this.#closed = false;\n this.#busyAttempts = 0;\n return new Promise<void>((resolve, reject) => {\n this.#firstConnect = { resolve, reject };\n this.#open();\n });\n }\n\n /** Kills every worker, closes the connection and stops reconnecting. */\n public close(): Promise<void> {\n this.#closed = true;\n if (this.#retry) clearTimeout(this.#retry);\n this.#retry = undefined;\n this.#fence();\n if (this.#watchdog) clearInterval(this.#watchdog);\n this.#watchdog = undefined;\n this.#connected = false;\n const socket = this.#socket;\n this.#socket = undefined;\n this.#firstConnect?.reject(new Error(\"The host was closed.\"));\n this.#firstConnect = undefined;\n if (!socket || socket.readyState === WebSocket.CLOSED)\n return Promise.resolve();\n return new Promise<void>((resolve) => {\n socket.addEventListener(\"close\", () => resolve(), { once: true });\n try {\n socket.close(1000, \"Host closed.\");\n } catch {\n resolve();\n }\n });\n }\n\n #open(): void {\n const socket = new WebSocket(this.#options.url);\n this.#socket = socket;\n socket.addEventListener(\"message\", (event) =>\n this.#message(socket, event.data),\n );\n socket.addEventListener(\"error\", () => {\n // The close event that follows carries the outcome.\n });\n socket.addEventListener(\"close\", (event) => {\n if (this.#socket === socket) this.#lost(event.code, event.reason);\n });\n }\n\n #send(socket: WebSocket, message: ToCoordinator): boolean {\n if (socket.readyState !== WebSocket.OPEN) return false;\n try {\n socket.send(JSON.stringify(message));\n return true;\n } catch {\n return false;\n }\n }\n\n /**\n * A spawn this host will not run (an environment variable outside the\n * allowlist, say) is answered with an error, so the coordinator fails the\n * cluster at once instead of waiting for it to start.\n */\n #refuseSpawn(socket: WebSocket, data: unknown): void {\n if (typeof data !== \"string\" || data.length > 65_536) return;\n let frame: { t?: unknown; cluster?: unknown; epoch?: unknown };\n try {\n frame = JSON.parse(data) as typeof frame;\n } catch {\n return;\n }\n if (\n frame?.t !== \"spawn\" ||\n !Number.isInteger(frame.cluster) ||\n !Number.isInteger(frame.epoch)\n )\n return;\n this.#send(socket, {\n t: \"error\",\n cluster: frame.cluster as number,\n epoch: frame.epoch as number,\n message:\n \"This host refused the spawn request (malformed or not allowed).\",\n });\n }\n\n #message(socket: WebSocket, data: unknown): void {\n if (this.#socket !== socket) return;\n const message = parseToHost(data);\n if (!message) {\n this.#emit(\n \"error\",\n new Error(\"The coordinator sent a malformed frame.\"),\n );\n this.#refuseSpawn(socket, data);\n return;\n }\n this.#lastRx = Date.now();\n switch (message.t) {\n case \"challenge\":\n if (message.version !== REMOTE_PROTOCOL_VERSION) {\n socket.close(\n RemoteClose.VersionMismatch,\n \"Protocol version mismatch.\",\n );\n return;\n }\n this.#send(socket, {\n t: \"hello\",\n id: this.#options.id,\n mac: signChallenge(\n this.#options.secret,\n message.nonce,\n this.#options.id,\n ),\n version: REMOTE_PROTOCOL_VERSION,\n });\n return;\n case \"welcome\":\n this.#lease = message.leaseMs;\n this.#connected = true;\n this.#delay = this.#options.reconnectDelay ?? 1000;\n this.#startWatchdog();\n this.#firstConnect?.resolve();\n this.#firstConnect = undefined;\n this.#emit(\"connected\");\n return;\n case \"ping\":\n this.#send(socket, { t: \"pong\", n: message.n });\n return;\n case \"bye\":\n this.#fence();\n return;\n case \"spawn\":\n this.#spawn(\n socket,\n message.cluster,\n message.epoch,\n message.env,\n );\n return;\n case \"frame\": {\n const run = this.#running.get(message.cluster);\n if (run?.epoch === message.epoch && run.child.connected)\n try {\n run.child.send(message.frame as never);\n } catch {\n // The worker is exiting; its exit event reports it.\n }\n return;\n }\n case \"kill\": {\n const run = this.#running.get(message.cluster);\n if (\n run?.epoch === message.epoch &&\n KILL_SIGNALS.has(message.signal)\n )\n try {\n run.child.kill(message.signal as NodeJS.Signals);\n } catch {\n // Already gone.\n }\n return;\n }\n }\n }\n\n #spawn(\n socket: WebSocket,\n cluster: number,\n epoch: number,\n env: Record<string, string>,\n ): void {\n const reply = (message: ToCoordinator): void => {\n if (this.#socket === socket) this.#send(socket, message);\n };\n if (this.#running.has(cluster)) {\n reply({\n t: \"error\",\n cluster,\n epoch,\n message: `Cluster ${cluster} is already running here.`,\n });\n return;\n }\n let child: ChildProcess;\n try {\n const forkWorker = this.#options.fork ?? defaultFork;\n child = forkWorker(this.#options.script, {\n ...process.env,\n ...env,\n });\n } catch (error) {\n reply({\n t: \"error\",\n cluster,\n epoch,\n message: error instanceof Error ? error.message : String(error),\n });\n return;\n }\n this.#running.set(cluster, { epoch, child });\n child.on(\"message\", (frame) =>\n reply({ t: \"frame\", cluster, epoch, frame }),\n );\n child.once(\"exit\", (code, signal) => {\n if (this.#running.get(cluster)?.child === child)\n this.#running.delete(cluster);\n this.#emit(\"clusterExit\", cluster, code, signal);\n reply({ t: \"exit\", cluster, epoch, code, signal });\n });\n child.on(\"error\", (error) =>\n this.#emit(\n \"error\",\n error instanceof Error ? error : new Error(String(error)),\n ),\n );\n reply({\n t: \"spawned\",\n cluster,\n epoch,\n ...(child.pid ? { pid: child.pid } : {}),\n });\n this.#emit(\"clusterSpawn\", cluster);\n }\n\n /** Kills every worker now. Used whenever the coordinator might consider the host gone. */\n #fence(): void {\n for (const { child } of [...this.#running.values()])\n try {\n child.kill(\"SIGKILL\");\n } catch {\n // Already gone.\n }\n }\n\n #startWatchdog(): void {\n if (this.#watchdog) clearInterval(this.#watchdog);\n this.#lastRx = Date.now();\n this.#watchdog = setInterval(\n () => {\n if (Date.now() - this.#lastRx > this.#lease) {\n this.#fence();\n // Closing triggers the reconnect path.\n this.#socket?.close(\n RemoteClose.HostLost,\n \"Coordinator silent.\",\n );\n }\n },\n Math.max(10, Math.floor(this.#lease / 3)),\n );\n (this.#watchdog as { unref?: () => void }).unref?.();\n }\n\n #lost(code: number, reason: string): void {\n if (this.#watchdog) clearInterval(this.#watchdog);\n this.#watchdog = undefined;\n this.#socket = undefined;\n const wasConnected = this.#connected;\n this.#connected = false;\n this.#fence();\n if (wasConnected) this.#emit(\"disconnected\", { code, reason });\n if (this.#closed) return;\n const refused =\n code === RemoteClose.AuthFailed ||\n code === RemoteClose.UnknownHost ||\n code === RemoteClose.DuplicateHost ||\n code === RemoteClose.VersionMismatch;\n const first = this.#firstConnect;\n // A busy coordinator is not a refusal: try again, with backoff.\n const busy =\n code === RemoteClose.TooManyConnections ||\n code === RemoteClose.HelloTimeout;\n if (\n first &&\n busy &&\n ++this.#busyAttempts < (this.#options.connectAttempts ?? 10)\n ) {\n this.#retryLater();\n return;\n }\n if (first) {\n this.#firstConnect = undefined;\n this.#closed = true;\n first.reject(\n new Error(\n `The coordinator refused this host (${code}${reason ? `: ${reason}` : \"\"}).`,\n ),\n );\n return;\n }\n if (refused)\n this.#emit(\n \"error\",\n new Error(\n `The coordinator refused this host (${code}: ${reason}).`,\n ),\n );\n this.#retryLater();\n }\n\n #retryLater(): void {\n const wait = Math.round(this.#delay * (1 + Math.random() * 0.2));\n this.#delay = Math.min(\n this.#options.maxReconnectDelay ?? 30_000,\n this.#delay * 2,\n );\n this.#retry = setTimeout(() => {\n this.#retry = undefined;\n if (!this.#closed) this.#open();\n }, wait);\n }\n}\n\nfunction defaultFork(\n script: string,\n env: Record<string, string | undefined>,\n): ChildProcess {\n return fork(script, [], { env: env as NodeJS.ProcessEnv });\n}\n",
|
|
32
|
+
"import {\n assertGuildsPerShard,\n fetchGatewayBot,\n resolveShardCount,\n type GatewayBotInfo,\n} from \"./gateway-bot.js\";\nimport { Gateway, GatewayState } from \"@lunibee/ws\";\nimport {\n readClusterContext,\n validateShardIds,\n type ClusterContext,\n} from \"./context.js\";\nimport type {\n ShardHealth,\n ShardStatus,\n WorkerMetrics,\n WorkerReport,\n} from \"./health.js\";\nimport { REQUEST_ASSIGN, REQUEST_HEALTH } from \"./protocol.js\";\nimport { ClusterWorker, type IdentifyGate } from \"./worker.js\";\n\n/** Runtime-agnostic delay used between shard starts (works under Node and Bun). @param ms Milliseconds to wait. */\nconst sleep = (ms: number): Promise<void> =>\n new Promise<void>((resolve) => setTimeout(resolve, ms));\n\nexport {\n MAX_GUILDS_PER_SHARD,\n fetchGatewayBot,\n fetchGuildCount,\n shardsForGuilds,\n type GatewayBotInfo,\n} from \"./gateway-bot.js\";\nexport { ShardBus } from \"./bus.js\";\nexport {\n BroadcastChannelTransport,\n IpcTransport,\n type IpcEndpoint,\n type ShardBusTransport,\n} from \"./transport.js\";\nexport { ShardSupervisor, type SupervisorOptions } from \"./supervisor.js\";\nexport type {\n ShardBusErrorHandler,\n ShardBusOptions,\n ShardMessage,\n ShardMessageHandler,\n ShardReply,\n} from \"./bus.js\";\n\nexport { ClusterManager } from \"./cluster.js\";\nexport type {\n ClusterManagerOptions,\n ClusterManagerEvents,\n ClusterInfo,\n BroadcastResult,\n RequestOptions,\n} from \"./cluster.js\";\nexport {\n allocateShards,\n assertCompleteAllocation,\n isClusterWorker,\n readClusterContext,\n validateShardIds,\n type ClusterContext,\n} from \"./context.js\";\nexport { ClusterWorker, type IdentifyGate } from \"./worker.js\";\nexport {\n ClusterHost,\n type ClusterHostEvents,\n type ClusterHostOptions,\n} from \"./host.js\";\nexport type { HostStatus, RemoteOptions } from \"./remote.js\";\nexport { RemoteClose, REMOTE_PROTOCOL_VERSION } from \"./remote-protocol.js\";\nexport {\n IdentifyCoordinator,\n IDENTIFY_INTERVAL,\n type IdentifyBudget,\n type IdentifyCoordinatorOptions,\n} from \"./identify.js\";\nexport { RequestTracker } from \"./requests.js\";\nexport type {\n ClusterHealth,\n ClusterManagerHealth,\n ClusterState,\n ShardHealth,\n ShardStatus,\n WorkerMetrics,\n WorkerReport,\n} from \"./health.js\";\n\n/** Whether a shard has no live socket, so connecting it costs an IDENTIFY. */\nfunction needsConnection(gateway: Gateway): boolean {\n return (\n gateway.state === GatewayState.Connect ||\n gateway.state === GatewayState.Reconnect ||\n gateway.state === GatewayState.Closed\n );\n}\n\n/** States reached only after IDENTIFY or RESUME has been sent. */\nconst HANDSHAKE_SENT = new Set<string>([\n GatewayState.Identify,\n GatewayState.Resume,\n GatewayState.Ready,\n GatewayState.Dispatch,\n GatewayState.Heartbeat,\n GatewayState.Closed,\n]);\n\n/** Resolves once the shard has sent IDENTIFY/RESUME (or closed), or after `timeout` ms. */\nfunction handshakeSent(gateway: Gateway, timeout: number): Promise<void> {\n if (HANDSHAKE_SENT.has(gateway.state)) return Promise.resolve();\n return new Promise((resolve) => {\n const done = (): void => {\n clearTimeout(timer);\n gateway.off(\"stateChange\", onState);\n resolve();\n };\n const onState = (change: unknown): void => {\n if (HANDSHAKE_SENT.has((change as { next: string }).next)) done();\n };\n const timer = setTimeout(done, timeout);\n gateway.on(\"stateChange\", onState);\n });\n}\n\n/** Configuration for a sharded Gateway client. */\nexport interface ShardManagerOptions {\n /** Bot token. */\n token: string;\n /** Gateway intents as an array, e.g. `[GatewayIntentBits.Guilds]`. */\n intents: ConstructorParameters<typeof Gateway>[0][\"intents\"];\n /** Number of shards. Use `\"auto\"` to request Discord's recommended count. */\n shardCount?: number | \"auto\";\n /**\n * With `shardCount: \"auto\"`, size the deployment by guilds instead of\n * Discord's recommendation: `ceil(guilds / guildsPerShard)` shards, at\n * least one. 1 to 2500 (Discord's limit); Discord recommends about 1000.\n * Guilds are assigned to shards by their ID, so this sets the average,\n * not an exact split.\n */\n guildsPerShard?: number;\n /** The bot's guild count for `guildsPerShard`, instead of asking Discord for it. */\n guildCount?: number;\n /** Gateway reconnect behavior. */\n reconnect?: boolean;\n /**\n * Delay between shard starts in milliseconds. Defaults to 5000 to respect\n * Discord's IDENTIFY rate limit (one per 5s per rate-limit key); set 0 to\n * opt out when an external scheduler already paces the handshakes.\n */ spawnDelay?: number;\n /**\n * Shards that may IDENTIFY at once (Discord's `max_concurrency`). Defaults\n * to the value from `/gateway/bot` when it was fetched (auto shard count),\n * else 1.\n */ maxConcurrency?: number;\n /**\n * How long a startup round waits for its shards to send IDENTIFY/RESUME\n * before the next round's `spawnDelay` starts. Defaults to 15000 ms.\n */ handshakeTimeout?: number;\n /** Interval in milliseconds to automatically check for recommended shard count and re-scale if needed. Must be an integer >= 1000. */\n autoScaleInterval?: number;\n /** Optional handler invoked when a background auto-scale check fails. Receives the thrown error. */\n onAutoScaleError?: (error: unknown) => void;\n /**\n * The shards this manager runs, out of `shardCount`. Defaults to all of\n * them, or to the cluster's own list inside a worker forked by\n * `ClusterManager`. Must be unique integers in `[0, shardCount)`.\n */\n shardIds?: number[];\n /**\n * The worker contract. Read from the environment inside a worker forked by\n * `ClusterManager`; pass `false` to ignore it, or a context to override it.\n */\n cluster?: ClusterContext | false;\n /**\n * Where to get IDENTIFY slots. Inside a coordinated cluster this is the\n * parent, so the limit holds across all clusters. Pass `false` to pace\n * locally with `spawnDelay` instead.\n */\n identify?: IdentifyGate | false;\n /** The worker to report to. Defaults to this process's `ClusterWorker` inside a cluster. */\n worker?: ClusterWorker | false;\n}\n/** What `ShardManager` emits about its shards. */\nexport interface ShardManagerEvents {\n /** A shard finished its handshake and is receiving events. */\n shardReady: [shardId: number];\n /** A shard's connection closed (it may reconnect by itself). */\n shardDisconnect: [shardId: number, close: { code: number; action: string }];\n /** A shard resumed its session after a reconnect. */\n shardResume: [shardId: number];\n /** A shard reported an error. */\n shardError: [shardId: number, error: Error];\n /** Every shard this manager owns is ready. Emitted again after a shard drops and recovers. */\n ready: [];\n /**\n * A shard's `Gateway` was created and has not connected yet: attach dispatch\n * listeners here so none of its events are missed. Fires again for the new\n * Gateways after `destroy()` + `connect()` or `reshard()`.\n */\n shardCreate: [shardId: number, gateway: Gateway];\n /** A shard reconnected too often too fast and is backing off (see `Gateway` `unstable`). */\n shardUnstable: [\n shardId: number,\n info: { reconnects: number; delay: number },\n ];\n}\n\n/** Runtime state for a managed shard. */\nexport interface ShardInfo {\n /** Shard identifier. */\n id: number;\n /** Gateway instance. */\n gateway: Gateway;\n}\n\n/** Manages independent Discord Gateway shards with explicit destruction and reinitialization semantics. */\nexport class ShardManager {\n /** Discord's minimum interval between IDENTIFY payloads, in milliseconds. */\n public static readonly IDENTIFY_INTERVAL = 5000;\n /** Active Gateway shards indexed by shard identifier. */\n public readonly shards = new Map<number, Gateway>();\n /** Total shards in the deployment once initialized, 0 before and after `destroy()`. */\n public get shardCount(): number {\n return this.#resolved ? this.#total : 0;\n }\n /** IDs of the shards this manager owns. */\n public get shardIds(): number[] {\n return [...this.shards.keys()].sort((a, b) => a - b);\n }\n readonly #options: ShardManagerOptions;\n /** Shards this manager was asked to own; every shard when `undefined`. */\n #ids: number[] | undefined;\n readonly #gate: IdentifyGate | undefined;\n readonly #worker: ClusterWorker | undefined;\n readonly #lifecycle = new Map<\n number,\n { status: ShardStatus; readyAt?: number; disconnectedAt?: number }\n >();\n /** Whether `ready` was announced since the last shard dropped. */\n #announced = false;\n /** Whether the shard set has been initialized. */\n #resolved = false;\n /** Whether this manager is currently destroyed. */\n #destroyed = false;\n /** Whether the manager was created in auto shard-count mode. Preserved across reshards so auto-scaling keeps running. */\n readonly #auto: boolean;\n /** The shard count when it is not \"auto\" (from the options or the cluster context). */\n readonly #fixedCount: number;\n #autoScaleTimer?: ReturnType<typeof setInterval>;\n /** The shard count of the current shard set; kept while a reshard has no shards so `getShardIdForGuild` stays right. */\n #total: number;\n readonly #listeners = new Map<\n keyof ShardManagerEvents,\n Set<(...args: unknown[]) => void>\n >();\n /** Last `/gateway/bot` answer, used for concurrency and the start limit. */\n #gatewayInfo?: GatewayBotInfo;\n /** Creates a shard manager. @param options Sharding configuration. @throws {TypeError} If token or intents are invalid. @throws {RangeError} If shard count is invalid. */\n public constructor(options: ShardManagerOptions) {\n if (!options.token?.trim())\n throw new TypeError(\"A Discord bot token is required.\");\n if (!Array.isArray(options.intents))\n throw new TypeError(\n \"Gateway intents must be an array, e.g. [GatewayIntentBits.Guilds].\",\n );\n const context =\n options.cluster === false\n ? null\n : (options.cluster ?? readClusterContext());\n if (context) {\n if (options.shardCount === \"auto\")\n throw new RangeError(\n 'shardCount \"auto\" is the ClusterManager\\'s decision inside a cluster worker.',\n );\n if (\n options.shardCount !== undefined &&\n options.shardCount !== context.shardCount\n )\n throw new RangeError(\n `shardCount ${options.shardCount} differs from the cluster's ${context.shardCount}.`,\n );\n if (options.autoScaleInterval !== undefined)\n throw new TypeError(\n \"autoScaleInterval belongs to ClusterManager inside a cluster worker.\",\n );\n if (\n options.shardIds &&\n !options.shardIds.every((id) => context.shardIds.includes(id))\n )\n throw new RangeError(\n \"shardIds must be a subset of the cluster's own shards.\",\n );\n }\n const count = context\n ? context.shardCount\n : options.shardCount === \"auto\" || options.shardCount === undefined\n ? 1\n : options.shardCount;\n if (!Number.isInteger(count) || count < 1)\n throw new RangeError(\"Shard count must be a positive integer.\");\n if (options.guildsPerShard !== undefined) {\n assertGuildsPerShard(options.guildsPerShard);\n if (options.shardCount !== \"auto\")\n throw new RangeError(\n 'guildsPerShard needs shardCount: \"auto\".',\n );\n }\n this.#ids = options.shardIds ?? context?.shardIds;\n if (this.#ids && options.shardCount !== \"auto\")\n validateShardIds(this.#ids, count);\n this.#worker =\n options.worker === false\n ? undefined\n : (options.worker ??\n (context && options.cluster === undefined\n ? ClusterWorker.current()\n : undefined));\n this.#gate =\n options.identify === false\n ? undefined\n : (options.identify ??\n (context?.coordinatedIdentify\n ? this.#worker?.identifyGate\n : undefined));\n if (\n options.autoScaleInterval !== undefined &&\n (!Number.isInteger(options.autoScaleInterval) ||\n options.autoScaleInterval < 1000)\n )\n throw new RangeError(\n \"autoScaleInterval must be an integer of at least 1000 milliseconds.\",\n );\n if (\n options.spawnDelay !== undefined &&\n (!Number.isFinite(options.spawnDelay) || options.spawnDelay < 0)\n )\n throw new RangeError(\n \"spawnDelay must be a non-negative finite number of milliseconds.\",\n );\n if (\n options.maxConcurrency !== undefined &&\n (!Number.isInteger(options.maxConcurrency) ||\n options.maxConcurrency < 1)\n )\n throw new RangeError(\"maxConcurrency must be a positive integer.\");\n if (\n options.handshakeTimeout !== undefined &&\n (!Number.isFinite(options.handshakeTimeout) ||\n options.handshakeTimeout < 0)\n )\n throw new RangeError(\n \"handshakeTimeout must be a non-negative number of milliseconds.\",\n );\n this.#total = count;\n this.#fixedCount = count;\n this.#auto = options.shardCount === \"auto\";\n // Discord allows one IDENTIFY per 5s per rate-limit key. Spawning\n // shards back-to-back trips that limit and the gateway answers with\n // close 4008 / invalid session, so 5s is the default rather than an\n // opt-in.\n this.#options = {\n spawnDelay: ShardManager.IDENTIFY_INTERVAL,\n ...options,\n };\n this.#worker?.onRequest(REQUEST_HEALTH, (data) =>\n this.report(\n (data as { metrics?: boolean } | null | undefined)?.metrics ===\n true,\n ),\n );\n this.#worker?.onRequest(REQUEST_ASSIGN, async (data) => {\n const ids = (data as { shardIds?: unknown } | null)?.shardIds;\n if (!Array.isArray(ids))\n throw new TypeError(\"shardIds must be an array.\");\n await this.addShards(ids as number[]);\n return { shardIds: ids };\n });\n if (options.shardCount !== \"auto\") this.#initialize(count);\n }\n /** Retrieves Discord's recommended shard count. @returns Recommended shard count. @throws {Error} If discovery fails or returns invalid data. */\n public async fetchRecommendedShardCount(): Promise<number> {\n return resolveShardCount(\n this.#options.token,\n await this.fetchGatewayInfo(),\n this.#options,\n );\n }\n /** Retrieves `/gateway/bot`: recommended shards and the IDENTIFY budget. Remembered for pacing the next connect. @throws {Error} If discovery fails or returns invalid data. */\n public async fetchGatewayInfo(): Promise<GatewayBotInfo> {\n const info = await fetchGatewayBot(this.#options.token);\n this.#gatewayInfo = info;\n return info;\n }\n /** Connects all shards sequentially. A destroyed manager is reinitialized before connecting. @returns A promise fulfilled after all shards connect. @throws {Error} If a shard fails to connect. */\n public async connect(): Promise<void> {\n // Clear any timer from a previous connect() so a second call cannot leak one.\n if (this.#autoScaleTimer) {\n clearInterval(this.#autoScaleTimer);\n this.#autoScaleTimer = undefined;\n }\n await this.#ensureInitialized();\n // Only shards without a live socket spend an IDENTIFY; connect() is a\n // no-op for the rest, so they are neither counted nor paced.\n await this.#start(\n [...this.shards]\n .filter(([, gateway]) => needsConnection(gateway))\n .map(([id]) => id),\n );\n // Auto-scaling only means something when the count is \"auto\".\n if (\n this.#options.autoScaleInterval &&\n this.#auto &&\n !this.#autoScaleTimer\n ) {\n this.#autoScaleTimer = setInterval(() => {\n void this.checkAutoScale();\n }, this.#options.autoScaleInterval);\n }\n }\n /**\n * Takes over more shards of the same deployment (recovery after another\n * cluster gave up) and connects them through the same IDENTIFY path.\n * @throws {RangeError} If an ID is invalid, already owned, or the manager is not initialized.\n */\n public async addShards(shardIds: readonly number[]): Promise<void> {\n await this.#ensureInitialized();\n validateShardIds(shardIds, this.#total);\n for (const id of shardIds)\n if (this.shards.has(id))\n throw new RangeError(`Shard ${id} is already owned.`);\n for (const id of shardIds)\n this.shards.set(id, this.#createShard(id, this.#total));\n if (this.#ids) this.#ids = [...this.#ids, ...shardIds];\n this.#announced = false;\n try {\n // Only the new shards are torn down on failure: the ones that were\n // already running keep their sessions.\n await this.#start([...shardIds], false);\n } catch (error) {\n for (const id of shardIds) {\n this.shards.get(id)?.close();\n this.shards.delete(id);\n this.#lifecycle.delete(id);\n }\n if (this.#ids)\n this.#ids = this.#ids.filter((id) => !shardIds.includes(id));\n throw error;\n }\n }\n /** Starts the given shards: through the coordinated IDENTIFY gate when there is one, else in paced rounds. */\n async #start(ids: number[], destroyOnFailure = true): Promise<void> {\n const gate = this.#gate;\n if (gate) {\n const timeout = this.#options.handshakeTimeout ?? 15_000;\n const results = await Promise.allSettled(\n ids.map(async (id) => {\n const gateway = this.shards.get(id)!;\n this.#setStatus(id, \"connecting\");\n await gate.acquire(id);\n try {\n await gateway.connect();\n await handshakeSent(gateway, timeout);\n } finally {\n gate.release(id);\n }\n }),\n );\n const failed = results.findIndex(\n (result) => result.status === \"rejected\",\n );\n if (failed !== -1) {\n if (destroyOnFailure) this.destroy();\n throw new Error(`Failed to connect shard ${ids[failed]}.`, {\n cause: (results[failed] as PromiseRejectedResult).reason,\n });\n }\n return;\n }\n const limit = this.#gatewayInfo?.sessionStartLimit;\n if (limit && limit.remaining < ids.length)\n // Live shards are left running: the budget only blocks new ones.\n throw new Error(\n `Session start limit exhausted: ${limit.remaining} of ${limit.total} IDENTIFYs left for ${ids.length} shards; resets in ${Math.ceil(limit.resetAfter / 1000)}s.`,\n );\n // Shards whose IDs differ modulo max_concurrency use different\n // IDENTIFY rate-limit keys, so each round of consecutive IDs may start\n // together; rounds are spaced by spawnDelay. An override may lower the\n // concurrency but never exceed what Discord reported.\n const concurrency = Math.min(\n this.#options.maxConcurrency ?? limit?.maxConcurrency ?? 1,\n limit?.maxConcurrency ?? Infinity,\n );\n // Discord's IDENTIFY rate-limit key is `shard_id % max_concurrency`: one\n // IDENTIFY per key per 5 seconds. Each round starts the next shard of\n // every bucket, so a round never puts two shards on one key.\n const buckets = new Map<number, number[]>();\n for (const id of ids) {\n const key = id % concurrency;\n const bucket = buckets.get(key);\n if (bucket) bucket.push(id);\n else buckets.set(key, [id]);\n }\n const rounds = Math.max(\n 0,\n ...[...buckets.values()].map((bucket) => bucket.length),\n );\n for (let index = 0; index < rounds; index++) {\n const round = [...buckets.values()]\n .map((bucket) => bucket[index])\n .filter((id): id is number => id !== undefined);\n for (const id of round) this.#setStatus(id, \"connecting\");\n const results = await Promise.allSettled(\n round.map((id) => this.shards.get(id)!.connect()),\n );\n const failed = results.findIndex(\n (result) => result.status === \"rejected\",\n );\n // Each started shard spends one IDENTIFY of the remembered budget,\n // so a later connect() checks against what is actually left.\n if (limit)\n limit.remaining = Math.max(\n 0,\n limit.remaining -\n results.filter((r) => r.status === \"fulfilled\").length,\n );\n if (failed !== -1) {\n if (destroyOnFailure) this.destroy();\n throw new Error(`Failed to connect shard ${round[failed]}.`, {\n cause: (results[failed] as PromiseRejectedResult).reason,\n });\n }\n if (\n index + 1 < rounds &&\n this.#options.spawnDelay &&\n this.#options.spawnDelay > 0\n ) {\n // connect() resolves when the socket opens; IDENTIFY follows\n // HELLO. Start the interval from the handshake itself so a late\n // HELLO cannot squeeze two rounds' IDENTIFYs together.\n await Promise.all(\n round.map((id) =>\n handshakeSent(\n this.shards.get(id)!,\n this.#options.handshakeTimeout ?? 15_000,\n ),\n ),\n );\n await sleep(this.#options.spawnDelay);\n }\n }\n }\n /** Checks if the recommended shard count has changed and reconnects if so. */\n public async checkAutoScale(): Promise<void> {\n if (!this.#auto) return;\n try {\n const recommended = await this.fetchRecommendedShardCount();\n if (recommended !== this.shardCount) {\n await this.reshard(recommended);\n }\n } catch (error) {\n // Surface the failure instead of silently swallowing it; the next\n // interval tick retries.\n this.#options.onAutoScaleError?.(error);\n }\n }\n /** Forces a reshard to a new shard count, replacing all active shards. @param count New shard count. */\n public async reshard(count: number): Promise<void> {\n this.#options.shardCount = count;\n this.destroy();\n await this.connect();\n }\n /** Permanently closes the current shard set and releases Gateway resources. The next connect recreates them. @returns Nothing. */\n public destroy(): void {\n for (const shard of this.shards.values()) shard.close();\n for (const id of this.shards.keys()) this.#setStatus(id, \"destroyed\");\n this.shards.clear();\n this.#lifecycle.clear();\n this.#announced = false;\n this.#resolved = false;\n this.#destroyed = true;\n if (this.#autoScaleTimer) {\n clearInterval(this.#autoScaleTimer);\n this.#autoScaleTimer = undefined;\n }\n }\n /** Listens for an aggregated shard event. @returns This manager. */\n public on<E extends keyof ShardManagerEvents>(\n event: E,\n listener: (...args: ShardManagerEvents[E]) => void,\n ): this {\n let set = this.#listeners.get(event);\n if (!set) this.#listeners.set(event, (set = new Set()));\n set.add(listener as unknown as (...args: unknown[]) => void);\n return this;\n }\n /** Stops listening for a shard event. @returns This manager. */\n public off<E extends keyof ShardManagerEvents>(\n event: E,\n listener: (...args: ShardManagerEvents[E]) => void,\n ): this {\n this.#listeners\n .get(event)\n ?.delete(listener as unknown as (...args: unknown[]) => void);\n return this;\n }\n #emit<E extends keyof ShardManagerEvents>(\n event: E,\n ...args: ShardManagerEvents[E]\n ): void {\n for (const listener of this.#listeners.get(event) ?? [])\n try {\n listener(...args);\n } catch {\n // A faulty listener must not disturb the shard.\n }\n }\n /** Calculates the target shard ID for a Discord guild snowflake. @param guildId Guild snowflake. @returns Shard identifier. */\n public getShardIdForGuild(guildId: string): number {\n const id = BigInt(guildId);\n return Number((id >> 22n) % BigInt(this.#total));\n }\n /** Sends a voice state update (op 4) on the shard that owns the guild. @returns Whether it was sent; `false` if that shard is absent or disconnected. */\n public sendVoiceState(\n guildId: string,\n channelId: string | null,\n options: { selfMute?: boolean; selfDeaf?: boolean } = {},\n ): boolean {\n return (\n this.shards\n .get(this.getShardIdForGuild(guildId))\n ?.sendVoiceState(guildId, channelId, options) ?? false\n );\n }\n /** Gets a shard by ID. @param id Shard identifier. @returns Gateway instance or undefined. */\n public get(id: number): Gateway | undefined {\n return this.shards.get(id);\n }\n /** Returns each shard's connection state, lifecycle status and heartbeat latency. */\n public health(): ShardHealth[] {\n return [...this.shards].map(([id, gateway]) => {\n const life = this.#lifecycle.get(id);\n return {\n id,\n state: gateway.state,\n ping: gateway.ping,\n status: life?.status ?? \"idle\",\n ...(life?.readyAt !== undefined\n ? { readyAt: life.readyAt }\n : {}),\n ...(life?.disconnectedAt !== undefined\n ? { disconnectedAt: life.disconnectedAt }\n : {}),\n };\n });\n }\n /** A health report for the parent: shard health and, on request, process metrics. */\n public report(metrics = false): WorkerReport {\n let numbers: WorkerMetrics | undefined;\n if (metrics) {\n const memory = process.memoryUsage();\n const cpu = process.cpuUsage();\n numbers = {\n rssBytes: memory.rss,\n heapUsedBytes: memory.heapUsed,\n cpuUserMicros: cpu.user,\n cpuSystemMicros: cpu.system,\n };\n }\n return {\n shards: this.health(),\n ...(numbers ? { metrics: numbers } : {}),\n reportedAt: Date.now(),\n };\n }\n #setStatus(id: number, status: ShardStatus): void {\n const life = this.#lifecycle.get(id) ?? { status };\n life.status = status;\n const now = Date.now();\n if (status === \"ready\") life.readyAt = now;\n if (status === \"disconnected\") life.disconnectedAt = now;\n this.#lifecycle.set(id, life);\n if (status !== \"ready\") this.#announced = false;\n else this.#maybeAnnounce();\n }\n /** Emits `ready` and tells the parent once every owned shard is ready. */\n #maybeAnnounce(): void {\n if (this.#announced || this.shards.size === 0) return;\n for (const id of this.shards.keys())\n if (this.#lifecycle.get(id)?.status !== \"ready\") return;\n this.#announced = true;\n this.#emit(\"ready\");\n this.#worker?.ready(this.shardIds);\n this.#worker?.reportHealth(this.report());\n }\n /** Returns information for all managed shards. @returns Shard information snapshots. */\n public values(): ShardInfo[] {\n return [...this.shards].map(([id, gateway]) => ({ id, gateway }));\n }\n /** Ensures a live shard set exists. @returns A promise fulfilled after initialization. @throws {Error} If initialization fails. */\n async #ensureInitialized(): Promise<void> {\n if (this.#resolved) return;\n if (this.#destroyed) this.#destroyed = false;\n const count =\n this.#options.shardCount === \"auto\"\n ? await this.fetchRecommendedShardCount()\n : this.#fixedCount;\n this.#initialize(count);\n }\n /** Creates every shard for a resolved shard count. @param count Number of shards. @returns Nothing. */\n #initialize(count: number): void {\n if (this.#resolved) return;\n this.#total = count;\n const ids = this.#ids ?? Array.from({ length: count }, (_, i) => i);\n validateShardIds(ids, count);\n for (const id of ids) this.shards.set(id, this.#createShard(id, count));\n this.#resolved = true;\n }\n /** Creates one Gateway instance with its shard identity. @param id Shard identifier. @param count Total shard count. @returns Configured Gateway. */\n #createShard(id: number, count: number): Gateway {\n const gateway = new Gateway({\n token: this.#options.token,\n intents: this.#options.intents,\n shardId: id,\n shardCount: count,\n reconnect: this.#options.reconnect ?? true,\n });\n gateway.on(\"ready\", () => {\n this.#setStatus(id, \"ready\");\n this.#emit(\"shardReady\", id);\n });\n gateway.on(\"resumed\", () => {\n this.#setStatus(id, \"ready\");\n this.#emit(\"shardResume\", id);\n });\n gateway.on(\"close\", (close) => {\n this.#setStatus(id, \"disconnected\");\n this.#emit(\n \"shardDisconnect\",\n id,\n close as { code: number; action: string },\n );\n });\n gateway.on(\"error\", (error) =>\n this.#emit(\"shardError\", id, error as Error),\n );\n gateway.on(\"unstable\", (info) => {\n this.#setStatus(id, \"unstable\");\n this.#emit(\n \"shardUnstable\",\n id,\n info as { reconnects: number; delay: number },\n );\n });\n this.#emit(\"shardCreate\", id, gateway);\n return gateway;\n }\n}\n\n/** Discord.js-familiar alias for {@link ShardManager}. */\nexport { ShardManager as ShardingManager };\n"
|
|
33
33
|
],
|
|
34
|
-
"mappings": "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",
|
|
35
|
-
"debugId": "
|
|
34
|
+
"mappings": "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",
|
|
35
|
+
"debugId": "8AD6CE4D87FD51CB64756E2164756E21",
|
|
36
36
|
"names": []
|
|
37
37
|
}
|