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.

373 lines • 15.8 kB
"use strict"; var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { if (k2 === undefined) k2 = k; var desc = Object.getOwnPropertyDescriptor(m, k); if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { desc = { enumerable: true, get: function() { return m[k]; } }; } Object.defineProperty(o, k2, desc); }) : (function(o, m, k, k2) { if (k2 === undefined) k2 = k; o[k2] = m[k]; })); var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { Object.defineProperty(o, "default", { enumerable: true, value: v }); }) : function(o, v) { o["default"] = v; }); var __importStar = (this && this.__importStar) || (function () { var ownKeys = function(o) { ownKeys = Object.getOwnPropertyNames || function (o) { var ar = []; for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k; return ar; }; return ownKeys(o); }; return function (mod) { if (mod && mod.__esModule) return mod; var result = {}; if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]); __setModuleDefault(result, mod); return result; }; })(); Object.defineProperty(exports, "__esModule", { value: true }); exports.invokePayloadSchema = exports.createStepTools = exports.STEP_INDEXING_SUFFIX = exports.getStepOptions = void 0; const ai_1 = require("@inngest/ai"); const zod_1 = require("zod"); const consts_js_1 = require("../helpers/consts.js"); const strings_js_1 = require("../helpers/strings.js"); const Temporal = __importStar(require("../helpers/temporal.js")); const types_js_1 = require("../types.js"); const InngestFunction_js_1 = require("./InngestFunction.js"); const InngestFunctionReference_js_1 = require("./InngestFunctionReference.js"); const getStepOptions = (options) => { if (typeof options === "string") { return { id: options }; } return options; }; exports.getStepOptions = getStepOptions; /** * Suffix used to namespace steps that are automatically indexed. */ exports.STEP_INDEXING_SUFFIX = ":"; /** * Create a new set of step function tools ready to be used in a step function. * This function should be run and a fresh set of tools provided every time a * function is run. * * An op stack (function state) is passed in as well as some mutable properties * that the tools can use to submit a new op. */ const createStepTools = (client, execution, stepHandler) => { /** * A local helper used to create tools that can be used to submit an op. * * When using this function, a generic type should be provided which is the * function signature exposed to the user. */ // eslint-disable-next-line @typescript-eslint/no-explicit-any const createTool = ( /** * A function that returns an ID for this op. This is used to ensure that * the op stack is correctly filled, submitted, and retrieved with the same * ID. * * It is passed the arguments passed by the user. * * Most simple tools will likely only need to define this. */ matchOp, opts) => { return (async (...args) => { const parsedArgs = args; return stepHandler({ args: parsedArgs, matchOp, opts }); }); }; /** * Create a new step run tool that can be used to run a step function using * `step.run()` as a shim. */ const createStepRun = ( /** * The sub-type of this step tool, exposed via `opts.type` when the op is * reported. */ type) => { return createTool(({ id, name }, _fn, ...input) => { const opts = Object.assign(Object.assign({}, (input.length ? { input } : {})), (type ? { type } : {})); return Object.assign({ id, op: types_js_1.StepOpCode.StepPlanned, name: id, displayName: name !== null && name !== void 0 ? name : id }, (Object.keys(opts).length ? { opts } : {})); }, { // eslint-disable-next-line @typescript-eslint/no-unsafe-argument fn: (_, fn, ...input) => fn(...input), }); }; /** * Define the set of tools the user has access to for their step functions. * * Each key is the function name and is expected to run `createTool` and pass * a generic type for that function as it will appear in the user's code. */ const tools = { /** * Send one or many events to Inngest. Should always be used in place of * `inngest.send()` to ensure that the event send is successfully retried * and not sent multiple times due to memoisation. * * @example * ```ts * await step.sendEvent("emit-user-creation", { * name: "app/user.created", * data: { id: 123 }, * }); * * await step.sendEvent("emit-user-updates", [ * { * name: "app/user.created", * data: { id: 123 }, * }, * { * name: "app/user.feed.created", * data: { id: 123 }, * }, * ]); * ``` * * Returns a promise that will resolve once the event has been sent. */ sendEvent: createTool(({ id, name }) => { return { id, op: types_js_1.StepOpCode.StepPlanned, name: "sendEvent", displayName: name !== null && name !== void 0 ? name : id, }; }, { fn: (idOrOptions, payload) => { return client["_send"]({ payload, headers: execution["options"]["headers"], }); }, }), /** * Wait for a particular event to be received before continuing. When the * event is received, it will be returned. * * You can also provide options to control the particular event that is * received, for example to ensure that a user ID matches between two * events, or to only wait a maximum amount of time before giving up and * returning `null` instead of any event data. */ waitForEvent: createTool(({ id, name }, /** * Options to control the event we're waiting for. */ opts) => { const matchOpts = { timeout: (0, strings_js_1.timeStr)(typeof opts === "string" ? opts : opts.timeout), }; if (typeof opts !== "string") { if (opts === null || opts === void 0 ? void 0 : opts.match) { matchOpts.if = `event.${opts.match} == async.${opts.match}`; } else if (opts === null || opts === void 0 ? void 0 : opts.if) { matchOpts.if = opts.if; } } return { id, op: types_js_1.StepOpCode.WaitForEvent, name: opts.event, opts: matchOpts, displayName: name !== null && name !== void 0 ? name : id, }; }), /** * Use this tool to run business logic. Each call to `run` will be retried * individually, meaning you can compose complex workflows that safely * retry dependent asynchronous actions. * * The function you pass to `run` will be called only when this "step" is to * be executed and can be synchronous or asynchronous. * * In either case, the return value of the function will be the return value * of the `run` tool, meaning you can return and reason about return data * for next steps. */ run: createStepRun(), /** * AI tooling for running AI models and other AI-related tasks. */ ai: { /** * Use this tool to have Inngest make your AI calls. Useful for agentic workflows. * * Input is also tracked for this tool, meaning you can pass input to the * function and it will be displayed and editable in the UI. */ infer: createTool(({ id, name }, options) => { var _a, _b; const modelCopy = Object.assign({}, options.model); // Allow the model to mutate options and body for this call (_b = (_a = options.model).onCall) === null || _b === void 0 ? void 0 : _b.call(_a, modelCopy, options.body); return { id, op: types_js_1.StepOpCode.AiGateway, displayName: name !== null && name !== void 0 ? name : id, opts: { type: "step.ai.infer", url: modelCopy.url, headers: modelCopy.headers, auth_key: modelCopy.authKey, format: modelCopy.format, body: options.body, }, }; }), /** * Use this tool to wrap AI models and other AI-related tasks. Each call * to `wrap` will be retried individually, meaning you can compose complex * workflows that safely retry dependent asynchronous actions. * * Input is also tracked for this tool, meaning you can pass input to the * function and it will be displayed and editable in the UI. */ wrap: createStepRun("step.ai.wrap"), /** * Models for AI inference and other AI-related tasks. */ models: Object.assign({}, ai_1.models), }, /** * Wait a specified amount of time before continuing. * * The time to wait can be specified using a `number` of milliseconds or an * `ms`-compatible time string like `"1 hour"`, `"30 mins"`, or `"2.5d"`. * * {@link https://npm.im/ms} * * To wait until a particular date, use `sleepUntil` instead. */ sleep: createTool(({ id, name }, time) => { /** * The presence of this operation in the returned stack indicates that the * sleep is over and we should continue execution. */ const msTimeStr = (0, strings_js_1.timeStr)(Temporal.isTemporalDuration(time) ? time.total({ unit: "milliseconds" }) : time); return { id, op: types_js_1.StepOpCode.Sleep, name: msTimeStr, displayName: name !== null && name !== void 0 ? name : id, }; }), /** * Wait until a particular date before continuing by passing a `Date`. * * To wait for a particular amount of time from now, always use `sleep` * instead. */ sleepUntil: createTool(({ id, name }, time) => { try { const iso = Temporal.getISOString(time); /** * The presence of this operation in the returned stack indicates that the * sleep is over and we should continue execution. */ return { id, op: types_js_1.StepOpCode.Sleep, name: iso, displayName: name !== null && name !== void 0 ? name : id, }; } catch (err) { /** * If we're here, it's because the date is invalid. We'll throw a custom * error here to standardise this response. */ // TODO PrettyError console.warn("Invalid `Date`, date string, `Temporal.Instant`, or `Temporal.ZonedDateTime` passed to sleepUntil;", err); // TODO PrettyError throw new Error(`Invalid \`Date\`, date string, \`Temporal.Instant\`, or \`Temporal.ZonedDateTime\` passed to sleepUntil: ${ // eslint-disable-next-line @typescript-eslint/no-explicit-any time}`); } }), /** * Invoke a passed Inngest `function` with the given `data`. Returns the * result of the returned value of the function or `null` if the function * does not return a value. * * A string ID can also be passed to reference functions outside of the * current app. */ invoke: createTool(({ id, name }, invokeOpts) => { // Create a discriminated union to operate on based on the input types // available for this tool. const optsSchema = exports.invokePayloadSchema.extend({ timeout: zod_1.z.union([zod_1.z.number(), zod_1.z.string(), zod_1.z.date()]).optional(), }); const parsedFnOpts = optsSchema .extend({ _type: zod_1.z.literal("fullId").optional().default("fullId"), function: zod_1.z.string().min(1), }) .or(optsSchema.extend({ _type: zod_1.z.literal("fnInstance").optional().default("fnInstance"), function: zod_1.z.instanceof(InngestFunction_js_1.InngestFunction), })) .or(optsSchema.extend({ _type: zod_1.z.literal("refInstance").optional().default("refInstance"), function: zod_1.z.instanceof(InngestFunctionReference_js_1.InngestFunctionReference), })) .safeParse(invokeOpts); if (!parsedFnOpts.success) { throw new Error(`Invalid invocation options passed to invoke; must include either a function or functionId.`); } const { _type, function: fn, data, user, v, timeout } = parsedFnOpts.data; const payload = { data, user, v }; const opts = { payload, function_id: "", timeout: typeof timeout === "undefined" ? undefined : (0, strings_js_1.timeStr)(timeout), }; switch (_type) { case "fnInstance": opts.function_id = fn.id(fn["client"].id); break; case "fullId": console.warn(`${consts_js_1.logPrefix} Invoking function with \`function: string\` is deprecated and will be removed in v4.0.0; use an imported function or \`referenceFunction()\` instead. See https://innge.st/ts-referencing-functions`); opts.function_id = fn; break; case "refInstance": opts.function_id = [fn.opts.appId || client.id, fn.opts.functionId] .filter(Boolean) .join("-"); break; } return { id, op: types_js_1.StepOpCode.InvokeFunction, displayName: name !== null && name !== void 0 ? name : id, opts, }; }), }; return tools; }; exports.createStepTools = createStepTools; /** * The event payload portion of the options for `step.invoke()`. This does not * include non-payload options like `timeout` or the function to invoke. */ exports.invokePayloadSchema = zod_1.z.object({ data: zod_1.z.record(zod_1.z.any()).optional(), user: zod_1.z.record(zod_1.z.any()).optional(), v: zod_1.z.string().optional(), }); //# sourceMappingURL=InngestStepTools.js.map