import { randomUUID } from 'node:crypto'; import http, { type IncomingMessage, type ServerResponse } from 'node:http'; import type { Socket } from 'node:net'; import type { LocalApplicationProfile } from '@qinglong/local-application'; import type { LocalApiAdmission, LocalApiAdmissionOperation, LocalApiAdmissionRequest, } from '../admission/localApiAdmission'; import type { BoundedRunListInput } from '@qinglong/runtime-core/bounded-run-list-projection'; import type { BoundedRunEventListInput } from '@qinglong/runtime-core/bounded-run-event-list-projection'; import type { BoundedRunStepListInput } from '@qinglong/runtime-core/bounded-run-step-list-projection'; import type { BoundedTaskListInput } from '@qinglong/runtime-core/bounded-task-list-projection'; import { loadLocalConsoleAssets, type LocalConsoleAsset, } from '../console/localConsoleAssets'; import type { LocalApiResponse } from './contract'; const MAX_HEADER_BYTES = 8 * 1_024; const MAX_URL_BYTES = 512; const MAX_RESPONSE_BYTES = 64 * 1_024; const RUN_READ_ROUTE_PATTERN = /^\/api\/v3\/projects\/([A-Za-z0-9][A-Za-z0-9._:-]{0,127})\/runs\/([A-Za-z0-9][A-Za-z0-9._:-]{0,127})$/; const RUN_LIST_ROUTE_PATTERN = /^\/api\/v3\/projects\/([A-Za-z0-9][A-Za-z0-9._:-]{0,127})\/runs$/; const RUN_EVENT_LIST_ROUTE_PATTERN = /^\/api\/v3\/projects\/([A-Za-z0-9][A-Za-z0-9._:-]{0,127})\/runs\/([A-Za-z0-9][A-Za-z0-9._:-]{0,127})\/events$/; const RUN_STEP_LIST_ROUTE_PATTERN = /^\/api\/v3\/projects\/([A-Za-z0-9][A-Za-z0-9._:-]{0,127})\/runs\/([A-Za-z0-9][A-Za-z0-9._:-]{0,127})\/steps$/; const RUN_CANCELLATION_ROUTE_PATTERN = /^\/api\/v3\/projects\/([A-Za-z0-9][A-Za-z0-9._:-]{0,127})\/runs\/([A-Za-z0-9][A-Za-z0-9._:-]{0,127})\/cancellation$/; const RUN_ATTEMPT_LOG_READ_ROUTE_PATTERN = /^\/api\/v3\/projects\/([A-Za-z0-9][A-Za-z0-9._:-]{0,127})\/runs\/([A-Za-z0-9][A-Za-z0-9._:-]{0,127})\/attempts\/([A-Za-z0-9][A-Za-z0-9._:-]{0,127})\/log$/; const TASK_LIST_ROUTE_PATTERN = /^\/api\/v3\/projects\/([A-Za-z0-9][A-Za-z0-9._:-]{0,127})\/tasks$/; const TASK_READ_ROUTE_PATTERN = /^\/api\/v3\/projects\/([A-Za-z0-9][A-Za-z0-9._:-]{0,127})\/tasks\/([A-Za-z0-9][A-Za-z0-9._:-]{0,127})$/; const TASK_START_ROUTE_PATTERN = /^\/api\/v3\/projects\/([A-Za-z0-9][A-Za-z0-9._:-]{0,127})\/tasks\/([A-Za-z0-9][A-Za-z0-9._:-]{0,127})\/runs$/; const RUN_ID_PATTERN = /^[A-Za-z0-9][A-Za-z0-9._:-]{0,127}$/; const TASK_ID_PATTERN = /^[A-Za-z0-9][A-Za-z0-9._:-]{0,127}$/; const LOCAL_CONSOLE_CONTENT_SECURITY_POLICY = "default-src 'none'; script-src 'self'; style-src 'self'; connect-src 'self'; img-src 'self'; base-uri 'none'; form-action 'none'; frame-ancestors 'none'"; type LocalApiRouteResolution = | LocalApiAdmissionOperation | Readonly<{ errorCode: | 'invalid_run_list_query' | 'invalid_run_event_list_query' | 'invalid_run_step_list_query' | 'invalid_run_log_read_query' | 'invalid_task_list_query'; }>; export interface LocalApiHttpSurfaceOptions { readonly profile: LocalApplicationProfile; readonly host: '127.0.0.1' | '::1'; readonly port: number; readonly admission: LocalApiAdmission; readonly randomUuid?: () => string; } export interface ActiveLocalApiHttpSurface { readonly host: '127.0.0.1' | '::1'; readonly port: number; stopAndDrain(): Promise<'stopped' | 'timed_out'>; } function rawHeaderValues( request: IncomingMessage, name: string, ): readonly string[] { const values: string[] = []; for (let index = 0; index < request.rawHeaders.length; index += 2) { if (request.rawHeaders[index]?.toLowerCase() === name) { values.push(request.rawHeaders[index + 1] ?? ''); } } return values; } function authorization(request: IncomingMessage): string | null { const values = rawHeaderValues(request, 'authorization'); return values.length === 1 ? values[0]! : null; } function hasRequestBody(request: IncomingMessage): boolean { const transferEncoding = rawHeaderValues(request, 'transfer-encoding'); const contentLength = rawHeaderValues(request, 'content-length'); return ( transferEncoding.length !== 0 || contentLength.length > 1 || (contentLength.length === 1 && contentLength[0] !== '0') ); } function jsonContentLength( request: IncomingMessage, maximumBodyBytes: number, ): number { const transferEncoding = rawHeaderValues(request, 'transfer-encoding'); const contentLength = rawHeaderValues(request, 'content-length'); const contentType = rawHeaderValues(request, 'content-type'); if ( transferEncoding.length !== 0 || contentLength.length !== 1 || contentType.length !== 1 || contentType[0]!.trim().toLowerCase() !== 'application/json' || !/^[1-9]\d*$/.test(contentLength[0]!) ) { throw new TypeError('invalid_request_body'); } const length = Number(contentLength[0]); if (!Number.isSafeInteger(length) || length > maximumBodyBytes) { throw new RangeError('request_body_too_large'); } return length; } function readJsonBody( request: IncomingMessage, expectedBytes: number, signal: AbortSignal, ): Promise { return new Promise((resolve, reject) => { const chunks: Buffer[] = []; let received = 0; const fail = (error: Error) => { cleanup(); reject(error); }; const aborted = () => fail(new Error('request_unavailable')); const cleanup = () => { signal.removeEventListener('abort', aborted); request.removeListener('data', data); request.removeListener('end', end); request.removeListener('error', fail); }; const data = (chunk: Buffer) => { received += chunk.byteLength; if (received > expectedBytes) { fail(new TypeError('invalid_request_body')); return; } chunks.push(chunk); }; const end = () => { cleanup(); if (received !== expectedBytes) { reject(new TypeError('invalid_request_body')); return; } try { const text = new TextDecoder('utf-8', { fatal: true }).decode( Buffer.concat(chunks, received), ); resolve(JSON.parse(text)); } catch { reject(new TypeError('invalid_request_body')); } }; signal.addEventListener('abort', aborted, { once: true }); request.on('data', data); request.once('end', end); request.once('error', fail); if (signal.aborted) aborted(); }); } function parseRunListQuery(rawQuery: string | undefined): BoundedRunListInput { if (rawQuery === undefined) return Object.freeze({}); if (rawQuery.length === 0) throw new TypeError(); const values = new Map(); for (const field of rawQuery.split('&')) { const separator = field.indexOf('='); if ( separator < 1 || separator !== field.lastIndexOf('=') || separator === field.length - 1 ) { throw new TypeError(); } const name = field.slice(0, separator); const value = field.slice(separator + 1); if ( values.has(name) || (name !== 'limit' && name !== 'after_created_at_ms' && name !== 'after_run_id') ) { throw new TypeError(); } values.set(name, value); } const rawLimit = values.get('limit'); const limit = rawLimit === undefined ? undefined : Number(rawLimit); if ( rawLimit !== undefined && (!Number.isSafeInteger(limit) || Number(limit) < 1 || Number(limit) > 64 || String(limit) !== rawLimit) ) { throw new TypeError(); } const rawCreatedAtMs = values.get('after_created_at_ms'); const runId = values.get('after_run_id'); if ((rawCreatedAtMs === undefined) !== (runId === undefined)) { throw new TypeError(); } if (rawCreatedAtMs === undefined || runId === undefined) { return Object.freeze({ ...(limit === undefined ? {} : { limit }) }); } const createdAtMs = Number(rawCreatedAtMs); if ( !Number.isSafeInteger(createdAtMs) || createdAtMs < 0 || String(createdAtMs) !== rawCreatedAtMs || !RUN_ID_PATTERN.test(runId) ) { throw new TypeError(); } return Object.freeze({ ...(limit === undefined ? {} : { limit }), after: Object.freeze({ createdAtMs, runId }), }); } function parseRunEventListQuery( rawQuery: string | undefined, ): BoundedRunEventListInput { if (rawQuery === undefined) return Object.freeze({}); if (rawQuery.length === 0) throw new TypeError(); const values = new Map(); for (const field of rawQuery.split('&')) { const separator = field.indexOf('='); if ( separator < 1 || separator !== field.lastIndexOf('=') || separator === field.length - 1 ) { throw new TypeError(); } const name = field.slice(0, separator); const value = field.slice(separator + 1); if (values.has(name) || (name !== 'limit' && name !== 'after_sequence')) { throw new TypeError(); } values.set(name, value); } const rawLimit = values.get('limit'); const limit = rawLimit === undefined ? undefined : Number(rawLimit); const rawAfterSequence = values.get('after_sequence'); const afterSequence = rawAfterSequence === undefined ? undefined : Number(rawAfterSequence); if ( (rawLimit !== undefined && (!Number.isSafeInteger(limit) || Number(limit) < 1 || Number(limit) > 64 || String(limit) !== rawLimit)) || (rawAfterSequence !== undefined && (!Number.isSafeInteger(afterSequence) || Number(afterSequence) < 0 || Number(afterSequence) > 2_147_483_647 || String(afterSequence) !== rawAfterSequence)) ) { throw new TypeError(); } return Object.freeze({ ...(afterSequence === undefined ? {} : { afterSequence }), ...(limit === undefined ? {} : { limit }), }); } function parseRunStepListQuery( rawQuery: string | undefined, ): BoundedRunStepListInput { if (rawQuery === undefined) return Object.freeze({}); if (rawQuery.length === 0) throw new TypeError(); const values = new Map(); for (const field of rawQuery.split('&')) { const separator = field.indexOf('='); if ( separator < 1 || separator !== field.lastIndexOf('=') || separator === field.length - 1 ) { throw new TypeError(); } const name = field.slice(0, separator); const value = field.slice(separator + 1); if ( values.has(name) || (name !== 'limit' && name !== 'after_step_key' && name !== 'after_step_run_id') ) { throw new TypeError(); } values.set(name, value); } const rawLimit = values.get('limit'); const limit = rawLimit === undefined ? undefined : Number(rawLimit); if ( rawLimit !== undefined && (!Number.isSafeInteger(limit) || Number(limit) < 1 || Number(limit) > 64 || String(limit) !== rawLimit) ) { throw new TypeError(); } const stepKey = values.get('after_step_key'); const stepRunId = values.get('after_step_run_id'); if ((stepKey === undefined) !== (stepRunId === undefined)) { throw new TypeError(); } if (stepKey === undefined || stepRunId === undefined) { return Object.freeze({ ...(limit === undefined ? {} : { limit }) }); } if (!RUN_ID_PATTERN.test(stepKey) || !RUN_ID_PATTERN.test(stepRunId)) { throw new TypeError(); } return Object.freeze({ ...(limit === undefined ? {} : { limit }), after: Object.freeze({ stepKey, stepRunId }), }); } function parseTaskListQuery( rawQuery: string | undefined, ): BoundedTaskListInput { if (rawQuery === undefined) return Object.freeze({}); if (rawQuery.length === 0) throw new TypeError(); const values = new Map(); for (const field of rawQuery.split('&')) { const separator = field.indexOf('='); if ( separator < 1 || separator !== field.lastIndexOf('=') || separator === field.length - 1 ) { throw new TypeError(); } const name = field.slice(0, separator); const value = field.slice(separator + 1); if (values.has(name) || (name !== 'limit' && name !== 'after_task_id')) { throw new TypeError(); } values.set(name, value); } const rawLimit = values.get('limit'); const limit = rawLimit === undefined ? undefined : Number(rawLimit); const taskId = values.get('after_task_id'); if ( (rawLimit !== undefined && (!Number.isSafeInteger(limit) || Number(limit) < 1 || Number(limit) > 64 || String(limit) !== rawLimit)) || (taskId !== undefined && !TASK_ID_PATTERN.test(taskId)) ) { throw new TypeError(); } return Object.freeze({ ...(limit === undefined ? {} : { limit }), ...(taskId === undefined ? {} : { after: Object.freeze({ taskId }) }), }); } function parseRunAttemptLogReadQuery( rawQuery: string | undefined, profile: LocalApplicationProfile, ): Readonly<{ offset: number; length: number }> { const defaultLength = profile === 'edge' ? 16 * 1024 : 32 * 1024; if (rawQuery === undefined) { return Object.freeze({ offset: 0, length: defaultLength }); } if (rawQuery.length === 0) throw new TypeError(); const values = new Map(); for (const field of rawQuery.split('&')) { const separator = field.indexOf('='); if ( separator < 1 || separator !== field.lastIndexOf('=') || separator === field.length - 1 ) { throw new TypeError(); } const name = field.slice(0, separator); const value = field.slice(separator + 1); if (values.has(name) || (name !== 'offset' && name !== 'length')) { throw new TypeError(); } values.set(name, value); } const rawOffset = values.get('offset'); const offset = rawOffset === undefined ? 0 : Number(rawOffset); const rawLength = values.get('length'); const length = rawLength === undefined ? defaultLength : Number(rawLength); if ( !Number.isSafeInteger(offset) || offset < 0 || (rawOffset !== undefined && String(offset) !== rawOffset) || !Number.isSafeInteger(length) || length < 1 || length > 32 * 1024 || (rawLength !== undefined && String(length) !== rawLength) ) { throw new TypeError(); } return Object.freeze({ offset, length }); } function route( request: IncomingMessage, profile: LocalApplicationProfile, ): LocalApiRouteResolution | null { const rawUrl = request.url; if ( typeof rawUrl !== 'string' || rawUrl.length < 1 || Buffer.byteLength(rawUrl, 'utf8') > MAX_URL_BYTES || rawUrl.includes('%') || rawUrl.includes('#') ) { return null; } const separator = rawUrl.indexOf('?'); if (separator !== rawUrl.lastIndexOf('?')) return null; const path = separator < 0 ? rawUrl : rawUrl.slice(0, separator); const rawQuery = separator < 0 ? undefined : rawUrl.slice(separator + 1); if (request.method === 'POST') { const taskStartMatch = TASK_START_ROUTE_PATTERN.exec(path); if (taskStartMatch && rawQuery === undefined) { return Object.freeze({ operationId: 'task.start', projectId: taskStartMatch[1]!, taskId: taskStartMatch[2]!, }); } const cancellationMatch = RUN_CANCELLATION_ROUTE_PATTERN.exec(path); return cancellationMatch && rawQuery === undefined ? Object.freeze({ operationId: 'run.cancel', projectId: cancellationMatch[1]!, runId: cancellationMatch[2]!, }) : null; } if (request.method !== 'GET') return null; const runAttemptLogReadMatch = RUN_ATTEMPT_LOG_READ_ROUTE_PATTERN.exec(path); if (runAttemptLogReadMatch) { try { return Object.freeze({ operationId: 'run.log.read', projectId: runAttemptLogReadMatch[1]!, runId: runAttemptLogReadMatch[2]!, attemptId: runAttemptLogReadMatch[3]!, ...parseRunAttemptLogReadQuery(rawQuery, profile), }); } catch { return Object.freeze({ errorCode: 'invalid_run_log_read_query' }); } } const taskReadMatch = TASK_READ_ROUTE_PATTERN.exec(path); if (taskReadMatch) { return rawQuery === undefined ? Object.freeze({ operationId: 'task.get', projectId: taskReadMatch[1]!, taskId: taskReadMatch[2]!, }) : null; } const taskListMatch = TASK_LIST_ROUTE_PATTERN.exec(path); if (taskListMatch) { try { return Object.freeze({ operationId: 'task.list', projectId: taskListMatch[1]!, input: parseTaskListQuery(rawQuery), }); } catch { return Object.freeze({ errorCode: 'invalid_task_list_query' }); } } const eventListMatch = RUN_EVENT_LIST_ROUTE_PATTERN.exec(path); if (eventListMatch) { try { return Object.freeze({ operationId: 'run.events.list', projectId: eventListMatch[1]!, runId: eventListMatch[2]!, input: parseRunEventListQuery(rawQuery), }); } catch { return Object.freeze({ errorCode: 'invalid_run_event_list_query' }); } } const stepListMatch = RUN_STEP_LIST_ROUTE_PATTERN.exec(path); if (stepListMatch) { try { return Object.freeze({ operationId: 'run.steps.list', projectId: stepListMatch[1]!, runId: stepListMatch[2]!, input: parseRunStepListQuery(rawQuery), }); } catch { return Object.freeze({ errorCode: 'invalid_run_step_list_query' }); } } const readMatch = RUN_READ_ROUTE_PATTERN.exec(path); if (readMatch) { return rawQuery === undefined ? Object.freeze({ operationId: 'run.get', projectId: readMatch[1]!, runId: readMatch[2]!, }) : null; } const listMatch = RUN_LIST_ROUTE_PATTERN.exec(path); if (!listMatch) return null; try { return Object.freeze({ operationId: 'run.list', projectId: listMatch[1]!, input: parseRunListQuery(rawQuery), }); } catch { return Object.freeze({ errorCode: 'invalid_run_list_query' }); } } function send( response: ServerResponse, requestId: string, value: Readonly, ): void { if (response.destroyed || response.headersSent) return; let body: string; try { body = JSON.stringify(value.body); } catch { body = JSON.stringify({ code: 'response_unavailable' }); value = Object.freeze({ statusCode: 503, body: Object.freeze({}) }); } if (Buffer.byteLength(body, 'utf8') > MAX_RESPONSE_BYTES) { body = JSON.stringify({ code: 'response_unavailable' }); value = Object.freeze({ statusCode: 503, body: Object.freeze({}) }); } response.statusCode = value.statusCode; response.setHeader('content-type', 'application/json; charset=utf-8'); response.setHeader('cache-control', 'no-store'); response.setHeader('x-content-type-options', 'nosniff'); response.setHeader('x-request-id', requestId); response.setHeader('content-length', Buffer.byteLength(body, 'utf8')); response.end(body); } function sendConsoleAsset( response: ServerResponse, requestId: string, asset: Readonly, ): void { if (response.destroyed || response.headersSent) return; response.statusCode = 200; response.setHeader('content-type', asset.contentType); response.setHeader('cache-control', 'no-store'); response.setHeader( 'content-security-policy', LOCAL_CONSOLE_CONTENT_SECURITY_POLICY, ); response.setHeader('cross-origin-opener-policy', 'same-origin'); response.setHeader('cross-origin-resource-policy', 'same-origin'); response.setHeader( 'permissions-policy', 'camera=(), geolocation=(), microphone=()', ); response.setHeader('referrer-policy', 'no-referrer'); response.setHeader('x-content-type-options', 'nosniff'); response.setHeader('x-frame-options', 'DENY'); response.setHeader('x-request-id', requestId); response.setHeader('etag', asset.etag); response.setHeader('content-length', asset.body.byteLength); response.end(asset.body); } function sendConsoleFavicon(response: ServerResponse, requestId: string): void { if (response.destroyed || response.headersSent) return; response.statusCode = 204; response.setHeader('cache-control', 'no-store'); response.setHeader('cross-origin-resource-policy', 'same-origin'); response.setHeader('referrer-policy', 'no-referrer'); response.setHeader('x-content-type-options', 'nosniff'); response.setHeader('x-request-id', requestId); response.end(); } function errorResponse(statusCode: number, code: string): LocalApiResponse { return Object.freeze({ statusCode, body: Object.freeze({ code }), }); } function validateOptions(options: LocalApiHttpSurfaceOptions): void { if ( !options || typeof options !== 'object' || Array.isArray(options) || (options.profile !== 'edge' && options.profile !== 'standalone') || (options.host !== '127.0.0.1' && options.host !== '::1') || !Number.isSafeInteger(options.port) || options.port < 1_024 || options.port > 65_535 || typeof options.admission?.prepare !== 'function' || (options.randomUuid !== undefined && typeof options.randomUuid !== 'function') ) { throw new TypeError('Local API HTTP surface options are invalid'); } } export async function startLocalApiHttpSurface( options: LocalApiHttpSurfaceOptions, ): Promise> { validateOptions(options); const consoleAssets = loadLocalConsoleAssets(); const uuid = options.randomUuid ?? randomUUID; const maxConcurrentRequests = options.profile === 'edge' ? 4 : 32; const drainTimeoutMs = options.profile === 'edge' ? 5_000 : 10_000; let accepting = true; const inFlight = new Set>(); const sockets = new Set(); const server = http.createServer( { maxHeaderSize: MAX_HEADER_BYTES, requestTimeout: 5_000, keepAlive: true, }, (request, response) => { const requestId = `local:${uuid()}`; if (!accepting) { send(response, requestId, errorResponse(503, 'server_draining')); return; } if (inFlight.size >= maxConcurrentRequests) { send(response, requestId, errorResponse(503, 'server_overloaded')); return; } const consoleAsset = request.method === 'GET' && typeof request.url === 'string' ? consoleAssets.get(request.url) : undefined; if (consoleAsset) { if (hasRequestBody(request)) { send(response, requestId, errorResponse(400, 'invalid_request_body')); request.resume(); return; } sendConsoleAsset(response, requestId, consoleAsset); return; } if (request.method === 'GET' && request.url === '/favicon.ico') { if (hasRequestBody(request)) { send(response, requestId, errorResponse(400, 'invalid_request_body')); request.resume(); return; } sendConsoleFavicon(response, requestId); return; } const resolvedRoute = route(request, options.profile); if (!resolvedRoute) { send(response, requestId, errorResponse(404, 'route_not_found')); return; } if ('errorCode' in resolvedRoute) { send(response, requestId, errorResponse(400, resolvedRoute.errorCode)); return; } const abort = new AbortController(); request.once('aborted', () => abort.abort()); response.once('close', () => { if (!response.writableFinished) abort.abort(); }); const admissionRequest: LocalApiAdmissionRequest = Object.freeze({ requestId, operation: resolvedRoute, authorization: authorization(request), signal: abort.signal, }); let operation: Promise; operation = options.admission .prepare(admissionRequest) .then(async (prepared) => { if (!('handle' in prepared)) { if (hasRequestBody(request)) { response.setHeader('connection', 'close'); } send(response, requestId, prepared); return; } if (prepared.bodyMode === 'none') { if (hasRequestBody(request)) { send( response, requestId, errorResponse(400, 'invalid_request_body'), ); request.resume(); return; } send(response, requestId, await prepared.handle(null)); return; } let body: unknown; try { const expectedBytes = jsonContentLength( request, prepared.maximumBodyBytes, ); body = await readJsonBody(request, expectedBytes, abort.signal); } catch (error) { const code = error instanceof RangeError ? 'request_body_too_large' : error instanceof Error && error.message === 'request_unavailable' ? 'request_unavailable' : 'invalid_request_body'; send( response, requestId, errorResponse( code === 'request_body_too_large' ? 413 : code === 'request_unavailable' ? 503 : 400, code, ), ); return; } send(response, requestId, await prepared.handle(body)); }) .catch(() => send(response, requestId, errorResponse(503, 'request_unavailable')), ) .finally(() => { inFlight.delete(operation); }); inFlight.add(operation); }, ); server.headersTimeout = 5_000; server.keepAliveTimeout = 5_000; server.maxRequestsPerSocket = 100; server.maxConnections = maxConcurrentRequests * 2; server.on('connection', (socket) => { sockets.add(socket); socket.once('close', () => sockets.delete(socket)); }); try { await new Promise((resolve, reject) => { const onError = (error: Error) => { server.off('listening', onListening); reject(error); }; const onListening = () => { server.off('error', onError); resolve(); }; server.once('error', onError); server.once('listening', onListening); server.listen(options.port, options.host); }); } catch (error) { accepting = false; for (const socket of sockets) socket.destroy(); throw error; } let stopPromise: Promise<'stopped' | 'timed_out'> | undefined; return Object.freeze({ host: options.host, port: options.port, stopAndDrain() { if (stopPromise) return stopPromise; accepting = false; stopPromise = (async () => { const closed = new Promise((resolve, reject) => { server.close((error?: Error) => { if (error) reject(error); else resolve(); }); server.closeIdleConnections(); }); let timer: NodeJS.Timeout | undefined; const timeout = new Promise<'timed_out'>((resolve) => { timer = setTimeout(() => resolve('timed_out'), drainTimeoutMs); }); const drained = Promise.allSettled([...inFlight]).then( () => 'stopped' as const, ); const result = await Promise.race([drained, timeout]); if (timer) clearTimeout(timer); if (result === 'timed_out') { for (const socket of sockets) socket.destroy(); } await closed; return result; })(); return stopPromise; }, }); }