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.

1 lines 12 kB
{"version":3,"file":"runner.cjs","names":["isMainThread","parentPort","ConnectionState","ConnectionCore","GatewayExecutorRequestData","MessageBuffer"],"sources":["../../../../../src/components/connect/strategies/workerThread/runner.ts"],"sourcesContent":["/**\n * Worker thread runner for Inngest Connect.\n *\n * This file runs in a separate worker thread and manages:\n * - WebSocket connection to the Inngest gateway\n * - Heartbeater\n * - Lease extender\n *\n * Userland code execution still happens in the main thread.\n */\n\nimport { isMainThread, parentPort } from \"node:worker_threads\";\nimport type { Logger } from \"../../../../middleware/logger.ts\";\nimport { GatewayExecutorRequestData } from \"../../../../proto/src/components/connect/protobuf/connect.ts\";\nimport { MessageBuffer } from \"../../buffer.ts\";\nimport { ConnectionState } from \"../../types.ts\";\nimport { ConnectionCore } from \"../core/connection.ts\";\nimport type {\n MainToWorkerMessage,\n SerializableConfig,\n WorkerToMainMessage,\n} from \"./protocol.ts\";\n\n/**\n * Time in milliseconds to wait for gateway acknowledgment of a response.\n * If no ACK is received within this deadline, the response is moved to the\n * buffer for later flush via HTTP.\n */\nconst responseAcknowledgeDeadline = 5_000;\n\nif (isMainThread) {\n throw new Error(\"This file should only be run in a worker thread\");\n}\n\nif (!parentPort) {\n throw new Error(\"No parent port available\");\n}\n\nfunction toError(value: unknown): Error {\n if (value instanceof Error) {\n return value;\n }\n return new Error(String(value));\n}\n\n/**\n * Parse pino-style (object, string) or plain (string) log args into a\n * structured { message, data } pair for sending over postMessage.\n */\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return value != null && typeof value === \"object\" && !Array.isArray(value);\n}\n\nfunction parsePinoArgs(args: unknown[]): {\n message: string;\n data?: Record<string, unknown>;\n} {\n // Pino-style: (object, string)\n if (args.length >= 2 && isRecord(args[0]) && typeof args[1] === \"string\") {\n return { data: args[0], message: args[1] };\n }\n\n return { message: String(args[0]) };\n}\n\n/**\n * Worker thread runner state.\n */\nclass WorkerRunner {\n private config: SerializableConfig | undefined;\n private state: ConnectionState = ConnectionState.CONNECTING;\n private core: ConnectionCore | undefined;\n private messageBuffer: MessageBuffer | undefined;\n private readonly logger: Logger;\n\n constructor() {\n this.logger = this.createMessageLogger();\n }\n\n /**\n * Pending execution responses waiting for user code to complete.\n */\n private pendingExecutions: Map<\n string,\n {\n resolve: (response: Uint8Array) => void;\n reject: (error: Error) => void;\n }\n > = new Map();\n\n private sendMessage(msg: WorkerToMainMessage) {\n parentPort?.postMessage(msg);\n }\n\n private createMessageLogger(): Logger {\n const sendLog = (\n level: \"debug\" | \"info\" | \"warn\" | \"error\",\n ...args: unknown[]\n ) => {\n const { message, data } = parsePinoArgs(args);\n this.sendMessage({ type: \"LOG\", level, message, data });\n };\n\n return {\n debug: (...args) => sendLog(\"debug\", ...args),\n info: (...args) => sendLog(\"info\", ...args),\n warn: (...args) => sendLog(\"warn\", ...args),\n error: (...args) => sendLog(\"error\", ...args),\n };\n }\n\n private setState(state: ConnectionState) {\n this.state = state;\n this.sendMessage({ type: \"STATE_CHANGE\", state });\n }\n\n handleMessage(msg: MainToWorkerMessage) {\n switch (msg.type) {\n case \"INIT\":\n this.config = msg.config;\n this.initializeCore();\n this.logger.debug(\"Worker initialized with config\");\n break;\n\n case \"CONNECT\":\n if (!this.core) {\n this.sendMessage({\n type: \"ERROR\",\n error: \"Worker not initialized\",\n fatal: true,\n });\n return;\n }\n this.core.connect(msg.attempt).catch((err) => {\n this.sendMessage({\n type: \"ERROR\",\n error: err instanceof Error ? err.message : \"Unknown error\",\n fatal: true,\n });\n });\n break;\n\n case \"CLOSE\":\n this.close().catch((err) => {\n this.logger.error({ err: toError(err) }, \"Error during close\");\n });\n break;\n\n case \"EXECUTION_RESPONSE\": {\n const pending = this.pendingExecutions.get(msg.requestId);\n if (pending) {\n pending.resolve(msg.response);\n this.pendingExecutions.delete(msg.requestId);\n }\n break;\n }\n\n case \"EXECUTION_ERROR\": {\n const pending = this.pendingExecutions.get(msg.requestId);\n if (pending) {\n pending.reject(new Error(msg.error));\n this.pendingExecutions.delete(msg.requestId);\n }\n break;\n }\n }\n }\n\n private initializeCore() {\n if (!this.config) {\n throw new Error(\"Config not set\");\n }\n\n this.core = new ConnectionCore(\n {\n ...this.config,\n gatewayUrl: this.config.gatewayUrl,\n },\n {\n logger: this.logger,\n onStateChange: (state) => {\n this.setState(state);\n if (state === ConnectionState.ACTIVE && this.core?.connectionId) {\n this.sendMessage({\n type: \"CONNECTION_READY\",\n connectionId: this.core.connectionId,\n });\n }\n },\n getState: () => this.state,\n handleExecutionRequest: async (request) => {\n // Send execution request to main thread and wait for response\n const requestPromise = new Promise<Uint8Array>((resolve, reject) => {\n this.pendingExecutions.set(request.requestId, { resolve, reject });\n });\n\n // Send the request to main thread (as serialized bytes)\n this.sendMessage({\n type: \"EXECUTION_REQUEST\",\n requestId: request.requestId,\n request: GatewayExecutorRequestData.encode(request).finish(),\n });\n\n // Wait for main thread to complete execution\n const responseBytes = await requestPromise;\n\n // Add to pending with deadline for acknowledgment tracking\n this.messageBuffer?.addPending(\n request.requestId,\n responseBytes,\n responseAcknowledgeDeadline,\n );\n\n return responseBytes;\n },\n onReplyAck: (requestId) => {\n this.messageBuffer?.acknowledgePending(requestId);\n },\n onBufferResponse: (requestId, responseBytes) => {\n this.messageBuffer?.append(requestId, responseBytes);\n },\n beforeConnect: async (signingKey) => {\n await this.messageBuffer?.flush(signingKey);\n },\n },\n );\n\n // Create message buffer for buffering responses when connection is lost\n this.messageBuffer = new MessageBuffer({\n envName: this.config.envName,\n getApiBaseUrl: () => this.core!.getApiBaseUrl(),\n logger: this.logger,\n });\n }\n\n async close(): Promise<void> {\n this.setState(ConnectionState.CLOSING);\n this.logger.debug(\"Cleaning up connection resources\");\n\n if (this.core) {\n await this.core.cleanup();\n }\n\n this.logger.debug(\"Connection closed\");\n this.logger.debug(\"Waiting for in-flight requests to complete\");\n\n if (this.core) {\n await this.core.waitForInProgress();\n }\n\n this.logger.debug(\"Flushing messages before closing\");\n\n if (this.messageBuffer) {\n try {\n await this.messageBuffer.flush(this.config?.hashedSigningKey);\n } catch (err) {\n this.logger.debug(\n { err: toError(err) },\n \"Failed to flush messages, using fallback key\",\n );\n await this.messageBuffer.flush(this.config?.hashedFallbackKey);\n }\n }\n\n this.setState(ConnectionState.CLOSED);\n\n this.sendMessage({ type: \"CLOSED\" });\n this.logger.debug(\"Fully closed\");\n\n // Exit the worker thread. Without this, the parentPort message listener\n // keeps the event loop alive and the worker never exits.\n process.exit(0);\n }\n}\n\n// Initialize the worker runner\nconst runner = new WorkerRunner();\n\n// Listen for messages from the main thread\nparentPort.on(\"message\", (msg: MainToWorkerMessage) => {\n runner.handleMessage(msg);\n});\n"],"mappings":"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"}