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.
311 lines (309 loc) • 9.51 kB
JavaScript
import { getErrorMessage } from "../helpers/errors.js";
import { err, ok } from "../types.js";
import { batchSchema, errorSchema, stepSchema } from "./schema.js";
import { fetchWithAuthFallback } from "../helpers/net.js";
import { hashSigningKey } from "../helpers/strings.js";
import { z } from "zod/v3";
//#region src/api/api.ts
const realtimeSubscriptionTokenSchema = z.object({ jwt: z.string() });
const sendSignalSuccessResponseSchema = z.object({ data: z.object({ run_id: z.string().min(1) }) });
const checkpointNewRunResponseSchema = z.object({ data: z.object({
fn_id: z.string().min(1),
app_id: z.string().min(1),
run_id: z.string().min(1),
token: z.string().min(1).optional()
}) });
var InngestApi = class {
_signingKey;
_signingKeyFallback;
_apiBaseUrl;
_fetch;
constructor({ baseUrl, signingKey, signingKeyFallback, fetch }) {
this._apiBaseUrl = baseUrl;
this._signingKey = signingKey;
this._signingKeyFallback = signingKeyFallback;
this._fetch = fetch;
}
get apiBaseUrl() {
return this._apiBaseUrl();
}
get signingKey() {
return this._signingKey();
}
get signingKeyFallback() {
return this._signingKeyFallback();
}
get hashedKey() {
return hashSigningKey(this.signingKey);
}
get hashedFallbackKey() {
if (!this.signingKeyFallback) return;
return hashSigningKey(this.signingKeyFallback);
}
async getTargetUrl(path) {
return new URL(path, this.apiBaseUrl);
}
async req(url, options) {
const finalUrl = typeof url === "string" ? await this.getTargetUrl(url) : url;
try {
return ok(await fetchWithAuthFallback({
authToken: this.hashedKey,
authTokenFallback: this.hashedFallbackKey,
fetch: this._fetch(),
url: finalUrl,
options: {
...options,
headers: {
"Content-Type": "application/json",
...options?.headers
}
}
}));
} catch (error) {
return err(error);
}
}
async getRunSteps(runId) {
const result = await this.req(`/v0/runs/${runId}/actions`);
if (result.ok) {
const res = result.value;
const data = await res.json();
if (res.ok) return ok(stepSchema.parse(data));
return err(errorSchema.parse(data));
}
return err({
error: getErrorMessage(result.error, "Unknown error retrieving step data"),
status: 500
});
}
async getRunBatch(runId) {
const result = await this.req(`/v0/runs/${runId}/batch`);
if (result.ok) {
const res = result.value;
const data = await res.json();
if (res.ok) return ok(batchSchema.parse(data));
return err(errorSchema.parse(data));
}
return err({
error: getErrorMessage(result.error, "Unknown error retrieving event batch"),
status: 500
});
}
async publish(publishOptions, data) {
const isStream = data instanceof ReadableStream;
const url = await this.getTargetUrl("/v1/realtime/publish");
url.searchParams.set("channel", publishOptions.channel || "");
if (publishOptions.runId) url.searchParams.set("run_id", publishOptions.runId);
for (const topic of publishOptions.topics) url.searchParams.append("topic", topic);
const result = await this.req(url, {
body: isStream ? data : typeof data === "string" ? data : JSON.stringify(data),
method: "POST",
headers: { "Content-Type": isStream ? "text/stream" : "application/json" },
...isStream ? { duplex: "half" } : {}
});
if (result.ok) {
const res = result.value;
if (!res.ok) throw new Error(`Failed to publish event: ${res.status} ${res.statusText}`);
return ok(void 0);
}
return err({
error: getErrorMessage(result.error, "Unknown error publishing event"),
status: 500
});
}
async sendSignal(signalOptions, options) {
const url = await this.getTargetUrl("/v1/signals");
const body = {
signal: signalOptions.signal,
data: signalOptions.data
};
return fetchWithAuthFallback({
authToken: this.hashedKey,
authTokenFallback: this.hashedFallbackKey,
fetch: this._fetch(),
url,
options: {
method: "POST",
body: JSON.stringify(body),
headers: {
"Content-Type": "application/json",
...options?.headers
}
}
}).then(async (res) => {
if (res.status === 404) return ok({ runId: void 0 });
const resClone = res.clone();
let json;
try {
json = await res.json();
} catch {
return err({
error: `Failed to send signal: ${res.status} ${res.statusText} - ${await resClone.text()}`,
status: res.status
});
}
if (!res.ok) try {
return err(errorSchema.parse(json));
} catch {
return err({
error: `Failed to send signal: ${res.status} ${res.statusText} - ${await res.text()}`,
status: res.status
});
}
const parseRes = sendSignalSuccessResponseSchema.safeParse(json);
if (!parseRes.success) return err({
error: `Successfully sent signal, but response parsing failed: ${res.status} ${res.statusText} - ${await resClone.text()}`,
status: res.status
});
return ok({ runId: parseRes.data.data.run_id });
}).catch((error) => {
return err({
error: getErrorMessage(error, "Unknown error sending signal"),
status: 500
});
});
}
async getSubscriptionToken(channel, topics) {
const url = await this.getTargetUrl("/v1/realtime/token");
const body = topics.map((topic) => ({
channel,
name: topic,
kind: "run"
}));
return fetchWithAuthFallback({
authToken: this.hashedKey,
authTokenFallback: this.hashedFallbackKey,
fetch: this._fetch(),
url,
options: {
method: "POST",
body: JSON.stringify(body),
headers: { "Content-Type": "application/json" }
}
}).then(async (res) => {
if (!res.ok) throw new Error(`Failed to get subscription token: ${res.status} ${res.statusText} - ${await res.text()}`);
return realtimeSubscriptionTokenSchema.parse(await res.json()).jwt;
}).catch((error) => {
throw new Error(getErrorMessage(error, "Unknown error getting subscription token"));
});
}
async updateMetadata(args, options) {
const payload = {
target: args.target,
metadata: args.metadata
};
const result = await this.req(`/v1/runs/${args.target.run_id}/metadata`, {
method: "POST",
body: JSON.stringify(payload),
headers: options?.headers
});
if (!result.ok) return err({
error: getErrorMessage(result.error, "Unknown error updating metadata"),
status: 500
});
const res = result.value;
if (res.ok) return ok(void 0);
const resClone = res.clone();
let json;
try {
json = await res.json();
} catch {
return err({
error: `Failed to update metadata: ${res.status} ${res.statusText} - ${await resClone.text()}`,
status: res.status
});
}
try {
return err(errorSchema.parse(json));
} catch {
return err({
error: `Failed to update metadata: ${res.status} ${res.statusText}`,
status: res.status
});
}
}
/**
* Start a new run, optionally passing in a number of steps to initialize the
* run with.
*/
async checkpointNewRun(args) {
const body = JSON.stringify({
run_id: args.runId,
event: args.event,
steps: args.steps,
ts: (/* @__PURE__ */ new Date()).valueOf(),
request_version: args.executionVersion,
retries: args.retries
});
const result = await this.req("/v1/checkpoint", {
method: "POST",
body
});
if (!result.ok) throw new Error(getErrorMessage(result.error, "Unknown error checkpointing new run"));
const res = result.value;
if (res.ok) {
const rawData = await res.json();
return checkpointNewRunResponseSchema.parse(rawData);
}
throw new Error(`Failed to checkpoint new run: ${res.status} ${res.statusText} - ${await res.text()}`);
}
/**
* Checkpoint steps for a given sync run.
*/
async checkpointSteps(args) {
const body = JSON.stringify({
fn_id: args.fnId,
app_id: args.appId,
run_id: args.runId,
steps: args.steps,
ts: (/* @__PURE__ */ new Date()).valueOf()
});
const result = await this.req(`/v1/checkpoint/${args.runId}/steps`, {
method: "POST",
body
});
if (!result.ok) throw new Error(getErrorMessage(result.error, "Unknown error checkpointing steps"));
const res = result.value;
if (!res.ok) throw new Error(`Failed to checkpoint steps: ${res.status} ${res.statusText} - ${await res.text()}`);
}
/**
* Checkpoint steps for a given async run.
*/
async checkpointStepsAsync(args) {
const body = JSON.stringify({
run_id: args.runId,
fn_id: args.fnId,
qi_id: args.queueItemId,
steps: args.steps,
ts: (/* @__PURE__ */ new Date()).valueOf()
});
const result = await this.req(`/v1/checkpoint/${args.runId}/async`, {
method: "POST",
body
});
if (!result.ok) throw new Error(getErrorMessage(result.error, "Unknown error checkpointing async"));
const res = result.value;
if (!res.ok) throw new Error(`Failed to checkpoint async: ${res.status} ${res.statusText} - ${await res.text()}`);
}
/**
* Fetch the output of a completed run using a token.
*
* This uses token-based auth (not signing key) and is intended for use by
* proxy endpoints that fetch results on behalf of users.
*
* @param runId - The ID of the run to fetch output for
* @param token - The token used to authenticate the request
* @returns The raw Response from the API
*/
async getRunOutput(runId, token) {
const url = await this.getTargetUrl(`/v1/http/runs/${runId}/output`);
url.searchParams.set("token", token);
return this._fetch()(url.toString(), {
method: "GET",
headers: { "Content-Type": "application/json" }
});
}
};
//#endregion
export { InngestApi };
//# sourceMappingURL=api.js.map