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 • 25.8 kB
Source Map (JSON)
{"version":3,"file":"InngestFunction.cjs","names":["queryKeys","triggers: FunctionConfig[\"triggers\"]","EventType","internalEvents","fn: FunctionConfig","eventName: string","ret: NonNullable<FunctionConfig[\"cancel\"]>[number]","timeStr","config: FunctionConfig[]","createExecutionEngine","defaultCheckpointingOptions"],"sources":["../../src/components/InngestFunction.ts"],"sourcesContent":["import { internalEvents, queryKeys } from \"../helpers/consts.ts\";\nimport { timeStr } from \"../helpers/strings.ts\";\nimport type { RecursiveTuple, StrictUnion } from \"../helpers/types.ts\";\nimport {\n type Cancellation,\n type CheckpointingOptions,\n type ConcurrencyOption,\n defaultCheckpointingOptions,\n type FunctionConfig,\n type Handler,\n type InternalCheckpointingOptions,\n type TimeStr,\n type TimeStrBatch,\n} from \"../types.ts\";\nimport { createExecutionEngine } from \"./execution/engine.ts\";\nimport type {\n IInngestExecution,\n InngestExecutionOptions,\n} from \"./execution/InngestExecution.ts\";\n\nimport type { Inngest } from \"./Inngest.ts\";\nimport type { Middleware } from \"./middleware/middleware.ts\";\nimport { EventType, type EventTypeWithAnySchema } from \"./triggers/triggers.ts\";\n\n/**\n * A stateless Inngest function, wrapping up function configuration and any\n * in-memory steps to run when triggered.\n *\n * This function can be \"registered\" to create a handler that Inngest can\n * trigger remotely.\n *\n * @public\n */\nexport class InngestFunction<\n TFnOpts extends InngestFunction.Options<TTriggers, TFailureHandler>,\n THandler extends Handler.Any,\n TFailureHandler extends Handler.Any,\n TClient extends Inngest.Any = Inngest.Any,\n TTriggers extends\n InngestFunction.Trigger<string>[] = InngestFunction.Trigger<string>[],\n> implements InngestFunction.Like\n{\n static stepId = \"step\";\n static failureSuffix = \"-failure\";\n\n get [Symbol.toStringTag](): typeof InngestFunction.Tag {\n return InngestFunction.Tag;\n }\n\n public readonly opts: TFnOpts;\n // biome-ignore lint/correctness/noUnusedPrivateClassMembers: used internally\n private readonly fn: THandler;\n private readonly onFailureFn?: TFailureHandler;\n protected readonly client: TClient;\n\n /**\n * A stateless Inngest function, wrapping up function configuration and any\n * in-memory steps to run when triggered.\n *\n * This function can be \"registered\" to create a handler that Inngest can\n * trigger remotely.\n */\n constructor(\n client: TClient,\n\n /**\n * Options\n */\n opts: TFnOpts,\n fn: THandler,\n ) {\n this.client = client;\n this.opts = opts;\n this.fn = fn;\n this.onFailureFn = this.opts.onFailure;\n }\n\n /**\n * The generated or given ID for this function.\n */\n public id(prefix?: string): string {\n return [prefix, this.opts.id].filter(Boolean).join(\"-\");\n }\n\n /**\n * The generated or given ID for this function, prefixed with the app ID. This\n * is used for routing invokes and identifying the function across apps.\n */\n protected get absoluteId(): string {\n return this.id(this.client.id);\n }\n\n /**\n * The name of this function as it will appear in the Inngest Cloud UI.\n */\n public get name(): string {\n return this.opts.name || this.id();\n }\n\n /**\n * The description of this function.\n */\n public get description(): string | undefined {\n return this.opts.description;\n }\n\n /**\n * Retrieve the Inngest config for this function.\n */\n\n // biome-ignore lint/correctness/noUnusedPrivateClassMembers: used within the SDK\n private getConfig({\n baseUrl,\n appPrefix,\n isConnect,\n }: {\n /**\n * Must be provided a URL that will be used to access the function and step.\n * This function can't be expected to know how it will be accessed, so\n * relies on an outside method providing context.\n */\n baseUrl: URL;\n\n /**\n * The prefix for the app that this function is part of.\n */\n appPrefix: string;\n\n /**\n * Whether this function is being used in a Connect handler.\n */\n isConnect?: boolean;\n }): FunctionConfig[] {\n const fnId = this.id(appPrefix);\n const stepUrl = new URL(baseUrl.href);\n stepUrl.searchParams.set(queryKeys.FnId, fnId);\n stepUrl.searchParams.set(queryKeys.StepId, InngestFunction.stepId);\n\n const {\n retries: attempts,\n cancelOn,\n idempotency,\n batchEvents,\n rateLimit,\n throttle,\n concurrency,\n debounce,\n timeouts,\n priority,\n singleton,\n } = this.opts;\n\n /**\n * Convert retries into the format required when defining function\n * configuration.\n */\n const retries = typeof attempts === \"undefined\" ? undefined : { attempts };\n\n const triggers: FunctionConfig[\"triggers\"] = [];\n for (const trigger of this.opts.triggers ?? []) {\n if (trigger.cron) {\n triggers.push({ cron: trigger.cron });\n continue;\n }\n\n if (!trigger.event) {\n continue;\n }\n\n // The invoke event is in the triggers if they used the `invoke` trigger\n // helper. But we need to remove it in the config, or else the function\n // will be triggered by any invoke.\n let eventName = trigger.event;\n if (eventName instanceof EventType) {\n eventName = eventName.name;\n }\n if (eventName === internalEvents.FunctionInvoked) {\n continue;\n }\n\n triggers.push({ event: eventName, expression: trigger.if });\n }\n\n const fn: FunctionConfig = {\n id: fnId,\n name: this.name,\n triggers,\n steps: {\n [InngestFunction.stepId]: {\n id: InngestFunction.stepId,\n name: InngestFunction.stepId,\n runtime: {\n type: isConnect ? \"ws\" : \"http\",\n url: stepUrl.href,\n },\n retries,\n },\n },\n idempotency,\n batchEvents,\n rateLimit,\n throttle,\n concurrency,\n debounce,\n priority,\n timeouts,\n singleton,\n };\n\n if (cancelOn) {\n fn.cancel = cancelOn.map(({ event, timeout, if: ifStr, match }) => {\n let eventName: string;\n if (typeof event === \"string\") {\n eventName = event;\n } else {\n eventName = event.name;\n }\n\n const ret: NonNullable<FunctionConfig[\"cancel\"]>[number] = {\n event: eventName,\n };\n\n if (timeout) {\n ret.timeout = timeStr(timeout);\n }\n\n if (match) {\n ret.if = `event.${match} == async.${match}`;\n } else if (ifStr) {\n ret.if = ifStr;\n }\n\n return ret;\n }, []);\n }\n\n const config: FunctionConfig[] = [fn];\n\n if (this.onFailureFn) {\n const id = `${fn.id}${InngestFunction.failureSuffix}`;\n const name = `${fn.name ?? fn.id} (failure)`;\n\n const failureStepUrl = new URL(stepUrl.href);\n failureStepUrl.searchParams.set(queryKeys.FnId, id);\n\n config.push({\n id,\n name,\n triggers: [\n {\n event: internalEvents.FunctionFailed,\n expression: `event.data.function_id == '${fnId}'`,\n },\n ],\n steps: {\n [InngestFunction.stepId]: {\n id: InngestFunction.stepId,\n name: InngestFunction.stepId,\n runtime: {\n type: \"http\",\n url: failureStepUrl.href,\n },\n retries: { attempts: 1 },\n },\n },\n });\n }\n\n return config;\n }\n\n protected createExecution(opts: CreateExecutionOptions): IInngestExecution {\n const options: InngestExecutionOptions = {\n fn: this,\n ...opts.partialOptions,\n };\n\n return createExecutionEngine(options);\n }\n\n // biome-ignore lint/correctness/noUnusedPrivateClassMembers: used within the SDK\n private shouldOptimizeParallelism(): boolean {\n // TODO We should check the commhandler's client instead of this one?\n return (\n this.opts.optimizeParallelism ??\n this.client[\"options\"].optimizeParallelism ??\n true\n );\n }\n\n // biome-ignore lint/correctness/noUnusedPrivateClassMembers: used within the SDK\n private shouldAsyncCheckpoint(\n requestedRunStep: string | undefined,\n internalFnId: string | undefined,\n disableImmediateExecution: boolean,\n ): InternalCheckpointingOptions | undefined {\n if (requestedRunStep || !internalFnId || disableImmediateExecution) {\n return;\n }\n\n // TODO We should check the commhandler's client instead of this one?\n const userCfg =\n this.opts.checkpointing ??\n this.client[\"options\"].checkpointing ??\n this.opts.experimentalCheckpointing ??\n this.client[\"options\"].experimentalCheckpointing ??\n true;\n\n if (!userCfg) {\n // Opted out\n return;\n }\n\n if (userCfg === true) {\n return defaultCheckpointingOptions;\n }\n\n return {\n bufferedSteps:\n userCfg.bufferedSteps ?? defaultCheckpointingOptions.bufferedSteps,\n maxRuntime: userCfg.maxRuntime ?? defaultCheckpointingOptions.maxRuntime,\n maxInterval:\n userCfg.maxInterval ?? defaultCheckpointingOptions.maxInterval,\n };\n }\n}\n\n/**\n * A stateless Inngest function, wrapping up function configuration and any\n * in-memory steps to run when triggered.\n *\n * This function can be \"registered\" to create a handler that Inngest can\n * trigger remotely.\n *\n * @public\n */\nexport namespace InngestFunction {\n export const Tag = \"Inngest.Function\" as const;\n\n /**\n * Represents any `InngestFunction` instance, regardless of generics and\n * inference.\n */\n export type Any = InngestFunction<\n // biome-ignore lint/suspicious/noExplicitAny: intentional\n InngestFunction.Options<any, any>,\n Handler.Any,\n Handler.Any,\n // biome-ignore lint/suspicious/noExplicitAny: intentional\n any,\n // biome-ignore lint/suspicious/noExplicitAny: intentional\n any\n >;\n\n export interface Like {\n readonly [Symbol.toStringTag]: typeof InngestFunction.Tag;\n }\n\n /**\n * A user-friendly method of specifying a trigger for an Inngest function.\n *\n * @public\n */\n export type Trigger<TName extends string> = StrictUnion<\n | {\n event: TName | EventTypeWithAnySchema<TName>;\n if?: string;\n }\n | {\n cron: string;\n }\n >;\n\n export type GetOptions<T extends InngestFunction.Any> =\n // biome-ignore lint/suspicious/noExplicitAny: intentional\n T extends InngestFunction<infer O, any, any, any, any> ? O : never;\n\n /**\n * A set of options for configuring an Inngest function.\n *\n * @public\n */\n export interface Options<\n TTriggers extends\n InngestFunction.Trigger<string>[] = InngestFunction.Trigger<string>[],\n TFailureHandler extends Handler.Any = Handler.Any,\n > {\n triggers?: TTriggers;\n\n /**\n * An unique ID used to identify the function. This is used internally for\n * versioning and referring to your function, so should not change between\n * deployments.\n *\n * If you'd like to set a prettier name for your function, use the `name`\n * option.\n */\n id: string;\n\n /**\n * A name for the function as it will appear in the Inngest Cloud UI.\n */\n name?: string;\n\n /**\n * A description of the function.\n */\n description?: string;\n\n /**\n * Concurrency specifies a limit on the total number of concurrent steps that\n * can occur across all runs of the function. A value of 0 (or undefined) means\n * use the maximum available concurrency.\n *\n * Specifying just a number means specifying only the concurrency limit. A\n * maximum of two concurrency options can be specified.\n */\n concurrency?:\n | number\n | ConcurrencyOption\n | RecursiveTuple<ConcurrencyOption, 2>;\n\n /**\n * batchEvents specifies the batch configuration on when this function\n * should be invoked when one of the requirements are fulfilled.\n */\n batchEvents?: {\n /**\n * The maximum number of events to be consumed in one batch.\n * Check the pricing page to verify the limit for each plan.\n */\n maxSize: number;\n\n /**\n * How long to wait before invoking the function with a list of events.\n * If timeout is reached, the function will be invoked with a batch\n * even if it's not filled up to `maxSize`.\n *\n * Expects a time string such as 1s, 60s or 15m15s.\n */\n timeout: TimeStrBatch;\n\n /**\n * An optional key to use for batching.\n *\n * See [batch documentation](https://innge.st/batching) for more\n * information on how to use `key` expressions.\n */\n key?: string;\n\n /**\n * An optional boolean expression to determine an event's eligibility for batching\n *\n * See [batch documentation](https://innge.st/batching) for more\n * information on how to use `if` expressions.\n */\n if?: string;\n };\n\n /**\n * Allow the specification of an idempotency key using event data. If\n * specified, this overrides the `rateLimit` object.\n */\n idempotency?: string;\n\n /**\n * Rate limit function runs, only running them a given number of times (limit) per\n * period. Note that rate limit is a lossy, hard limit. Once the limit is hit,\n * new runs will be skipped. To enqueue work when a rate limit is hit, use the\n * {@link throttle} parameter.\n */\n rateLimit?: {\n /**\n * An optional key to use for rate limiting, similar to idempotency.\n */\n key?: string;\n\n /**\n * The number of times to allow the function to run per the given `period`.\n */\n limit: number;\n\n /**\n * The period of time to allow the function to run `limit` times.\n */\n period: TimeStr;\n };\n\n /**\n * Throttles function runs, only running them a given number of times (limit) per\n * period. Once the limit is hit, new runs will be enqueued and will start when there's\n * capacity. This may lead to a large backlog. For hard rate limiting, use the\n * {@link rateLimit} parameter.\n */\n throttle?: {\n /**\n * An optional expression which returns a throttling key for controlling throttling.\n * Every unique key is its own throttle limit. Event data may be used within this\n * expression, eg \"event.data.user_id\".\n */\n key?: string;\n\n /**\n * The total number of runs allowed to start within the given `period`. The limit is\n * applied evenly over the period.\n */\n limit: number;\n\n /**\n * The period of time for the rate limit. Run starts are evenly spaced through\n * the given period. The minimum granularity is 1 second.\n */\n period: TimeStr;\n\n /**\n * The number of runs allowed to start in the given window in a single burst.\n * A burst > 1 bypasses smoothing for the burst and allows many runs to start\n * at once, if desired. Defaults to 1, which disables bursting.\n */\n burst?: number;\n };\n\n /**\n * Debounce delays functions for the `period` specified. If an event is sent,\n * the function will not run until at least `period` has elapsed.\n *\n * If any new events are received that match the same debounce `key`, the\n * function is rescheduled for another `period` delay, and the triggering\n * event is replaced with the latest event received.\n *\n * See the [Debounce documentation](https://innge.st/debounce) for more\n * information.\n */\n debounce?: {\n /**\n * An optional key to use for debouncing.\n *\n * See [Debounce documentation](https://innge.st/debounce) for more\n * information on how to use `key` expressions.\n */\n key?: string;\n\n /**\n * The period of time to delay after receiving the last trigger to run the\n * function.\n *\n * See [Debounce documentation](https://innge.st/debounce) for more\n * information.\n */\n period: TimeStr;\n\n /**\n * The maximum time that a debounce can be extended before running.\n * If events are continually received within the given period, a function\n * will always run after the given timeout period.\n *\n * See [Debounce documentation](https://innge.st/debounce) for more\n * information.\n */\n timeout?: TimeStr;\n };\n\n /**\n * Configure how the priority of a function run is decided when multiple\n * functions are triggered at the same time.\n *\n * See the [Priority documentation](https://innge.st/priority) for more\n * information.\n */\n priority?: {\n /**\n * An expression to use to determine the priority of a function run. The\n * expression can return a number between `-600` and `600`, where `600`\n * declares that this run should be executed before any others enqueued in\n * the last 600 seconds (10 minutes), and `-600` declares that this run\n * should be executed after any others enqueued in the last 600 seconds.\n *\n * See the [Priority documentation](https://innge.st/priority) for more\n * information.\n */\n run?: string;\n };\n\n /**\n * Configure timeouts for the function. If any of the timeouts are hit, the\n * function run will be cancelled.\n */\n timeouts?: {\n /**\n * Start represents the timeout for starting a function. If the time\n * between scheduling and starting a function exceeds this value, the\n * function will be cancelled.\n *\n * This is, essentially, the amount of time that a function sits in the\n * queue before starting.\n *\n * A function may exceed this duration because of concurrency limits,\n * throttling, etc.\n */\n start?: TimeStr;\n\n /**\n * Finish represents the time between a function starting and the function\n * finishing. If a function takes longer than this time to finish, the\n * function is marked as cancelled.\n *\n * The start time is taken from the time that the first successful\n * function request begins, and does not include the time spent in the\n * queue before the function starts.\n *\n * Note that if the final request to a function begins before this\n * timeout, and completes after this timeout, the function will succeed.\n */\n finish?: TimeStr;\n };\n\n /**\n * Ensures that only one run of the function is active at a time for a given key.\n * If a new run is triggered while another is still in progress with the same key,\n * the new run will either be skipped or replace the active one, depending on the mode.\n *\n * This is useful for deduplication or enforcing exclusive execution.\n */\n singleton?: {\n /**\n * An optional key expression used to scope singleton execution.\n * Each unique key has its own singleton lock. Event data can be referenced,\n * e.g. \"event.data.user_id\".\n */\n key?: string;\n\n /**\n * Determines how to handle new runs when one is already active for the same key.\n * - `\"skip\"` skips the new run.\n * - `\"cancel\"` cancels the existing run and starts the new one.\n */\n mode: \"skip\" | \"cancel\";\n };\n\n cancelOn?: Cancellation[];\n\n /**\n * Specifies the maximum number of retries for all steps across this function.\n *\n * Can be a number from `0` to `20`. Defaults to `3`.\n */\n retries?:\n | 0\n | 1\n | 2\n | 3\n | 4\n | 5\n | 6\n | 7\n | 8\n | 9\n | 10\n | 11\n | 12\n | 13\n | 14\n | 15\n | 16\n | 17\n | 18\n | 19\n | 20;\n\n /**\n * Provide a function to be called if your function fails, meaning\n * that it ran out of retries and was unable to complete successfully.\n *\n * This is useful for sending warning notifications or cleaning up\n * after a failure and supports all the same functionality as a\n * regular handler.\n */\n onFailure?: TFailureHandler;\n\n /**\n * Define a set of middleware that can be registered to hook into\n * various lifecycles of the SDK and affect input and output of\n * Inngest functionality.\n *\n * See {@link https://innge.st/middleware}\n */\n middleware?: Middleware.Class[];\n\n /**\n * Optimizes parallel steps to reduce traffic during `Promise` resolution,\n * reducing time and requests per run. `Promise.*()` waits for all promises\n * to settle before resolving. Use `group.parallel()` for `Promise.race()`\n * semantics.\n *\n * Overrides the client-level setting.\n *\n * @default true\n */\n optimizeParallelism?: boolean;\n\n /**\n * Whether or not to use checkpointing for this function's executions.\n *\n * If `true`, enables checkpointing with default settings, which is a safe,\n * blocking version of checkpointing, where we check in with Inngest after\n * every step is run.\n *\n * If an object, you can tweak the settings to batch, set a maximum runtime\n * before going async, and more. Note that if your server dies before the\n * checkpoint completes, step data will be lost and steps will be rerun.\n *\n * We recommend starting with the default `true` configuration and only tweak\n * the parameters directly if necessary.\n *\n * @deprecated Use `checkpointing` instead.\n */\n experimentalCheckpointing?: CheckpointingOptions;\n\n /**\n * Whether or not to use checkpointing for this function's executions.\n *\n * If `false`, disables checkpointing.\n *\n * If `true`, enables checkpointing with default settings, which is a safe,\n * blocking version of checkpointing, where we check in with Inngest after\n * every step is run.\n *\n * If an object, you can tweak the settings to batch, set a maximum runtime\n * before going async, and more. Note that if your server dies before the\n * checkpoint completes, step data will be lost and steps will be rerun.\n *\n * We recommend starting with the default `true` configuration and only tweak\n * the parameters directly if necessary.\n *\n * @default true\n */\n checkpointing?: CheckpointingOptions;\n }\n}\n\nexport type CreateExecutionOptions = {\n partialOptions: Omit<InngestExecutionOptions, \"fn\">;\n};\n"],"mappings":"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"}