inngest
Version:
Official SDK for Inngest.com. Inngest is the reliability layer for modern applications. Inngest combines durable execution, events, and queues into a zero-infra platform with built-in observability.
1 lines • 14.6 kB
Source Map (JSON)
{"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\" | \"step_attempt\" | \"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.warnings\"\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\ninterface 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 if (config.spanId !== undefined) {\n return {\n run_id: targetRunId,\n step_id: config.stepId ?? ctxStepId,\n step_index: config.stepIndex,\n step_attempt: config.attempt ?? ctxAttempt,\n span_id: config.spanId,\n };\n } else if (config.attempt !== undefined) {\n return {\n run_id: targetRunId,\n step_id: config.stepId ?? ctxStepId,\n step_index: config.stepIndex,\n step_attempt: config.attempt ?? ctxAttempt,\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 };\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.attempt !== undefined) return \"step_attempt\";\n if (config.stepId !== undefined) return \"step\";\n if (config.runId !== undefined) return \"run\";\n\n return \"step_attempt\";\n}\n\nasync 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 if (isInsideRun && !isInsideStep) {\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 stepId = config.stepId ?? ctx?.execution?.executingStep?.id;\n // TODO: get step index from ctx?\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 stepId === ctx?.execution?.executingStep?.id &&\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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"}