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 • 13.1 kB
Source Map (JSON)
{"version":3,"file":"node.cjs","names":["frameworkName: SupportedFrameworkName","chunks: Buffer[]","URL","InngestCommHandler","http","createWebApiCommHandler","handleDurableEndpointProxyRequest","InngestEndpointAdapter","resHeaders: Record<string, string>"],"sources":["../src/node.ts"],"sourcesContent":["import http from \"node:http\";\nimport type { TLSSocket } from \"node:tls\";\nimport { URL } from \"node:url\";\nimport { createWebApiCommHandler } from \"./components/createWebApiCommHandler.ts\";\nimport type { Inngest } from \"./components/Inngest.ts\";\nimport {\n InngestCommHandler,\n type ServeHandlerOptions,\n type SyncHandlerOptions,\n} from \"./components/InngestCommHandler.ts\";\nimport { handleDurableEndpointProxyRequest } from \"./components/InngestDurableEndpointProxy.ts\";\nimport { InngestEndpointAdapter } from \"./components/InngestEndpointAdapter.ts\";\nimport type { RegisterOptions, SupportedFrameworkName } from \"./types.ts\";\n\n/**\n * The name of the framework, used to identify the framework in Inngest\n * dashboards and during testing.\n */\nexport const frameworkName: SupportedFrameworkName = \"nodejs\";\n\n/**\n * Read the incoming message body as text.\n *\n * Collects Buffer chunks and decodes once with `Buffer.concat` so multi-byte\n * UTF-8 characters aren't corrupted when split across chunk boundaries.\n * Reads via `req.on('data'|'end')` so body-replay wrappers — notably\n * `@vercel/node`'s `restoreBody()`, which patches only those two events —\n * deliver the replayed bytes; async-iterator and `readable`-event readers\n * see an empty body under that wrapper.\n */\nexport async function readRequestBody(\n req: http.IncomingMessage,\n): Promise<string> {\n return new Promise((resolve, reject) => {\n const chunks: Buffer[] = [];\n req.on(\"data\", (chunk: Buffer) => chunks.push(chunk));\n req.on(\"end\", () => resolve(Buffer.concat(chunks).toString(\"utf8\")));\n req.on(\"error\", reject);\n });\n}\n\nfunction getURL(req: http.IncomingMessage, hostnameOption?: string): URL {\n const protocol =\n (req.headers[\"x-forwarded-proto\"] as string) ||\n ((req.socket as TLSSocket)?.encrypted ? \"https\" : \"http\");\n const origin = hostnameOption || `${protocol}://${req.headers.host}`;\n return new URL(req.url || \"\", origin);\n}\n\nconst commHandler = (options: ServeHandlerOptions | SyncHandlerOptions) => {\n const handler = new InngestCommHandler({\n frameworkName,\n ...options,\n handler: (req: http.IncomingMessage, res: http.ServerResponse) => {\n return {\n body: () => readRequestBody(req),\n headers: (key) => {\n return req.headers[key] && Array.isArray(req.headers[key])\n ? req.headers[key][0]\n : req.headers[key];\n },\n method: () => {\n if (!req.method) {\n throw new Error(\n \"Request method not defined. Potential use outside of context of Server.\",\n );\n }\n return req.method;\n },\n url: () => getURL(req, options.serveOrigin),\n transformResponse: ({ body, status, headers }) => {\n res.writeHead(status, headers);\n res.end(body);\n },\n\n transformStreamingResponse: async ({ body, headers, status }) => {\n res.writeHead(status, headers);\n\n const reader = body.getReader();\n try {\n let done = false;\n while (!done) {\n const result = await reader.read();\n done = result.done;\n if (!done) {\n res.write(result.value);\n }\n }\n res.end();\n } catch (error) {\n if (error instanceof Error) {\n res.destroy(error);\n } else {\n res.destroy(new Error(String(error)));\n }\n }\n },\n };\n },\n });\n\n return handler;\n};\n\n/**\n * Serve and register any declared functions with Inngest, making them available\n * to be triggered by events.\n *\n * @example Serve Inngest functions on all paths\n * ```ts\n * import { serve } from \"inngest/node\";\n * import { inngest } from \"./src/inngest/client\";\n * import myFn from \"./src/inngest/myFn\"; // Your own function\n *\n * const server = http.createServer(serve({\n * client: inngest, functions: [myFn]\n * }));\n * server.listen(3000);\n * ```\n *\n * @example Serve Inngest on a specific path\n * ```ts\n * import { serve } from \"inngest/node\";\n * import { inngest } from \"./src/inngest/client\";\n * import myFn from \"./src/inngest/myFn\"; // Your own function\n *\n * const server = http.createServer((req, res) => {\n * if (req.url.start === '/api/inngest') {\n * return serve({\n * client: inngest, functions: [myFn]\n * })(req, res);\n * }\n * // ...\n * });\n * server.listen(3000);\n * ```\n *\n * @public\n */\n// Has explicit return type to avoid JSR-defined \"slow types\"\nexport const serve = (options: ServeHandlerOptions): http.RequestListener => {\n return commHandler(options).createHandler() as http.RequestListener;\n};\n\n/**\n * EXPERIMENTAL - Create an http server to serve Inngest functions.\n *\n * @example\n * ```ts\n * import { createServer } from \"inngest/node\";\n * import { inngest } from \"./src/inngest/client\";\n * import myFn from \"./src/inngest/myFn\"; // Your own function\n *\n * const server = createServer({\n * client: inngest, functions: [myFn]\n * });\n * server.listen(3000);\n * ```\n *\n * @public\n */\nexport const createServer = (options: ServeHandlerOptions) => {\n const server = http.createServer((req, res) => {\n const url = getURL(req, options.serveOrigin);\n const pathname = options.servePath || \"/api/inngest\";\n if (url.pathname === pathname) {\n return serve(options)(req, res);\n }\n res.writeHead(404);\n res.end();\n });\n server.on(\"clientError\", (_err, socket) => {\n socket.end(\"HTTP/1.1 400 Bad Request\\r\\n\\r\\n\");\n });\n return server;\n};\n\nexport type EndpointHandler = (req: Request) => Promise<Response>;\n\n/**\n * Comm handler for durable endpoints. Uses Web API Request/Response since\n * that's the interface users write against, regardless of the underlying\n * runtime.\n */\nfunction endpointCommHandler(\n options: RegisterOptions & { client: Inngest.Like },\n syncOptions?: SyncHandlerOptions,\n): InngestCommHandler {\n return createWebApiCommHandler(frameworkName, options, syncOptions);\n}\n\n/**\n * Creates a durable endpoint proxy handler for Node.js environments.\n */\nfunction createDurableEndpointProxyHandler(\n options: InngestEndpointAdapter.ProxyHandlerOptions,\n): http.RequestListener {\n return async (\n req: http.IncomingMessage,\n res: http.ServerResponse,\n ): Promise<void> => {\n const url = getURL(req);\n\n const result = await handleDurableEndpointProxyRequest(\n options.client as Inngest.Any,\n {\n runId: url.searchParams.get(\"runId\"),\n token: url.searchParams.get(\"token\"),\n method: req.method || \"GET\",\n },\n );\n\n res.writeHead(result.status, result.headers);\n res.end(result.body);\n };\n}\n\n/**\n * In a Node.js environment, create a function that can wrap any endpoint to be\n * able to use steps seamlessly within that API.\n */\nexport const endpointAdapter = InngestEndpointAdapter.create((options) => {\n return endpointCommHandler(options, options).createSyncHandler();\n}, createDurableEndpointProxyHandler);\n\n/**\n * Bridge a Web API endpoint handler to a Node.js `http.RequestListener`.\n *\n * Converts an incoming `http.IncomingMessage` into a Web API `Request`,\n * invokes the handler, then streams the resulting `Response` back through\n * the Node.js `http.ServerResponse`.\n *\n * Important: uses `value != null` (not `value`) when forwarding headers so\n * that empty-string headers (like `X-Inngest-Signature: \"\"` in dev mode)\n * are preserved. Dropping them breaks `isInngestReq()` detection.\n */\nexport function serveEndpoint(handler: EndpointHandler): http.RequestListener {\n return async (req: http.IncomingMessage, res: http.ServerResponse) => {\n const body = await readRequestBody(req);\n\n const headers = new Headers();\n for (const [key, value] of Object.entries(req.headers)) {\n if (value != null) {\n if (Array.isArray(value)) {\n for (const v of value) {\n headers.append(key, v);\n }\n } else if (typeof value === \"string\") {\n headers.set(key, value);\n }\n }\n }\n\n const url = getURL(req);\n const webRequest = new Request(url.href, {\n method: req.method,\n headers,\n body: body.length > 0 ? body : undefined,\n });\n\n try {\n const webResponse = await handler(webRequest);\n\n const resHeaders: Record<string, string> = {};\n webResponse.headers.forEach((v, k) => {\n resHeaders[k] = v;\n });\n res.writeHead(webResponse.status, resHeaders);\n\n if (webResponse.body) {\n const reader = webResponse.body.getReader();\n while (true) {\n const { done, value } = await reader.read();\n if (done) {\n break;\n }\n res.write(value);\n }\n }\n res.end();\n } catch (err) {\n if (!res.headersSent) {\n res.writeHead(500);\n }\n res.end(String(err));\n }\n };\n}\n\n/**\n * Create an HTTP server that serves a durable endpoint handler.\n *\n * This bridges the Web API `Request`/`Response` interface that Durable\n * Endpoints use with Node.js's `http.Server`.\n */\nexport function createEndpointServer(handler: EndpointHandler): http.Server {\n const listener = serveEndpoint(handler);\n const server = http.createServer(listener);\n server.on(\"clientError\", (_err, socket) => {\n socket.end(\"HTTP/1.1 400 Bad Request\\r\\n\\r\\n\");\n });\n return server;\n}\n"],"mappings":"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"}