{"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.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.stepId !== 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.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\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":";;;;;;;;;;;;;;AAiHA,IAAa,0BAAb,MAAa,wBAAmD;CAC9D,YACE,AAAQA,QACR,AAAQC,SAAwB,EAAE,EAClC;EAFQ;EACA;;CAGV,IAAI,IAAsC;AACxC,SAAO,IAAI,wBAAwB,KAAK,QAAQ;GAC9C,GAAG,KAAK;GACR,OAAO,MAAM;GACd,CAAC;;CAGJ,KAAK,IAAa,OAAyC;AACzD,SAAO,IAAI,wBAAwB,KAAK,QAAQ;GAC9C,GAAG,KAAK;GACR,QAAQ,MAAM;GACd,WAAW,SAAS;GACrB,CAAC;;CAGJ,QAAQ,SAA2C;AACjD,SAAO,IAAI,wBAAwB,KAAK,QAAQ;GAC9C,GAAG,KAAK;GACR,SAAS,WAAW;GACrB,CAAC;;CAGJ,KAAK,IAAqC;AACxC,SAAO,IAAI,wBAAwB,KAAK,QAAQ;GAC9C,GAAG,KAAK;GACR,QAAQ;GACT,CAAC;;CAGJ,MAAM,OACJ,QACA,OAAe,WACA;AACf,QAAM,UACJ,KAAK,QACL,KAAK,QACL,QACA,YAAY,QACZ,QACD;;CAGH,SAAS;AACP,SAAO,KAAK;;;;;;;AAQhB,SAAgB,YACd,QACA,KACgB;CAChB,MAAM,eAAe,KAAK;CAC1B,MAAM,WAAW,cAAc,KAAK;CACpC,MAAM,YAAY,cAAc,eAAe;CAC/C,MAAM,aAAa,cAAc,KAAK;CACtC,MAAM,cAAc,OAAO,SAAS;AACpC,KAAI,CAAC,YAAa,OAAM,IAAI,MAAM,2BAA2B;CAE7D,MAAM,iBAAiB,aAAa,UAAa,gBAAgB;CAEjE,MAAM,gBAAgB,CAAC,eACnB,+CACA,CAAC,aAAa,gBACZ,6CACA;AAEN,KACE,OAAO,YAAY,SAClB,CAAC,kBAAkB,CAAC,cAAc,eAEnC,OAAM,IAAI,MACR,6CAA6C,gBAC9C;AACH,KACE,OAAO,WAAW,SACjB,CAAC,kBAAkB,CAAC,cAAc,eAEnC,OAAM,IAAI,MAAM,0CAA0C,gBAAgB;AAE5E,KAAI,OAAO,WAAW,OACpB,QAAO;EACL,QAAQ;EACR,SAAS,OAAO,UAAU;EAC1B,YAAY,OAAO;EACnB,cAAc,OAAO,WAAW;EAChC,SAAS,OAAO;EACjB;UACQ,OAAO,WAAW,OAC3B,QAAO;EACL,QAAQ;EACR,SAAS,OAAO,UAAU;EAC1B,YAAY,OAAO;EACnB,cAAc,OAAO,WAAW;EACjC;UACQ,OAAO,UAAU,OAC1B,QAAO,EACL,QAAQ,aACT;UACQ,aAAa,eAAe,OACrC,QAAO;EACL,QAAQ;EACR,SAAS;EACT,cAAc;EACf;KAED,QAAO,EACL,QAAQ,aACT;;;;;AAOL,SAAgB,sBACd,MACA,IACA,UACA;AACA,QAAO,CACL;EACE;EACA;EACA,QAAQ;EACT,CACF;;;;;AAMH,eAAsB,mBACpB,QACA,QACA,MACA,IACA,UACA,SACe;CACf,MAAM,gBAAgB,sBAAsB,MAAM,IAAI,SAAS;AAE/D,OAAM,OAAO,kBAAkB;EAC7B;EACA,UAAU;EACV;EACD,CAAC;;AAGJ,SAAS,cAAc,QAAsC;AAC3D,KAAI,OAAO,WAAW,OAAW,QAAO;AACxC,KAAI,OAAO,WAAW,OAAW,QAAO;AACxC,KAAI,OAAO,UAAU,OAAW,QAAO;AAEvC,QAAO;;AAGT,eAAe,UACb,QACA,QACA,QACA,MACA,IACe;CACf,MAAM,MAAM,MAAMC,yBAAa;CAC/B,MAAM,SAAS,YAAY,QAAQ,IAAI;CAEvC,MAAM,cAAc,CAAC,CAAC,KAAK;CAC3B,MAAM,eAAe,CAAC,CAAC,KAAK,WAAW;AACvC,KAAI,eAAe,CAAC,aAClB,QAAOC,sCAAsB,KAC3B,sIACD;CAGH,MAAM,QAAQ,OAAO,SAAS,KAAK,WAAW,KAAK;CACnD,MAAM,SAAS,OAAO,UAAU,KAAK,WAAW,eAAe;CAE/D,MAAM,UAAU,OAAO,WAAW,KAAK,WAAW,KAAK;AASvD,KALE,UAAU,KAAK,WAAW,KAAK,SAC/B,WAAW,KAAK,WAAW,eAAe,MAC1C,YAAY,KAAK,WAAW,KAAK,WACjC,CAAC,OAAO,QAEI;EACZ,MAAM,gBAAgB,KAAK,WAAW;EACtC,MAAM,eAAe,KAAK,WAAW;EACrC,MAAM,QAAQ,cAAc,OAAO;AAEnC,MACE,eAAe,MACf,gBACA,aAAa,YAAY,cAAc,IAAI,MAAM,OAAO,IAAI,OAAO,CAEnE;;AAWJ,OAAM,mBAAmB,QAAQ,QAAQ,MAAM,IAAI,SAL/C,KAAK,WAAW,WAGf,SAAS,WAAW,OAE0C;;AAGrE,MAAa,iBAAiB,OAAO,IAAI,wBAAwB;;;;;;;;;;;;;;AAejE,MAAa,2BAA2B;CACtC,MAAM,2BAA2BC,8BAAW,eAAe;EACzD,AAAS,KAAK;EAEd,OAAgB,WAAW,EAAE,UAAqC;AAChE,UAAO,iCAAiC;;EAG1C,AAAS,uBACP,KAwBA;AACA,UAAO;IACL,GAAG;IACH,KAAK;KACH,GAAG,IAAI;KACP,MAAM;MACJ,GAAG,IAAI,IAAI;MAEX,UAAW,IAAI,IAAI,KACjB;MAEH;KACF;IACF;;;AAIL,QAAO"}