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
JavaScript
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