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.49 kB
JavaScript
const require_rolldown_runtime = require('../../_virtual/rolldown_runtime.cjs');
const require_types = require('../../helpers/types.cjs');
const require_utils = require('../middleware/utils.cjs');
let zod_v3 = require("zod/v3");
//#region src/components/execution/streaming.ts
const sseMetadataSchema = zod_v3.z.object({
type: zod_v3.z.literal("inngest.metadata"),
runId: zod_v3.z.string()
});
const sseStreamSchema = zod_v3.z.object({
type: zod_v3.z.literal("inngest.stream"),
data: zod_v3.z.unknown(),
hashedStepId: zod_v3.z.string().optional()
});
const sseCommitSchema = zod_v3.z.object({
type: zod_v3.z.literal("inngest.commit"),
hashedStepId: zod_v3.z.string().nullable()
});
const sseRollbackSchema = zod_v3.z.object({
type: zod_v3.z.literal("inngest.rollback"),
hashedStepId: zod_v3.z.string().nullable()
});
const sseResultSchema = zod_v3.z.object({
type: zod_v3.z.literal("inngest.response"),
status: zod_v3.z.union([zod_v3.z.literal("succeeded"), zod_v3.z.literal("failed")]),
response: zod_v3.z.object({
body: zod_v3.z.string(),
headers: zod_v3.z.record(zod_v3.z.string()),
statusCode: zod_v3.z.number()
})
});
const sseRedirectSchema = zod_v3.z.object({
type: zod_v3.z.literal("inngest.redirect_info"),
runId: zod_v3.z.string(),
url: zod_v3.z.string()
});
/**
* Builds a single SSE event with the given event name and JSON-serialized data.
*
* `undefined` is normalized to `null` so that the `data:` field is always valid
* JSON (since `JSON.stringify(undefined)` returns the JS primitive `undefined`,
* not the string `"null"`).
*/
function buildSseEvent(event, data) {
return `event: ${event}\ndata: ${JSON.stringify(data ?? null)}\n\n`;
}
/**
* Builds an SSE metadata event string for a streaming response.
*
* The event follows the Server-Sent Events format and provides run context
* (run ID) to consumers of the stream.
*/
function buildSseMetadataEvent(runId) {
return buildSseEvent("inngest.metadata", { runId });
}
/**
* Builds an SSE stream event string for user-pushed data.
*
* Used by `stream.push()` and `stream.pipe()` to send arbitrary data to
* clients as part of a streaming response.
*/
function buildSseStreamEvent(data, hashedStepId) {
const payload = { data };
if (hashedStepId) payload.hashedStepId = hashedStepId;
return buildSseEvent("inngest.stream", payload);
}
/**
* Builds an `inngest.response` SSE event with status `succeeded`.
*/
function buildSseSucceededEvent(response) {
return buildSseEvent("inngest.response", {
status: "succeeded",
response
});
}
/**
* Builds an `inngest.response` SSE event with status `failed`.
*/
function buildSseFailedEvent(error) {
return buildSseEvent("inngest.response", {
status: "failed",
response: {
body: JSON.stringify(error),
statusCode: 500,
headers: { "content-type": "application/json" }
}
});
}
/**
* Builds an SSE redirect event telling the client that execution has switched
* to async mode and it should reconnect elsewhere to get remaining output.
*
* The `url` already contains the realtime JWT as a query parameter, so no
* separate token field is needed.
*/
function buildSseRedirectEvent(data) {
return buildSseEvent("inngest.redirect_info", data);
}
/**
* Returns a new `ReadableStream` that emits `prefix` first, then pipes
* through all chunks from the original `stream`.
*/
function prependToStream(prefix, stream) {
return new ReadableStream({ async start(controller) {
controller.enqueue(prefix);
const reader = stream.getReader();
try {
while (true) {
const { done, value } = await reader.read();
if (done) break;
controller.enqueue(value);
}
controller.close();
} catch (err) {
controller.error(err);
} finally {
reader.releaseLock();
}
} });
}
/**
* Builds an `inngest.commit` SSE event indicating a step's data is committed.
*/
function buildSseCommitEvent(hashedStepId) {
return buildSseEvent("inngest.commit", { hashedStepId });
}
/**
* Builds an `inngest.rollback` SSE event indicating a step's data should be
* rolled back (e.g. step errored and will retry, or disconnect mid-step).
*/
function buildSseRollbackEvent(hashedStepId) {
return buildSseEvent("inngest.rollback", { hashedStepId });
}
/**
* Parses a `ReadableStream<Uint8Array>` as an SSE byte stream, yielding
* `RawSseEvent` objects for each complete event.
*/
async function* iterSse(body) {
const reader = body.getReader();
const decoder = new TextDecoder();
let buffer = "";
try {
while (true) {
const { done, value } = await reader.read();
if (done) break;
buffer += decoder.decode(value, { stream: true });
const parts = buffer.split("\n\n");
buffer = parts.pop() ?? "";
for (const part of parts) {
if (!part.trim()) continue;
let event = "message";
const dataLines = [];
for (const line of part.split("\n")) if (line.startsWith("event: ")) event = line.slice(7);
else if (line.startsWith("data: ")) dataLines.push(line.slice(6));
const data = dataLines.join("\n");
yield {
event,
data
};
}
}
} finally {
reader.releaseLock();
}
}
const sseSchemasByEvent = {
"inngest.metadata": sseMetadataSchema,
"inngest.stream": sseStreamSchema,
"inngest.response": sseResultSchema,
"inngest.commit": sseCommitSchema,
"inngest.rollback": sseRollbackSchema,
"inngest.redirect_info": sseRedirectSchema
};
/**
* Converts a `RawSseEvent` into a typed `SseEvent`, or returns `undefined`
* if the event type is unrecognised or fails validation.
*/
function parseSseEvent(raw) {
const schema = sseSchemasByEvent[raw.event];
if (!schema) return;
let parsed;
try {
parsed = JSON.parse(raw.data);
} catch {
throw new require_utils.UnreachableError("SSE data is not a valid JSON string");
}
if (!require_types.isRecord(parsed)) return;
const result = schema.safeParse({
...parsed,
type: raw.event
});
if (!result.success) throw new Error("Unknown SSE event", { cause: result.error });
return result.data;
}
//#endregion
exports.buildSseCommitEvent = buildSseCommitEvent;
exports.buildSseFailedEvent = buildSseFailedEvent;
exports.buildSseMetadataEvent = buildSseMetadataEvent;
exports.buildSseRedirectEvent = buildSseRedirectEvent;
exports.buildSseRollbackEvent = buildSseRollbackEvent;
exports.buildSseStreamEvent = buildSseStreamEvent;
exports.buildSseSucceededEvent = buildSseSucceededEvent;
exports.iterSse = iterSse;
exports.parseSseEvent = parseSseEvent;
exports.prependToStream = prependToStream;
//# sourceMappingURL=streaming.cjs.map