inngest 4.22.0 → 4.24.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +50 -0
- package/components/Inngest.cjs +5 -5
- package/components/Inngest.cjs.map +1 -1
- package/components/Inngest.d.cts +9 -5
- package/components/Inngest.d.cts.map +1 -1
- package/components/Inngest.d.ts +9 -5
- package/components/Inngest.d.ts.map +1 -1
- package/components/Inngest.js +5 -5
- package/components/Inngest.js.map +1 -1
- package/components/InngestGroupTools.cjs +4 -3
- package/components/InngestGroupTools.cjs.map +1 -1
- package/components/InngestGroupTools.d.cts +27 -1
- package/components/InngestGroupTools.d.cts.map +1 -1
- package/components/InngestGroupTools.d.ts +27 -1
- package/components/InngestGroupTools.d.ts.map +1 -1
- package/components/InngestGroupTools.js +5 -4
- package/components/InngestGroupTools.js.map +1 -1
- package/components/InngestMetadata.cjs +10 -4
- package/components/InngestMetadata.cjs.map +1 -1
- package/components/InngestMetadata.d.cts +39 -8
- package/components/InngestMetadata.d.cts.map +1 -1
- package/components/InngestMetadata.d.ts +39 -8
- package/components/InngestMetadata.d.ts.map +1 -1
- package/components/InngestMetadata.js +10 -4
- package/components/InngestMetadata.js.map +1 -1
- package/components/InngestStepTools.cjs +57 -2
- package/components/InngestStepTools.cjs.map +1 -1
- package/components/InngestStepTools.d.cts.map +1 -1
- package/components/InngestStepTools.d.ts.map +1 -1
- package/components/InngestStepTools.js +58 -4
- package/components/InngestStepTools.js.map +1 -1
- package/components/execution/InngestExecution.cjs.map +1 -1
- package/components/execution/InngestExecution.d.cts +2 -2
- package/components/execution/InngestExecution.d.cts.map +1 -1
- package/components/execution/InngestExecution.d.ts +2 -2
- package/components/execution/InngestExecution.d.ts.map +1 -1
- package/components/execution/InngestExecution.js.map +1 -1
- package/components/execution/als.cjs.map +1 -1
- package/components/execution/als.d.cts +8 -1
- package/components/execution/als.d.cts.map +1 -1
- package/components/execution/als.d.ts +8 -1
- package/components/execution/als.d.ts.map +1 -1
- package/components/execution/als.js.map +1 -1
- package/components/execution/engine.cjs +11 -4
- package/components/execution/engine.cjs.map +1 -1
- package/components/execution/engine.d.cts.map +1 -1
- package/components/execution/engine.d.ts.map +1 -1
- package/components/execution/engine.js +11 -4
- package/components/execution/engine.js.map +1 -1
- package/components/execution/otel/middleware.d.cts +6 -6
- package/components/execution/otel/middleware.d.ts +6 -6
- package/components/execution/otel/processor.cjs +5 -0
- package/components/execution/otel/processor.cjs.map +1 -1
- package/components/execution/otel/processor.d.cts.map +1 -1
- package/components/execution/otel/processor.d.ts.map +1 -1
- package/components/execution/otel/processor.js +5 -0
- package/components/execution/otel/processor.js.map +1 -1
- package/components/realtime/types.d.cts +4 -4
- package/components/realtime/types.d.cts.map +1 -1
- package/components/realtime/types.d.ts +4 -4
- package/components/realtime/types.d.ts.map +1 -1
- package/components/sandbox/client.cjs +6 -4
- package/components/sandbox/client.cjs.map +1 -1
- package/components/sandbox/client.js +6 -4
- package/components/sandbox/client.js.map +1 -1
- package/components/sandbox/durable.cjs +59 -20
- package/components/sandbox/durable.cjs.map +1 -1
- package/components/sandbox/durable.js +59 -20
- package/components/sandbox/durable.js.map +1 -1
- package/components/sandbox/group.cjs +56 -0
- package/components/sandbox/group.cjs.map +1 -0
- package/components/sandbox/group.d.cts +16 -0
- package/components/sandbox/group.d.cts.map +1 -0
- package/components/sandbox/group.d.ts +16 -0
- package/components/sandbox/group.d.ts.map +1 -0
- package/components/sandbox/group.js +56 -0
- package/components/sandbox/group.js.map +1 -0
- package/components/sandbox/middleware.cjs +28 -2
- package/components/sandbox/middleware.cjs.map +1 -1
- package/components/sandbox/middleware.d.cts +5 -0
- package/components/sandbox/middleware.d.cts.map +1 -1
- package/components/sandbox/middleware.d.ts +5 -0
- package/components/sandbox/middleware.d.ts.map +1 -1
- package/components/sandbox/middleware.js +28 -2
- package/components/sandbox/middleware.js.map +1 -1
- package/components/sandbox/protocol.cjs +6 -2
- package/components/sandbox/protocol.cjs.map +1 -1
- package/components/sandbox/protocol.d.cts.map +1 -1
- package/components/sandbox/protocol.d.ts.map +1 -1
- package/components/sandbox/protocol.js +5 -4
- package/components/sandbox/protocol.js.map +1 -1
- package/components/sandbox/trace.cjs +113 -0
- package/components/sandbox/trace.cjs.map +1 -0
- package/components/sandbox/trace.js +112 -0
- package/components/sandbox/trace.js.map +1 -0
- package/components/sandbox/types.cjs.map +1 -1
- package/components/sandbox/types.d.cts +30 -4
- package/components/sandbox/types.d.cts.map +1 -1
- package/components/sandbox/types.d.ts +30 -4
- package/components/sandbox/types.d.ts.map +1 -1
- package/components/sandbox/types.js.map +1 -1
- package/components/sandbox/validation.cjs +25 -16
- package/components/sandbox/validation.cjs.map +1 -1
- package/components/sandbox/validation.js +25 -16
- package/components/sandbox/validation.js.map +1 -1
- package/experimental.cjs +1 -1
- package/experimental.d.cts +2 -1
- package/experimental.d.ts +2 -1
- package/experimental.js +1 -1
- package/helpers/duration.cjs +46 -0
- package/helpers/duration.cjs.map +1 -0
- package/helpers/duration.d.cts +25 -0
- package/helpers/duration.d.cts.map +1 -0
- package/helpers/duration.d.ts +25 -0
- package/helpers/duration.d.ts.map +1 -0
- package/helpers/duration.js +44 -0
- package/helpers/duration.js.map +1 -0
- package/index.cjs +3 -1
- package/index.d.cts +2 -1
- package/index.d.ts +2 -1
- package/index.js +3 -2
- package/package.json +1 -1
- package/react.d.cts +1 -1
- package/react.d.ts +1 -1
- package/types.cjs.map +1 -1
- package/types.d.cts +66 -13
- package/types.d.cts.map +1 -1
- package/types.d.ts +66 -13
- package/types.d.ts.map +1 -1
- package/types.js.map +1 -1
- package/version.cjs +1 -1
- package/version.cjs.map +1 -1
- package/version.d.cts +1 -1
- package/version.d.ts +1 -1
- package/version.js +1 -1
- package/version.js.map +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"InngestGroupTools.js","names":["options: ParallelOptions","nestedCtx: AsyncContext","experiment: GroupExperiment","experimentStepHashedId: string | undefined","selectedVariant: string","currentCtx","selectCtx: AsyncContext","result","result: unknown"],"sources":["../../src/components/InngestGroupTools.ts"],"sourcesContent":["import type { IsNever } from \"../helpers/types.ts\";\nimport type { ExperimentRef, StepOptionsOrId } from \"../types.ts\";\nimport {\n type AsyncContext,\n getAsyncCtxSync,\n getAsyncLocalStorage,\n isALSFallback,\n} from \"./execution/als.ts\";\nimport { getStepOptions } from \"./InngestStepTools.ts\";\nimport { NonRetriableError } from \"./NonRetriableError.ts\";\n\n/**\n * Options for the `group.parallel()` helper.\n */\nexport interface ParallelOptions {\n /**\n * The parallel mode to apply to all steps created within the callback.\n *\n * - `\"race\"`: Steps will be executed with race semantics, meaning the first\n * step to complete will \"win\" and remaining steps may be cancelled.\n */\n mode?: \"race\";\n}\n\n/**\n * A helper that sets the parallel mode for all steps created within the\n * callback. This allows you to use native `Promise.race()` with cleaner syntax.\n *\n * @example\n * ```ts\n * // Defaults to \"race\" mode\n * const winner = await group.parallel(async () => {\n * return Promise.race([\n * step.run(\"a\", () => \"a\"),\n * step.run(\"b\", () => \"b\"),\n * step.run(\"c\", () => \"c\"),\n * ]);\n * });\n *\n * // Or explicitly specify the mode\n * const winner = await group.parallel({ mode: \"race\" }, async () => {\n * return Promise.race([\n * step.run(\"a\", () => \"a\"),\n * step.run(\"b\", () => \"b\"),\n * ]);\n * });\n * ```\n */\nconst parallel = async <T>(\n optionsOrCallback: ParallelOptions | (() => Promise<T>),\n maybeCallback?: () => Promise<T>,\n): Promise<T> => {\n const options: ParallelOptions =\n typeof optionsOrCallback === \"function\" ? {} : optionsOrCallback;\n const callback =\n typeof optionsOrCallback === \"function\" ? optionsOrCallback : maybeCallback;\n\n if (!callback) {\n throw new Error(\"`group.parallel()` requires a callback function\");\n }\n\n const currentCtx = getAsyncCtxSync();\n\n if (!currentCtx?.execution) {\n throw new Error(\n \"`group.parallel()` must be called within an Inngest function execution\",\n );\n }\n\n const als = await getAsyncLocalStorage();\n\n if (isALSFallback()) {\n throw new Error(\n \"`group.parallel()` requires AsyncLocalStorage support, which is not available in this runtime. \" +\n \"Workaround: Pass `parallelMode` directly to each step:\\n\" +\n ' step.run({ id: \"my-step\", parallelMode: \"race\" }, fn)',\n );\n }\n\n // Create a new context with the parallelMode set\n const nestedCtx: AsyncContext = {\n ...currentCtx,\n execution: {\n ...currentCtx.execution,\n parallelMode: options.mode ?? \"race\",\n },\n };\n\n // Run the callback inside the nested context\n return als.run(nestedCtx, callback);\n};\n\n/**\n * Configuration for how the experiment selects a variant.\n */\nexport interface ExperimentStrategyConfig {\n strategy: string;\n weights?: Record<string, number>;\n nullishBucket?: boolean;\n}\n\n/**\n * A callable selection function that also carries strategy metadata.\n */\nexport interface ExperimentSelectFn {\n (variantNames?: string[]): Promise<string> | string;\n __experimentConfig: ExperimentStrategyConfig;\n}\n\n/**\n * Options for `group.experiment()`.\n */\nexport interface ExperimentOptions<\n TVariants extends Record<string, () => unknown>,\n> {\n /**\n * A map of variant names to callbacks. The selected variant's callback will\n * be executed at the top level so that any `step.*` calls inside it go\n * through normal step discovery.\n */\n variants: TVariants;\n\n /**\n * A selection function that returns the name of the variant to execute.\n * The result is memoized via a step so the same variant is used on retries.\n */\n select: ExperimentSelectFn;\n}\n\n/**\n * Computes the return type of an experiment based on variant callbacks.\n *\n * When `TConstraint` is `never`, the return type is inferred as the union of\n * all variant callback return types. Otherwise `TConstraint` is used directly.\n */\nexport type VariantResult<\n TConstraint,\n TVariants extends Record<string, () => unknown>,\n> = IsNever<TConstraint> extends true\n ? Awaited<ReturnType<TVariants[keyof TVariants]>>\n : TConstraint;\n\n/**\n * Metadata values stored alongside the experiment step for UI rendering.\n */\nexport interface ExperimentMetadataValues {\n name: string;\n variant: string;\n selection_strategy: string;\n available_variants: string[];\n variant_weights?: Record<string, number>;\n}\n\n/** Run an A/B experiment; returns the result, the selected variant, and a\n * replay-stable handle for scoring the experiment later. */\nexport interface GroupExperiment {\n <TVariants extends Record<string, () => unknown>>(\n idOrOptions: StepOptionsOrId,\n options: ExperimentOptions<TVariants>,\n ): Promise<{\n result: VariantResult<never, TVariants>;\n variant: string;\n experimentRef: ExperimentRef;\n }>;\n}\n\n/**\n * Tools for grouping and coordinating steps.\n *\n * @public\n */\nexport interface GroupTools {\n /**\n * Run a callback where all steps automatically receive a `parallelMode`\n * option, removing the need to tag each step individually. Defaults to\n * `\"race\"` mode.\n *\n * @example\n * ```ts\n * // Defaults to \"race\" mode\n * const winner = await group.parallel(async () => {\n * return Promise.race([\n * step.run(\"a\", () => \"a\"),\n * step.run(\"b\", () => \"b\"),\n * step.run(\"c\", () => \"c\"),\n * ]);\n * });\n *\n * // Or explicitly specify the mode\n * const winner = await group.parallel({ mode: \"race\" }, async () => {\n * return Promise.race([\n * step.run(\"a\", () => \"a\"),\n * step.run(\"b\", () => \"b\"),\n * ]);\n * });\n * ```\n */\n parallel: <T>(\n optionsOrCallback: ParallelOptions | (() => Promise<T>),\n maybeCallback?: () => Promise<T>,\n ) => Promise<T>;\n\n /**\n * Run an A/B experiment within a function. Selects a variant via a memoized\n * step, then executes the selected variant's callback at the top level so\n * its `step.*` calls go through normal step discovery.\n *\n * @example\n * ```ts\n * const result = await group.experiment(\"checkout-flow\", {\n * variants: {\n * control: () => step.run(\"control-checkout\", () => oldCheckout()),\n * new_flow: () => step.run(\"new-checkout\", () => newCheckout()),\n * },\n * select: Object.assign(() => \"control\", {\n * __experimentConfig: { strategy: \"weighted\", weights: { control: 80, new_flow: 20 } },\n * }),\n * });\n * ```\n */\n experiment: GroupExperiment;\n}\n\n/**\n * Dependencies injected into `createGroupTools` from the execution engine.\n */\nexport interface GroupToolsDeps {\n /**\n * A `step.run` variant with `opts.type = \"group.experiment\"`, extracted from\n * step tools via the experiment symbol. Undefined when not available.\n */\n // biome-ignore lint/suspicious/noExplicitAny: internal plumbing\n experimentStepRun?: (...args: any[]) => Promise<any>;\n}\n\n/**\n * Create the `group` tools object provided on the function execution context.\n *\n * @public\n */\nexport const createGroupTools = (deps?: GroupToolsDeps): GroupTools => {\n const experiment: GroupExperiment = (async (\n idOrOptions: StepOptionsOrId,\n // biome-ignore lint/suspicious/noExplicitAny: implementation signature for overloaded interface\n options: any,\n ) => {\n if (!deps?.experimentStepRun) {\n throw new Error(\n \"`group.experiment()` requires step tools to be available. \" +\n \"Ensure you are calling this within an Inngest function execution.\",\n );\n }\n\n const { variants, select } = options;\n const variantNames = Object.keys(variants);\n\n if (variantNames.length === 0) {\n throw new Error(\n \"`group.experiment()` requires at least one variant to be defined.\",\n );\n }\n\n if (isALSFallback()) {\n throw new Error(\n \"`group.experiment()` requires AsyncLocalStorage support, which is not available in this runtime.\",\n );\n }\n\n const stepOpts = getStepOptions(idOrOptions);\n\n // Use the experiment step run to memoize the variant selection.\n // This creates a StepPlanned opcode with opts.type = \"group.experiment\".\n let experimentStepHashedId: string | undefined;\n\n const selectedVariant: string = await deps.experimentStepRun(\n idOrOptions,\n async () => {\n // Capture the hashed step ID so we can propagate it to variant sub-steps.\n experimentStepHashedId =\n getAsyncCtxSync()?.execution?.executingStep?.id;\n\n const alsInstance = await getAsyncLocalStorage();\n const currentCtx = getAsyncCtxSync()!;\n const selectCtx: AsyncContext = {\n ...currentCtx,\n execution: {\n ...currentCtx.execution!,\n insideExperimentSelect: true,\n },\n };\n const result = await alsInstance.run(selectCtx, () =>\n select(variantNames),\n );\n\n if (!variantNames.includes(result)) {\n throw new NonRetriableError(\n `group.experiment(\"${stepOpts.id}\"): select() returned \"${result}\" ` +\n `which is not a known variant. Available variants: ${variantNames.join(\", \")}`,\n );\n }\n\n // Attach experiment metadata to this step's OutgoingOp.\n const ctx = getAsyncCtxSync();\n const execInstance = ctx?.execution?.instance;\n\n if (execInstance && experimentStepHashedId) {\n execInstance.addMetadata(\n experimentStepHashedId,\n \"inngest.experiment\",\n \"step\",\n \"merge\",\n {\n name: stepOpts.id,\n variant: result,\n selection_strategy: select.__experimentConfig.strategy,\n available_variants: variantNames,\n ...(select.__experimentConfig.weights && {\n variant_weights: select.__experimentConfig.weights,\n }),\n } satisfies ExperimentMetadataValues,\n );\n\n if (select.__experimentConfig.nullishBucket) {\n execInstance.addMetadata(\n experimentStepHashedId,\n \"inngest.warnings\",\n \"step\",\n \"merge\",\n {\n \"sdk.group.experiment.nullishBucket\":\n \"experiment.bucket() received a null/undefined value; \" +\n 'hashing empty string \"\" for variant selection',\n },\n );\n }\n }\n\n return result;\n },\n );\n\n // Look up and execute the selected variant's callback at the top level\n // so its step.* calls go through normal step discovery.\n const variantFn = variants[selectedVariant];\n\n if (!variantFn) {\n throw new Error(\n `group.experiment(\"${stepOpts.id}\"): variant \"${selectedVariant}\" ` +\n `was selected but is not defined. Available variants: ${variantNames.join(\", \")}`,\n );\n }\n\n // Propagate experiment context via ALS so variant sub-steps include\n // experiment fields in their OutgoingOp.opts. The executor reads these\n // fields from opts and emits the step-scoped `inngest.experiment`\n // metadata span itself — the SDK does not need to call addMetadata()\n // for variant steps. See the companion executor change in inngest/inngest\n // for the server-side emission path.\n //\n // Also track whether any step tool is invoked to detect zero-step\n // variants.\n //\n // NOTE: experimentStepHashedId may be undefined on replay because it\n // is captured inside the selection step callback, which doesn't run\n // when memoized. We still set experimentContext (with an empty string\n // for the hashed ID fallback) so that variant sub-steps discovered on\n // replay still carry experiment fields in their opts and the executor\n // can attach metadata to their ClickHouse rows.\n const currentCtx = getAsyncCtxSync();\n const stepTracker = { found: false };\n let result: unknown;\n\n if (currentCtx?.execution && !isALSFallback()) {\n const als = await getAsyncLocalStorage();\n const nestedCtx: AsyncContext = {\n ...currentCtx,\n execution: {\n ...currentCtx.execution,\n experimentContext: {\n experimentStepID: experimentStepHashedId ?? \"\",\n experimentName: stepOpts.id,\n variant: selectedVariant,\n selectionStrategy: select.__experimentConfig.strategy,\n },\n experimentStepTracker: stepTracker,\n },\n };\n result = await als.run(nestedCtx, () => variantFn());\n } else {\n result = await variantFn();\n }\n\n // If the variant returned without invoking any step tools, it will\n // silently re-execute on every replay. Throw a non-retriable error\n // to prevent this.\n if (!stepTracker.found && !isALSFallback()) {\n throw new NonRetriableError(\n `group.experiment(\"${stepOpts.id}\"): variant \"${selectedVariant}\" ` +\n \"did not invoke any step tools. Wrap your variant logic in \" +\n \"step.run() to ensure it is memoized and not re-executed on replay.\",\n );\n }\n\n return {\n result,\n variant: selectedVariant,\n experimentRef: { experimentName: stepOpts.id, variant: selectedVariant },\n };\n }) as GroupExperiment;\n\n return { parallel, experiment };\n};\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"InngestGroupTools.js","names":["options: ParallelOptions","nestedCtx: AsyncContext","experiment: GroupExperiment","experimentStepHashedId: string | undefined","selectedVariant: string","currentCtx","selectCtx: AsyncContext","result","result: unknown"],"sources":["../../src/components/InngestGroupTools.ts"],"sourcesContent":["import type { IsNever } from \"../helpers/types.ts\";\nimport type { ExperimentRef, StepOptionsOrId, StepSpan } from \"../types.ts\";\nimport {\n type AsyncContext,\n getAsyncCtxSync,\n getAsyncLocalStorage,\n isALSFallback,\n} from \"./execution/als.ts\";\nimport { getStepOptions, withSpan } from \"./InngestStepTools.ts\";\nimport { NonRetriableError } from \"./NonRetriableError.ts\";\n\n/**\n * Options for the `group.parallel()` helper.\n */\nexport interface ParallelOptions {\n /**\n * The parallel mode to apply to all steps created within the callback.\n *\n * - `\"race\"`: Steps will be executed with race semantics, meaning the first\n * step to complete will \"win\" and remaining steps may be cancelled.\n */\n mode?: \"race\";\n}\n\n/**\n * A helper that sets the parallel mode for all steps created within the\n * callback. This allows you to use native `Promise.race()` with cleaner syntax.\n *\n * @example\n * ```ts\n * // Defaults to \"race\" mode\n * const winner = await group.parallel(async () => {\n * return Promise.race([\n * step.run(\"a\", () => \"a\"),\n * step.run(\"b\", () => \"b\"),\n * step.run(\"c\", () => \"c\"),\n * ]);\n * });\n *\n * // Or explicitly specify the mode\n * const winner = await group.parallel({ mode: \"race\" }, async () => {\n * return Promise.race([\n * step.run(\"a\", () => \"a\"),\n * step.run(\"b\", () => \"b\"),\n * ]);\n * });\n * ```\n */\nconst parallel = async <T>(\n optionsOrCallback: ParallelOptions | (() => Promise<T>),\n maybeCallback?: () => Promise<T>,\n): Promise<T> => {\n const options: ParallelOptions =\n typeof optionsOrCallback === \"function\" ? {} : optionsOrCallback;\n const callback =\n typeof optionsOrCallback === \"function\" ? optionsOrCallback : maybeCallback;\n\n if (!callback) {\n throw new Error(\"`group.parallel()` requires a callback function\");\n }\n\n const currentCtx = getAsyncCtxSync();\n\n if (!currentCtx?.execution) {\n throw new Error(\n \"`group.parallel()` must be called within an Inngest function execution\",\n );\n }\n\n const als = await getAsyncLocalStorage();\n\n if (isALSFallback()) {\n throw new Error(\n \"`group.parallel()` requires AsyncLocalStorage support, which is not available in this runtime. \" +\n \"Workaround: Pass `parallelMode` directly to each step:\\n\" +\n ' step.run({ id: \"my-step\", parallelMode: \"race\" }, fn)',\n );\n }\n\n // Create a new context with the parallelMode set\n const nestedCtx: AsyncContext = {\n ...currentCtx,\n execution: {\n ...currentCtx.execution,\n parallelMode: options.mode ?? \"race\",\n },\n };\n\n // Run the callback inside the nested context\n return als.run(nestedCtx, callback);\n};\n\n/**\n * Configuration for how the experiment selects a variant.\n */\nexport interface ExperimentStrategyConfig {\n strategy: string;\n weights?: Record<string, number>;\n nullishBucket?: boolean;\n}\n\n/**\n * A callable selection function that also carries strategy metadata.\n */\nexport interface ExperimentSelectFn {\n (variantNames?: string[]): Promise<string> | string;\n __experimentConfig: ExperimentStrategyConfig;\n}\n\n/**\n * Options for `group.experiment()`.\n */\nexport interface ExperimentOptions<\n TVariants extends Record<string, () => unknown>,\n> {\n /**\n * A map of variant names to callbacks. The selected variant's callback will\n * be executed at the top level so that any `step.*` calls inside it go\n * through normal step discovery.\n */\n variants: TVariants;\n\n /**\n * A selection function that returns the name of the variant to execute.\n * The result is memoized via a step so the same variant is used on retries.\n */\n select: ExperimentSelectFn;\n}\n\n/**\n * Computes the return type of an experiment based on variant callbacks.\n *\n * When `TConstraint` is `never`, the return type is inferred as the union of\n * all variant callback return types. Otherwise `TConstraint` is used directly.\n */\nexport type VariantResult<\n TConstraint,\n TVariants extends Record<string, () => unknown>,\n> = IsNever<TConstraint> extends true\n ? Awaited<ReturnType<TVariants[keyof TVariants]>>\n : TConstraint;\n\n/**\n * Metadata values stored alongside the experiment step for UI rendering.\n */\nexport interface ExperimentMetadataValues {\n name: string;\n variant: string;\n selection_strategy: string;\n available_variants: string[];\n variant_weights?: Record<string, number>;\n}\n\n/** Run an A/B experiment; returns the result, the selected variant, and a\n * replay-stable handle for scoring the experiment later. */\nexport interface GroupExperiment {\n <TVariants extends Record<string, () => unknown>>(\n idOrOptions: StepOptionsOrId,\n options: ExperimentOptions<TVariants>,\n ): Promise<{\n result: VariantResult<never, TVariants>;\n variant: string;\n experimentRef: ExperimentRef;\n }>;\n}\n\n/**\n * Tools for grouping and coordinating steps.\n *\n * @public\n */\nexport interface GroupTools {\n /**\n * Run a callback where all steps automatically receive a `parallelMode`\n * option, removing the need to tag each step individually. Defaults to\n * `\"race\"` mode.\n *\n * @example\n * ```ts\n * // Defaults to \"race\" mode\n * const winner = await group.parallel(async () => {\n * return Promise.race([\n * step.run(\"a\", () => \"a\"),\n * step.run(\"b\", () => \"b\"),\n * step.run(\"c\", () => \"c\"),\n * ]);\n * });\n *\n * // Or explicitly specify the mode\n * const winner = await group.parallel({ mode: \"race\" }, async () => {\n * return Promise.race([\n * step.run(\"a\", () => \"a\"),\n * step.run(\"b\", () => \"b\"),\n * ]);\n * });\n * ```\n */\n parallel: <T>(\n optionsOrCallback: ParallelOptions | (() => Promise<T>),\n maybeCallback?: () => Promise<T>,\n ) => Promise<T>;\n\n /**\n * Run an A/B experiment within a function. Selects a variant via a memoized\n * step, then executes the selected variant's callback at the top level so\n * its `step.*` calls go through normal step discovery.\n *\n * @example\n * ```ts\n * const result = await group.experiment(\"checkout-flow\", {\n * variants: {\n * control: () => step.run(\"control-checkout\", () => oldCheckout()),\n * new_flow: () => step.run(\"new-checkout\", () => newCheckout()),\n * },\n * select: Object.assign(() => \"control\", {\n * __experimentConfig: { strategy: \"weighted\", weights: { control: 80, new_flow: 20 } },\n * }),\n * });\n * ```\n */\n experiment: GroupExperiment;\n\n /**\n * Run a callback where every step is grouped under a span in the trace,\n * nested inside any span this is called in. The same ID under the same\n * parent re-enters the same span.\n *\n * Without AsyncLocalStorage, the callback runs ungrouped.\n *\n * EXPERIMENTAL: a library that runs steps on the user's behalf can give\n * the span an `origin`, the library's `\"<package>@<version>\"`, such as\n * `\"@inngest/ci@0.1.0\"`. Steps inside inherit it unless they set their own\n * `\"~origin\"`; a nested span inherits it too unless it sets its own. Leave\n * it unset for spans that stand for the user's own code. The Inngest UI\n * de-emphasises spans and steps whose origin is an Inngest package.\n *\n * @example\n * ```ts\n * await group[\"~span\"](\n * { id: \"setup\", name: \"Start sandbox\", origin: \"@inngest/ci@0.1.0\" },\n * () => step.run(\"create\", () => createSandbox()),\n * );\n * ```\n *\n * @internal Unstable and may change or be removed without a major version\n * bump.\n */\n \"~span\": <T>(span: StepSpan, callback: () => T) => T;\n}\n\n/**\n * Dependencies injected into `createGroupTools` from the execution engine.\n */\nexport interface GroupToolsDeps {\n /**\n * A `step.run` variant with `opts.type = \"group.experiment\"`, extracted from\n * step tools via the experiment symbol. Undefined when not available.\n */\n // biome-ignore lint/suspicious/noExplicitAny: internal plumbing\n experimentStepRun?: (...args: any[]) => Promise<any>;\n}\n\n/**\n * Create the `group` tools object provided on the function execution context.\n *\n * @public\n */\nexport const createGroupTools = (deps?: GroupToolsDeps): GroupTools => {\n const experiment: GroupExperiment = (async (\n idOrOptions: StepOptionsOrId,\n // biome-ignore lint/suspicious/noExplicitAny: implementation signature for overloaded interface\n options: any,\n ) => {\n if (!deps?.experimentStepRun) {\n throw new Error(\n \"`group.experiment()` requires step tools to be available. \" +\n \"Ensure you are calling this within an Inngest function execution.\",\n );\n }\n\n const { variants, select } = options;\n const variantNames = Object.keys(variants);\n\n if (variantNames.length === 0) {\n throw new Error(\n \"`group.experiment()` requires at least one variant to be defined.\",\n );\n }\n\n if (isALSFallback()) {\n throw new Error(\n \"`group.experiment()` requires AsyncLocalStorage support, which is not available in this runtime.\",\n );\n }\n\n const stepOpts = getStepOptions(idOrOptions);\n\n // Use the experiment step run to memoize the variant selection.\n // This creates a StepPlanned opcode with opts.type = \"group.experiment\".\n let experimentStepHashedId: string | undefined;\n\n const selectedVariant: string = await deps.experimentStepRun(\n idOrOptions,\n async () => {\n // Capture the hashed step ID so we can propagate it to variant sub-steps.\n experimentStepHashedId =\n getAsyncCtxSync()?.execution?.executingStep?.id;\n\n const alsInstance = await getAsyncLocalStorage();\n const currentCtx = getAsyncCtxSync()!;\n const selectCtx: AsyncContext = {\n ...currentCtx,\n execution: {\n ...currentCtx.execution!,\n insideExperimentSelect: true,\n },\n };\n const result = await alsInstance.run(selectCtx, () =>\n select(variantNames),\n );\n\n if (!variantNames.includes(result)) {\n throw new NonRetriableError(\n `group.experiment(\"${stepOpts.id}\"): select() returned \"${result}\" ` +\n `which is not a known variant. Available variants: ${variantNames.join(\", \")}`,\n );\n }\n\n // Attach experiment metadata to this step's OutgoingOp.\n const ctx = getAsyncCtxSync();\n const execInstance = ctx?.execution?.instance;\n\n if (execInstance && experimentStepHashedId) {\n execInstance.addMetadata(\n experimentStepHashedId,\n \"inngest.experiment\",\n \"step\",\n \"set\",\n {\n name: stepOpts.id,\n variant: result,\n selection_strategy: select.__experimentConfig.strategy,\n available_variants: variantNames,\n ...(select.__experimentConfig.weights && {\n variant_weights: select.__experimentConfig.weights,\n }),\n } satisfies ExperimentMetadataValues,\n );\n\n if (select.__experimentConfig.nullishBucket) {\n execInstance.addMetadata(\n experimentStepHashedId,\n \"inngest.warnings\",\n \"step\",\n \"set\",\n {\n \"sdk.group.experiment.nullishBucket\":\n \"experiment.bucket() received a null/undefined value; \" +\n 'hashing empty string \"\" for variant selection',\n },\n );\n }\n }\n\n return result;\n },\n );\n\n // Look up and execute the selected variant's callback at the top level\n // so its step.* calls go through normal step discovery.\n const variantFn = variants[selectedVariant];\n\n if (!variantFn) {\n throw new Error(\n `group.experiment(\"${stepOpts.id}\"): variant \"${selectedVariant}\" ` +\n `was selected but is not defined. Available variants: ${variantNames.join(\", \")}`,\n );\n }\n\n // Propagate experiment context via ALS so variant sub-steps include\n // experiment fields in their OutgoingOp.opts. The executor reads these\n // fields from opts and emits the step-scoped `inngest.experiment`\n // metadata span itself — the SDK does not need to call addMetadata()\n // for variant steps. See the companion executor change in inngest/inngest\n // for the server-side emission path.\n //\n // Also track whether any step tool is invoked to detect zero-step\n // variants.\n //\n // NOTE: experimentStepHashedId may be undefined on replay because it\n // is captured inside the selection step callback, which doesn't run\n // when memoized. We still set experimentContext (with an empty string\n // for the hashed ID fallback) so that variant sub-steps discovered on\n // replay still carry experiment fields in their opts and the executor\n // can attach metadata to their ClickHouse rows.\n const currentCtx = getAsyncCtxSync();\n const stepTracker = { found: false };\n let result: unknown;\n\n if (currentCtx?.execution && !isALSFallback()) {\n const als = await getAsyncLocalStorage();\n const nestedCtx: AsyncContext = {\n ...currentCtx,\n execution: {\n ...currentCtx.execution,\n experimentContext: {\n experimentStepID: experimentStepHashedId ?? \"\",\n experimentName: stepOpts.id,\n variant: selectedVariant,\n selectionStrategy: select.__experimentConfig.strategy,\n },\n experimentStepTracker: stepTracker,\n },\n };\n result = await als.run(nestedCtx, () => variantFn());\n } else {\n result = await variantFn();\n }\n\n // If the variant returned without invoking any step tools, it will\n // silently re-execute on every replay. Throw a non-retriable error\n // to prevent this.\n if (!stepTracker.found && !isALSFallback()) {\n throw new NonRetriableError(\n `group.experiment(\"${stepOpts.id}\"): variant \"${selectedVariant}\" ` +\n \"did not invoke any step tools. Wrap your variant logic in \" +\n \"step.run() to ensure it is memoized and not re-executed on replay.\",\n );\n }\n\n return {\n result,\n variant: selectedVariant,\n experimentRef: { experimentName: stepOpts.id, variant: selectedVariant },\n };\n }) as GroupExperiment;\n\n return { parallel, experiment, \"~span\": withSpan };\n};\n"],"mappings":"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"}
|
|
@@ -42,8 +42,14 @@ var UnscopedMetadataBuilder = class UnscopedMetadataBuilder {
|
|
|
42
42
|
spanId: id
|
|
43
43
|
});
|
|
44
44
|
}
|
|
45
|
+
async set(values, kind = "default") {
|
|
46
|
+
await performOp(this.client, this.config, values, `userland.${kind}`, "set");
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* @deprecated Use `set()` instead. See `MetadataBuilder.update()`.
|
|
50
|
+
*/
|
|
45
51
|
async update(values, kind = "default") {
|
|
46
|
-
await performOp(this.client, this.config, values, `userland.${kind}`, "merge
|
|
52
|
+
await performOp(this.client, this.config, values, `userland.${kind}`, "set", { merge: true });
|
|
47
53
|
}
|
|
48
54
|
toJSON() {
|
|
49
55
|
return this.config;
|
|
@@ -126,19 +132,19 @@ function targetsCurrentStep(config, ctx) {
|
|
|
126
132
|
* Internal metadata write helper shared by metadata and score helpers.
|
|
127
133
|
* @internal
|
|
128
134
|
*/
|
|
129
|
-
async function performOp(client, config, values, kind, op) {
|
|
135
|
+
async function performOp(client, config, values, kind, op, opts) {
|
|
130
136
|
const ctx = await require_als.getAsyncCtx();
|
|
131
137
|
const target = buildTarget(config, ctx);
|
|
132
138
|
const isInsideRun = !!ctx?.execution;
|
|
133
139
|
const isInsideStep = !!ctx?.execution?.executingStep;
|
|
134
|
-
if (isInsideRun && !isInsideStep && !(kind === "inngest.score" || kind === "inngest.experiment")) client[require_Inngest.internalLoggerSymbol].warn("metadata.update() called outside of a step; this metadata may be lost on retries. Wrap the call in step.run() for durable metadata.");
|
|
140
|
+
if (isInsideRun && !isInsideStep && !(kind === "inngest.score" || kind === "inngest.experiment")) client[require_Inngest.internalLoggerSymbol].warn("metadata.set() (or update()) called outside of a step; this metadata may be lost on retries. Wrap the call in step.run() for durable metadata.");
|
|
135
141
|
const runId = config.runId ?? ctx?.execution?.ctx?.runId;
|
|
136
142
|
const attempt = config.attempt ?? ctx?.execution?.ctx?.attempt;
|
|
137
143
|
if (runId === ctx?.execution?.ctx?.runId && targetsCurrentStep(config, ctx) && attempt === ctx?.execution?.ctx?.attempt && !config.spanId) {
|
|
138
144
|
const executingStep = ctx?.execution?.executingStep;
|
|
139
145
|
const execInstance = ctx?.execution?.instance;
|
|
140
146
|
const scope = getBatchScope(config);
|
|
141
|
-
if (executingStep?.id && execInstance && execInstance.addMetadata(executingStep.id, kind, scope, op, values)) return;
|
|
147
|
+
if (executingStep?.id && execInstance && execInstance.addMetadata(executingStep.id, kind, scope, op, values, opts)) return;
|
|
142
148
|
}
|
|
143
149
|
await sendMetadataViaAPI(client, target, kind, op, values, (ctx?.execution?.instance)?.options?.headers ?? void 0);
|
|
144
150
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"InngestMetadata.cjs","names":["client: Inngest","config: BuilderConfig","getAsyncCtx","internalLoggerSymbol","Middleware"],"sources":["../../src/components/InngestMetadata.ts"],"sourcesContent":["import type { Simplify } from \"../helpers/types.ts\";\nimport type { MetadataTarget } from \"../types.ts\";\nimport { type AsyncContext, getAsyncCtx } from \"./execution/als.ts\";\nimport { type Inngest, internalLoggerSymbol } from \"./Inngest.ts\";\nimport type { ExperimentalStepTools } from \"./InngestStepTools.ts\";\nimport { Middleware } from \"./middleware/middleware.ts\";\n\n/**\n * The level at which to attach the metadata.\n */\nexport type MetadataScope = \"run\" | \"step\" | \"extended_trace\";\n\n/**\n * Metadata of the same kind attached to the same item at the same scope are combined.\n */\nexport type MetadataKind =\n | \"inngest.experiment\"\n | \"inngest.score\"\n | \"inngest.warnings\"\n | \"inngest.ai\"\n | `userland.${string}`;\n\n/**\n * The operation use to combine multiple metadata updates of the same kind.\n */\nexport type MetadataOpcode = \"merge\";\n\n/**\n * A metadata update containing `values` to be merged according to `op`\n * at the configured `scope` for the configured `kind`.\n */\nexport type MetadataUpdate = {\n kind: MetadataKind;\n scope: MetadataScope;\n op: MetadataOpcode;\n values: MetadataValues;\n};\n\nexport type MetadataValues = Record<string, unknown>;\n\n/**\n * Internal metadata target config shared by metadata and score helpers.\n * @internal\n */\nexport interface BuilderConfig {\n runId?: string | null;\n stepId?: string | null;\n stepIndex?: number;\n attempt?: number | null;\n spanId?: string;\n}\n\n/**\n * Configures and sends metadata updates.\n *\n * This is used to limit the available methods as target is\n * configured and the specified scope narrows.\n */\nexport type MetadataBuilder<Extras = {}> = Simplify<\n {\n /**\n * Sets the metadata context to a specific (or current if omitted) run.\n */\n run(id?: string): Simplify<Omit<MetadataBuilder<Extras>, \"run\">>;\n\n /**\n * Sets the metadata context to a specific (or current if omitted) step.\n */\n step(\n id?: string,\n index?: number,\n ): Simplify<Omit<MetadataBuilder<Extras>, \"run\" | \"step\">>;\n\n /**\n * Sets the metadata context to a specific (or current if omitted) step attempt.\n */\n attempt(\n index?: number,\n ): Simplify<Omit<MetadataBuilder<Extras>, \"run\" | \"step\" | \"attempt\">>;\n\n /**\n * Sets the metadata context to a specific span.\n */\n span(\n id: string,\n ): Simplify<\n Omit<MetadataBuilder<Extras>, \"run\" | \"step\" | \"attempt\" | \"span\">\n >;\n\n /**\n * Attach metadata to the configured run/step/step attempt/span.\n *\n * By default it will attach metadata to the current run if\n * executed inside the body of `createFunction` or to the\n * current step attempt if executed inside `step.run`.\n */\n update(values: Record<string, unknown>, kind?: string): Promise<void>;\n } & Extras\n>;\n\n/**\n * A wrapper around `MetadataBuilder` to attach metadata as a step.\n */\nexport type MetadataStepTool = MetadataBuilder<{\n /**\n * Allows many `updates` to be sent with the same scope.\n */\n do: (fn: (builder: MetadataBuilder) => Promise<void>) => Promise<void>;\n}>;\n\n/**\n * Configures and sends metadata updates.\n *\n * It sends metadata updates via step opcodes if the metadata is\n * configured to be attached to the current run/step/step attempt\n * and `update` is called inside of `step.run`.\n *\n * Otherwise it sends updates via the Inngest API.\n */\nexport class UnscopedMetadataBuilder implements MetadataBuilder {\n constructor(\n private client: Inngest,\n private config: BuilderConfig = {},\n ) {}\n\n run(id?: string): UnscopedMetadataBuilder {\n return new UnscopedMetadataBuilder(this.client, {\n ...this.config,\n runId: id ?? null,\n });\n }\n\n step(id?: string, index?: number): UnscopedMetadataBuilder {\n return new UnscopedMetadataBuilder(this.client, {\n ...this.config,\n stepId: id ?? null,\n stepIndex: index ?? 0,\n });\n }\n\n attempt(attempt?: number): UnscopedMetadataBuilder {\n return new UnscopedMetadataBuilder(this.client, {\n ...this.config,\n attempt: attempt ?? null,\n });\n }\n\n span(id: string): UnscopedMetadataBuilder {\n return new UnscopedMetadataBuilder(this.client, {\n ...this.config,\n spanId: id,\n });\n }\n\n async update(\n values: Record<string, unknown>,\n kind: string = \"default\",\n ): Promise<void> {\n await performOp(\n this.client,\n this.config,\n values,\n `userland.${kind}`,\n \"merge\",\n );\n }\n\n toJSON() {\n return this.config;\n }\n}\n\n/**\n * Creates a `MetadataTarget` based on the current execution context and the `BuilderConfig` created using\n * `MetadataBuilder`.\n */\nexport function buildTarget(\n config: BuilderConfig,\n ctx?: AsyncContext,\n): MetadataTarget {\n const ctxExecution = ctx?.execution;\n const ctxRunId = ctxExecution?.ctx?.runId;\n const ctxStepId = ctxExecution?.executingStep?.id;\n const ctxAttempt = ctxExecution?.ctx?.attempt;\n const targetRunId = config.runId ?? ctxRunId;\n if (!targetRunId) throw new Error(\"No run context available\");\n\n const isSameRunAsCtx = ctxRunId !== undefined && targetRunId === ctxRunId;\n\n const stepCtxReason = !ctxExecution\n ? \"no function execution context is available\"\n : !ctxExecution.executingStep\n ? \"you are not inside a step.run() callback\"\n : \"you are targeting a different run\";\n\n if (\n config.attempt === null &&\n (!isSameRunAsCtx || !ctxExecution?.executingStep)\n )\n throw new Error(\n `attempt() was called without a value, but ${stepCtxReason}`,\n );\n if (\n config.stepId === null &&\n (!isSameRunAsCtx || !ctxExecution?.executingStep)\n )\n throw new Error(`step() was called without a value, but ${stepCtxReason}`);\n\n const targetAttempt =\n config.attempt !== undefined\n ? (config.attempt ?? ctxAttempt)\n : config.stepId === null\n ? ctxAttempt\n : undefined;\n\n if (config.spanId !== undefined) {\n return {\n run_id: targetRunId,\n step_id: config.stepId ?? ctxStepId,\n step_index: config.stepIndex,\n step_attempt:\n targetAttempt ?? (config.stepId === undefined ? ctxAttempt : undefined),\n span_id: config.spanId,\n };\n } else if (config.stepId !== undefined) {\n return {\n run_id: targetRunId,\n step_id: config.stepId ?? ctxStepId,\n step_index: config.stepIndex,\n step_attempt: targetAttempt,\n };\n } else if (config.runId !== undefined) {\n return {\n run_id: targetRunId,\n };\n } else if (ctxStepId && ctxAttempt !== undefined) {\n return {\n run_id: targetRunId,\n step_id: ctxStepId,\n step_attempt: ctxAttempt,\n };\n } else {\n return {\n run_id: targetRunId,\n };\n }\n}\n\n/**\n * Creates a metadata array payload for API calls.\n */\nexport function createMetadataPayload(\n kind: string,\n op: MetadataOpcode,\n metadata: Record<string, unknown>,\n) {\n return [\n {\n kind,\n op,\n values: metadata,\n },\n ];\n}\n\n/**\n * Sends metadata update via REST API to a specific target.\n */\nexport async function sendMetadataViaAPI(\n client: Inngest,\n target: MetadataTarget,\n kind: string,\n op: MetadataOpcode,\n metadata: Record<string, unknown>,\n headers?: Record<string, string>,\n): Promise<void> {\n const metadataArray = createMetadataPayload(kind, op, metadata);\n\n await client[\"updateMetadata\"]({\n target,\n metadata: metadataArray,\n headers,\n });\n}\n\nfunction getBatchScope(config: BuilderConfig): MetadataScope {\n if (config.spanId !== undefined) return \"extended_trace\";\n if (config.stepId !== undefined) return \"step\";\n if (config.runId !== undefined) return \"run\";\n\n return \"step\";\n}\n\nfunction targetsCurrentStep(\n config: BuilderConfig,\n ctx?: AsyncContext,\n): boolean {\n const executingStep = ctx?.execution?.executingStep;\n if (!executingStep) {\n return false;\n }\n\n const targetStepId = config.stepId;\n const currentUserlandStepId = executingStep.userlandId;\n\n if (targetStepId === undefined) {\n return true;\n }\n\n if (targetStepId === null) {\n return true;\n }\n\n return targetStepId === currentUserlandStepId;\n}\n\n/**\n * Internal metadata write helper shared by metadata and score helpers.\n * @internal\n */\nexport async function performOp(\n client: Inngest,\n config: BuilderConfig,\n values: Record<string, unknown>,\n kind: MetadataKind,\n op: MetadataOpcode,\n): Promise<void> {\n const ctx = await getAsyncCtx();\n const target = buildTarget(config, ctx);\n\n const isInsideRun = !!ctx?.execution;\n const isInsideStep = !!ctx?.execution?.executingStep;\n const isScoreOrExperimentWrite =\n kind === \"inngest.score\" || kind === \"inngest.experiment\";\n if (isInsideRun && !isInsideStep && !isScoreOrExperimentWrite) {\n client[internalLoggerSymbol].warn(\n \"metadata.update() called outside of a step; this metadata may be lost on retries. Wrap the call in step.run() for durable metadata.\",\n );\n }\n\n const runId = config.runId ?? ctx?.execution?.ctx?.runId;\n const attempt = config.attempt ?? ctx?.execution?.ctx?.attempt;\n\n // We can batch metadata if we're updating the current run\n const canBatch =\n runId === ctx?.execution?.ctx?.runId &&\n targetsCurrentStep(config, ctx) &&\n attempt === ctx?.execution?.ctx?.attempt &&\n !config.spanId;\n\n if (canBatch) {\n const executingStep = ctx?.execution?.executingStep;\n const execInstance = ctx?.execution?.instance;\n const scope = getBatchScope(config);\n\n if (\n executingStep?.id &&\n execInstance &&\n execInstance.addMetadata(executingStep.id, kind, scope, op, values)\n ) {\n return;\n }\n }\n\n const headers =\n (\n ctx?.execution?.instance as\n | { options?: { headers?: Record<string, string> } }\n | undefined\n )?.options?.headers ?? undefined;\n\n await sendMetadataViaAPI(client, target, kind, op, values, headers);\n}\n\nexport const metadataSymbol = Symbol.for(\"inngest.step.metadata\");\n\n/**\n * Middleware that enables the experimental step.metadata() feature.\n *\n * @example\n * ```ts\n * import { metadataMiddleware } from \"inngest/experimental\";\n *\n * const inngest = new Inngest({\n * id: \"my-app\",\n * middleware: [metadataMiddleware()],\n * });\n * ```\n */\nexport const metadataMiddleware = () => {\n class MetadataMiddleware extends Middleware.BaseMiddleware {\n readonly id = \"inngest:metadata\";\n\n static override onRegister({ client }: Middleware.OnRegisterArgs) {\n client[\"experimentalMetadataEnabled\"] = true;\n }\n\n override transformFunctionInput(\n arg: Middleware.TransformFunctionInputArgs,\n ): Middleware.TransformFunctionInputArgs & {\n ctx: {\n step: {\n /**\n * Create a durable metadata update wrapped in a step\n *\n * @param memoizationId - The step ID used for the step itself, ensuring the\n * metadata update is only performed once even on function retries.\n *\n * @example\n * ```ts\n * // Update metadata for the current run\n * await step.metadata(\"update-status\").update({ status: \"processing\" });\n *\n * // Update metadata for a different run\n * await step.metadata(\"notify-parent\")\n * .run(parentRunId)\n * .update({ childCompleted: true });\n * ```\n */\n metadata: ExperimentalStepTools[typeof metadataSymbol];\n };\n };\n } {\n return {\n ...arg,\n ctx: {\n ...arg.ctx,\n step: {\n ...arg.ctx.step,\n // Access the hidden symbol-keyed metadata tool from step tools\n metadata: (arg.ctx.step as unknown as ExperimentalStepTools)[\n metadataSymbol\n ],\n },\n },\n };\n }\n }\n\n return MetadataMiddleware;\n};\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"InngestMetadata.cjs","names":["client: Inngest","config: BuilderConfig","getAsyncCtx","internalLoggerSymbol","Middleware"],"sources":["../../src/components/InngestMetadata.ts"],"sourcesContent":["import type { Simplify } from \"../helpers/types.ts\";\nimport type { MetadataTarget } from \"../types.ts\";\nimport { type AsyncContext, getAsyncCtx } from \"./execution/als.ts\";\nimport { type Inngest, internalLoggerSymbol } from \"./Inngest.ts\";\nimport type { ExperimentalStepTools } from \"./InngestStepTools.ts\";\nimport { Middleware } from \"./middleware/middleware.ts\";\n\n/**\n * The level at which to attach the metadata.\n */\nexport type MetadataScope = \"run\" | \"step\" | \"extended_trace\";\n\n/**\n * Metadata of the same kind attached to the same item at the same scope are combined.\n */\nexport type MetadataKind =\n | \"inngest.experiment\"\n | \"inngest.score\"\n | \"inngest.warnings\"\n | \"inngest.ai\"\n | \"inngest.sandbox\"\n | `userland.${string}`;\n\n/**\n * The operation used to combine multiple metadata updates of the same kind.\n *\n * `set` replaces all values for the kind. `merge` is legacy: the server treats\n * every write as `set`, so values for a kind are always replaced.\n */\nexport type MetadataOpcode = \"merge\" | \"set\";\n\n/**\n * A metadata update containing `values` to be merged according to `op`\n * at the configured `scope` for the configured `kind`.\n */\nexport type MetadataUpdate = {\n kind: MetadataKind;\n scope: MetadataScope;\n op: MetadataOpcode;\n values: MetadataValues;\n};\n\nexport type MetadataValues = Record<string, unknown>;\n\n/**\n * Internal metadata target config shared by metadata and score helpers.\n * @internal\n */\nexport interface BuilderConfig {\n runId?: string | null;\n stepId?: string | null;\n stepIndex?: number;\n attempt?: number | null;\n spanId?: string;\n}\n\n/**\n * Configures and sends metadata updates.\n *\n * This is used to limit the available methods as target is\n * configured and the specified scope narrows.\n */\nexport type MetadataBuilder<Extras = {}> = Simplify<\n {\n /**\n * Sets the metadata context to a specific (or current if omitted) run.\n */\n run(id?: string): Simplify<Omit<MetadataBuilder<Extras>, \"run\">>;\n\n /**\n * Sets the metadata context to a specific (or current if omitted) step.\n */\n step(\n id?: string,\n index?: number,\n ): Simplify<Omit<MetadataBuilder<Extras>, \"run\" | \"step\">>;\n\n /**\n * Sets the metadata context to a specific (or current if omitted) step attempt.\n */\n attempt(\n index?: number,\n ): Simplify<Omit<MetadataBuilder<Extras>, \"run\" | \"step\" | \"attempt\">>;\n\n /**\n * Sets the metadata context to a specific span.\n */\n span(\n id: string,\n ): Simplify<\n Omit<MetadataBuilder<Extras>, \"run\" | \"step\" | \"attempt\" | \"span\">\n >;\n\n /**\n * Set the metadata of the given `kind` on the configured\n * run/step/step attempt/span, replacing any values previously set for\n * that kind.\n *\n * By default it will attach metadata to the current run if\n * executed inside the body of `createFunction` or to the\n * current step attempt if executed inside `step.run`.\n */\n set(values: Record<string, unknown>, kind?: string): Promise<void>;\n\n /**\n * Attach metadata to the configured run/step/step attempt/span.\n *\n * By default it will attach metadata to the current run if\n * executed inside the body of `createFunction` or to the\n * current step attempt if executed inside `step.run`.\n *\n * @deprecated Use `set()` instead. Values for a kind now replace any\n * previous values rather than merging with them. `update()` only merges\n * calls made within the same step that target the current run/step/step\n * attempt. Calls that are sent via the Inngest API (IE targeting another\n * run/step/attempt/span, or made outside of a step) replace each other.\n */\n update(values: Record<string, unknown>, kind?: string): Promise<void>;\n } & Extras\n>;\n\n/**\n * A wrapper around `MetadataBuilder` to attach metadata as a step.\n */\nexport type MetadataStepTool = MetadataBuilder<{\n /**\n * Allows many `updates` to be sent with the same scope.\n */\n do: (fn: (builder: MetadataBuilder) => Promise<void>) => Promise<void>;\n}>;\n\n/**\n * Configures and sends metadata updates.\n *\n * It sends metadata updates via step opcodes if the metadata is\n * configured to be attached to the current run/step/step attempt\n * and `update` is called inside of `step.run`.\n *\n * Otherwise it sends updates via the Inngest API.\n */\nexport class UnscopedMetadataBuilder implements MetadataBuilder {\n constructor(\n private client: Inngest,\n private config: BuilderConfig = {},\n ) {}\n\n run(id?: string): UnscopedMetadataBuilder {\n return new UnscopedMetadataBuilder(this.client, {\n ...this.config,\n runId: id ?? null,\n });\n }\n\n step(id?: string, index?: number): UnscopedMetadataBuilder {\n return new UnscopedMetadataBuilder(this.client, {\n ...this.config,\n stepId: id ?? null,\n stepIndex: index ?? 0,\n });\n }\n\n attempt(attempt?: number): UnscopedMetadataBuilder {\n return new UnscopedMetadataBuilder(this.client, {\n ...this.config,\n attempt: attempt ?? null,\n });\n }\n\n span(id: string): UnscopedMetadataBuilder {\n return new UnscopedMetadataBuilder(this.client, {\n ...this.config,\n spanId: id,\n });\n }\n\n async set(\n values: Record<string, unknown>,\n kind: string = \"default\",\n ): Promise<void> {\n await performOp(\n this.client,\n this.config,\n values,\n `userland.${kind}`,\n \"set\",\n );\n }\n\n /**\n * @deprecated Use `set()` instead. See `MetadataBuilder.update()`.\n */\n async update(\n values: Record<string, unknown>,\n kind: string = \"default\",\n ): Promise<void> {\n // Merge w/ earlier `update()` calls in the same step so that several\n // calls in one step still behave like a merge.\n await performOp(\n this.client,\n this.config,\n values,\n `userland.${kind}`,\n \"set\",\n { merge: true },\n );\n }\n\n toJSON() {\n return this.config;\n }\n}\n\n/**\n * Creates a `MetadataTarget` based on the current execution context and the `BuilderConfig` created using\n * `MetadataBuilder`.\n */\nexport function buildTarget(\n config: BuilderConfig,\n ctx?: AsyncContext,\n): MetadataTarget {\n const ctxExecution = ctx?.execution;\n const ctxRunId = ctxExecution?.ctx?.runId;\n const ctxStepId = ctxExecution?.executingStep?.id;\n const ctxAttempt = ctxExecution?.ctx?.attempt;\n const targetRunId = config.runId ?? ctxRunId;\n if (!targetRunId) throw new Error(\"No run context available\");\n\n const isSameRunAsCtx = ctxRunId !== undefined && targetRunId === ctxRunId;\n\n const stepCtxReason = !ctxExecution\n ? \"no function execution context is available\"\n : !ctxExecution.executingStep\n ? \"you are not inside a step.run() callback\"\n : \"you are targeting a different run\";\n\n if (\n config.attempt === null &&\n (!isSameRunAsCtx || !ctxExecution?.executingStep)\n )\n throw new Error(\n `attempt() was called without a value, but ${stepCtxReason}`,\n );\n if (\n config.stepId === null &&\n (!isSameRunAsCtx || !ctxExecution?.executingStep)\n )\n throw new Error(`step() was called without a value, but ${stepCtxReason}`);\n\n const targetAttempt =\n config.attempt !== undefined\n ? (config.attempt ?? ctxAttempt)\n : config.stepId === null\n ? ctxAttempt\n : undefined;\n\n if (config.spanId !== undefined) {\n return {\n run_id: targetRunId,\n step_id: config.stepId ?? ctxStepId,\n step_index: config.stepIndex,\n step_attempt:\n targetAttempt ?? (config.stepId === undefined ? ctxAttempt : undefined),\n span_id: config.spanId,\n };\n } else if (config.stepId !== undefined) {\n return {\n run_id: targetRunId,\n step_id: config.stepId ?? ctxStepId,\n step_index: config.stepIndex,\n step_attempt: targetAttempt,\n };\n } else if (config.runId !== undefined) {\n return {\n run_id: targetRunId,\n };\n } else if (ctxStepId && ctxAttempt !== undefined) {\n return {\n run_id: targetRunId,\n step_id: ctxStepId,\n step_attempt: ctxAttempt,\n };\n } else {\n return {\n run_id: targetRunId,\n };\n }\n}\n\n/**\n * Creates a metadata array payload for API calls.\n */\nexport function createMetadataPayload(\n kind: string,\n op: MetadataOpcode,\n metadata: Record<string, unknown>,\n) {\n return [\n {\n kind,\n op,\n values: metadata,\n },\n ];\n}\n\n/**\n * Sends metadata update via REST API to a specific target.\n */\nexport async function sendMetadataViaAPI(\n client: Inngest,\n target: MetadataTarget,\n kind: string,\n op: MetadataOpcode,\n metadata: Record<string, unknown>,\n headers?: Record<string, string>,\n): Promise<void> {\n const metadataArray = createMetadataPayload(kind, op, metadata);\n\n await client[\"updateMetadata\"]({\n target,\n metadata: metadataArray,\n headers,\n });\n}\n\nfunction getBatchScope(config: BuilderConfig): MetadataScope {\n if (config.spanId !== undefined) return \"extended_trace\";\n if (config.stepId !== undefined) return \"step\";\n if (config.runId !== undefined) return \"run\";\n\n return \"step\";\n}\n\nfunction targetsCurrentStep(\n config: BuilderConfig,\n ctx?: AsyncContext,\n): boolean {\n const executingStep = ctx?.execution?.executingStep;\n if (!executingStep) {\n return false;\n }\n\n const targetStepId = config.stepId;\n const currentUserlandStepId = executingStep.userlandId;\n\n if (targetStepId === undefined) {\n return true;\n }\n\n if (targetStepId === null) {\n return true;\n }\n\n return targetStepId === currentUserlandStepId;\n}\n\n/**\n * Options for how a metadata write combines w/ earlier writes.\n * @internal\n */\nexport interface MetadataWriteOptions {\n /**\n * Merge `values` into earlier writes of the same kind and scope made in\n * the same step instead of replacing them. Writes sent via the Inngest API\n * can't be merged. Defaults to `op === \"merge\"`.\n */\n merge?: boolean;\n}\n\n/**\n * Internal metadata write helper shared by metadata and score helpers.\n * @internal\n */\nexport async function performOp(\n client: Inngest,\n config: BuilderConfig,\n values: Record<string, unknown>,\n kind: MetadataKind,\n op: MetadataOpcode,\n opts?: MetadataWriteOptions,\n): Promise<void> {\n const ctx = await getAsyncCtx();\n const target = buildTarget(config, ctx);\n\n const isInsideRun = !!ctx?.execution;\n const isInsideStep = !!ctx?.execution?.executingStep;\n const isScoreOrExperimentWrite =\n kind === \"inngest.score\" || kind === \"inngest.experiment\";\n if (isInsideRun && !isInsideStep && !isScoreOrExperimentWrite) {\n client[internalLoggerSymbol].warn(\n \"metadata.set() (or update()) called outside of a step; this metadata may be lost on retries. Wrap the call in step.run() for durable metadata.\",\n );\n }\n\n const runId = config.runId ?? ctx?.execution?.ctx?.runId;\n const attempt = config.attempt ?? ctx?.execution?.ctx?.attempt;\n\n // We can batch metadata if we're updating the current run\n const canBatch =\n runId === ctx?.execution?.ctx?.runId &&\n targetsCurrentStep(config, ctx) &&\n attempt === ctx?.execution?.ctx?.attempt &&\n !config.spanId;\n\n if (canBatch) {\n const executingStep = ctx?.execution?.executingStep;\n const execInstance = ctx?.execution?.instance;\n const scope = getBatchScope(config);\n\n if (\n executingStep?.id &&\n execInstance &&\n execInstance.addMetadata(executingStep.id, kind, scope, op, values, opts)\n ) {\n return;\n }\n }\n\n const headers =\n (\n ctx?.execution?.instance as\n | { options?: { headers?: Record<string, string> } }\n | undefined\n )?.options?.headers ?? undefined;\n\n await sendMetadataViaAPI(client, target, kind, op, values, headers);\n}\n\nexport const metadataSymbol = Symbol.for(\"inngest.step.metadata\");\n\n/**\n * Middleware that enables the experimental step.metadata() feature.\n *\n * @example\n * ```ts\n * import { metadataMiddleware } from \"inngest/experimental\";\n *\n * const inngest = new Inngest({\n * id: \"my-app\",\n * middleware: [metadataMiddleware()],\n * });\n * ```\n */\nexport const metadataMiddleware = () => {\n class MetadataMiddleware extends Middleware.BaseMiddleware {\n readonly id = \"inngest:metadata\";\n\n static override onRegister({ client }: Middleware.OnRegisterArgs) {\n client[\"experimentalMetadataEnabled\"] = true;\n }\n\n override transformFunctionInput(\n arg: Middleware.TransformFunctionInputArgs,\n ): Middleware.TransformFunctionInputArgs & {\n ctx: {\n step: {\n /**\n * Create a durable metadata update wrapped in a step\n *\n * @param memoizationId - The step ID used for the step itself, ensuring the\n * metadata update is only performed once even on function retries.\n *\n * @example\n * ```ts\n * // Set metadata for the current run\n * await step.metadata(\"update-status\").set({ status: \"processing\" });\n *\n * // Set metadata for a different run\n * await step.metadata(\"notify-parent\")\n * .run(parentRunId)\n * .set({ childCompleted: true });\n * ```\n */\n metadata: ExperimentalStepTools[typeof metadataSymbol];\n };\n };\n } {\n return {\n ...arg,\n ctx: {\n ...arg.ctx,\n step: {\n ...arg.ctx.step,\n // Access the hidden symbol-keyed metadata tool from step tools\n metadata: (arg.ctx.step as unknown as ExperimentalStepTools)[\n metadataSymbol\n ],\n },\n },\n };\n }\n }\n\n return MetadataMiddleware;\n};\n"],"mappings":"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"}
|
|
@@ -13,11 +13,14 @@ type MetadataScope = "run" | "step" | "extended_trace";
|
|
|
13
13
|
/**
|
|
14
14
|
* Metadata of the same kind attached to the same item at the same scope are combined.
|
|
15
15
|
*/
|
|
16
|
-
type MetadataKind = "inngest.experiment" | "inngest.score" | "inngest.warnings" | "inngest.ai" | `userland.${string}`;
|
|
16
|
+
type MetadataKind = "inngest.experiment" | "inngest.score" | "inngest.warnings" | "inngest.ai" | "inngest.sandbox" | `userland.${string}`;
|
|
17
17
|
/**
|
|
18
|
-
* The operation
|
|
18
|
+
* The operation used to combine multiple metadata updates of the same kind.
|
|
19
|
+
*
|
|
20
|
+
* `set` replaces all values for the kind. `merge` is legacy: the server treats
|
|
21
|
+
* every write as `set`, so values for a kind are always replaced.
|
|
19
22
|
*/
|
|
20
|
-
type MetadataOpcode = "merge";
|
|
23
|
+
type MetadataOpcode = "merge" | "set";
|
|
21
24
|
/**
|
|
22
25
|
* A metadata update containing `values` to be merged according to `op`
|
|
23
26
|
* at the configured `scope` for the configured `kind`.
|
|
@@ -52,12 +55,28 @@ type MetadataBuilder<Extras = {}> = Simplify<{
|
|
|
52
55
|
* Sets the metadata context to a specific span.
|
|
53
56
|
*/
|
|
54
57
|
span(id: string): Simplify<Omit<MetadataBuilder<Extras>, "run" | "step" | "attempt" | "span">>;
|
|
58
|
+
/**
|
|
59
|
+
* Set the metadata of the given `kind` on the configured
|
|
60
|
+
* run/step/step attempt/span, replacing any values previously set for
|
|
61
|
+
* that kind.
|
|
62
|
+
*
|
|
63
|
+
* By default it will attach metadata to the current run if
|
|
64
|
+
* executed inside the body of `createFunction` or to the
|
|
65
|
+
* current step attempt if executed inside `step.run`.
|
|
66
|
+
*/
|
|
67
|
+
set(values: Record<string, unknown>, kind?: string): Promise<void>;
|
|
55
68
|
/**
|
|
56
69
|
* Attach metadata to the configured run/step/step attempt/span.
|
|
57
70
|
*
|
|
58
71
|
* By default it will attach metadata to the current run if
|
|
59
72
|
* executed inside the body of `createFunction` or to the
|
|
60
73
|
* current step attempt if executed inside `step.run`.
|
|
74
|
+
*
|
|
75
|
+
* @deprecated Use `set()` instead. Values for a kind now replace any
|
|
76
|
+
* previous values rather than merging with them. `update()` only merges
|
|
77
|
+
* calls made within the same step that target the current run/step/step
|
|
78
|
+
* attempt. Calls that are sent via the Inngest API (IE targeting another
|
|
79
|
+
* run/step/attempt/span, or made outside of a step) replace each other.
|
|
61
80
|
*/
|
|
62
81
|
update(values: Record<string, unknown>, kind?: string): Promise<void>;
|
|
63
82
|
} & Extras>;
|
|
@@ -70,6 +89,18 @@ type MetadataStepTool = MetadataBuilder<{
|
|
|
70
89
|
*/
|
|
71
90
|
do: (fn: (builder: MetadataBuilder) => Promise<void>) => Promise<void>;
|
|
72
91
|
}>;
|
|
92
|
+
/**
|
|
93
|
+
* Options for how a metadata write combines w/ earlier writes.
|
|
94
|
+
* @internal
|
|
95
|
+
*/
|
|
96
|
+
interface MetadataWriteOptions {
|
|
97
|
+
/**
|
|
98
|
+
* Merge `values` into earlier writes of the same kind and scope made in
|
|
99
|
+
* the same step instead of replacing them. Writes sent via the Inngest API
|
|
100
|
+
* can't be merged. Defaults to `op === "merge"`.
|
|
101
|
+
*/
|
|
102
|
+
merge?: boolean;
|
|
103
|
+
}
|
|
73
104
|
declare const metadataSymbol: unique symbol;
|
|
74
105
|
/**
|
|
75
106
|
* Middleware that enables the experimental step.metadata() feature.
|
|
@@ -102,13 +133,13 @@ declare const metadataMiddleware: () => {
|
|
|
102
133
|
*
|
|
103
134
|
* @example
|
|
104
135
|
* ```ts
|
|
105
|
-
* //
|
|
106
|
-
* await step.metadata("update-status").
|
|
136
|
+
* // Set metadata for the current run
|
|
137
|
+
* await step.metadata("update-status").set({ status: "processing" });
|
|
107
138
|
*
|
|
108
|
-
* //
|
|
139
|
+
* // Set metadata for a different run
|
|
109
140
|
* await step.metadata("notify-parent")
|
|
110
141
|
* .run(parentRunId)
|
|
111
|
-
* .
|
|
142
|
+
* .set({ childCompleted: true });
|
|
112
143
|
* ```
|
|
113
144
|
*/
|
|
114
145
|
metadata: ExperimentalStepTools[typeof metadataSymbol];
|
|
@@ -138,5 +169,5 @@ declare const metadataMiddleware: () => {
|
|
|
138
169
|
}: Middleware.OnRegisterArgs): void;
|
|
139
170
|
};
|
|
140
171
|
//#endregion
|
|
141
|
-
export { MetadataBuilder, MetadataKind, MetadataOpcode, MetadataScope, MetadataStepTool, MetadataUpdate, metadataMiddleware, metadataSymbol };
|
|
172
|
+
export { MetadataBuilder, MetadataKind, MetadataOpcode, MetadataScope, MetadataStepTool, MetadataUpdate, MetadataWriteOptions, metadataMiddleware, metadataSymbol };
|
|
142
173
|
//# sourceMappingURL=InngestMetadata.d.cts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"InngestMetadata.d.cts","names":[],"sources":["../../src/components/InngestMetadata.ts"],"sourcesContent":[],"mappings":";;;;;;;;;AAUA;AAKA;
|
|
1
|
+
{"version":3,"file":"InngestMetadata.d.cts","names":[],"sources":["../../src/components/InngestMetadata.ts"],"sourcesContent":[],"mappings":";;;;;;;;;AAUA;AAKA;AAcY,KAnBA,aAAA,GAmBc,KAAA,GAAA,MAAA,GAAA,gBAAA;AAM1B;;;AAES,KAtBG,YAAA,GAsBH,oBAAA,GAAA,eAAA,GAAA,kBAAA,GAAA,YAAA,GAAA,iBAAA,GAAA,YAAA,MAAA,EAAA;;;;AAKT;AAoBA;;AAKoD,KAtCxC,cAAA,GAsCwC,OAAA,GAAA,KAAA;;;;;AAQ/B,KAxCT,cAAA,GAwCS;MAAL,EAvCR,YAuCQ;OAAT,EAtCE,aAsCF;MArCD,cA4C+B;QAAhB,EA3CX,cA2CW;;AAAd,KAxCK,cAAA,GAAiB,MAwCtB,CAAA,MAAA,EAAA,OAAA,CAAA;AA0CP;;;;;;AAA8C,KA9DlC,eA8DkC,CAAA,SAAA,CAAA,CAAA,CAAA,GA9DH,QA8DG,CAAA;EA4O7B;AAoEjB;AAeA;EAmDC,GAAA,CAAA,EAAA,CAAA,EAAA,MAAA,CAAA,EA3aqB,QA2arB,CA3a8B,IA2a9B,CA3amC,eA2anC,CA3amD,MA2anD,CAAA,EAAA,KAAA,CAAA,CAAA;;;;MAzCM,CAAA,EAAW,CAAX,EAAA,MAAW,EAAA,KAAA,CAAA,EAAA,MAAA,CAAA,EA1XX,QA0XW,CA1XF,IA0XE,CA1XG,eA0XH,CA1XmB,MA0XnB,CAAA,EAAA,KAAA,GAAA,MAAA,CAAA,CAAA;;;;2BAnXX,SAAS,KAAK,gBAAgB;;;;oBAO9B,SACD,KAAK,gBAAgB;;;;;;;;;;cAYX,yCAAyC;;;;;;;;;;;;;;iBAetC,yCAAyC;IACtD;;;;KAMM,gBAAA,GAAmB;;;;qBAIV,oBAAoB,kBAAkB;;;;;;UAwO1C,oBAAA;;;;;;;;cAoEJ;;;;;;;;;;;;;;cAeA;;;;;;;gCASF,UAAA,CAAW,6BACf,UAAA,CAAW;;;;;;;;;;;;;;;;;;;;oBAoBE,6BAA6B;;;;;;;;;;;;;;;;;;;;;;;;KA1BN,UAAA,CAAW"}
|
|
@@ -13,11 +13,14 @@ type MetadataScope = "run" | "step" | "extended_trace";
|
|
|
13
13
|
/**
|
|
14
14
|
* Metadata of the same kind attached to the same item at the same scope are combined.
|
|
15
15
|
*/
|
|
16
|
-
type MetadataKind = "inngest.experiment" | "inngest.score" | "inngest.warnings" | "inngest.ai" | `userland.${string}`;
|
|
16
|
+
type MetadataKind = "inngest.experiment" | "inngest.score" | "inngest.warnings" | "inngest.ai" | "inngest.sandbox" | `userland.${string}`;
|
|
17
17
|
/**
|
|
18
|
-
* The operation
|
|
18
|
+
* The operation used to combine multiple metadata updates of the same kind.
|
|
19
|
+
*
|
|
20
|
+
* `set` replaces all values for the kind. `merge` is legacy: the server treats
|
|
21
|
+
* every write as `set`, so values for a kind are always replaced.
|
|
19
22
|
*/
|
|
20
|
-
type MetadataOpcode = "merge";
|
|
23
|
+
type MetadataOpcode = "merge" | "set";
|
|
21
24
|
/**
|
|
22
25
|
* A metadata update containing `values` to be merged according to `op`
|
|
23
26
|
* at the configured `scope` for the configured `kind`.
|
|
@@ -52,12 +55,28 @@ type MetadataBuilder<Extras = {}> = Simplify<{
|
|
|
52
55
|
* Sets the metadata context to a specific span.
|
|
53
56
|
*/
|
|
54
57
|
span(id: string): Simplify<Omit<MetadataBuilder<Extras>, "run" | "step" | "attempt" | "span">>;
|
|
58
|
+
/**
|
|
59
|
+
* Set the metadata of the given `kind` on the configured
|
|
60
|
+
* run/step/step attempt/span, replacing any values previously set for
|
|
61
|
+
* that kind.
|
|
62
|
+
*
|
|
63
|
+
* By default it will attach metadata to the current run if
|
|
64
|
+
* executed inside the body of `createFunction` or to the
|
|
65
|
+
* current step attempt if executed inside `step.run`.
|
|
66
|
+
*/
|
|
67
|
+
set(values: Record<string, unknown>, kind?: string): Promise<void>;
|
|
55
68
|
/**
|
|
56
69
|
* Attach metadata to the configured run/step/step attempt/span.
|
|
57
70
|
*
|
|
58
71
|
* By default it will attach metadata to the current run if
|
|
59
72
|
* executed inside the body of `createFunction` or to the
|
|
60
73
|
* current step attempt if executed inside `step.run`.
|
|
74
|
+
*
|
|
75
|
+
* @deprecated Use `set()` instead. Values for a kind now replace any
|
|
76
|
+
* previous values rather than merging with them. `update()` only merges
|
|
77
|
+
* calls made within the same step that target the current run/step/step
|
|
78
|
+
* attempt. Calls that are sent via the Inngest API (IE targeting another
|
|
79
|
+
* run/step/attempt/span, or made outside of a step) replace each other.
|
|
61
80
|
*/
|
|
62
81
|
update(values: Record<string, unknown>, kind?: string): Promise<void>;
|
|
63
82
|
} & Extras>;
|
|
@@ -70,6 +89,18 @@ type MetadataStepTool = MetadataBuilder<{
|
|
|
70
89
|
*/
|
|
71
90
|
do: (fn: (builder: MetadataBuilder) => Promise<void>) => Promise<void>;
|
|
72
91
|
}>;
|
|
92
|
+
/**
|
|
93
|
+
* Options for how a metadata write combines w/ earlier writes.
|
|
94
|
+
* @internal
|
|
95
|
+
*/
|
|
96
|
+
interface MetadataWriteOptions {
|
|
97
|
+
/**
|
|
98
|
+
* Merge `values` into earlier writes of the same kind and scope made in
|
|
99
|
+
* the same step instead of replacing them. Writes sent via the Inngest API
|
|
100
|
+
* can't be merged. Defaults to `op === "merge"`.
|
|
101
|
+
*/
|
|
102
|
+
merge?: boolean;
|
|
103
|
+
}
|
|
73
104
|
declare const metadataSymbol: unique symbol;
|
|
74
105
|
/**
|
|
75
106
|
* Middleware that enables the experimental step.metadata() feature.
|
|
@@ -102,13 +133,13 @@ declare const metadataMiddleware: () => {
|
|
|
102
133
|
*
|
|
103
134
|
* @example
|
|
104
135
|
* ```ts
|
|
105
|
-
* //
|
|
106
|
-
* await step.metadata("update-status").
|
|
136
|
+
* // Set metadata for the current run
|
|
137
|
+
* await step.metadata("update-status").set({ status: "processing" });
|
|
107
138
|
*
|
|
108
|
-
* //
|
|
139
|
+
* // Set metadata for a different run
|
|
109
140
|
* await step.metadata("notify-parent")
|
|
110
141
|
* .run(parentRunId)
|
|
111
|
-
* .
|
|
142
|
+
* .set({ childCompleted: true });
|
|
112
143
|
* ```
|
|
113
144
|
*/
|
|
114
145
|
metadata: ExperimentalStepTools[typeof metadataSymbol];
|
|
@@ -138,5 +169,5 @@ declare const metadataMiddleware: () => {
|
|
|
138
169
|
}: Middleware.OnRegisterArgs): void;
|
|
139
170
|
};
|
|
140
171
|
//#endregion
|
|
141
|
-
export { MetadataBuilder, MetadataKind, MetadataOpcode, MetadataScope, MetadataStepTool, MetadataUpdate, metadataMiddleware, metadataSymbol };
|
|
172
|
+
export { MetadataBuilder, MetadataKind, MetadataOpcode, MetadataScope, MetadataStepTool, MetadataUpdate, MetadataWriteOptions, metadataMiddleware, metadataSymbol };
|
|
142
173
|
//# sourceMappingURL=InngestMetadata.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"InngestMetadata.d.ts","names":[],"sources":["../../src/components/InngestMetadata.ts"],"sourcesContent":[],"mappings":";;;;;;;;;AAUA;AAKA;
|
|
1
|
+
{"version":3,"file":"InngestMetadata.d.ts","names":[],"sources":["../../src/components/InngestMetadata.ts"],"sourcesContent":[],"mappings":";;;;;;;;;AAUA;AAKA;AAcY,KAnBA,aAAA,GAmBc,KAAA,GAAA,MAAA,GAAA,gBAAA;AAM1B;;;AAES,KAtBG,YAAA,GAsBH,oBAAA,GAAA,eAAA,GAAA,kBAAA,GAAA,YAAA,GAAA,iBAAA,GAAA,YAAA,MAAA,EAAA;;;;AAKT;AAoBA;;AAKoD,KAtCxC,cAAA,GAsCwC,OAAA,GAAA,KAAA;;;;;AAQ/B,KAxCT,cAAA,GAwCS;MAAL,EAvCR,YAuCQ;OAAT,EAtCE,aAsCF;MArCD,cA4C+B;QAAhB,EA3CX,cA2CW;;AAAd,KAxCK,cAAA,GAAiB,MAwCtB,CAAA,MAAA,EAAA,OAAA,CAAA;AA0CP;;;;;;AAA8C,KA9DlC,eA8DkC,CAAA,SAAA,CAAA,CAAA,CAAA,GA9DH,QA8DG,CAAA;EA4O7B;AAoEjB;AAeA;EAmDC,GAAA,CAAA,EAAA,CAAA,EAAA,MAAA,CAAA,EA3aqB,QA2arB,CA3a8B,IA2a9B,CA3amC,eA2anC,CA3amD,MA2anD,CAAA,EAAA,KAAA,CAAA,CAAA;;;;MAzCM,CAAA,EAAW,CAAX,EAAA,MAAW,EAAA,KAAA,CAAA,EAAA,MAAA,CAAA,EA1XX,QA0XW,CA1XF,IA0XE,CA1XG,eA0XH,CA1XmB,MA0XnB,CAAA,EAAA,KAAA,GAAA,MAAA,CAAA,CAAA;;;;2BAnXX,SAAS,KAAK,gBAAgB;;;;oBAO9B,SACD,KAAK,gBAAgB;;;;;;;;;;cAYX,yCAAyC;;;;;;;;;;;;;;iBAetC,yCAAyC;IACtD;;;;KAMM,gBAAA,GAAmB;;;;qBAIV,oBAAoB,kBAAkB;;;;;;UAwO1C,oBAAA;;;;;;;;cAoEJ;;;;;;;;;;;;;;cAeA;;;;;;;gCASF,UAAA,CAAW,6BACf,UAAA,CAAW;;;;;;;;;;;;;;;;;;;;oBAoBE,6BAA6B;;;;;;;;;;;;;;;;;;;;;;;;KA1BN,UAAA,CAAW"}
|
|
@@ -42,8 +42,14 @@ var UnscopedMetadataBuilder = class UnscopedMetadataBuilder {
|
|
|
42
42
|
spanId: id
|
|
43
43
|
});
|
|
44
44
|
}
|
|
45
|
+
async set(values, kind = "default") {
|
|
46
|
+
await performOp(this.client, this.config, values, `userland.${kind}`, "set");
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* @deprecated Use `set()` instead. See `MetadataBuilder.update()`.
|
|
50
|
+
*/
|
|
45
51
|
async update(values, kind = "default") {
|
|
46
|
-
await performOp(this.client, this.config, values, `userland.${kind}`, "merge
|
|
52
|
+
await performOp(this.client, this.config, values, `userland.${kind}`, "set", { merge: true });
|
|
47
53
|
}
|
|
48
54
|
toJSON() {
|
|
49
55
|
return this.config;
|
|
@@ -126,19 +132,19 @@ function targetsCurrentStep(config, ctx) {
|
|
|
126
132
|
* Internal metadata write helper shared by metadata and score helpers.
|
|
127
133
|
* @internal
|
|
128
134
|
*/
|
|
129
|
-
async function performOp(client, config, values, kind, op) {
|
|
135
|
+
async function performOp(client, config, values, kind, op, opts) {
|
|
130
136
|
const ctx = await getAsyncCtx();
|
|
131
137
|
const target = buildTarget(config, ctx);
|
|
132
138
|
const isInsideRun = !!ctx?.execution;
|
|
133
139
|
const isInsideStep = !!ctx?.execution?.executingStep;
|
|
134
|
-
if (isInsideRun && !isInsideStep && !(kind === "inngest.score" || kind === "inngest.experiment")) client[internalLoggerSymbol].warn("metadata.update() called outside of a step; this metadata may be lost on retries. Wrap the call in step.run() for durable metadata.");
|
|
140
|
+
if (isInsideRun && !isInsideStep && !(kind === "inngest.score" || kind === "inngest.experiment")) client[internalLoggerSymbol].warn("metadata.set() (or update()) called outside of a step; this metadata may be lost on retries. Wrap the call in step.run() for durable metadata.");
|
|
135
141
|
const runId = config.runId ?? ctx?.execution?.ctx?.runId;
|
|
136
142
|
const attempt = config.attempt ?? ctx?.execution?.ctx?.attempt;
|
|
137
143
|
if (runId === ctx?.execution?.ctx?.runId && targetsCurrentStep(config, ctx) && attempt === ctx?.execution?.ctx?.attempt && !config.spanId) {
|
|
138
144
|
const executingStep = ctx?.execution?.executingStep;
|
|
139
145
|
const execInstance = ctx?.execution?.instance;
|
|
140
146
|
const scope = getBatchScope(config);
|
|
141
|
-
if (executingStep?.id && execInstance && execInstance.addMetadata(executingStep.id, kind, scope, op, values)) return;
|
|
147
|
+
if (executingStep?.id && execInstance && execInstance.addMetadata(executingStep.id, kind, scope, op, values, opts)) return;
|
|
142
148
|
}
|
|
143
149
|
await sendMetadataViaAPI(client, target, kind, op, values, (ctx?.execution?.instance)?.options?.headers ?? void 0);
|
|
144
150
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"InngestMetadata.js","names":["client: Inngest","config: BuilderConfig"],"sources":["../../src/components/InngestMetadata.ts"],"sourcesContent":["import type { Simplify } from \"../helpers/types.ts\";\nimport type { MetadataTarget } from \"../types.ts\";\nimport { type AsyncContext, getAsyncCtx } from \"./execution/als.ts\";\nimport { type Inngest, internalLoggerSymbol } from \"./Inngest.ts\";\nimport type { ExperimentalStepTools } from \"./InngestStepTools.ts\";\nimport { Middleware } from \"./middleware/middleware.ts\";\n\n/**\n * The level at which to attach the metadata.\n */\nexport type MetadataScope = \"run\" | \"step\" | \"extended_trace\";\n\n/**\n * Metadata of the same kind attached to the same item at the same scope are combined.\n */\nexport type MetadataKind =\n | \"inngest.experiment\"\n | \"inngest.score\"\n | \"inngest.warnings\"\n | \"inngest.ai\"\n | `userland.${string}`;\n\n/**\n * The operation use to combine multiple metadata updates of the same kind.\n */\nexport type MetadataOpcode = \"merge\";\n\n/**\n * A metadata update containing `values` to be merged according to `op`\n * at the configured `scope` for the configured `kind`.\n */\nexport type MetadataUpdate = {\n kind: MetadataKind;\n scope: MetadataScope;\n op: MetadataOpcode;\n values: MetadataValues;\n};\n\nexport type MetadataValues = Record<string, unknown>;\n\n/**\n * Internal metadata target config shared by metadata and score helpers.\n * @internal\n */\nexport interface BuilderConfig {\n runId?: string | null;\n stepId?: string | null;\n stepIndex?: number;\n attempt?: number | null;\n spanId?: string;\n}\n\n/**\n * Configures and sends metadata updates.\n *\n * This is used to limit the available methods as target is\n * configured and the specified scope narrows.\n */\nexport type MetadataBuilder<Extras = {}> = Simplify<\n {\n /**\n * Sets the metadata context to a specific (or current if omitted) run.\n */\n run(id?: string): Simplify<Omit<MetadataBuilder<Extras>, \"run\">>;\n\n /**\n * Sets the metadata context to a specific (or current if omitted) step.\n */\n step(\n id?: string,\n index?: number,\n ): Simplify<Omit<MetadataBuilder<Extras>, \"run\" | \"step\">>;\n\n /**\n * Sets the metadata context to a specific (or current if omitted) step attempt.\n */\n attempt(\n index?: number,\n ): Simplify<Omit<MetadataBuilder<Extras>, \"run\" | \"step\" | \"attempt\">>;\n\n /**\n * Sets the metadata context to a specific span.\n */\n span(\n id: string,\n ): Simplify<\n Omit<MetadataBuilder<Extras>, \"run\" | \"step\" | \"attempt\" | \"span\">\n >;\n\n /**\n * Attach metadata to the configured run/step/step attempt/span.\n *\n * By default it will attach metadata to the current run if\n * executed inside the body of `createFunction` or to the\n * current step attempt if executed inside `step.run`.\n */\n update(values: Record<string, unknown>, kind?: string): Promise<void>;\n } & Extras\n>;\n\n/**\n * A wrapper around `MetadataBuilder` to attach metadata as a step.\n */\nexport type MetadataStepTool = MetadataBuilder<{\n /**\n * Allows many `updates` to be sent with the same scope.\n */\n do: (fn: (builder: MetadataBuilder) => Promise<void>) => Promise<void>;\n}>;\n\n/**\n * Configures and sends metadata updates.\n *\n * It sends metadata updates via step opcodes if the metadata is\n * configured to be attached to the current run/step/step attempt\n * and `update` is called inside of `step.run`.\n *\n * Otherwise it sends updates via the Inngest API.\n */\nexport class UnscopedMetadataBuilder implements MetadataBuilder {\n constructor(\n private client: Inngest,\n private config: BuilderConfig = {},\n ) {}\n\n run(id?: string): UnscopedMetadataBuilder {\n return new UnscopedMetadataBuilder(this.client, {\n ...this.config,\n runId: id ?? null,\n });\n }\n\n step(id?: string, index?: number): UnscopedMetadataBuilder {\n return new UnscopedMetadataBuilder(this.client, {\n ...this.config,\n stepId: id ?? null,\n stepIndex: index ?? 0,\n });\n }\n\n attempt(attempt?: number): UnscopedMetadataBuilder {\n return new UnscopedMetadataBuilder(this.client, {\n ...this.config,\n attempt: attempt ?? null,\n });\n }\n\n span(id: string): UnscopedMetadataBuilder {\n return new UnscopedMetadataBuilder(this.client, {\n ...this.config,\n spanId: id,\n });\n }\n\n async update(\n values: Record<string, unknown>,\n kind: string = \"default\",\n ): Promise<void> {\n await performOp(\n this.client,\n this.config,\n values,\n `userland.${kind}`,\n \"merge\",\n );\n }\n\n toJSON() {\n return this.config;\n }\n}\n\n/**\n * Creates a `MetadataTarget` based on the current execution context and the `BuilderConfig` created using\n * `MetadataBuilder`.\n */\nexport function buildTarget(\n config: BuilderConfig,\n ctx?: AsyncContext,\n): MetadataTarget {\n const ctxExecution = ctx?.execution;\n const ctxRunId = ctxExecution?.ctx?.runId;\n const ctxStepId = ctxExecution?.executingStep?.id;\n const ctxAttempt = ctxExecution?.ctx?.attempt;\n const targetRunId = config.runId ?? ctxRunId;\n if (!targetRunId) throw new Error(\"No run context available\");\n\n const isSameRunAsCtx = ctxRunId !== undefined && targetRunId === ctxRunId;\n\n const stepCtxReason = !ctxExecution\n ? \"no function execution context is available\"\n : !ctxExecution.executingStep\n ? \"you are not inside a step.run() callback\"\n : \"you are targeting a different run\";\n\n if (\n config.attempt === null &&\n (!isSameRunAsCtx || !ctxExecution?.executingStep)\n )\n throw new Error(\n `attempt() was called without a value, but ${stepCtxReason}`,\n );\n if (\n config.stepId === null &&\n (!isSameRunAsCtx || !ctxExecution?.executingStep)\n )\n throw new Error(`step() was called without a value, but ${stepCtxReason}`);\n\n const targetAttempt =\n config.attempt !== undefined\n ? (config.attempt ?? ctxAttempt)\n : config.stepId === null\n ? ctxAttempt\n : undefined;\n\n if (config.spanId !== undefined) {\n return {\n run_id: targetRunId,\n step_id: config.stepId ?? ctxStepId,\n step_index: config.stepIndex,\n step_attempt:\n targetAttempt ?? (config.stepId === undefined ? ctxAttempt : undefined),\n span_id: config.spanId,\n };\n } else if (config.stepId !== undefined) {\n return {\n run_id: targetRunId,\n step_id: config.stepId ?? ctxStepId,\n step_index: config.stepIndex,\n step_attempt: targetAttempt,\n };\n } else if (config.runId !== undefined) {\n return {\n run_id: targetRunId,\n };\n } else if (ctxStepId && ctxAttempt !== undefined) {\n return {\n run_id: targetRunId,\n step_id: ctxStepId,\n step_attempt: ctxAttempt,\n };\n } else {\n return {\n run_id: targetRunId,\n };\n }\n}\n\n/**\n * Creates a metadata array payload for API calls.\n */\nexport function createMetadataPayload(\n kind: string,\n op: MetadataOpcode,\n metadata: Record<string, unknown>,\n) {\n return [\n {\n kind,\n op,\n values: metadata,\n },\n ];\n}\n\n/**\n * Sends metadata update via REST API to a specific target.\n */\nexport async function sendMetadataViaAPI(\n client: Inngest,\n target: MetadataTarget,\n kind: string,\n op: MetadataOpcode,\n metadata: Record<string, unknown>,\n headers?: Record<string, string>,\n): Promise<void> {\n const metadataArray = createMetadataPayload(kind, op, metadata);\n\n await client[\"updateMetadata\"]({\n target,\n metadata: metadataArray,\n headers,\n });\n}\n\nfunction getBatchScope(config: BuilderConfig): MetadataScope {\n if (config.spanId !== undefined) return \"extended_trace\";\n if (config.stepId !== undefined) return \"step\";\n if (config.runId !== undefined) return \"run\";\n\n return \"step\";\n}\n\nfunction targetsCurrentStep(\n config: BuilderConfig,\n ctx?: AsyncContext,\n): boolean {\n const executingStep = ctx?.execution?.executingStep;\n if (!executingStep) {\n return false;\n }\n\n const targetStepId = config.stepId;\n const currentUserlandStepId = executingStep.userlandId;\n\n if (targetStepId === undefined) {\n return true;\n }\n\n if (targetStepId === null) {\n return true;\n }\n\n return targetStepId === currentUserlandStepId;\n}\n\n/**\n * Internal metadata write helper shared by metadata and score helpers.\n * @internal\n */\nexport async function performOp(\n client: Inngest,\n config: BuilderConfig,\n values: Record<string, unknown>,\n kind: MetadataKind,\n op: MetadataOpcode,\n): Promise<void> {\n const ctx = await getAsyncCtx();\n const target = buildTarget(config, ctx);\n\n const isInsideRun = !!ctx?.execution;\n const isInsideStep = !!ctx?.execution?.executingStep;\n const isScoreOrExperimentWrite =\n kind === \"inngest.score\" || kind === \"inngest.experiment\";\n if (isInsideRun && !isInsideStep && !isScoreOrExperimentWrite) {\n client[internalLoggerSymbol].warn(\n \"metadata.update() called outside of a step; this metadata may be lost on retries. Wrap the call in step.run() for durable metadata.\",\n );\n }\n\n const runId = config.runId ?? ctx?.execution?.ctx?.runId;\n const attempt = config.attempt ?? ctx?.execution?.ctx?.attempt;\n\n // We can batch metadata if we're updating the current run\n const canBatch =\n runId === ctx?.execution?.ctx?.runId &&\n targetsCurrentStep(config, ctx) &&\n attempt === ctx?.execution?.ctx?.attempt &&\n !config.spanId;\n\n if (canBatch) {\n const executingStep = ctx?.execution?.executingStep;\n const execInstance = ctx?.execution?.instance;\n const scope = getBatchScope(config);\n\n if (\n executingStep?.id &&\n execInstance &&\n execInstance.addMetadata(executingStep.id, kind, scope, op, values)\n ) {\n return;\n }\n }\n\n const headers =\n (\n ctx?.execution?.instance as\n | { options?: { headers?: Record<string, string> } }\n | undefined\n )?.options?.headers ?? undefined;\n\n await sendMetadataViaAPI(client, target, kind, op, values, headers);\n}\n\nexport const metadataSymbol = Symbol.for(\"inngest.step.metadata\");\n\n/**\n * Middleware that enables the experimental step.metadata() feature.\n *\n * @example\n * ```ts\n * import { metadataMiddleware } from \"inngest/experimental\";\n *\n * const inngest = new Inngest({\n * id: \"my-app\",\n * middleware: [metadataMiddleware()],\n * });\n * ```\n */\nexport const metadataMiddleware = () => {\n class MetadataMiddleware extends Middleware.BaseMiddleware {\n readonly id = \"inngest:metadata\";\n\n static override onRegister({ client }: Middleware.OnRegisterArgs) {\n client[\"experimentalMetadataEnabled\"] = true;\n }\n\n override transformFunctionInput(\n arg: Middleware.TransformFunctionInputArgs,\n ): Middleware.TransformFunctionInputArgs & {\n ctx: {\n step: {\n /**\n * Create a durable metadata update wrapped in a step\n *\n * @param memoizationId - The step ID used for the step itself, ensuring the\n * metadata update is only performed once even on function retries.\n *\n * @example\n * ```ts\n * // Update metadata for the current run\n * await step.metadata(\"update-status\").update({ status: \"processing\" });\n *\n * // Update metadata for a different run\n * await step.metadata(\"notify-parent\")\n * .run(parentRunId)\n * .update({ childCompleted: true });\n * ```\n */\n metadata: ExperimentalStepTools[typeof metadataSymbol];\n };\n };\n } {\n return {\n ...arg,\n ctx: {\n ...arg.ctx,\n step: {\n ...arg.ctx.step,\n // Access the hidden symbol-keyed metadata tool from step tools\n metadata: (arg.ctx.step as unknown as ExperimentalStepTools)[\n metadataSymbol\n ],\n },\n },\n };\n }\n }\n\n return MetadataMiddleware;\n};\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"InngestMetadata.js","names":["client: Inngest","config: BuilderConfig"],"sources":["../../src/components/InngestMetadata.ts"],"sourcesContent":["import type { Simplify } from \"../helpers/types.ts\";\nimport type { MetadataTarget } from \"../types.ts\";\nimport { type AsyncContext, getAsyncCtx } from \"./execution/als.ts\";\nimport { type Inngest, internalLoggerSymbol } from \"./Inngest.ts\";\nimport type { ExperimentalStepTools } from \"./InngestStepTools.ts\";\nimport { Middleware } from \"./middleware/middleware.ts\";\n\n/**\n * The level at which to attach the metadata.\n */\nexport type MetadataScope = \"run\" | \"step\" | \"extended_trace\";\n\n/**\n * Metadata of the same kind attached to the same item at the same scope are combined.\n */\nexport type MetadataKind =\n | \"inngest.experiment\"\n | \"inngest.score\"\n | \"inngest.warnings\"\n | \"inngest.ai\"\n | \"inngest.sandbox\"\n | `userland.${string}`;\n\n/**\n * The operation used to combine multiple metadata updates of the same kind.\n *\n * `set` replaces all values for the kind. `merge` is legacy: the server treats\n * every write as `set`, so values for a kind are always replaced.\n */\nexport type MetadataOpcode = \"merge\" | \"set\";\n\n/**\n * A metadata update containing `values` to be merged according to `op`\n * at the configured `scope` for the configured `kind`.\n */\nexport type MetadataUpdate = {\n kind: MetadataKind;\n scope: MetadataScope;\n op: MetadataOpcode;\n values: MetadataValues;\n};\n\nexport type MetadataValues = Record<string, unknown>;\n\n/**\n * Internal metadata target config shared by metadata and score helpers.\n * @internal\n */\nexport interface BuilderConfig {\n runId?: string | null;\n stepId?: string | null;\n stepIndex?: number;\n attempt?: number | null;\n spanId?: string;\n}\n\n/**\n * Configures and sends metadata updates.\n *\n * This is used to limit the available methods as target is\n * configured and the specified scope narrows.\n */\nexport type MetadataBuilder<Extras = {}> = Simplify<\n {\n /**\n * Sets the metadata context to a specific (or current if omitted) run.\n */\n run(id?: string): Simplify<Omit<MetadataBuilder<Extras>, \"run\">>;\n\n /**\n * Sets the metadata context to a specific (or current if omitted) step.\n */\n step(\n id?: string,\n index?: number,\n ): Simplify<Omit<MetadataBuilder<Extras>, \"run\" | \"step\">>;\n\n /**\n * Sets the metadata context to a specific (or current if omitted) step attempt.\n */\n attempt(\n index?: number,\n ): Simplify<Omit<MetadataBuilder<Extras>, \"run\" | \"step\" | \"attempt\">>;\n\n /**\n * Sets the metadata context to a specific span.\n */\n span(\n id: string,\n ): Simplify<\n Omit<MetadataBuilder<Extras>, \"run\" | \"step\" | \"attempt\" | \"span\">\n >;\n\n /**\n * Set the metadata of the given `kind` on the configured\n * run/step/step attempt/span, replacing any values previously set for\n * that kind.\n *\n * By default it will attach metadata to the current run if\n * executed inside the body of `createFunction` or to the\n * current step attempt if executed inside `step.run`.\n */\n set(values: Record<string, unknown>, kind?: string): Promise<void>;\n\n /**\n * Attach metadata to the configured run/step/step attempt/span.\n *\n * By default it will attach metadata to the current run if\n * executed inside the body of `createFunction` or to the\n * current step attempt if executed inside `step.run`.\n *\n * @deprecated Use `set()` instead. Values for a kind now replace any\n * previous values rather than merging with them. `update()` only merges\n * calls made within the same step that target the current run/step/step\n * attempt. Calls that are sent via the Inngest API (IE targeting another\n * run/step/attempt/span, or made outside of a step) replace each other.\n */\n update(values: Record<string, unknown>, kind?: string): Promise<void>;\n } & Extras\n>;\n\n/**\n * A wrapper around `MetadataBuilder` to attach metadata as a step.\n */\nexport type MetadataStepTool = MetadataBuilder<{\n /**\n * Allows many `updates` to be sent with the same scope.\n */\n do: (fn: (builder: MetadataBuilder) => Promise<void>) => Promise<void>;\n}>;\n\n/**\n * Configures and sends metadata updates.\n *\n * It sends metadata updates via step opcodes if the metadata is\n * configured to be attached to the current run/step/step attempt\n * and `update` is called inside of `step.run`.\n *\n * Otherwise it sends updates via the Inngest API.\n */\nexport class UnscopedMetadataBuilder implements MetadataBuilder {\n constructor(\n private client: Inngest,\n private config: BuilderConfig = {},\n ) {}\n\n run(id?: string): UnscopedMetadataBuilder {\n return new UnscopedMetadataBuilder(this.client, {\n ...this.config,\n runId: id ?? null,\n });\n }\n\n step(id?: string, index?: number): UnscopedMetadataBuilder {\n return new UnscopedMetadataBuilder(this.client, {\n ...this.config,\n stepId: id ?? null,\n stepIndex: index ?? 0,\n });\n }\n\n attempt(attempt?: number): UnscopedMetadataBuilder {\n return new UnscopedMetadataBuilder(this.client, {\n ...this.config,\n attempt: attempt ?? null,\n });\n }\n\n span(id: string): UnscopedMetadataBuilder {\n return new UnscopedMetadataBuilder(this.client, {\n ...this.config,\n spanId: id,\n });\n }\n\n async set(\n values: Record<string, unknown>,\n kind: string = \"default\",\n ): Promise<void> {\n await performOp(\n this.client,\n this.config,\n values,\n `userland.${kind}`,\n \"set\",\n );\n }\n\n /**\n * @deprecated Use `set()` instead. See `MetadataBuilder.update()`.\n */\n async update(\n values: Record<string, unknown>,\n kind: string = \"default\",\n ): Promise<void> {\n // Merge w/ earlier `update()` calls in the same step so that several\n // calls in one step still behave like a merge.\n await performOp(\n this.client,\n this.config,\n values,\n `userland.${kind}`,\n \"set\",\n { merge: true },\n );\n }\n\n toJSON() {\n return this.config;\n }\n}\n\n/**\n * Creates a `MetadataTarget` based on the current execution context and the `BuilderConfig` created using\n * `MetadataBuilder`.\n */\nexport function buildTarget(\n config: BuilderConfig,\n ctx?: AsyncContext,\n): MetadataTarget {\n const ctxExecution = ctx?.execution;\n const ctxRunId = ctxExecution?.ctx?.runId;\n const ctxStepId = ctxExecution?.executingStep?.id;\n const ctxAttempt = ctxExecution?.ctx?.attempt;\n const targetRunId = config.runId ?? ctxRunId;\n if (!targetRunId) throw new Error(\"No run context available\");\n\n const isSameRunAsCtx = ctxRunId !== undefined && targetRunId === ctxRunId;\n\n const stepCtxReason = !ctxExecution\n ? \"no function execution context is available\"\n : !ctxExecution.executingStep\n ? \"you are not inside a step.run() callback\"\n : \"you are targeting a different run\";\n\n if (\n config.attempt === null &&\n (!isSameRunAsCtx || !ctxExecution?.executingStep)\n )\n throw new Error(\n `attempt() was called without a value, but ${stepCtxReason}`,\n );\n if (\n config.stepId === null &&\n (!isSameRunAsCtx || !ctxExecution?.executingStep)\n )\n throw new Error(`step() was called without a value, but ${stepCtxReason}`);\n\n const targetAttempt =\n config.attempt !== undefined\n ? (config.attempt ?? ctxAttempt)\n : config.stepId === null\n ? ctxAttempt\n : undefined;\n\n if (config.spanId !== undefined) {\n return {\n run_id: targetRunId,\n step_id: config.stepId ?? ctxStepId,\n step_index: config.stepIndex,\n step_attempt:\n targetAttempt ?? (config.stepId === undefined ? ctxAttempt : undefined),\n span_id: config.spanId,\n };\n } else if (config.stepId !== undefined) {\n return {\n run_id: targetRunId,\n step_id: config.stepId ?? ctxStepId,\n step_index: config.stepIndex,\n step_attempt: targetAttempt,\n };\n } else if (config.runId !== undefined) {\n return {\n run_id: targetRunId,\n };\n } else if (ctxStepId && ctxAttempt !== undefined) {\n return {\n run_id: targetRunId,\n step_id: ctxStepId,\n step_attempt: ctxAttempt,\n };\n } else {\n return {\n run_id: targetRunId,\n };\n }\n}\n\n/**\n * Creates a metadata array payload for API calls.\n */\nexport function createMetadataPayload(\n kind: string,\n op: MetadataOpcode,\n metadata: Record<string, unknown>,\n) {\n return [\n {\n kind,\n op,\n values: metadata,\n },\n ];\n}\n\n/**\n * Sends metadata update via REST API to a specific target.\n */\nexport async function sendMetadataViaAPI(\n client: Inngest,\n target: MetadataTarget,\n kind: string,\n op: MetadataOpcode,\n metadata: Record<string, unknown>,\n headers?: Record<string, string>,\n): Promise<void> {\n const metadataArray = createMetadataPayload(kind, op, metadata);\n\n await client[\"updateMetadata\"]({\n target,\n metadata: metadataArray,\n headers,\n });\n}\n\nfunction getBatchScope(config: BuilderConfig): MetadataScope {\n if (config.spanId !== undefined) return \"extended_trace\";\n if (config.stepId !== undefined) return \"step\";\n if (config.runId !== undefined) return \"run\";\n\n return \"step\";\n}\n\nfunction targetsCurrentStep(\n config: BuilderConfig,\n ctx?: AsyncContext,\n): boolean {\n const executingStep = ctx?.execution?.executingStep;\n if (!executingStep) {\n return false;\n }\n\n const targetStepId = config.stepId;\n const currentUserlandStepId = executingStep.userlandId;\n\n if (targetStepId === undefined) {\n return true;\n }\n\n if (targetStepId === null) {\n return true;\n }\n\n return targetStepId === currentUserlandStepId;\n}\n\n/**\n * Options for how a metadata write combines w/ earlier writes.\n * @internal\n */\nexport interface MetadataWriteOptions {\n /**\n * Merge `values` into earlier writes of the same kind and scope made in\n * the same step instead of replacing them. Writes sent via the Inngest API\n * can't be merged. Defaults to `op === \"merge\"`.\n */\n merge?: boolean;\n}\n\n/**\n * Internal metadata write helper shared by metadata and score helpers.\n * @internal\n */\nexport async function performOp(\n client: Inngest,\n config: BuilderConfig,\n values: Record<string, unknown>,\n kind: MetadataKind,\n op: MetadataOpcode,\n opts?: MetadataWriteOptions,\n): Promise<void> {\n const ctx = await getAsyncCtx();\n const target = buildTarget(config, ctx);\n\n const isInsideRun = !!ctx?.execution;\n const isInsideStep = !!ctx?.execution?.executingStep;\n const isScoreOrExperimentWrite =\n kind === \"inngest.score\" || kind === \"inngest.experiment\";\n if (isInsideRun && !isInsideStep && !isScoreOrExperimentWrite) {\n client[internalLoggerSymbol].warn(\n \"metadata.set() (or update()) called outside of a step; this metadata may be lost on retries. Wrap the call in step.run() for durable metadata.\",\n );\n }\n\n const runId = config.runId ?? ctx?.execution?.ctx?.runId;\n const attempt = config.attempt ?? ctx?.execution?.ctx?.attempt;\n\n // We can batch metadata if we're updating the current run\n const canBatch =\n runId === ctx?.execution?.ctx?.runId &&\n targetsCurrentStep(config, ctx) &&\n attempt === ctx?.execution?.ctx?.attempt &&\n !config.spanId;\n\n if (canBatch) {\n const executingStep = ctx?.execution?.executingStep;\n const execInstance = ctx?.execution?.instance;\n const scope = getBatchScope(config);\n\n if (\n executingStep?.id &&\n execInstance &&\n execInstance.addMetadata(executingStep.id, kind, scope, op, values, opts)\n ) {\n return;\n }\n }\n\n const headers =\n (\n ctx?.execution?.instance as\n | { options?: { headers?: Record<string, string> } }\n | undefined\n )?.options?.headers ?? undefined;\n\n await sendMetadataViaAPI(client, target, kind, op, values, headers);\n}\n\nexport const metadataSymbol = Symbol.for(\"inngest.step.metadata\");\n\n/**\n * Middleware that enables the experimental step.metadata() feature.\n *\n * @example\n * ```ts\n * import { metadataMiddleware } from \"inngest/experimental\";\n *\n * const inngest = new Inngest({\n * id: \"my-app\",\n * middleware: [metadataMiddleware()],\n * });\n * ```\n */\nexport const metadataMiddleware = () => {\n class MetadataMiddleware extends Middleware.BaseMiddleware {\n readonly id = \"inngest:metadata\";\n\n static override onRegister({ client }: Middleware.OnRegisterArgs) {\n client[\"experimentalMetadataEnabled\"] = true;\n }\n\n override transformFunctionInput(\n arg: Middleware.TransformFunctionInputArgs,\n ): Middleware.TransformFunctionInputArgs & {\n ctx: {\n step: {\n /**\n * Create a durable metadata update wrapped in a step\n *\n * @param memoizationId - The step ID used for the step itself, ensuring the\n * metadata update is only performed once even on function retries.\n *\n * @example\n * ```ts\n * // Set metadata for the current run\n * await step.metadata(\"update-status\").set({ status: \"processing\" });\n *\n * // Set metadata for a different run\n * await step.metadata(\"notify-parent\")\n * .run(parentRunId)\n * .set({ childCompleted: true });\n * ```\n */\n metadata: ExperimentalStepTools[typeof metadataSymbol];\n };\n };\n } {\n return {\n ...arg,\n ctx: {\n ...arg.ctx,\n step: {\n ...arg.ctx.step,\n // Access the hidden symbol-keyed metadata tool from step tools\n metadata: (arg.ctx.step as unknown as ExperimentalStepTools)[\n metadataSymbol\n ],\n },\n },\n };\n }\n }\n\n return MetadataMiddleware;\n};\n"],"mappings":"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"}
|