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.

246 lines • 10.4 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.fetchAllFnData = exports.parseFnData = exports.undefinedToNull = exports.waterfall = exports.cacheFn = void 0; const zod_1 = require("zod"); const schema_js_1 = require("../api/schema.js"); const InngestExecution_js_1 = require("../components/execution/InngestExecution.js"); const types_js_1 = require("../types.js"); const errors_js_1 = require("./errors.js"); /** * Wraps a function with a cache. When the returned function is run, it will * cache the result and return it on subsequent calls. */ // eslint-disable-next-line @typescript-eslint/no-explicit-any const cacheFn = (fn) => { const key = "value"; const cache = new Map(); return ((...args) => { if (!cache.has(key)) { // eslint-disable-next-line @typescript-eslint/no-unsafe-argument cache.set(key, fn(...args)); } return cache.get(key); }); }; exports.cacheFn = cacheFn; /** * Given an array of functions, return a new function that will run each * function in series and return the result of the final function. Regardless of * if the functions are synchronous or asynchronous, they'll be made into an * async promise chain. * * If an error is thrown, the waterfall will stop and return the error. * * Because this needs to support both sync and async functions, it only allows * functions that accept a single argument. */ // eslint-disable-next-line @typescript-eslint/no-explicit-any const waterfall = (fns, /** * A function that transforms the result of each function in the waterfall, * ready for the next function. * * Will not be called on the final function. */ // eslint-disable-next-line @typescript-eslint/no-explicit-any transform) => { return (...args) => { const chain = fns.reduce(async (acc, fn) => { // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment const prev = await acc; const output = (await fn(prev)); if (transform) { // eslint-disable-next-line @typescript-eslint/no-unsafe-return return await transform(prev, output); } if (typeof output === "undefined") { // eslint-disable-next-line @typescript-eslint/no-unsafe-return return prev; } return output; }, Promise.resolve(args[0])); return chain; }; }; exports.waterfall = waterfall; /** * Given a value `v`, return `v` if it's not undefined, otherwise return `null`. */ const undefinedToNull = (v) => { const isUndefined = typeof v === "undefined"; return isUndefined ? null : v; }; exports.undefinedToNull = undefinedToNull; const fnDataVersionSchema = zod_1.z.object({ version: zod_1.z .literal(-1) .or(zod_1.z.literal(0)) .or(zod_1.z.literal(1)) .or(zod_1.z.literal(2)) .optional() .transform((v) => { if (typeof v === "undefined") { console.debug(`No request version specified by executor; defaulting to v${InngestExecution_js_1.PREFERRED_EXECUTION_VERSION}`); return InngestExecution_js_1.PREFERRED_EXECUTION_VERSION; } return v === -1 ? InngestExecution_js_1.PREFERRED_EXECUTION_VERSION : v; }), }); const parseFnData = (data) => { let version; try { ({ version } = fnDataVersionSchema.parse(data)); const versionHandlers = { [InngestExecution_js_1.ExecutionVersion.V0]: () => (Object.assign({ version: InngestExecution_js_1.ExecutionVersion.V0 }, zod_1.z .object({ event: zod_1.z.record(zod_1.z.any()), events: zod_1.z.array(zod_1.z.record(zod_1.z.any())).default([]), steps: schema_js_1.stepsSchemas[InngestExecution_js_1.ExecutionVersion.V0], ctx: zod_1.z .object({ run_id: zod_1.z.string(), attempt: zod_1.z.number().default(0), stack: zod_1.z .object({ stack: zod_1.z .array(zod_1.z.string()) .nullable() .transform((v) => (Array.isArray(v) ? v : [])), current: zod_1.z.number(), }) .passthrough() .optional() .nullable(), }) .optional() .nullable(), use_api: zod_1.z.boolean().default(false), }) .parse(data))), [InngestExecution_js_1.ExecutionVersion.V1]: () => (Object.assign({ version: InngestExecution_js_1.ExecutionVersion.V1 }, zod_1.z .object({ event: zod_1.z.record(zod_1.z.any()), events: zod_1.z.array(zod_1.z.record(zod_1.z.any())).default([]), steps: schema_js_1.stepsSchemas[InngestExecution_js_1.ExecutionVersion.V1], ctx: zod_1.z .object({ run_id: zod_1.z.string(), attempt: zod_1.z.number().default(0), disable_immediate_execution: zod_1.z.boolean().default(false), use_api: zod_1.z.boolean().default(false), stack: zod_1.z .object({ stack: zod_1.z .array(zod_1.z.string()) .nullable() .transform((v) => (Array.isArray(v) ? v : [])), current: zod_1.z.number(), }) .passthrough() .optional() .nullable(), }) .optional() .nullable(), }) .parse(data))), [InngestExecution_js_1.ExecutionVersion.V2]: () => (Object.assign({ version: InngestExecution_js_1.ExecutionVersion.V2 }, zod_1.z .object({ event: zod_1.z.record(zod_1.z.any()), events: zod_1.z.array(zod_1.z.record(zod_1.z.any())).default([]), steps: schema_js_1.stepsSchemas[InngestExecution_js_1.ExecutionVersion.V2], ctx: zod_1.z .object({ run_id: zod_1.z.string(), attempt: zod_1.z.number().default(0), disable_immediate_execution: zod_1.z.boolean().default(false), use_api: zod_1.z.boolean().default(false), stack: zod_1.z .object({ stack: zod_1.z .array(zod_1.z.string()) .nullable() .transform((v) => (Array.isArray(v) ? v : [])), current: zod_1.z.number(), }) .passthrough() .optional() .nullable(), }) .optional() .nullable(), }) .parse(data))), }; return versionHandlers[version](); } catch (err) { throw new Error(parseFailureErr(err)); } }; exports.parseFnData = parseFnData; const fetchAllFnData = async ({ data, api, version, }) => { var _a, _b, _c, _d; const result = Object.assign({}, data); try { if ((result.version === InngestExecution_js_1.ExecutionVersion.V0 && result.use_api) || (result.version === InngestExecution_js_1.ExecutionVersion.V1 && ((_a = result.ctx) === null || _a === void 0 ? void 0 : _a.use_api))) { if (!((_b = result.ctx) === null || _b === void 0 ? void 0 : _b.run_id)) { return (0, types_js_1.err)((0, errors_js_1.prettyError)({ whatHappened: "failed to attempt retrieving data from API", consequences: "function execution can't continue", why: "run_id is missing from context", stack: true, })); } const [evtResp, stepResp] = await Promise.all([ api.getRunBatch(result.ctx.run_id), api.getRunSteps(result.ctx.run_id, version), ]); if (evtResp.ok) { result.events = evtResp.value; } else { return (0, types_js_1.err)((0, errors_js_1.prettyError)({ whatHappened: "failed to retrieve list of events", consequences: "function execution can't continue", why: (_c = evtResp.error) === null || _c === void 0 ? void 0 : _c.error, stack: true, })); } if (stepResp.ok) { result.steps = stepResp.value; } else { return (0, types_js_1.err)((0, errors_js_1.prettyError)({ whatHappened: "failed to retrieve steps for function run", consequences: "function execution can't continue", why: (_d = stepResp.error) === null || _d === void 0 ? void 0 : _d.error, stack: true, })); } } return (0, types_js_1.ok)(result); } catch (error) { // print it out for now. // move to something like protobuf so we don't have to deal with this console.error(error); return (0, types_js_1.err)(parseFailureErr(error)); } }; exports.fetchAllFnData = fetchAllFnData; const parseFailureErr = (err) => { let why; if (err instanceof zod_1.ZodError) { why = err.toString(); } return (0, errors_js_1.prettyError)({ whatHappened: "Failed to parse data from executor.", consequences: "Function execution can't continue.", toFixNow: "Make sure that your API is set up to parse incoming request bodies as JSON, like body-parser for Express (https://expressjs.com/en/resources/middleware/body-parser.html).", stack: true, why, }); }; //# sourceMappingURL=functions.js.map