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.

1 lines 13.3 kB
{"version":3,"file":"typeHelpers.cjs","names":[],"sources":["../../../src/components/triggers/typeHelpers.ts"],"sourcesContent":["// biome-ignore-all lint/suspicious/noExplicitAny: it's fine\n\nimport type { StandardSchemaV1 } from \"@standard-schema/spec\";\nimport type { AsTuple } from \"../../helpers/types.ts\";\nimport type { createGroupTools } from \"../InngestGroupTools.ts\";\nimport type { EventType, EventTypeWithAnySchema } from \"./triggers.ts\";\n\nexport type AnySchema = StandardSchemaV1<any>;\ntype BasicDataUnknown = Record<string, unknown>;\ntype BasicDataAny = Record<string, any>;\n\ntype InvokeEventName = \"inngest/function.invoked\";\ntype CronEventName = \"inngest/scheduled.timer\";\ntype CronEventData = { cron: string };\n\n/**\n * Detects if a string type contains a wildcard character (*).\n */\ntype ContainsWildcard<T extends string> = T extends `${string}*${string}`\n ? true\n : false;\n\n/**\n * Converts wildcard event names to `unknown`, preserving literal names.\n */\ntype WildcardToUnknown<T extends string> = ContainsWildcard<T> extends true\n ? unknown\n : T;\n\n/**\n * Represents the structure of an event as received by function handlers.\n *\n * This is the runtime event shape that your function receives when triggered.\n *\n * @template TName - The event name as a string literal type\n * @template TData - The event data object type\n */\nexport type ReceivedEvent<TName, TData extends BasicDataUnknown> = {\n data: TData;\n id: string;\n name: TName;\n ts: number;\n v: string;\n};\n\n/**\n * Recursively checks if a trigger array contains an invoke trigger.\n *\n * This type is used to determine whether to add \"inngest/function.invoked\"\n * event to the received events tuple.\n *\n * @template T - Array of trigger definitions to check\n * @returns `true` if array contains an invoke trigger, `false` otherwise\n */\ntype HasInvokeTrigger<T extends readonly any[]> = T extends readonly [\n infer First,\n ...infer Rest,\n]\n ? First extends EventTypeWithAnySchema<InvokeEventName>\n ? true\n : HasInvokeTrigger<Rest>\n : false;\n\n/**\n * Converts an EventType instance to a ReceivedEvent type.\n *\n * Extracts the event name and schema from EventType and transforms it into\n * the ReceivedEvent shape that function handlers receive.\n *\n * @template TEventType - The EventType instance to convert\n * @returns ReceivedEvent type with extracted name and data\n */\ntype EventTypeToEvent<TEventType> = TEventType extends EventType<\n infer TName,\n infer TSchema\n>\n ? TSchema extends StandardSchemaV1<infer TData extends BasicDataUnknown>\n ? ReceivedEvent<WildcardToUnknown<TName>, TData>\n : ReceivedEvent<WildcardToUnknown<TName>, BasicDataAny>\n : never;\n\n/**\n * Converts a plain event object trigger to a ReceivedEvent type.\n *\n * Handles triggers like `{ event: \"my-event\", schema?: z.object(...) }`.\n *\n * @template TName - Event name\n * @template TSchema - Optional schema type\n * @returns ReceivedEvent with typed data from schema, or Record<string, any> if no schema\n */\ntype PlainEventToReceivedEvent<\n TName extends string,\n TSchema,\n> = TSchema extends StandardSchemaV1<infer TData extends BasicDataUnknown>\n ? ReceivedEvent<WildcardToUnknown<TName>, TData>\n : ReceivedEvent<WildcardToUnknown<TName>, BasicDataAny>;\n\n/**\n * Processes a single trigger and converts it to ReceivedEvent(s).\n *\n * @template TTrigger - The trigger to process\n * @template TSeenCron - Whether we've already seen a cron trigger\n * @returns Tuple of ReceivedEvent(s), or empty array if trigger should be skipped\n */\ntype ProcessSingleTrigger<\n TTrigger,\n TSeenCron extends boolean,\n> = TTrigger extends EventTypeWithAnySchema<InvokeEventName> // Is this an invoke trigger?\n ? [] // Skip invoke triggers (handled separately by ToReceivedEvent)\n : TTrigger extends EventTypeWithAnySchema<string> // Is this an event type trigger?\n ? [EventTypeToEvent<TTrigger>]\n : TTrigger extends { cron: string } // Is this a cron trigger?\n ? TSeenCron extends true\n ? [] // Skip additional cron triggers (they're merged into one)\n : [ReceivedEvent<CronEventName, CronEventData>]\n : // Is this an event trigger using an EventType?\n TTrigger extends {\n event: EventType<infer TName, infer TSchema>;\n if?: string;\n }\n ? [\n TSchema extends StandardSchemaV1<\n infer TData extends BasicDataUnknown\n >\n ? ReceivedEvent<WildcardToUnknown<TName>, TData>\n : ReceivedEvent<WildcardToUnknown<TName>, BasicDataAny>,\n ]\n : // Is this an event trigger using a string name (i.e. not an EventType)?\n TTrigger extends {\n event: infer TName extends string;\n schema?: infer TSchema;\n }\n ? [PlainEventToReceivedEvent<TName, TSchema>]\n : []; // Unknown trigger type, skip it\n\n/**\n * Recursively processes trigger array, converting to event types while tracking\n * crons.\n *\n * This type iterates through a trigger array, converting each trigger to its\n * corresponding ReceivedEvent type. It handles:\n * - EventType instances → ReceivedEvent with typed data\n * - Cron triggers → \"inngest/scheduled.timer\" event (merged if multiple)\n * - Plain event objects → ReceivedEvent with typed or untyped data\n * - Invoke triggers → skipped (handled by ToReceivedEvent)\n *\n * @template T - Array of trigger definitions to process\n * @template SeenCron - Tracks whether we've already encountered a cron trigger\n *\n * @remarks\n * Multiple cron triggers are merged into a single \"inngest/scheduled.timer\" event.\n */\ntype TriggersToEventsWithCron<\n T extends readonly any[],\n SeenCron extends boolean = false,\n> = T extends readonly [infer First, ...infer Rest]\n ? [\n ...ProcessSingleTrigger<First, SeenCron>,\n ...TriggersToEventsWithCron<\n Rest,\n First extends { cron: string } ? true : SeenCron\n >,\n ]\n : [];\n\n/**\n * Alias for TriggersToEventsWithCron that processes all non-invoke triggers.\n *\n * Despite the name \"Filter\", this type actually:\n * 1. Converts event triggers to ReceivedEvent types\n * 2. Converts cron triggers to \"inngest/scheduled.timer\" events\n * 3. Merges multiple cron triggers into one\n * 4. Preserves the order of non-invoke triggers\n *\n * @template T - Array of trigger definitions to process\n */\ntype FilterNonInvokeTriggers<T extends readonly any[]> =\n TriggersToEventsWithCron<T>;\n\n/**\n * Extracts and builds a union of schema output types from invoke triggers.\n *\n * This recursively processes the trigger array and accumulates the output types\n * from all invoke trigger schemas (those with event: \"inngest/function.invoked\")\n * into a union type.\n *\n * @template T - Array of trigger definitions to process\n * @returns Union of all invoke trigger schema output types, or `never` if none found\n */\ntype ExtractInvokeSchemas<T extends readonly any[]> = T extends readonly [\n infer First,\n ...infer Rest,\n]\n ? First extends EventType<InvokeEventName, infer TSchema>\n ? TSchema extends StandardSchemaV1<infer TData>\n ? TData | ExtractInvokeSchemas<Rest>\n : ExtractInvokeSchemas<Rest>\n : ExtractInvokeSchemas<Rest>\n : never;\n\n/**\n * Extracts and builds a union of all data types from all trigger schemas.\n *\n * Unlike ExtractInvokeSchemas which only processes invoke triggers, this type\n * processes ALL triggers (event and invoke triggers) and builds a union of\n * their data types. For event triggers without schemas, it returns Record<string, any>.\n *\n * @template T - Array of trigger definitions to process\n * @returns Union of all trigger data types, or `never` if none found\n */\ntype ExtractAllSchemaOutputs<T extends readonly any[]> = T extends readonly [\n infer First,\n ...infer Rest,\n]\n ? First extends EventType<string, infer TSchema>\n ? TSchema extends StandardSchemaV1<infer TData>\n ? TData | ExtractAllSchemaOutputs<Rest>\n : BasicDataAny | ExtractAllSchemaOutputs<Rest>\n : First extends { event: EventType<string, infer TSchema> }\n ? TSchema extends StandardSchemaV1<infer TData>\n ? TData | ExtractAllSchemaOutputs<Rest>\n : BasicDataAny | ExtractAllSchemaOutputs<Rest>\n : First extends {\n event: string;\n schema: StandardSchemaV1<infer TData>;\n }\n ? TData | ExtractAllSchemaOutputs<Rest>\n : First extends { event: string }\n ? BasicDataAny | ExtractAllSchemaOutputs<Rest>\n : ExtractAllSchemaOutputs<Rest>\n : never;\n\n/**\n * Converts `never` type to empty object `{}` for data types.\n *\n * This utility is used to ensure that when no schemas are found, the data type\n * becomes `{}` instead of `never`, which provides better TypeScript error\n * messages and autocomplete.\n *\n * @template T - Type to check and potentially convert\n * @returns `{}` if T is `never`, otherwise returns T unchanged\n */\ntype NeverToEmpty<T> = [T] extends [never] ? {} : T;\n\n/**\n * Converts a trigger array to a tuple of ReceivedEvent types.\n *\n * This type transforms trigger definitions into the event types that a function\n * handler will receive. It always includes an invoke event type because\n * functions can be invoked directly regardless of their declared triggers.\n *\n * When invoke triggers are present, it uses their schemas for the invoke event\n * data. Otherwise, it derives the invoke event data type from all trigger\n * schemas.\n *\n * @template T - Array of trigger definitions to process\n */\nexport type ToReceivedEvent<T extends readonly any[]> =\n HasInvokeTrigger<T> extends true\n ? [\n ...FilterNonInvokeTriggers<T>,\n ReceivedEvent<InvokeEventName, NeverToEmpty<ExtractInvokeSchemas<T>>>,\n ]\n : [\n ...TriggersToEventsWithCron<T>,\n ReceivedEvent<\n InvokeEventName,\n NeverToEmpty<ExtractAllSchemaOutputs<T>>\n >,\n ];\n\n/**\n * Converts a tuple of ReceivedEvent types to a union.\n * @internal\n */\ntype ReceivedEventTupleToUnion<T extends readonly any[]> = T[number];\n\n/**\n * Base context object for handlers using the new trigger-based event typing.\n * This uses ToReceivedEvent to derive event types directly from triggers\n * rather than looking up from the client's event registry.\n *\n * @template TStepTools - The step tools type for this context\n * @template TTriggers - Array of trigger definitions\n * @internal\n */\nexport type BaseContextWithTriggers<\n TStepTools,\n TTriggers extends readonly any[],\n> = {\n /**\n * The event data present in the payload.\n */\n event: ReceivedEventTupleToUnion<ToReceivedEvent<TTriggers>>;\n events: AsTuple<ReceivedEventTupleToUnion<ToReceivedEvent<TTriggers>>>;\n\n /**\n * The run ID for the current function execution\n */\n runId: string;\n\n step: TStepTools;\n\n /**\n * Tools for grouping and coordinating steps.\n */\n group: ReturnType<typeof createGroupTools>;\n\n /**\n * The current zero-indexed attempt number for this function execution.\n */\n attempt: number;\n\n /**\n * The maximum number of attempts allowed for this function.\n */\n maxAttempts?: number;\n};\n\n/**\n * Context object for handlers using trigger-based event typing with middleware overrides.\n *\n * @template TStepTools - The step tools type for this context\n * @template TTriggers - Array of trigger definitions\n * @template TOverrides - Properties to override from middleware\n * @internal\n */\nexport type ContextWithTriggers<\n TStepTools,\n TTriggers extends readonly any[],\n TOverrides extends BasicDataUnknown = Record<never, never>,\n> = Omit<BaseContextWithTriggers<TStepTools, TTriggers>, keyof TOverrides> &\n TOverrides;\n\n/**\n * A handler type that computes its event type from a trigger array using ToReceivedEvent.\n * This provides proper typing for EventType instances and schema-bearing triggers.\n *\n * @template TStepTools - The step tools type for this context\n * @template TTriggers - Array of trigger definitions\n * @template TOverrides - Properties to override from middleware\n * @internal\n */\nexport type HandlerWithTriggers<\n TStepTools,\n TTriggers extends readonly any[],\n TOverrides extends BasicDataUnknown = Record<never, never>,\n> = (\n /**\n * The context argument provides access to all data and tooling available to\n * the function.\n */\n ctx: ContextWithTriggers<TStepTools, TTriggers, TOverrides>,\n) => unknown;\n\n/**\n * Type guard to check if an object has a `validate` method. The use case is for\n * safely validating an event payload that might have a `validate` method\n */\nexport function isValidatable<T>(\n value: T,\n): value is T & { validate: () => Promise<void> } {\n if (typeof value !== \"object\" || value === null) {\n return false;\n }\n if (!(\"validate\" in value)) {\n return false;\n }\n return typeof value.validate === \"function\";\n}\n"],"mappings":";;;;;;AAuWA,SAAgB,cACd,OACgD;AAChD,KAAI,OAAO,UAAU,YAAY,UAAU,KACzC,QAAO;AAET,KAAI,EAAE,cAAc,OAClB,QAAO;AAET,QAAO,OAAO,MAAM,aAAa"}