inngest 4.8.0 → 4.10.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 +22 -0
- package/components/InngestMetadata.cjs +1 -1
- package/components/InngestMetadata.cjs.map +1 -1
- package/components/InngestMetadata.d.cts.map +1 -1
- package/components/InngestMetadata.d.ts.map +1 -1
- package/components/InngestMetadata.js +1 -1
- package/components/InngestMetadata.js.map +1 -1
- package/components/InngestScore.cjs +1 -1
- package/components/InngestScore.cjs.map +1 -1
- package/components/InngestScore.d.cts.map +1 -1
- package/components/InngestScore.d.ts.map +1 -1
- package/components/InngestScore.js +1 -1
- package/components/InngestScore.js.map +1 -1
- package/components/execution/engine.cjs +2 -2
- package/components/execution/engine.cjs.map +1 -1
- package/components/execution/engine.js +2 -2
- package/components/execution/engine.js.map +1 -1
- package/components/execution/otel/aiExtractor.cjs +127 -123
- package/components/execution/otel/aiExtractor.cjs.map +1 -1
- package/components/execution/otel/aiExtractor.d.cts +53 -5
- package/components/execution/otel/aiExtractor.d.cts.map +1 -1
- package/components/execution/otel/aiExtractor.d.ts +53 -5
- package/components/execution/otel/aiExtractor.d.ts.map +1 -1
- package/components/execution/otel/aiExtractor.js +127 -123
- package/components/execution/otel/aiExtractor.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/package.json +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
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,27 @@
|
|
|
1
1
|
# inngest
|
|
2
2
|
|
|
3
|
+
## 4.10.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- [#1599](https://github.com/inngest/inngest-js/pull/1599) [`c02cc265`](https://github.com/inngest/inngest-js/commit/c02cc265bfe73ead0a761c80ddcad52393193649) Thanks [@scottnuma](https://github.com/scottnuma)! - Support deprecated `gen_ai.system` for AI Metadata Extraction
|
|
8
|
+
|
|
9
|
+
- [#1600](https://github.com/inngest/inngest-js/pull/1600) [`2462e875`](https://github.com/inngest/inngest-js/commit/2462e875c01b43f9b24a386d5cc7655fc64be37e) Thanks [@scottnuma](https://github.com/scottnuma)! - Support parsing finish reasons for AI Metadata Extraction
|
|
10
|
+
|
|
11
|
+
### Patch Changes
|
|
12
|
+
|
|
13
|
+
- [#1605](https://github.com/inngest/inngest-js/pull/1605) [`cd18ed18`](https://github.com/inngest/inngest-js/commit/cd18ed1843ab2696b87778c0e947ad7d77f7feaf) Thanks [@Linell](https://github.com/Linell)! - Suppress unnecessary warning log for inngest.experiment.score.
|
|
14
|
+
|
|
15
|
+
- [#1602](https://github.com/inngest/inngest-js/pull/1602) [`e2e89ead`](https://github.com/inngest/inngest-js/commit/e2e89ead7475cec06693d4357b920820a1687ecf) Thanks [@amh4r](https://github.com/amh4r)! - Fix Extended Traces not appearing when checkpointing disabled
|
|
16
|
+
|
|
17
|
+
## 4.9.0
|
|
18
|
+
|
|
19
|
+
### Minor Changes
|
|
20
|
+
|
|
21
|
+
- [#1594](https://github.com/inngest/inngest-js/pull/1594) [`124e55f8`](https://github.com/inngest/inngest-js/commit/124e55f8d159af9145a073c9756a6281da60185b) Thanks [@scottnuma](https://github.com/scottnuma)! - Expand AI step metadata extraction to more OTel GenAI span attributes. The SDK now records additional model, provider, response id, token, and request parameter fields from OTel GenAI spans while continuing to ignore prompt and response content.
|
|
22
|
+
|
|
23
|
+
This removes the previous metadata extraction for Open Inference, Vercel AI SDK-specific, and Langfuse-specific span attributes.
|
|
24
|
+
|
|
3
25
|
## 4.8.0
|
|
4
26
|
|
|
5
27
|
### Minor Changes
|
|
@@ -131,7 +131,7 @@ async function performOp(client, config, values, kind, op) {
|
|
|
131
131
|
const target = buildTarget(config, ctx);
|
|
132
132
|
const isInsideRun = !!ctx?.execution;
|
|
133
133
|
const isInsideStep = !!ctx?.execution?.executingStep;
|
|
134
|
-
if (isInsideRun && !isInsideStep) 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.");
|
|
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.");
|
|
135
135
|
const runId = config.runId ?? ctx?.execution?.ctx?.runId;
|
|
136
136
|
const attempt = config.attempt ?? ctx?.execution?.ctx?.attempt;
|
|
137
137
|
if (runId === ctx?.execution?.ctx?.runId && targetsCurrentStep(config, ctx) && attempt === ctx?.execution?.ctx?.attempt && !config.spanId) {
|
|
@@ -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 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 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 | `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 +1 @@
|
|
|
1
|
-
{"version":3,"file":"InngestMetadata.d.cts","names":[],"sources":["../../src/components/InngestMetadata.ts"],"sourcesContent":[],"mappings":";;;;;;;;;AAUA;AAKA;AAUY,KAfA,aAAA,GAec,KAAA,GAAA,MAAA,GAAA,gBAAA;AAM1B;;;AAES,KAlBG,YAAA,GAkBH,oBAAA,GAAA,eAAA,GAAA,kBAAA,GAAA,YAAA,GAAA,YAAA,MAAA,EAAA;;;;AAKG,KAbA,cAAA,GAac,OAAG;AAoB7B;;;;AAK+B,KAhCnB,cAAA,GAgCmB;MAAT,EA/Bd,YA+Bc;OAQe,EAtC5B,aAsC4B;MArC/B,cAqCe;QAAL,EApCN,cAoCM;;AAOqB,KAxCzB,cAAA,GAAiB,MAwCQ,CAAA,MAAA,EAAA,OAAA,CAAA;;AAyBrC;;;;;AAA+B,KA7CnB,eA6CmB,CAAA,SAAA,CAAA,CAAA,CAAA,GA7CY,QA6CZ,CAAA;EAAe;
|
|
1
|
+
{"version":3,"file":"InngestMetadata.d.cts","names":[],"sources":["../../src/components/InngestMetadata.ts"],"sourcesContent":[],"mappings":";;;;;;;;;AAUA;AAKA;AAUY,KAfA,aAAA,GAec,KAAA,GAAA,MAAA,GAAA,gBAAA;AAM1B;;;AAES,KAlBG,YAAA,GAkBH,oBAAA,GAAA,eAAA,GAAA,kBAAA,GAAA,YAAA,GAAA,YAAA,MAAA,EAAA;;;;AAKG,KAbA,cAAA,GAac,OAAG;AAoB7B;;;;AAK+B,KAhCnB,cAAA,GAgCmB;MAAT,EA/Bd,YA+Bc;OAQe,EAtC5B,aAsC4B;MArC/B,cAqCe;QAAL,EApCN,cAoCM;;AAOqB,KAxCzB,cAAA,GAAiB,MAwCQ,CAAA,MAAA,EAAA,OAAA,CAAA;;AAyBrC;;;;;AAA+B,KA7CnB,eA6CmB,CAAA,SAAA,CAAA,CAAA,CAAA,GA7CY,QA6CZ,CAAA;EAAe;AA+Q9C;AAeA;EAmDC,GAAA,CAAA,EAAA,CAAA,EAAA,MAAA,CAAA,EAzXqB,QAyXrB,CAzX8B,IAyX9B,CAzXmC,eAyXnC,CAzXmD,MAyXnD,CAAA,EAAA,KAAA,CAAA,CAAA;;;;MAzCM,CAAA,EAAW,CAAX,EAAA,MAAW,EAAA,KAAA,CAAA,EAAA,MAAA,CAAA,EAxUX,QAwUW,CAxUF,IAwUE,CAxUG,eAwUH,CAxUmB,MAwUnB,CAAA,EAAA,KAAA,GAAA,MAAA,CAAA,CAAA;;;;2BAjUX,SAAS,KAAK,gBAAgB;;;;oBAO9B,SACD,KAAK,gBAAgB;;;;;;;;iBAUR,yCAAyC;IACtD;;;;KAMM,gBAAA,GAAmB;;;;qBAIV,oBAAoB,kBAAkB;;cA2Q9C;;;;;;;;;;;;;;cAeA;;;;;;;gCASF,UAAA,CAAW,6BACf,UAAA,CAAW;;;;;;;;;;;;;;;;;;;;oBAoBE,6BAA6B;;;;;;;;;;;;;;;;;;;;;;;;KA1BN,UAAA,CAAW"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"InngestMetadata.d.ts","names":[],"sources":["../../src/components/InngestMetadata.ts"],"sourcesContent":[],"mappings":";;;;;;;;;AAUA;AAKA;AAUY,KAfA,aAAA,GAec,KAAA,GAAA,MAAA,GAAA,gBAAA;AAM1B;;;AAES,KAlBG,YAAA,GAkBH,oBAAA,GAAA,eAAA,GAAA,kBAAA,GAAA,YAAA,GAAA,YAAA,MAAA,EAAA;;;;AAKG,KAbA,cAAA,GAac,OAAG;AAoB7B;;;;AAK+B,KAhCnB,cAAA,GAgCmB;MAAT,EA/Bd,YA+Bc;OAQe,EAtC5B,aAsC4B;MArC/B,cAqCe;QAAL,EApCN,cAoCM;;AAOqB,KAxCzB,cAAA,GAAiB,MAwCQ,CAAA,MAAA,EAAA,OAAA,CAAA;;AAyBrC;;;;;AAA+B,KA7CnB,eA6CmB,CAAA,SAAA,CAAA,CAAA,CAAA,GA7CY,QA6CZ,CAAA;EAAe;
|
|
1
|
+
{"version":3,"file":"InngestMetadata.d.ts","names":[],"sources":["../../src/components/InngestMetadata.ts"],"sourcesContent":[],"mappings":";;;;;;;;;AAUA;AAKA;AAUY,KAfA,aAAA,GAec,KAAA,GAAA,MAAA,GAAA,gBAAA;AAM1B;;;AAES,KAlBG,YAAA,GAkBH,oBAAA,GAAA,eAAA,GAAA,kBAAA,GAAA,YAAA,GAAA,YAAA,MAAA,EAAA;;;;AAKG,KAbA,cAAA,GAac,OAAG;AAoB7B;;;;AAK+B,KAhCnB,cAAA,GAgCmB;MAAT,EA/Bd,YA+Bc;OAQe,EAtC5B,aAsC4B;MArC/B,cAqCe;QAAL,EApCN,cAoCM;;AAOqB,KAxCzB,cAAA,GAAiB,MAwCQ,CAAA,MAAA,EAAA,OAAA,CAAA;;AAyBrC;;;;;AAA+B,KA7CnB,eA6CmB,CAAA,SAAA,CAAA,CAAA,CAAA,GA7CY,QA6CZ,CAAA;EAAe;AA+Q9C;AAeA;EAmDC,GAAA,CAAA,EAAA,CAAA,EAAA,MAAA,CAAA,EAzXqB,QAyXrB,CAzX8B,IAyX9B,CAzXmC,eAyXnC,CAzXmD,MAyXnD,CAAA,EAAA,KAAA,CAAA,CAAA;;;;MAzCM,CAAA,EAAW,CAAX,EAAA,MAAW,EAAA,KAAA,CAAA,EAAA,MAAA,CAAA,EAxUX,QAwUW,CAxUF,IAwUE,CAxUG,eAwUH,CAxUmB,MAwUnB,CAAA,EAAA,KAAA,GAAA,MAAA,CAAA,CAAA;;;;2BAjUX,SAAS,KAAK,gBAAgB;;;;oBAO9B,SACD,KAAK,gBAAgB;;;;;;;;iBAUR,yCAAyC;IACtD;;;;KAMM,gBAAA,GAAmB;;;;qBAIV,oBAAoB,kBAAkB;;cA2Q9C;;;;;;;;;;;;;;cAeA;;;;;;;gCASF,UAAA,CAAW,6BACf,UAAA,CAAW;;;;;;;;;;;;;;;;;;;;oBAoBE,6BAA6B;;;;;;;;;;;;;;;;;;;;;;;;KA1BN,UAAA,CAAW"}
|
|
@@ -131,7 +131,7 @@ async function performOp(client, config, values, kind, op) {
|
|
|
131
131
|
const target = buildTarget(config, ctx);
|
|
132
132
|
const isInsideRun = !!ctx?.execution;
|
|
133
133
|
const isInsideStep = !!ctx?.execution?.executingStep;
|
|
134
|
-
if (isInsideRun && !isInsideStep) 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.");
|
|
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.");
|
|
135
135
|
const runId = config.runId ?? ctx?.execution?.ctx?.runId;
|
|
136
136
|
const attempt = config.attempt ?? ctx?.execution?.ctx?.attempt;
|
|
137
137
|
if (runId === ctx?.execution?.ctx?.runId && targetsCurrentStep(config, ctx) && attempt === ctx?.execution?.ctx?.attempt && !config.spanId) {
|
|
@@ -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 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 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 | `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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"}
|
|
@@ -56,7 +56,7 @@ async function sendScoreExperiment(client, options) {
|
|
|
56
56
|
stepId: options.stepId
|
|
57
57
|
};
|
|
58
58
|
await require_InngestMetadata.performOp(client, target, {
|
|
59
|
-
|
|
59
|
+
name: options.experiment.experimentName,
|
|
60
60
|
variant: options.experiment.variant
|
|
61
61
|
}, experimentKind, "merge");
|
|
62
62
|
await require_InngestMetadata.performOp(client, target, { [options.name]: { value: options.value } }, scoreKind, "merge");
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"InngestScore.cjs","names":["isRecord","isFiniteNumber","performOp","Middleware"],"sources":["../../src/components/InngestScore.ts"],"sourcesContent":["import { isFiniteNumber, isRecord } from \"../helpers/types.ts\";\nimport type { ExperimentRef } from \"../types.ts\";\nimport type { Inngest } from \"./Inngest.ts\";\nimport { performOp } from \"./InngestMetadata.ts\";\nimport type { ExperimentalStepTools } from \"./InngestStepTools.ts\";\nimport { Middleware } from \"./middleware/middleware.ts\";\n\nconst scoreKind = \"inngest.score\" as const;\nconst experimentKind = \"inngest.experiment\" as const;\nconst maxKindByteLength = 128;\nconst maxScoreNameByteLength = maxKindByteLength;\n\ntype ScoreValue = number | boolean;\n\nexport type ScoreOptions = {\n runId?: string;\n stepId?: string;\n name: string;\n value: ScoreValue;\n};\n\nexport type ScoreExperimentOptions = ScoreOptions & {\n experiment: ExperimentRef;\n};\n\n/**\n * The client `score` API. Call it directly to write a live score for a run or\n * step; use `.experiment(...)` to attach a score to a `group.experiment()`\n * variant.\n */\nexport interface ClientScore {\n /**\n * Write a live score for a run or a specific run step. Explicit targets win;\n * otherwise the current run or step is inferred from the execution context.\n * For standalone durable score writes, prefer `step.score()`.\n */\n (options: ScoreOptions): Promise<void>;\n\n /**\n * Attach a score to a previously-selected experiment variant, using the\n * `experiment` ref returned by `group.experiment()`. Writes the score and the\n * experiment attribution together so they co-locate on one row.\n *\n * **Call at the function-body level** (outside any `step.run()` callback), or\n * pass an explicit `runId`, so the write is run-scoped and attaches to the\n * experiment. Calling inside `step.run()` without `runId` produces a\n * step-scoped write the experiment detail backend never surfaces.\n */\n experiment(options: ScoreExperimentOptions): Promise<void>;\n}\n\nexport type ScoreStepTool = (\n memoizationId: string,\n options: ScoreOptions,\n) => Promise<void>;\n\nexport const scoreSymbol = Symbol.for(\"inngest.step.score\");\n\nfunction validateIdField({\n value,\n field,\n required,\n}: {\n value: unknown;\n field: string;\n required: boolean;\n}): void {\n if (!required && value === undefined) {\n return;\n }\n\n const isValidString = typeof value === \"string\" && value.trim().length > 0;\n if (!isValidString) {\n throw new Error(`${field} must be a non-empty string`);\n }\n}\n\nfunction validateScoreFields(\n options: unknown,\n requiredTargetIds: readonly (\"runId\" | \"stepId\")[],\n): asserts options is {\n runId?: unknown;\n stepId?: unknown;\n name: unknown;\n value: unknown;\n} {\n if (!isRecord(options)) {\n throw new Error(\"score options must be an object\");\n }\n\n for (const field of [\"runId\", \"stepId\"] as const) {\n validateIdField({\n value: options[field],\n field,\n required: requiredTargetIds.includes(field),\n });\n }\n\n if (typeof options.name !== \"string\" || options.name.trim().length === 0) {\n throw new Error(\"score name must be a non-empty string\");\n }\n\n // Single quote rejection mirrors the cloud MetricKeyRegex; without it,\n // valid-looking score names like \"it's-broken\" would silently drop in\n // variant aggregation.\n // biome-ignore lint/suspicious/noControlCharactersInRegex: intentional — rejecting control chars and single quotes in user-supplied names\n if (/[\\x00-\\x1f\\x7f']/.test(options.name)) {\n throw new Error(\n \"score name must not contain control characters or single quotes\",\n );\n }\n\n const nameByteLength = new TextEncoder().encode(options.name).length;\n if (nameByteLength > maxScoreNameByteLength) {\n throw new Error(\n `score name must be ${maxScoreNameByteLength} bytes or fewer in UTF-8 (got ${nameByteLength})`,\n );\n }\n\n if (typeof options.value !== \"boolean\" && !isFiniteNumber(options.value)) {\n throw new Error(\"score value must be a finite number or boolean\");\n }\n}\n\nfunction validateSendScoreOptions(\n options: unknown,\n): asserts options is ScoreOptions {\n validateScoreFields(options, []);\n}\n\nexport function validateStepScoreOptions(\n options: unknown,\n): asserts options is ScoreOptions {\n validateScoreFields(options, []);\n}\n\nexport async function sendScore(\n client: Inngest,\n options: ScoreOptions,\n): Promise<void> {\n validateSendScoreOptions(options);\n\n await performOp(\n client,\n {\n runId: options.runId,\n stepId: options.stepId,\n },\n { [options.name]: { value: options.value } },\n `${scoreKind}`,\n \"merge\",\n );\n}\n\nexport async function sendStepScore(\n client: Inngest,\n options: ScoreOptions,\n): Promise<void> {\n validateStepScoreOptions(options);\n\n await performOp(\n client,\n {\n // Omitted stepId means run scope and null keeps current-run lookup intact.\n runId:\n options.stepId === undefined ? (options.runId ?? null) : options.runId,\n stepId: options.stepId,\n },\n { [options.name]: { value: options.value } },\n `${scoreKind}`,\n \"merge\",\n );\n}\n\nfunction validateExperimentRef(\n experiment: unknown,\n): asserts experiment is ExperimentRef {\n if (!isRecord(experiment)) {\n throw new Error(\"experiment must be an object\");\n }\n for (const field of [\"experimentName\", \"variant\"] as const) {\n if (\n typeof experiment[field] !== \"string\" ||\n (experiment[field] as string).trim().length === 0\n ) {\n throw new Error(`experiment.${field} must be a non-empty string`);\n }\n }\n}\n\nexport async function sendScoreExperiment(\n client: Inngest,\n options: ScoreExperimentOptions,\n): Promise<void> {\n validateSendScoreOptions(options);\n validateExperimentRef(options.experiment);\n\n const target = { runId: options.runId, stepId: options.stepId };\n\n // Write the experiment attribution first, then the score. These are two\n // non-atomic metadata writes; if the second fails, attribution-without-score\n // is the benign state the system already produces (it's exactly what\n // `group.experiment()` leaves after selecting a variant but before scoring).\n // Writing the score first would instead risk a bare, unattributed score that\n // never surfaces in the experiment view.\n await performOp(\n client,\n target,\n {\n experiment_name: options.experiment.experimentName,\n variant: options.experiment.variant,\n },\n experimentKind,\n \"merge\",\n );\n await performOp(\n client,\n target,\n { [options.name]: { value: options.value } },\n scoreKind,\n \"merge\",\n );\n}\n\nexport const scoreMiddleware = () => {\n class ScoreMiddleware extends Middleware.BaseMiddleware {\n readonly id = \"inngest:score\";\n\n static override onRegister({ client }: Middleware.OnRegisterArgs) {\n client[\"experimentalScoreEnabled\"] = true;\n }\n\n override transformFunctionInput(\n arg: Middleware.TransformFunctionInputArgs,\n ): Middleware.TransformFunctionInputArgs & {\n ctx: {\n step: {\n /**\n * Create a durable score update wrapped in a step.\n * Omit `stepId` to attach the score to the run.\n * Use `inngest.score()` for live score writes inside `step.run()`.\n *\n * @param memoizationId - The durable step ID used to memoize this score write.\n */\n score: ExperimentalStepTools[typeof scoreSymbol];\n };\n };\n } {\n return {\n ...arg,\n ctx: {\n ...arg.ctx,\n step: {\n ...arg.ctx.step,\n score: (arg.ctx.step as unknown as ExperimentalStepTools)[\n scoreSymbol\n ],\n },\n },\n };\n }\n }\n\n return ScoreMiddleware;\n};\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"InngestScore.cjs","names":["isRecord","isFiniteNumber","performOp","Middleware"],"sources":["../../src/components/InngestScore.ts"],"sourcesContent":["import { isFiniteNumber, isRecord } from \"../helpers/types.ts\";\nimport type { ExperimentRef } from \"../types.ts\";\nimport type { Inngest } from \"./Inngest.ts\";\nimport type { ExperimentMetadataValues } from \"./InngestGroupTools.ts\";\nimport { performOp } from \"./InngestMetadata.ts\";\nimport type { ExperimentalStepTools } from \"./InngestStepTools.ts\";\nimport { Middleware } from \"./middleware/middleware.ts\";\n\nconst scoreKind = \"inngest.score\" as const;\nconst experimentKind = \"inngest.experiment\" as const;\nconst maxKindByteLength = 128;\nconst maxScoreNameByteLength = maxKindByteLength;\n\ntype ScoreValue = number | boolean;\n\nexport type ScoreOptions = {\n runId?: string;\n stepId?: string;\n name: string;\n value: ScoreValue;\n};\n\nexport type ScoreExperimentOptions = ScoreOptions & {\n experiment: ExperimentRef;\n};\n\n/**\n * The client `score` API. Call it directly to write a live score for a run or\n * step; use `.experiment(...)` to attach a score to a `group.experiment()`\n * variant.\n */\nexport interface ClientScore {\n /**\n * Write a live score for a run or a specific run step. Explicit targets win;\n * otherwise the current run or step is inferred from the execution context.\n * For standalone durable score writes, prefer `step.score()`.\n */\n (options: ScoreOptions): Promise<void>;\n\n /**\n * Attach a score to a previously-selected experiment variant, using the\n * `experiment` ref returned by `group.experiment()`. Writes the score and the\n * experiment attribution together so they co-locate on one row.\n *\n * **Call at the function-body level** (outside any `step.run()` callback), or\n * pass an explicit `runId`, so the write is run-scoped and attaches to the\n * experiment. Calling inside `step.run()` without `runId` produces a\n * step-scoped write the experiment detail backend never surfaces.\n */\n experiment(options: ScoreExperimentOptions): Promise<void>;\n}\n\nexport type ScoreStepTool = (\n memoizationId: string,\n options: ScoreOptions,\n) => Promise<void>;\n\nexport const scoreSymbol = Symbol.for(\"inngest.step.score\");\n\nfunction validateIdField({\n value,\n field,\n required,\n}: {\n value: unknown;\n field: string;\n required: boolean;\n}): void {\n if (!required && value === undefined) {\n return;\n }\n\n const isValidString = typeof value === \"string\" && value.trim().length > 0;\n if (!isValidString) {\n throw new Error(`${field} must be a non-empty string`);\n }\n}\n\nfunction validateScoreFields(\n options: unknown,\n requiredTargetIds: readonly (\"runId\" | \"stepId\")[],\n): asserts options is {\n runId?: unknown;\n stepId?: unknown;\n name: unknown;\n value: unknown;\n} {\n if (!isRecord(options)) {\n throw new Error(\"score options must be an object\");\n }\n\n for (const field of [\"runId\", \"stepId\"] as const) {\n validateIdField({\n value: options[field],\n field,\n required: requiredTargetIds.includes(field),\n });\n }\n\n if (typeof options.name !== \"string\" || options.name.trim().length === 0) {\n throw new Error(\"score name must be a non-empty string\");\n }\n\n // Single quote rejection mirrors the cloud MetricKeyRegex; without it,\n // valid-looking score names like \"it's-broken\" would silently drop in\n // variant aggregation.\n // biome-ignore lint/suspicious/noControlCharactersInRegex: intentional — rejecting control chars and single quotes in user-supplied names\n if (/[\\x00-\\x1f\\x7f']/.test(options.name)) {\n throw new Error(\n \"score name must not contain control characters or single quotes\",\n );\n }\n\n const nameByteLength = new TextEncoder().encode(options.name).length;\n if (nameByteLength > maxScoreNameByteLength) {\n throw new Error(\n `score name must be ${maxScoreNameByteLength} bytes or fewer in UTF-8 (got ${nameByteLength})`,\n );\n }\n\n if (typeof options.value !== \"boolean\" && !isFiniteNumber(options.value)) {\n throw new Error(\"score value must be a finite number or boolean\");\n }\n}\n\nfunction validateSendScoreOptions(\n options: unknown,\n): asserts options is ScoreOptions {\n validateScoreFields(options, []);\n}\n\nexport function validateStepScoreOptions(\n options: unknown,\n): asserts options is ScoreOptions {\n validateScoreFields(options, []);\n}\n\nexport async function sendScore(\n client: Inngest,\n options: ScoreOptions,\n): Promise<void> {\n validateSendScoreOptions(options);\n\n await performOp(\n client,\n {\n runId: options.runId,\n stepId: options.stepId,\n },\n { [options.name]: { value: options.value } },\n `${scoreKind}`,\n \"merge\",\n );\n}\n\nexport async function sendStepScore(\n client: Inngest,\n options: ScoreOptions,\n): Promise<void> {\n validateStepScoreOptions(options);\n\n await performOp(\n client,\n {\n // Omitted stepId means run scope and null keeps current-run lookup intact.\n runId:\n options.stepId === undefined ? (options.runId ?? null) : options.runId,\n stepId: options.stepId,\n },\n { [options.name]: { value: options.value } },\n `${scoreKind}`,\n \"merge\",\n );\n}\n\nfunction validateExperimentRef(\n experiment: unknown,\n): asserts experiment is ExperimentRef {\n if (!isRecord(experiment)) {\n throw new Error(\"experiment must be an object\");\n }\n for (const field of [\"experimentName\", \"variant\"] as const) {\n if (\n typeof experiment[field] !== \"string\" ||\n (experiment[field] as string).trim().length === 0\n ) {\n throw new Error(`experiment.${field} must be a non-empty string`);\n }\n }\n}\n\nexport async function sendScoreExperiment(\n client: Inngest,\n options: ScoreExperimentOptions,\n): Promise<void> {\n validateSendScoreOptions(options);\n validateExperimentRef(options.experiment);\n\n const target = { runId: options.runId, stepId: options.stepId };\n\n // Write the experiment attribution first, then the score. These are two\n // non-atomic metadata writes; if the second fails, attribution-without-score\n // is the benign state the system already produces (it's exactly what\n // `group.experiment()` leaves after selecting a variant but before scoring).\n // Writing the score first would instead risk a bare, unattributed score that\n // never surfaces in the experiment view.\n await performOp(\n client,\n target,\n {\n name: options.experiment.experimentName,\n variant: options.experiment.variant,\n } satisfies Pick<ExperimentMetadataValues, \"name\" | \"variant\">,\n experimentKind,\n \"merge\",\n );\n await performOp(\n client,\n target,\n { [options.name]: { value: options.value } },\n scoreKind,\n \"merge\",\n );\n}\n\nexport const scoreMiddleware = () => {\n class ScoreMiddleware extends Middleware.BaseMiddleware {\n readonly id = \"inngest:score\";\n\n static override onRegister({ client }: Middleware.OnRegisterArgs) {\n client[\"experimentalScoreEnabled\"] = true;\n }\n\n override transformFunctionInput(\n arg: Middleware.TransformFunctionInputArgs,\n ): Middleware.TransformFunctionInputArgs & {\n ctx: {\n step: {\n /**\n * Create a durable score update wrapped in a step.\n * Omit `stepId` to attach the score to the run.\n * Use `inngest.score()` for live score writes inside `step.run()`.\n *\n * @param memoizationId - The durable step ID used to memoize this score write.\n */\n score: ExperimentalStepTools[typeof scoreSymbol];\n };\n };\n } {\n return {\n ...arg,\n ctx: {\n ...arg.ctx,\n step: {\n ...arg.ctx.step,\n score: (arg.ctx.step as unknown as ExperimentalStepTools)[\n scoreSymbol\n ],\n },\n },\n };\n }\n }\n\n return ScoreMiddleware;\n};\n"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"InngestScore.d.cts","names":[],"sources":["../../src/components/InngestScore.ts"],"sourcesContent":[],"mappings":";;;;;;;
|
|
1
|
+
{"version":3,"file":"InngestScore.d.cts","names":[],"sources":["../../src/components/InngestScore.ts"],"sourcesContent":[],"mappings":";;;;;;;KAaK,UAAA;KAEO,YAAA;;EAFP,MAAA,CAAA,EAAA,MAAU;EAEH,IAAA,EAAA,MAAA;EAOA,KAAA,EAHH,UAGG;CAAsB;AAAG,KAAzB,sBAAA,GAAyB,YAAA,GAAA;YACvB,EAAA,aAAA;CAAa;AAQ3B;;;;;AAkB+C,UAlB9B,WAAA,CAkB8B;EAAO;AAGtD;;;;EAGY,CAAA,OAAA,EAlBA,YAkBA,CAAA,EAlBe,OAkBf,CAAA,IAAA,CAAA;EAEC;AAwKb;;;;;;;;;sBAhLsB,yBAAyB;;KAGnC,aAAA,oCAED,iBACN;cAEQ;cAwKA;;;;;;;gCASF,UAAA,CAAW,6BACf,UAAA,CAAW;;;;;;;;;;iBAUD,6BAA6B;;;;;;;;;;;;IAhBH,WAAW,EAAA,GAAA,4BAAA,CAAA,cAAA,CAAA,IAAA,CAAA;IAAc,WAAA,EAAA,GAAA,4BAAA,CAAA,cAAA,CAAA,IAAA,CAAA;;;;;;;;;;;KAAzB,UAAA,CAAW"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"InngestScore.d.ts","names":[],"sources":["../../src/components/InngestScore.ts"],"sourcesContent":[],"mappings":";;;;;;;
|
|
1
|
+
{"version":3,"file":"InngestScore.d.ts","names":[],"sources":["../../src/components/InngestScore.ts"],"sourcesContent":[],"mappings":";;;;;;;KAaK,UAAA;KAEO,YAAA;;EAFP,MAAA,CAAA,EAAA,MAAU;EAEH,IAAA,EAAA,MAAA;EAOA,KAAA,EAHH,UAGG;CAAsB;AAAG,KAAzB,sBAAA,GAAyB,YAAA,GAAA;YACvB,EAAA,aAAA;CAAa;AAQ3B;;;;;AAkB+C,UAlB9B,WAAA,CAkB8B;EAAO;AAGtD;;;;EAGY,CAAA,OAAA,EAlBA,YAkBA,CAAA,EAlBe,OAkBf,CAAA,IAAA,CAAA;EAEC;AAwKb;;;;;;;;;sBAhLsB,yBAAyB;;KAGnC,aAAA,oCAED,iBACN;cAEQ;cAwKA;;;;;;;gCASF,UAAA,CAAW,6BACf,UAAA,CAAW;;;;;;;;;;iBAUD,6BAA6B;;;;;;;;;;;;IAhBH,WAAW,EAAA,GAAA,4BAAA,CAAA,cAAA,CAAA,IAAA,CAAA;IAAc,WAAA,EAAA,GAAA,4BAAA,CAAA,cAAA,CAAA,IAAA,CAAA;;;;;;;;;;;KAAzB,UAAA,CAAW"}
|
|
@@ -56,7 +56,7 @@ async function sendScoreExperiment(client, options) {
|
|
|
56
56
|
stepId: options.stepId
|
|
57
57
|
};
|
|
58
58
|
await performOp(client, target, {
|
|
59
|
-
|
|
59
|
+
name: options.experiment.experimentName,
|
|
60
60
|
variant: options.experiment.variant
|
|
61
61
|
}, experimentKind, "merge");
|
|
62
62
|
await performOp(client, target, { [options.name]: { value: options.value } }, scoreKind, "merge");
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"InngestScore.js","names":[],"sources":["../../src/components/InngestScore.ts"],"sourcesContent":["import { isFiniteNumber, isRecord } from \"../helpers/types.ts\";\nimport type { ExperimentRef } from \"../types.ts\";\nimport type { Inngest } from \"./Inngest.ts\";\nimport { performOp } from \"./InngestMetadata.ts\";\nimport type { ExperimentalStepTools } from \"./InngestStepTools.ts\";\nimport { Middleware } from \"./middleware/middleware.ts\";\n\nconst scoreKind = \"inngest.score\" as const;\nconst experimentKind = \"inngest.experiment\" as const;\nconst maxKindByteLength = 128;\nconst maxScoreNameByteLength = maxKindByteLength;\n\ntype ScoreValue = number | boolean;\n\nexport type ScoreOptions = {\n runId?: string;\n stepId?: string;\n name: string;\n value: ScoreValue;\n};\n\nexport type ScoreExperimentOptions = ScoreOptions & {\n experiment: ExperimentRef;\n};\n\n/**\n * The client `score` API. Call it directly to write a live score for a run or\n * step; use `.experiment(...)` to attach a score to a `group.experiment()`\n * variant.\n */\nexport interface ClientScore {\n /**\n * Write a live score for a run or a specific run step. Explicit targets win;\n * otherwise the current run or step is inferred from the execution context.\n * For standalone durable score writes, prefer `step.score()`.\n */\n (options: ScoreOptions): Promise<void>;\n\n /**\n * Attach a score to a previously-selected experiment variant, using the\n * `experiment` ref returned by `group.experiment()`. Writes the score and the\n * experiment attribution together so they co-locate on one row.\n *\n * **Call at the function-body level** (outside any `step.run()` callback), or\n * pass an explicit `runId`, so the write is run-scoped and attaches to the\n * experiment. Calling inside `step.run()` without `runId` produces a\n * step-scoped write the experiment detail backend never surfaces.\n */\n experiment(options: ScoreExperimentOptions): Promise<void>;\n}\n\nexport type ScoreStepTool = (\n memoizationId: string,\n options: ScoreOptions,\n) => Promise<void>;\n\nexport const scoreSymbol = Symbol.for(\"inngest.step.score\");\n\nfunction validateIdField({\n value,\n field,\n required,\n}: {\n value: unknown;\n field: string;\n required: boolean;\n}): void {\n if (!required && value === undefined) {\n return;\n }\n\n const isValidString = typeof value === \"string\" && value.trim().length > 0;\n if (!isValidString) {\n throw new Error(`${field} must be a non-empty string`);\n }\n}\n\nfunction validateScoreFields(\n options: unknown,\n requiredTargetIds: readonly (\"runId\" | \"stepId\")[],\n): asserts options is {\n runId?: unknown;\n stepId?: unknown;\n name: unknown;\n value: unknown;\n} {\n if (!isRecord(options)) {\n throw new Error(\"score options must be an object\");\n }\n\n for (const field of [\"runId\", \"stepId\"] as const) {\n validateIdField({\n value: options[field],\n field,\n required: requiredTargetIds.includes(field),\n });\n }\n\n if (typeof options.name !== \"string\" || options.name.trim().length === 0) {\n throw new Error(\"score name must be a non-empty string\");\n }\n\n // Single quote rejection mirrors the cloud MetricKeyRegex; without it,\n // valid-looking score names like \"it's-broken\" would silently drop in\n // variant aggregation.\n // biome-ignore lint/suspicious/noControlCharactersInRegex: intentional — rejecting control chars and single quotes in user-supplied names\n if (/[\\x00-\\x1f\\x7f']/.test(options.name)) {\n throw new Error(\n \"score name must not contain control characters or single quotes\",\n );\n }\n\n const nameByteLength = new TextEncoder().encode(options.name).length;\n if (nameByteLength > maxScoreNameByteLength) {\n throw new Error(\n `score name must be ${maxScoreNameByteLength} bytes or fewer in UTF-8 (got ${nameByteLength})`,\n );\n }\n\n if (typeof options.value !== \"boolean\" && !isFiniteNumber(options.value)) {\n throw new Error(\"score value must be a finite number or boolean\");\n }\n}\n\nfunction validateSendScoreOptions(\n options: unknown,\n): asserts options is ScoreOptions {\n validateScoreFields(options, []);\n}\n\nexport function validateStepScoreOptions(\n options: unknown,\n): asserts options is ScoreOptions {\n validateScoreFields(options, []);\n}\n\nexport async function sendScore(\n client: Inngest,\n options: ScoreOptions,\n): Promise<void> {\n validateSendScoreOptions(options);\n\n await performOp(\n client,\n {\n runId: options.runId,\n stepId: options.stepId,\n },\n { [options.name]: { value: options.value } },\n `${scoreKind}`,\n \"merge\",\n );\n}\n\nexport async function sendStepScore(\n client: Inngest,\n options: ScoreOptions,\n): Promise<void> {\n validateStepScoreOptions(options);\n\n await performOp(\n client,\n {\n // Omitted stepId means run scope and null keeps current-run lookup intact.\n runId:\n options.stepId === undefined ? (options.runId ?? null) : options.runId,\n stepId: options.stepId,\n },\n { [options.name]: { value: options.value } },\n `${scoreKind}`,\n \"merge\",\n );\n}\n\nfunction validateExperimentRef(\n experiment: unknown,\n): asserts experiment is ExperimentRef {\n if (!isRecord(experiment)) {\n throw new Error(\"experiment must be an object\");\n }\n for (const field of [\"experimentName\", \"variant\"] as const) {\n if (\n typeof experiment[field] !== \"string\" ||\n (experiment[field] as string).trim().length === 0\n ) {\n throw new Error(`experiment.${field} must be a non-empty string`);\n }\n }\n}\n\nexport async function sendScoreExperiment(\n client: Inngest,\n options: ScoreExperimentOptions,\n): Promise<void> {\n validateSendScoreOptions(options);\n validateExperimentRef(options.experiment);\n\n const target = { runId: options.runId, stepId: options.stepId };\n\n // Write the experiment attribution first, then the score. These are two\n // non-atomic metadata writes; if the second fails, attribution-without-score\n // is the benign state the system already produces (it's exactly what\n // `group.experiment()` leaves after selecting a variant but before scoring).\n // Writing the score first would instead risk a bare, unattributed score that\n // never surfaces in the experiment view.\n await performOp(\n client,\n target,\n {\n experiment_name: options.experiment.experimentName,\n variant: options.experiment.variant,\n },\n experimentKind,\n \"merge\",\n );\n await performOp(\n client,\n target,\n { [options.name]: { value: options.value } },\n scoreKind,\n \"merge\",\n );\n}\n\nexport const scoreMiddleware = () => {\n class ScoreMiddleware extends Middleware.BaseMiddleware {\n readonly id = \"inngest:score\";\n\n static override onRegister({ client }: Middleware.OnRegisterArgs) {\n client[\"experimentalScoreEnabled\"] = true;\n }\n\n override transformFunctionInput(\n arg: Middleware.TransformFunctionInputArgs,\n ): Middleware.TransformFunctionInputArgs & {\n ctx: {\n step: {\n /**\n * Create a durable score update wrapped in a step.\n * Omit `stepId` to attach the score to the run.\n * Use `inngest.score()` for live score writes inside `step.run()`.\n *\n * @param memoizationId - The durable step ID used to memoize this score write.\n */\n score: ExperimentalStepTools[typeof scoreSymbol];\n };\n };\n } {\n return {\n ...arg,\n ctx: {\n ...arg.ctx,\n step: {\n ...arg.ctx.step,\n score: (arg.ctx.step as unknown as ExperimentalStepTools)[\n scoreSymbol\n ],\n },\n },\n };\n }\n }\n\n return ScoreMiddleware;\n};\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"InngestScore.js","names":[],"sources":["../../src/components/InngestScore.ts"],"sourcesContent":["import { isFiniteNumber, isRecord } from \"../helpers/types.ts\";\nimport type { ExperimentRef } from \"../types.ts\";\nimport type { Inngest } from \"./Inngest.ts\";\nimport type { ExperimentMetadataValues } from \"./InngestGroupTools.ts\";\nimport { performOp } from \"./InngestMetadata.ts\";\nimport type { ExperimentalStepTools } from \"./InngestStepTools.ts\";\nimport { Middleware } from \"./middleware/middleware.ts\";\n\nconst scoreKind = \"inngest.score\" as const;\nconst experimentKind = \"inngest.experiment\" as const;\nconst maxKindByteLength = 128;\nconst maxScoreNameByteLength = maxKindByteLength;\n\ntype ScoreValue = number | boolean;\n\nexport type ScoreOptions = {\n runId?: string;\n stepId?: string;\n name: string;\n value: ScoreValue;\n};\n\nexport type ScoreExperimentOptions = ScoreOptions & {\n experiment: ExperimentRef;\n};\n\n/**\n * The client `score` API. Call it directly to write a live score for a run or\n * step; use `.experiment(...)` to attach a score to a `group.experiment()`\n * variant.\n */\nexport interface ClientScore {\n /**\n * Write a live score for a run or a specific run step. Explicit targets win;\n * otherwise the current run or step is inferred from the execution context.\n * For standalone durable score writes, prefer `step.score()`.\n */\n (options: ScoreOptions): Promise<void>;\n\n /**\n * Attach a score to a previously-selected experiment variant, using the\n * `experiment` ref returned by `group.experiment()`. Writes the score and the\n * experiment attribution together so they co-locate on one row.\n *\n * **Call at the function-body level** (outside any `step.run()` callback), or\n * pass an explicit `runId`, so the write is run-scoped and attaches to the\n * experiment. Calling inside `step.run()` without `runId` produces a\n * step-scoped write the experiment detail backend never surfaces.\n */\n experiment(options: ScoreExperimentOptions): Promise<void>;\n}\n\nexport type ScoreStepTool = (\n memoizationId: string,\n options: ScoreOptions,\n) => Promise<void>;\n\nexport const scoreSymbol = Symbol.for(\"inngest.step.score\");\n\nfunction validateIdField({\n value,\n field,\n required,\n}: {\n value: unknown;\n field: string;\n required: boolean;\n}): void {\n if (!required && value === undefined) {\n return;\n }\n\n const isValidString = typeof value === \"string\" && value.trim().length > 0;\n if (!isValidString) {\n throw new Error(`${field} must be a non-empty string`);\n }\n}\n\nfunction validateScoreFields(\n options: unknown,\n requiredTargetIds: readonly (\"runId\" | \"stepId\")[],\n): asserts options is {\n runId?: unknown;\n stepId?: unknown;\n name: unknown;\n value: unknown;\n} {\n if (!isRecord(options)) {\n throw new Error(\"score options must be an object\");\n }\n\n for (const field of [\"runId\", \"stepId\"] as const) {\n validateIdField({\n value: options[field],\n field,\n required: requiredTargetIds.includes(field),\n });\n }\n\n if (typeof options.name !== \"string\" || options.name.trim().length === 0) {\n throw new Error(\"score name must be a non-empty string\");\n }\n\n // Single quote rejection mirrors the cloud MetricKeyRegex; without it,\n // valid-looking score names like \"it's-broken\" would silently drop in\n // variant aggregation.\n // biome-ignore lint/suspicious/noControlCharactersInRegex: intentional — rejecting control chars and single quotes in user-supplied names\n if (/[\\x00-\\x1f\\x7f']/.test(options.name)) {\n throw new Error(\n \"score name must not contain control characters or single quotes\",\n );\n }\n\n const nameByteLength = new TextEncoder().encode(options.name).length;\n if (nameByteLength > maxScoreNameByteLength) {\n throw new Error(\n `score name must be ${maxScoreNameByteLength} bytes or fewer in UTF-8 (got ${nameByteLength})`,\n );\n }\n\n if (typeof options.value !== \"boolean\" && !isFiniteNumber(options.value)) {\n throw new Error(\"score value must be a finite number or boolean\");\n }\n}\n\nfunction validateSendScoreOptions(\n options: unknown,\n): asserts options is ScoreOptions {\n validateScoreFields(options, []);\n}\n\nexport function validateStepScoreOptions(\n options: unknown,\n): asserts options is ScoreOptions {\n validateScoreFields(options, []);\n}\n\nexport async function sendScore(\n client: Inngest,\n options: ScoreOptions,\n): Promise<void> {\n validateSendScoreOptions(options);\n\n await performOp(\n client,\n {\n runId: options.runId,\n stepId: options.stepId,\n },\n { [options.name]: { value: options.value } },\n `${scoreKind}`,\n \"merge\",\n );\n}\n\nexport async function sendStepScore(\n client: Inngest,\n options: ScoreOptions,\n): Promise<void> {\n validateStepScoreOptions(options);\n\n await performOp(\n client,\n {\n // Omitted stepId means run scope and null keeps current-run lookup intact.\n runId:\n options.stepId === undefined ? (options.runId ?? null) : options.runId,\n stepId: options.stepId,\n },\n { [options.name]: { value: options.value } },\n `${scoreKind}`,\n \"merge\",\n );\n}\n\nfunction validateExperimentRef(\n experiment: unknown,\n): asserts experiment is ExperimentRef {\n if (!isRecord(experiment)) {\n throw new Error(\"experiment must be an object\");\n }\n for (const field of [\"experimentName\", \"variant\"] as const) {\n if (\n typeof experiment[field] !== \"string\" ||\n (experiment[field] as string).trim().length === 0\n ) {\n throw new Error(`experiment.${field} must be a non-empty string`);\n }\n }\n}\n\nexport async function sendScoreExperiment(\n client: Inngest,\n options: ScoreExperimentOptions,\n): Promise<void> {\n validateSendScoreOptions(options);\n validateExperimentRef(options.experiment);\n\n const target = { runId: options.runId, stepId: options.stepId };\n\n // Write the experiment attribution first, then the score. These are two\n // non-atomic metadata writes; if the second fails, attribution-without-score\n // is the benign state the system already produces (it's exactly what\n // `group.experiment()` leaves after selecting a variant but before scoring).\n // Writing the score first would instead risk a bare, unattributed score that\n // never surfaces in the experiment view.\n await performOp(\n client,\n target,\n {\n name: options.experiment.experimentName,\n variant: options.experiment.variant,\n } satisfies Pick<ExperimentMetadataValues, \"name\" | \"variant\">,\n experimentKind,\n \"merge\",\n );\n await performOp(\n client,\n target,\n { [options.name]: { value: options.value } },\n scoreKind,\n \"merge\",\n );\n}\n\nexport const scoreMiddleware = () => {\n class ScoreMiddleware extends Middleware.BaseMiddleware {\n readonly id = \"inngest:score\";\n\n static override onRegister({ client }: Middleware.OnRegisterArgs) {\n client[\"experimentalScoreEnabled\"] = true;\n }\n\n override transformFunctionInput(\n arg: Middleware.TransformFunctionInputArgs,\n ): Middleware.TransformFunctionInputArgs & {\n ctx: {\n step: {\n /**\n * Create a durable score update wrapped in a step.\n * Omit `stepId` to attach the score to the run.\n * Use `inngest.score()` for live score writes inside `step.run()`.\n *\n * @param memoizationId - The durable step ID used to memoize this score write.\n */\n score: ExperimentalStepTools[typeof scoreSymbol];\n };\n };\n } {\n return {\n ...arg,\n ctx: {\n ...arg.ctx,\n step: {\n ...arg.ctx.step,\n score: (arg.ctx.step as unknown as ExperimentalStepTools)[\n scoreSymbol\n ],\n },\n },\n };\n }\n }\n\n return ScoreMiddleware;\n};\n"],"mappings":"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"}
|
|
@@ -916,7 +916,7 @@ var InngestExecutionEngine = class extends require_InngestExecution.InngestExecu
|
|
|
916
916
|
userlandId: userland.id
|
|
917
917
|
};
|
|
918
918
|
this.devDebug(`executing step "${id}"`);
|
|
919
|
-
if (this.rootSpanId
|
|
919
|
+
if (this.rootSpanId) require_access.clientProcessorMap.get(this.options.client)?.declareStepExecution(this.rootSpanId, userland.id ?? "", userland.index ?? 0, hashedId, this.options.data?.attempt ?? 0);
|
|
920
920
|
this.state.executingStepAIMetadata = void 0;
|
|
921
921
|
let interval;
|
|
922
922
|
const actualHandler = () => require_promises.runAsPromise(fn);
|
|
@@ -933,7 +933,7 @@ var InngestExecutionEngine = class extends require_InngestExecution.InngestExecu
|
|
|
933
933
|
return require_promises.goIntervalTiming(() => wrappedActualHandler()).finally(() => {
|
|
934
934
|
this.devDebug(`finished executing step "${id}"`);
|
|
935
935
|
this.state.executingStep = void 0;
|
|
936
|
-
if (this.rootSpanId
|
|
936
|
+
if (this.rootSpanId) require_access.clientProcessorMap.get(this.options.client)?.clearStepExecution(this.rootSpanId);
|
|
937
937
|
const aiMetadata = this.state.executingStepAIMetadata;
|
|
938
938
|
this.state.executingStepAIMetadata = void 0;
|
|
939
939
|
const aiValues = aiMetadata && require_aiExtractor.toInngestAIMetadataValues(aiMetadata);
|