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.

208 lines (206 loc) 6.35 kB
const require_rolldown_runtime = require('../../../../_virtual/rolldown_runtime.cjs'); const require_connect = require('../../../../proto/src/components/connect/protobuf/connect.cjs'); const require_buffer = require('../../buffer.cjs'); const require_types = require('../../types.cjs'); const require_connection = require('../core/connection.cjs'); let node_worker_threads = require("node:worker_threads"); //#region src/components/connect/strategies/workerThread/runner.ts /** * Worker thread runner for Inngest Connect. * * This file runs in a separate worker thread and manages: * - WebSocket connection to the Inngest gateway * - Heartbeater * - Lease extender * * Userland code execution still happens in the main thread. */ /** * Time in milliseconds to wait for gateway acknowledgment of a response. * If no ACK is received within this deadline, the response is moved to the * buffer for later flush via HTTP. */ const responseAcknowledgeDeadline = 5e3; if (node_worker_threads.isMainThread) throw new Error("This file should only be run in a worker thread"); if (!node_worker_threads.parentPort) throw new Error("No parent port available"); function toError(value) { if (value instanceof Error) return value; return new Error(String(value)); } /** * Parse pino-style (object, string) or plain (string) log args into a * structured { message, data } pair for sending over postMessage. */ function isRecord(value) { return value != null && typeof value === "object" && !Array.isArray(value); } function parsePinoArgs(args) { if (args.length >= 2 && isRecord(args[0]) && typeof args[1] === "string") return { data: args[0], message: args[1] }; return { message: String(args[0]) }; } /** * Worker thread runner state. */ var WorkerRunner = class { config; state = require_types.ConnectionState.CONNECTING; core; messageBuffer; logger; constructor() { this.logger = this.createMessageLogger(); } /** * Pending execution responses waiting for user code to complete. */ pendingExecutions = /* @__PURE__ */ new Map(); sendMessage(msg) { node_worker_threads.parentPort?.postMessage(msg); } createMessageLogger() { const sendLog = (level, ...args) => { const { message, data } = parsePinoArgs(args); this.sendMessage({ type: "LOG", level, message, data }); }; return { debug: (...args) => sendLog("debug", ...args), info: (...args) => sendLog("info", ...args), warn: (...args) => sendLog("warn", ...args), error: (...args) => sendLog("error", ...args) }; } setState(state) { this.state = state; this.sendMessage({ type: "STATE_CHANGE", state }); } handleMessage(msg) { switch (msg.type) { case "INIT": this.config = msg.config; this.initializeCore(); this.logger.debug("Worker initialized with config"); break; case "CONNECT": if (!this.core) { this.sendMessage({ type: "ERROR", error: "Worker not initialized", fatal: true }); return; } this.core.connect(msg.attempt).catch((err) => { this.sendMessage({ type: "ERROR", error: err instanceof Error ? err.message : "Unknown error", fatal: true }); }); break; case "CLOSE": this.close().catch((err) => { this.logger.error({ err: toError(err) }, "Error during close"); }); break; case "EXECUTION_RESPONSE": { const pending = this.pendingExecutions.get(msg.requestId); if (pending) { pending.resolve(msg.response); this.pendingExecutions.delete(msg.requestId); } break; } case "EXECUTION_ERROR": { const pending = this.pendingExecutions.get(msg.requestId); if (pending) { pending.reject(new Error(msg.error)); this.pendingExecutions.delete(msg.requestId); } break; } } } initializeCore() { if (!this.config) throw new Error("Config not set"); this.core = new require_connection.ConnectionCore({ ...this.config, gatewayUrl: this.config.gatewayUrl }, { logger: this.logger, onStateChange: (state) => { this.setState(state); if (state === require_types.ConnectionState.ACTIVE && this.core?.connectionId) this.sendMessage({ type: "CONNECTION_READY", connectionId: this.core.connectionId }); }, getState: () => this.state, handleExecutionRequest: async (request) => { const requestPromise = new Promise((resolve, reject) => { this.pendingExecutions.set(request.requestId, { resolve, reject }); }); this.sendMessage({ type: "EXECUTION_REQUEST", requestId: request.requestId, request: require_connect.GatewayExecutorRequestData.encode(request).finish() }); const responseBytes = await requestPromise; this.messageBuffer?.addPending(request.requestId, responseBytes, responseAcknowledgeDeadline); return responseBytes; }, onReplyAck: (requestId) => { this.messageBuffer?.acknowledgePending(requestId); }, onBufferResponse: (requestId, responseBytes) => { this.messageBuffer?.append(requestId, responseBytes); }, beforeConnect: async (signingKey) => { await this.messageBuffer?.flush(signingKey); } }); this.messageBuffer = new require_buffer.MessageBuffer({ envName: this.config.envName, getApiBaseUrl: () => this.core.getApiBaseUrl(), logger: this.logger }); } async close() { this.setState(require_types.ConnectionState.CLOSING); this.logger.debug("Cleaning up connection resources"); if (this.core) await this.core.cleanup(); this.logger.debug("Connection closed"); this.logger.debug("Waiting for in-flight requests to complete"); if (this.core) await this.core.waitForInProgress(); this.logger.debug("Flushing messages before closing"); if (this.messageBuffer) try { await this.messageBuffer.flush(this.config?.hashedSigningKey); } catch (err) { this.logger.debug({ err: toError(err) }, "Failed to flush messages, using fallback key"); await this.messageBuffer.flush(this.config?.hashedFallbackKey); } this.setState(require_types.ConnectionState.CLOSED); this.sendMessage({ type: "CLOSED" }); this.logger.debug("Fully closed"); process.exit(0); } }; const runner = new WorkerRunner(); node_worker_threads.parentPort.on("message", (msg) => { runner.handleMessage(msg); }); //#endregion //# sourceMappingURL=runner.cjs.map