UNPKG

inngest

Version:

Official SDK for Inngest.com. Inngest is the reliability layer for modern applications. Inngest combines durable execution, events, and queues into a zero-infra platform with built-in observability.

209 lines (207 loc) 8.89 kB
const require_rolldown_runtime = require('../../../../_virtual/rolldown_runtime.cjs'); const require_consts = require('../../../../helpers/consts.cjs'); const require_version = require('../../../../version.cjs'); const require_env = require('../../../../helpers/env.cjs'); const require_connect = require('../../../../proto/src/components/connect/protobuf/connect.cjs'); const require_util = require('../../util.cjs'); const require_buffer = require('../../buffer.cjs'); const require_os = require('../../os.cjs'); const require_url = require('../../../../helpers/url.cjs'); const require_messages = require('../../messages.cjs'); let ms = require("ms"); ms = require_rolldown_runtime.__toESM(ms); //#region src/components/connect/strategies/core/handshake.ts /** * Connection establishment logic: HTTP start request + WebSocket handshake. * * This module is stateless — callers pass in configuration and receive a * fully-handshaked {@link Connection} object. Post-handshake handler wiring * (error/close/message) is the caller's responsibility. */ const ConnectWebSocketProtocol = "v0.connect.inngest.com"; /** * Send the HTTP start request to the Inngest API to obtain a gateway endpoint * and session tokens. */ async function sendStartRequest(config, hashedSigningKey, attempt, excludeGateways, logger) { const msg = require_messages.createStartRequest(Array.from(excludeGateways)); const headers = { "Content-Type": "application/protobuf", ...hashedSigningKey ? { Authorization: `Bearer ${hashedSigningKey}` } : {} }; if (config.envName) headers[require_consts.headerKeys.Environment] = config.envName; const targetUrl = new URL("/v0/connect/start", await require_url.resolveApiBaseUrl({ apiBaseUrl: config.apiBaseUrl, mode: config.mode })); let resp; try { resp = await fetch(targetUrl, { method: "POST", body: new Uint8Array(msg), headers }); } catch (err) { const errMsg = err instanceof Error ? err.message : "Unknown error"; throw new require_util.ReconnectError(`Failed initial API handshake request to ${targetUrl.toString()}, ${errMsg}`, attempt); } if (!resp.ok) { if (resp.status === 401) throw new require_util.AuthError(`Failed initial API handshake request to ${targetUrl.toString()}${config.envName ? ` (env: ${config.envName})` : ""}, ${await resp.text()}`, attempt); if (resp.status === 429) throw new require_util.ConnectionLimitError(attempt); throw new require_util.ReconnectError(`Failed initial API handshake request to ${targetUrl.toString()}, ${await resp.text()}`, attempt); } const { parseStartResponse } = await Promise.resolve().then(() => require("../../messages.cjs")); return parseStartResponse(resp); } /** * Establish a WebSocket connection to the gateway. * * Performs the full handshake sequence (HTTP start → WS open → HELLO → * WORKER_CONNECT → CONNECTION_READY) and returns a {@link Connection} with * post-handshake handlers left unset — the caller must wire `ws.onerror`, * `ws.onclose`, and `ws.onmessage`. */ async function establishConnection(config, hashedSigningKey, attempt, excludeGateways, logger) { logger.debug({ attempt }, "Preparing connection"); const startedAt = /* @__PURE__ */ new Date(); const startResp = await sendStartRequest(config, hashedSigningKey, attempt, excludeGateways, logger); const connectionId = startResp.connectionId; let resolveWsConnected; let rejectWsConnected; const wsConnectedPromise = new Promise((resolve, reject) => { resolveWsConnected = resolve; rejectWsConnected = reject; }); const connectTimeout = setTimeout(() => { excludeGateways.add(startResp.gatewayGroup); rejectWsConnected?.(new require_util.ReconnectError(`Connection ${connectionId} timed out`, attempt)); }, 1e4); const finalEndpoint = config.gatewayUrl || startResp.gatewayEndpoint; if (finalEndpoint !== startResp.gatewayEndpoint) logger.debug({ original: startResp.gatewayEndpoint, override: finalEndpoint }, "Overriding gateway endpoint"); logger.debug({ endpoint: finalEndpoint, gatewayGroup: startResp.gatewayGroup, connectionId }, "Connecting to gateway"); const ws = new WebSocket(finalEndpoint, [ConnectWebSocketProtocol]); ws.binaryType = "arraybuffer"; let settled = false; const rejectHandshake = (error) => { if (settled) return; settled = true; excludeGateways.add(startResp.gatewayGroup); clearTimeout(connectTimeout); ws.onerror = () => {}; ws.onclose = () => {}; ws.close(4001, require_connect.workerDisconnectReasonToJSON(require_connect.WorkerDisconnectReason.UNEXPECTED)); rejectWsConnected?.(new require_util.ReconnectError(`Error while connecting (${connectionId}): ${error instanceof Error ? error.message : "Unknown error"}`, attempt)); }; ws.onerror = (err) => rejectHandshake(err); ws.onclose = (ev) => { rejectHandshake(new require_util.ReconnectError(`Connection ${connectionId} closed: ${ev.reason}`, attempt)); }; const setupState = { receivedGatewayHello: false, sentWorkerConnect: false, receivedConnectionReady: false }; let heartbeatIntervalMs; let extendLeaseIntervalMs; let statusIntervalMs; ws.onmessage = async (event) => { const connectMessage = require_messages.parseConnectMessage(new Uint8Array(event.data)); logger.debug({ kind: require_connect.gatewayMessageTypeToJSON(connectMessage.kind), connectionId }, "Received message"); if (!setupState.receivedGatewayHello) { if (connectMessage.kind !== require_connect.GatewayMessageType.GATEWAY_HELLO) { rejectHandshake(new require_util.ReconnectError(`Expected hello message, got ${require_connect.gatewayMessageTypeToJSON(connectMessage.kind)}`, attempt)); return; } setupState.receivedGatewayHello = true; } if (!setupState.sentWorkerConnect) { const workerConnectRequestMsg = require_connect.WorkerConnectRequestData.create({ connectionId: startResp.connectionId, environment: config.envName, platform: require_env.getPlatformName({ ...require_env.getProcessEnv() }), sdkVersion: `v${require_version.version}`, sdkLanguage: "typescript", framework: "connect", workerManualReadinessAck: config.connectionData.manualReadinessAck, systemAttributes: await require_os.retrieveSystemAttributes(), authData: { sessionToken: startResp.sessionToken, syncToken: startResp.syncToken }, apps: config.connectionData.apps, capabilities: new TextEncoder().encode(config.connectionData.marshaledCapabilities), startedAt, instanceId: config.instanceId || await require_os.getHostname(), maxWorkerConcurrency: config.maxWorkerConcurrency }); const workerConnectRequestMsgBytes = require_connect.WorkerConnectRequestData.encode(workerConnectRequestMsg).finish(); ws.send(require_buffer.ensureUnsharedArrayBuffer(require_connect.ConnectMessage.encode(require_connect.ConnectMessage.create({ kind: require_connect.GatewayMessageType.WORKER_CONNECT, payload: workerConnectRequestMsgBytes })).finish())); setupState.sentWorkerConnect = true; return; } if (!setupState.receivedConnectionReady) { if (connectMessage.kind !== require_connect.GatewayMessageType.GATEWAY_CONNECTION_READY) { rejectHandshake(new require_util.ReconnectError(`Expected ready message, got ${require_connect.gatewayMessageTypeToJSON(connectMessage.kind)}`, attempt)); return; } const readyPayload = require_connect.GatewayConnectionReadyData.decode(connectMessage.payload); setupState.receivedConnectionReady = true; heartbeatIntervalMs = readyPayload.heartbeatInterval.length > 0 ? (0, ms.default)(readyPayload.heartbeatInterval) : 1e4; extendLeaseIntervalMs = readyPayload.extendLeaseInterval.length > 0 ? (0, ms.default)(readyPayload.extendLeaseInterval) : 5e3; statusIntervalMs = readyPayload.statusInterval.length > 0 ? (0, ms.default)(readyPayload.statusInterval) : 0; resolveWsConnected?.(); return; } logger.warn({ kind: require_connect.gatewayMessageTypeToJSON(connectMessage.kind), rawKind: connectMessage.kind, attempt, setupState, connectionId }, "Unexpected message type during setup"); }; await wsConnectedPromise; clearTimeout(connectTimeout); excludeGateways.delete(startResp.gatewayGroup); const conn = { id: connectionId, ws, pendingHeartbeats: 0, dead: false, heartbeatIntervalMs: heartbeatIntervalMs ?? 1e4, extendLeaseIntervalMs: extendLeaseIntervalMs ?? 5e3, statusIntervalMs: statusIntervalMs ?? 0, connectedAt: Date.now(), close: () => { if (conn.dead) return; conn.dead = true; ws.onerror = () => {}; ws.onclose = () => {}; ws.close(); } }; logger.info({ connectionId, gatewayGroup: startResp.gatewayGroup }, "Connection established"); return { conn, gatewayGroup: startResp.gatewayGroup }; } //#endregion exports.establishConnection = establishConnection; //# sourceMappingURL=handshake.cjs.map