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.

123 lines (121 loc) 5.63 kB
const require_NonRetriableError = require('./NonRetriableError.cjs'); const require_als = require('./execution/als.cjs'); const require_InngestStepTools = require('./InngestStepTools.cjs'); //#region src/components/InngestGroupTools.ts /** * A helper that sets the parallel mode for all steps created within the * callback. This allows you to use native `Promise.race()` with cleaner syntax. * * @example * ```ts * // Defaults to "race" mode * const winner = await group.parallel(async () => { * return Promise.race([ * step.run("a", () => "a"), * step.run("b", () => "b"), * step.run("c", () => "c"), * ]); * }); * * // Or explicitly specify the mode * const winner = await group.parallel({ mode: "race" }, async () => { * return Promise.race([ * step.run("a", () => "a"), * step.run("b", () => "b"), * ]); * }); * ``` */ const parallel = async (optionsOrCallback, maybeCallback) => { const options = typeof optionsOrCallback === "function" ? {} : optionsOrCallback; const callback = typeof optionsOrCallback === "function" ? optionsOrCallback : maybeCallback; if (!callback) throw new Error("`group.parallel()` requires a callback function"); const currentCtx = require_als.getAsyncCtxSync(); if (!currentCtx?.execution) throw new Error("`group.parallel()` must be called within an Inngest function execution"); const als = await require_als.getAsyncLocalStorage(); if (require_als.isALSFallback()) throw new Error("`group.parallel()` requires AsyncLocalStorage support, which is not available in this runtime. Workaround: Pass `parallelMode` directly to each step:\n step.run({ id: \"my-step\", parallelMode: \"race\" }, fn)"); const nestedCtx = { ...currentCtx, execution: { ...currentCtx.execution, parallelMode: options.mode ?? "race" } }; return als.run(nestedCtx, callback); }; /** * Create the `group` tools object provided on the function execution context. * * @public */ const createGroupTools = (deps) => { const experiment = (async (idOrOptions, options) => { if (!deps?.experimentStepRun) throw new Error("`group.experiment()` requires step tools to be available. Ensure you are calling this within an Inngest function execution."); const { variants, select, withVariant } = options; const variantNames = Object.keys(variants); if (variantNames.length === 0) throw new Error("`group.experiment()` requires at least one variant to be defined."); if (require_als.isALSFallback()) throw new Error("`group.experiment()` requires AsyncLocalStorage support, which is not available in this runtime."); const stepOpts = require_InngestStepTools.getStepOptions(idOrOptions); let experimentStepHashedId; const selectedVariant = await deps.experimentStepRun(idOrOptions, async () => { experimentStepHashedId = require_als.getAsyncCtxSync()?.execution?.executingStep?.id; const alsInstance = await require_als.getAsyncLocalStorage(); const currentCtx$1 = require_als.getAsyncCtxSync(); const selectCtx = { ...currentCtx$1, execution: { ...currentCtx$1.execution, insideExperimentSelect: true } }; const result$1 = await alsInstance.run(selectCtx, () => select(variantNames)); if (!variantNames.includes(result$1)) throw new require_NonRetriableError.NonRetriableError(`group.experiment("${stepOpts.id}"): select() returned "${result$1}" which is not a known variant. Available variants: ${variantNames.join(", ")}`); const execInstance = require_als.getAsyncCtxSync()?.execution?.instance; if (execInstance && experimentStepHashedId) { execInstance.addMetadata(experimentStepHashedId, "inngest.experiment", "step", "merge", { experiment_name: stepOpts.id, variant_selected: result$1, selection_strategy: select.__experimentConfig.strategy, available_variants: variantNames, ...select.__experimentConfig.weights && { variant_weights: select.__experimentConfig.weights } }); if (select.__experimentConfig.nullishBucket) execInstance.addMetadata(experimentStepHashedId, "inngest.warnings", "step", "merge", { message: "experiment.bucket() received a null/undefined value; hashing empty string \"\" for variant selection" }); } return result$1; }); const variantFn = variants[selectedVariant]; if (!variantFn) throw new Error(`group.experiment("${stepOpts.id}"): variant "${selectedVariant}" was selected but is not defined. Available variants: ${variantNames.join(", ")}`); const currentCtx = require_als.getAsyncCtxSync(); const stepTracker = { found: false }; let result; if (currentCtx?.execution && !require_als.isALSFallback()) { const als = await require_als.getAsyncLocalStorage(); const nestedCtx = { ...currentCtx, execution: { ...currentCtx.execution, ...experimentStepHashedId && { experimentContext: { experimentStepID: experimentStepHashedId, experimentName: stepOpts.id, variant: selectedVariant } }, experimentStepTracker: stepTracker } }; result = await als.run(nestedCtx, () => variantFn()); } else result = await variantFn(); if (!stepTracker.found && !require_als.isALSFallback()) throw new require_NonRetriableError.NonRetriableError(`group.experiment("${stepOpts.id}"): variant "${selectedVariant}" did not invoke any step tools. Wrap your variant logic in step.run() to ensure it is memoized and not re-executed on replay.`); if (withVariant) return { result, variant: selectedVariant }; return result; }); return { parallel, experiment }; }; //#endregion exports.createGroupTools = createGroupTools; //# sourceMappingURL=InngestGroupTools.cjs.map