@mux/ai 0.8.0 → 0.8.2

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.
@@ -394,8 +394,44 @@ var DEFAULT_LANGUAGE_MODELS = {
394
394
  var LANGUAGE_MODELS = {
395
395
  openai: ["gpt-5.1", "gpt-5-mini"],
396
396
  anthropic: ["claude-sonnet-4-5"],
397
- google: ["gemini-3-flash-preview", "gemini-2.5-flash"]
397
+ google: ["gemini-3-flash-preview", "gemini-3.1-flash-lite-preview", "gemini-2.5-flash"]
398
398
  };
399
+ var LANGUAGE_MODEL_DEPRECATIONS = [
400
+ {
401
+ provider: "google",
402
+ modelId: "gemini-2.5-flash",
403
+ replacementModelId: "gemini-3.1-flash-lite-preview",
404
+ phase: "warn",
405
+ deprecatedOn: "2026-03-03",
406
+ sunsetOn: "2026-06-30",
407
+ reason: "Gemini 3.1 Flash-Lite Preview offers better quality/latency/cost balance in current evals."
408
+ }
409
+ ];
410
+ var warnedDeprecatedLanguageModels = /* @__PURE__ */ new Set();
411
+ function getLanguageModelDeprecation(provider, modelId) {
412
+ return LANGUAGE_MODEL_DEPRECATIONS.find(
413
+ (deprecation) => deprecation.provider === provider && deprecation.modelId === modelId
414
+ );
415
+ }
416
+ function maybeWarnOrThrowForDeprecatedLanguageModel(provider, modelId) {
417
+ const deprecation = getLanguageModelDeprecation(provider, modelId);
418
+ if (!deprecation) {
419
+ return;
420
+ }
421
+ const replacementText = deprecation.replacementModelId ? ` Use replacement provider="${provider}" model="${deprecation.replacementModelId}" instead.` : "";
422
+ const sunsetText = deprecation.sunsetOn ? ` Planned removal date: ${deprecation.sunsetOn}.` : "";
423
+ const reasonText = deprecation.reason ? ` Reason: ${deprecation.reason}` : "";
424
+ const message = deprecation.phase === "blocked" ? `Language model is no longer supported for provider="${provider}" model="${modelId}".${replacementText}${reasonText}` : `Language model is deprecated and in a grace period for provider="${provider}" model="${modelId}".${replacementText}${sunsetText}${reasonText}`;
425
+ if (deprecation.phase === "blocked") {
426
+ throw new Error(message);
427
+ }
428
+ const warningKey = `${provider}:${modelId}`;
429
+ if (warnedDeprecatedLanguageModels.has(warningKey)) {
430
+ return;
431
+ }
432
+ warnedDeprecatedLanguageModels.add(warningKey);
433
+ console.warn(message);
434
+ }
399
435
  function getDefaultEvalModelConfigs() {
400
436
  return Object.entries(DEFAULT_LANGUAGE_MODELS).map(([provider, modelId]) => ({ provider, modelId }));
401
437
  }
@@ -426,6 +462,7 @@ function parseEvalModelPair(value) {
426
462
  `Unsupported eval model "${modelId}" for provider "${provider}". Supported models: ${supportedModels.join(", ")}.`
427
463
  );
428
464
  }
465
+ maybeWarnOrThrowForDeprecatedLanguageModel(provider, modelId);
429
466
  return {
430
467
  provider,
431
468
  modelId
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/lib/providers.ts","../../src/env.ts","../../src/lib/workflow-crypto.ts","../../src/lib/workflow-credentials.ts","../../src/lib/client-factory.ts","../../src/primitives/heatmap.ts","../../src/primitives/hotspots.ts","../../src/lib/mux-image-url.ts","../../src/lib/url-signing.ts","../../src/primitives/storyboards.ts","../../src/primitives/text-chunking.ts","../../src/primitives/thumbnails.ts","../../src/primitives/transcripts.ts"],"sourcesContent":["import { createAnthropic } from \"@ai-sdk/anthropic\";\nimport { createGoogleGenerativeAI } from \"@ai-sdk/google\";\nimport { createOpenAI } from \"@ai-sdk/openai\";\n\nimport type { Env } from \"@mux/ai/env\";\nimport env from \"@mux/ai/env\";\nimport { resolveProviderApiKey } from \"@mux/ai/lib/workflow-credentials\";\nimport type { MuxAIOptions, WorkflowCredentialsInput } from \"@mux/ai/types\";\n\nimport type { EmbeddingModel, LanguageModel } from \"ai\";\n\nexport type SupportedProvider = \"openai\" | \"anthropic\" | \"google\";\nexport type SupportedEmbeddingProvider = \"openai\" | \"google\";\n\n// Model ID unions inferred from ai-sdk provider call signatures\ntype OpenAIModelId = Parameters<ReturnType<typeof createOpenAI>[\"chat\"]>[0];\ntype AnthropicModelId = Parameters<ReturnType<typeof createAnthropic>[\"chat\"]>[0];\ntype GoogleModelId = Parameters<ReturnType<typeof createGoogleGenerativeAI>[\"chat\"]>[0];\n\ntype OpenAIEmbeddingModelId = Parameters<ReturnType<typeof createOpenAI>[\"embedding\"]>[0];\ntype GoogleEmbeddingModelId = Parameters<ReturnType<typeof createGoogleGenerativeAI>[\"textEmbeddingModel\"]>[0];\n\nexport interface ModelIdByProvider {\n openai: OpenAIModelId;\n anthropic: AnthropicModelId;\n google: GoogleModelId;\n}\n\nexport interface EmbeddingModelIdByProvider {\n openai: OpenAIEmbeddingModelId;\n google: GoogleEmbeddingModelId;\n}\n\nexport interface ModelRequestOptions<P extends SupportedProvider = SupportedProvider> extends MuxAIOptions {\n provider?: P;\n model?: ModelIdByProvider[P];\n}\n\nexport interface ResolvedModel<P extends SupportedProvider = SupportedProvider> {\n provider: P;\n modelId: ModelIdByProvider[P];\n model: LanguageModel;\n}\n\nexport const DEFAULT_LANGUAGE_MODELS: { [K in SupportedProvider]: ModelIdByProvider[K] } = {\n openai: \"gpt-5.1\",\n anthropic: \"claude-sonnet-4-5\",\n google: \"gemini-3-flash-preview\",\n};\n\nconst DEFAULT_EMBEDDING_MODELS: { [K in SupportedEmbeddingProvider]: EmbeddingModelIdByProvider[K] } = {\n openai: \"text-embedding-3-small\",\n google: \"gemini-embedding-001\",\n};\n\n/**\n * All language models available per provider.\n * Includes the default model plus any additional models for evaluation and selection.\n * New models are additive — existing defaults are unchanged.\n */\nexport const LANGUAGE_MODELS: { [K in SupportedProvider]: ModelIdByProvider[K][] } = {\n openai: [\"gpt-5.1\", \"gpt-5-mini\"],\n anthropic: [\"claude-sonnet-4-5\"],\n google: [\"gemini-3-flash-preview\", \"gemini-2.5-flash\"],\n};\n\n/**\n * A (provider, modelId) pair used for evaluation iteration.\n */\nexport interface EvalModelConfig {\n provider: SupportedProvider;\n modelId: ModelIdByProvider[SupportedProvider];\n}\n\nexport type EvalModelSelection = \"default\" | \"all\";\n\nexport interface ResolveEvalModelConfigsOptions {\n selection?: EvalModelSelection;\n modelPairs?: string[];\n}\n\nfunction getDefaultEvalModelConfigs(): EvalModelConfig[] {\n return (Object.entries(DEFAULT_LANGUAGE_MODELS) as [SupportedProvider, ModelIdByProvider[SupportedProvider]][])\n .map(([provider, modelId]) => ({ provider, modelId }));\n}\n\nfunction getAllEvalModelConfigs(): EvalModelConfig[] {\n return (Object.entries(LANGUAGE_MODELS) as [SupportedProvider, ModelIdByProvider[SupportedProvider][]][])\n .flatMap(([provider, models]) => models.map(modelId => ({ provider, modelId })));\n}\n\nfunction isSupportedProvider(value: string): value is SupportedProvider {\n return value === \"openai\" || value === \"anthropic\" || value === \"google\";\n}\n\nfunction parseEvalModelPair(value: string): EvalModelConfig {\n const trimmed = value.trim();\n const [providerRaw, modelIdRaw] = trimmed.split(\":\", 2);\n const provider = providerRaw?.trim();\n const modelId = modelIdRaw?.trim();\n\n if (!provider || !modelId) {\n throw new Error(\n `Invalid eval model pair \"${value}\". Use \"provider:model\" (example: \"openai:gpt-5.1\").`,\n );\n }\n\n if (!isSupportedProvider(provider)) {\n throw new Error(\n `Unsupported eval provider \"${provider}\" in \"${value}\". Supported providers: ${Object.keys(LANGUAGE_MODELS).join(\", \")}.`,\n );\n }\n\n const supportedModels = LANGUAGE_MODELS[provider] as string[];\n if (!supportedModels.includes(modelId)) {\n throw new Error(\n `Unsupported eval model \"${modelId}\" for provider \"${provider}\". Supported models: ${supportedModels.join(\", \")}.`,\n );\n }\n\n return {\n provider,\n modelId: modelId as ModelIdByProvider[SupportedProvider],\n };\n}\n\n/**\n * Resolves eval model configurations.\n *\n * Selection order:\n * 1) Explicit model pairs (provider:model)\n * 2) Selection mode (\"default\" | \"all\")\n * 3) Default mode (\"default\")\n */\nexport function resolveEvalModelConfigs(options: ResolveEvalModelConfigsOptions = {}): EvalModelConfig[] {\n const explicitPairs = options.modelPairs?.map(value => value.trim()).filter(Boolean) ?? [];\n if (explicitPairs.length > 0) {\n const dedupedPairs = Array.from(new Set(explicitPairs));\n return dedupedPairs.map(parseEvalModelPair);\n }\n\n const selection = options.selection ?? \"default\";\n if (selection === \"all\") {\n return getAllEvalModelConfigs();\n }\n\n return getDefaultEvalModelConfigs();\n}\n\n/**\n * Environment variables for selecting eval models at runtime.\n *\n * - MUX_AI_EVAL_MODEL_SET: \"default\" | \"all\" (default: \"default\")\n * - MUX_AI_EVAL_MODELS: comma-separated \"provider:model\" pairs\n * (takes precedence over MUX_AI_EVAL_MODEL_SET)\n */\nexport function resolveEvalModelConfigsFromEnv(environment: Env = env): EvalModelConfig[] {\n const rawSelection = environment.MUX_AI_EVAL_MODEL_SET?.trim();\n const rawModelPairs = environment.MUX_AI_EVAL_MODELS?.trim();\n let selection: EvalModelSelection;\n if (!rawSelection || rawSelection === \"default\") {\n selection = \"default\";\n } else if (rawSelection === \"all\") {\n selection = \"all\";\n } else {\n throw new Error(\n `Invalid MUX_AI_EVAL_MODEL_SET=\"${rawSelection}\". Expected \"default\" or \"all\".`,\n );\n }\n\n let modelPairs: string[] | undefined;\n if (rawModelPairs) {\n modelPairs = rawModelPairs.split(\",\").map(value => value.trim()).filter(Boolean);\n }\n\n return resolveEvalModelConfigs({\n selection,\n modelPairs,\n });\n}\n\n/**\n * Flattened list of (provider, modelId) pairs for evaluation iteration.\n * Resolved from env so eval runs can target default models, all models, or an explicit list.\n */\nexport const EVAL_MODEL_CONFIGS: EvalModelConfig[] = resolveEvalModelConfigsFromEnv();\n\nexport function resolveLanguageModelConfig<P extends SupportedProvider = SupportedProvider>(\n options: ModelRequestOptions<P> = {},\n): { provider: P; modelId: ModelIdByProvider[P] } {\n const provider = options.provider || (\"openai\" as P);\n const modelId = (options.model || DEFAULT_LANGUAGE_MODELS[provider]) as ModelIdByProvider[P];\n\n return { provider, modelId };\n}\n\nexport function resolveEmbeddingModelConfig<P extends SupportedEmbeddingProvider = \"openai\">(\n options: MuxAIOptions & { provider?: P; model?: EmbeddingModelIdByProvider[P] } = {},\n): { provider: P; modelId: EmbeddingModelIdByProvider[P] } {\n const provider = options.provider || (\"openai\" as P);\n const modelId = (options.model || DEFAULT_EMBEDDING_MODELS[provider]) as EmbeddingModelIdByProvider[P];\n\n return { provider, modelId };\n}\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Model Pricing\n// ─────────────────────────────────────────────────────────────────────────────\n//\n// Pricing is in USD per million tokens. These values are used for cost estimation\n// in evaluations and should be periodically verified against official sources.\n//\n// Sources (verified on 2026-02-17):\n// - OpenAI: https://openai.com/api/pricing\n// - Anthropic: https://www.anthropic.com/pricing\n// - Google: https://ai.google.dev/gemini-api/docs/pricing\n// ─────────────────────────────────────────────────────────────────────────────\n\n/**\n * Pricing structure for a language model.\n * All costs are in USD per million tokens.\n */\nexport interface ModelPricing {\n /** Cost per million input tokens (USD). */\n inputPerMillion: number;\n /** Cost per million output tokens (USD). */\n outputPerMillion: number;\n /** Cost per million cached input tokens (USD), if supported. */\n cachedInputPerMillion?: number;\n /** URL to the official pricing page for verification. */\n pricingUrl: string;\n}\n\n/**\n * Per-model pricing data for all supported language models.\n * Used for model-specific cost estimation in evaluations and expense tracking.\n *\n * @remarks\n * Prices are subject to change. Verify against official sources before production use.\n * When adding a new model to LANGUAGE_MODELS, add its pricing here as well.\n */\nexport const MODEL_PRICING: Record<string, ModelPricing> = {\n // OpenAI models\n // Reference: https://openai.com/api/pricing\n \"gpt-5.1\": {\n inputPerMillion: 1.25,\n outputPerMillion: 10.00,\n cachedInputPerMillion: 0.125,\n pricingUrl: \"https://openai.com/api/pricing\",\n },\n \"gpt-5-mini\": {\n inputPerMillion: 0.25,\n outputPerMillion: 2.00,\n cachedInputPerMillion: 0.025,\n pricingUrl: \"https://openai.com/api/pricing\",\n },\n\n // Anthropic models\n // Reference: https://www.anthropic.com/pricing\n \"claude-sonnet-4-5\": {\n inputPerMillion: 3.00,\n outputPerMillion: 15.00,\n cachedInputPerMillion: 0.30,\n pricingUrl: \"https://www.anthropic.com/pricing\",\n },\n\n // Google models\n // Reference: https://ai.google.dev/pricing\n \"gemini-3-flash-preview\": {\n inputPerMillion: 0.50,\n outputPerMillion: 3.00,\n cachedInputPerMillion: 0.05,\n pricingUrl: \"https://ai.google.dev/gemini-api/docs/pricing\",\n },\n \"gemini-2.5-flash\": {\n inputPerMillion: 0.30,\n outputPerMillion: 2.50,\n cachedInputPerMillion: 0.03,\n pricingUrl: \"https://ai.google.dev/gemini-api/docs/pricing\",\n },\n};\n\n/**\n * Calculates the estimated cost for a request based on token usage and model-specific pricing.\n *\n * @param modelId - The specific model ID used\n * @param inputTokens - Number of input tokens consumed\n * @param outputTokens - Number of output tokens generated\n * @param cachedInputTokens - Number of input tokens served from cache (optional)\n * @returns Estimated cost in USD\n *\n * @example\n * ```typescript\n * const cost = calculateModelCost('gpt-5-mini', 2000, 500);\n * console.log(`Estimated cost: $${cost.toFixed(6)}`);\n * ```\n */\nexport function calculateModelCost(\n modelId: string,\n inputTokens: number,\n outputTokens: number,\n cachedInputTokens: number = 0,\n): number {\n const pricing = MODEL_PRICING[modelId];\n if (!pricing) {\n throw new Error(`No pricing data for model: ${modelId}. Add pricing to MODEL_PRICING in providers.ts.`);\n }\n\n const uncachedInputTokens = Math.max(0, inputTokens - cachedInputTokens);\n\n const inputCost = (uncachedInputTokens / 1_000_000) * pricing.inputPerMillion;\n const outputCost = (outputTokens / 1_000_000) * pricing.outputPerMillion;\n let cachedCost = 0;\n if (pricing.cachedInputPerMillion) {\n cachedCost = (cachedInputTokens / 1_000_000) * pricing.cachedInputPerMillion;\n }\n\n return inputCost + outputCost + cachedCost;\n}\n\n/**\n * Calculates the estimated cost for a request based on token usage.\n * Uses each provider's default model pricing from MODEL_PRICING.\n *\n * @param provider - The AI provider used\n * @param inputTokens - Number of input tokens consumed\n * @param outputTokens - Number of output tokens generated\n * @param cachedInputTokens - Number of input tokens served from cache (optional)\n * @returns Estimated cost in USD\n *\n * @example\n * ```typescript\n * const cost = calculateCost('openai', 2000, 500);\n * console.log(`Estimated cost: $${cost.toFixed(6)}`);\n * ```\n */\nexport function calculateCost(\n provider: SupportedProvider,\n inputTokens: number,\n outputTokens: number,\n cachedInputTokens: number = 0,\n): number {\n const defaultModelId = DEFAULT_LANGUAGE_MODELS[provider];\n return calculateModelCost(defaultModelId, inputTokens, outputTokens, cachedInputTokens);\n}\n\nfunction requireEnv(value: string | undefined, name: string): string {\n if (!value) {\n throw new Error(`Missing ${name}. Set ${name} in your environment or pass it in options.`);\n }\n return value;\n}\n\n/**\n * Creates a language model instance from serializable config.\n * Use this in steps to instantiate models from config passed through workflow.\n * Fetches credentials internally from environment variables to avoid exposing them in step I/O.\n */\nexport async function createLanguageModelFromConfig<P extends SupportedProvider = SupportedProvider>(\n provider: P,\n modelId: ModelIdByProvider[P],\n credentials?: WorkflowCredentialsInput,\n): Promise<LanguageModel> {\n switch (provider) {\n case \"openai\": {\n const apiKey = await resolveProviderApiKey(\"openai\", credentials);\n const openai = createOpenAI({ apiKey });\n return openai(modelId);\n }\n case \"anthropic\": {\n const apiKey = await resolveProviderApiKey(\"anthropic\", credentials);\n const anthropic = createAnthropic({ apiKey });\n return anthropic(modelId);\n }\n case \"google\": {\n const apiKey = await resolveProviderApiKey(\"google\", credentials);\n const google = createGoogleGenerativeAI({ apiKey });\n return google(modelId);\n }\n default: {\n const exhaustiveCheck: never = provider;\n throw new Error(`Unsupported provider: ${exhaustiveCheck}`);\n }\n }\n}\n\n/**\n * Creates an embedding model instance from serializable config.\n * Use this in steps to instantiate embedding models from config passed through workflow.\n * Fetches credentials internally from environment variables to avoid exposing them in step I/O.\n */\nexport async function createEmbeddingModelFromConfig<\n P extends SupportedEmbeddingProvider = SupportedEmbeddingProvider,\n>(\n provider: P,\n modelId: EmbeddingModelIdByProvider[P],\n credentials?: WorkflowCredentialsInput,\n): Promise<EmbeddingModel> {\n switch (provider) {\n case \"openai\": {\n const apiKey = await resolveProviderApiKey(\"openai\", credentials);\n const openai = createOpenAI({ apiKey });\n return openai.embedding(modelId);\n }\n case \"google\": {\n const apiKey = await resolveProviderApiKey(\"google\", credentials);\n const google = createGoogleGenerativeAI({ apiKey });\n return google.textEmbeddingModel(modelId);\n }\n default: {\n const exhaustiveCheck: never = provider;\n throw new Error(`Unsupported embedding provider: ${exhaustiveCheck}`);\n }\n }\n}\n\n/**\n * Resolves a language model from a suggested provider.\n */\nexport function resolveLanguageModel<P extends SupportedProvider = SupportedProvider>(\n options: ModelRequestOptions<P> = {},\n): ResolvedModel<P> {\n const provider = options.provider || (\"openai\" as P);\n const modelId = (options.model || DEFAULT_LANGUAGE_MODELS[provider]) as ModelIdByProvider[P];\n\n switch (provider) {\n case \"openai\": {\n const apiKey = env.OPENAI_API_KEY;\n requireEnv(apiKey, \"OPENAI_API_KEY\");\n const openai = createOpenAI({\n apiKey,\n });\n\n return {\n provider,\n modelId,\n model: openai(modelId),\n };\n }\n case \"anthropic\": {\n const apiKey = env.ANTHROPIC_API_KEY;\n requireEnv(apiKey, \"ANTHROPIC_API_KEY\");\n const anthropic = createAnthropic({\n apiKey,\n });\n\n return {\n provider,\n modelId,\n model: anthropic(modelId),\n };\n }\n case \"google\": {\n const apiKey = env.GOOGLE_GENERATIVE_AI_API_KEY;\n requireEnv(apiKey, \"GOOGLE_GENERATIVE_AI_API_KEY\");\n const google = createGoogleGenerativeAI({\n apiKey,\n });\n\n return {\n provider,\n modelId,\n model: google(modelId),\n };\n }\n default: {\n const exhaustiveCheck: never = provider;\n throw new Error(`Unsupported provider: ${exhaustiveCheck}`);\n }\n }\n}\n\n/**\n * Resolves an embedding model from a suggested provider.\n */\nexport function resolveEmbeddingModel<P extends SupportedEmbeddingProvider = \"openai\">(\n options: MuxAIOptions & { provider?: P; model?: EmbeddingModelIdByProvider[P] } = {},\n): { provider: P; modelId: EmbeddingModelIdByProvider[P]; model: EmbeddingModel } {\n const provider = options.provider || (\"openai\" as P);\n const modelId = (options.model || DEFAULT_EMBEDDING_MODELS[provider]) as EmbeddingModelIdByProvider[P];\n\n switch (provider) {\n case \"openai\": {\n const apiKey = env.OPENAI_API_KEY;\n requireEnv(apiKey, \"OPENAI_API_KEY\");\n const openai = createOpenAI({\n apiKey,\n });\n\n return {\n provider,\n modelId,\n model: openai.embedding(modelId),\n };\n }\n case \"google\": {\n const apiKey = env.GOOGLE_GENERATIVE_AI_API_KEY;\n requireEnv(apiKey, \"GOOGLE_GENERATIVE_AI_API_KEY\");\n const google = createGoogleGenerativeAI({\n apiKey,\n });\n\n return {\n provider,\n modelId,\n model: google.textEmbeddingModel(modelId),\n };\n }\n default: {\n const exhaustiveCheck: never = provider;\n throw new Error(`Unsupported embedding provider: ${exhaustiveCheck}`);\n }\n }\n}\n","/* eslint-disable node/no-process-env */\n\nimport { z } from \"zod\";\n\nimport \"dotenv/config\";\n\nfunction optionalString(description: string, message?: string) {\n return z.preprocess(\n value => typeof value === \"string\" && value.trim().length === 0 ? undefined : value,\n z.string().trim().min(1, message).optional(),\n ).describe(description);\n}\n\n// eslint-disable-next-line unused-imports/no-unused-vars\nfunction requiredString(description: string, message?: string) {\n return z.preprocess(\n value => typeof value === \"string\" ? value.trim().length > 0 ? value.trim() : undefined : value,\n z.string().trim().min(1, message),\n ).describe(description);\n}\n\nconst EnvSchema = z.object({\n NODE_ENV: z.string().default(\"development\").describe(\"Runtime environment.\"),\n\n MUX_TOKEN_ID: optionalString(\"Mux access token ID.\", \"Required to access Mux APIs\"),\n MUX_TOKEN_SECRET: optionalString(\"Mux access token secret.\", \"Required to access Mux APIs\"),\n MUX_AI_WORKFLOW_SECRET_KEY: optionalString(\n \"Base64-encoded 32-byte key for workflow encryption/decryption.\",\n \"Workflow secret key\",\n ),\n EVALITE_INGEST_SECRET: optionalString(\n \"Shared secret for posting Evalite results.\",\n \"Evalite ingest secret\",\n ),\n\n MUX_SIGNING_KEY: optionalString(\"Mux signing key ID for signed playback URLs.\", \"Used to sign playback URLs\"),\n MUX_PRIVATE_KEY: optionalString(\"Mux signing private key for signed playback URLs.\", \"Used to sign playback URLs\"),\n MUX_IMAGE_URL_OVERRIDE: optionalString(\n \"Override for Mux image base URL (defaults to https://image.mux.com).\",\n \"Mux image URL override\",\n ),\n\n // Test-only helpers (used by this repo's integration tests)\n MUX_TEST_ASSET_ID: optionalString(\"Mux asset ID used by integration tests.\", \"Mux test asset id\"),\n MUX_TEST_ASSET_ID_CHAPTERS: optionalString(\"Mux asset ID used by integration tests for chapters.\", \"Mux test asset id for chapters\"),\n MUX_TEST_ASSET_ID_VIOLENT: optionalString(\"Mux violent asset ID used by integration tests.\", \"Mux violent test asset id\"),\n MUX_TEST_ASSET_ID_BURNED_IN_CAPTIONS: optionalString(\n \"Mux burned-in captions asset ID used by integration tests.\",\n \"Mux burned-in captions test asset id\",\n ),\n MUX_TEST_ASSET_ID_BURNED_IN_CAPTIONS_2: optionalString(\n \"Mux burned-in captions asset ID 2 (a different asset) used by integration tests.\",\n \"Mux burned-in captions test asset id 2 (a different asset)\",\n ),\n MUX_TEST_ASSET_ID_WITHOUT_BURNED_IN_CAPTIONS: optionalString(\n \"Mux without burned-in captions asset ID used by integration tests.\",\n \"Mux without burned-in captions test asset id\",\n ),\n MUX_TEST_ASSET_ID_AUDIO_ONLY: optionalString(\"Mux test asset ID for audio-only assets.\", \"Mux test asset id for audio-only assets for testing\"),\n MUX_TEST_ASSET_ID_VIOLENT_AUDIO_ONLY: optionalString(\"Mux test asset ID for audio-only assets with violent content.\", \"Mux test asset id for audio-only assets with violent content for testing\"),\n\n // Eval config\n MUX_AI_EVAL_MODEL_SET: optionalString(\"Eval model selection mode.\", \"Choose between 'default' (provider defaults only) or 'all' (all configured models)\"),\n MUX_AI_EVAL_MODELS: optionalString(\"Comma-separated eval model pairs.\", \"Comma-separated provider:model pairs (e.g. 'openai:gpt-5.1,anthropic:claude-sonnet-4-5,google:gemini-3-flash-preview')\"),\n\n // AI Providers\n OPENAI_API_KEY: optionalString(\"OpenAI API key for OpenAI-backed workflows.\", \"OpenAI API key\"),\n ANTHROPIC_API_KEY: optionalString(\"Anthropic API key for Claude-backed workflows.\", \"Anthropic API key\"),\n GOOGLE_GENERATIVE_AI_API_KEY: optionalString(\"Google Generative AI API key for Gemini-backed workflows.\", \"Google Generative AI API key\"),\n\n ELEVENLABS_API_KEY: optionalString(\"ElevenLabs API key for audio translation.\", \"ElevenLabs API key\"),\n HIVE_API_KEY: optionalString(\"Hive Visual Moderation API key.\", \"Hive API key\"),\n\n // S3-Compatible Storage (required for translation & audio dubbing)\n S3_ENDPOINT: optionalString(\"S3-compatible endpoint for uploads.\", \"S3 endpoint\"),\n S3_REGION: optionalString(\"S3 region (defaults to 'auto' when omitted).\"),\n S3_BUCKET: optionalString(\"Bucket used for caption and audio uploads.\", \"S3 bucket\"),\n S3_ACCESS_KEY_ID: optionalString(\"Access key ID for S3-compatible uploads.\", \"S3 access key id\"),\n S3_SECRET_ACCESS_KEY: optionalString(\"Secret access key for S3-compatible uploads.\", \"S3 secret access key\"),\n S3_ALLOWED_ENDPOINT_HOSTS: optionalString(\n \"Comma-separated S3 endpoint allowlist (supports exact hosts and *.suffix patterns).\",\n ),\n\n EVALITE_RESULTS_ENDPOINT: optionalString(\n \"Full URL for posting Evalite results (e.g., https://example.com/api/evalite-results).\",\n \"Evalite results endpoint\",\n ),\n}).refine(\n (env) => {\n const hasMuxCredentials = Boolean(env.MUX_TOKEN_ID && env.MUX_TOKEN_SECRET);\n const hasWorkflowKey = Boolean(env.MUX_AI_WORKFLOW_SECRET_KEY);\n return hasMuxCredentials || hasWorkflowKey;\n },\n {\n message: \"Either MUX_TOKEN_ID + MUX_TOKEN_SECRET or MUX_AI_WORKFLOW_SECRET_KEY must be set.\",\n },\n);\n\nexport type Env = z.infer<typeof EnvSchema>;\n\nfunction parseEnv(): Env {\n const parsedEnv = EnvSchema.safeParse(process.env);\n\n if (!parsedEnv.success) {\n console.error(\"❌ Invalid env:\");\n console.error(JSON.stringify(parsedEnv.error.flatten().fieldErrors, null, 2));\n process.exit(1);\n }\n\n return parsedEnv.data;\n}\n\nconst env: Env = parseEnv();\n\nexport function reloadEnv(): Env {\n const parsed = parseEnv();\n Object.assign(env, parsed);\n return env;\n}\n\nexport { env };\nexport default env;\n","/**\n * Workflow Crypto\n *\n * Provides AES-256-GCM encryption for securely passing credentials to workflows.\n * Encrypted payloads are JSON-serializable and include version/algorithm metadata\n * for forward compatibility.\n */\n\nimport { gcm } from \"@noble/ciphers/aes.js\";\n\nconst BASE64_CHUNK_SIZE = 0x8000;\nconst BASE64_ALPHABET = \"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/\";\nconst BASE64_LOOKUP = (() => {\n const table = new Uint8Array(256);\n table.fill(255);\n for (let i = 0; i < BASE64_ALPHABET.length; i++) {\n table[BASE64_ALPHABET.charCodeAt(i)] = i;\n }\n return table;\n})();\n\n// Encryption parameters (AES-256-GCM with standard IV/tag sizes)\nconst WORKFLOW_ENCRYPTION_VERSION = 1;\nconst WORKFLOW_ENCRYPTION_ALGORITHM = \"aes-256-gcm\" as const;\nconst IV_LENGTH_BYTES = 12; // 96-bit IV per NIST recommendation for GCM\nconst AUTH_TAG_LENGTH_BYTES = 16; // 128-bit authentication tag\n\n/**\n * Structure of an encrypted payload (all fields are base64-encoded where applicable).\n *\n * The optional `kid` (key ID) field supports key rotation scenarios:\n * - Include a `kid` when encrypting to identify which key was used\n * - On decryption, read `payload.kid` to look up the correct key\n * - Keys should be invalidated/deleted after rotation, not kept indefinitely\n *\n * Security notes:\n * - `kid` is stored in plaintext (not encrypted) - don't put sensitive data in it\n * - Tampering with `kid` doesn't weaken security - wrong key = decryption fails\n */\nexport interface EncryptedPayload {\n v: typeof WORKFLOW_ENCRYPTION_VERSION;\n alg: typeof WORKFLOW_ENCRYPTION_ALGORITHM;\n kid?: string;\n iv: string;\n tag: string;\n ciphertext: string;\n}\n\n/** Branded type that preserves the original type information for decryption */\nexport type Encrypted<T> = EncryptedPayload & { __type?: T };\n\nfunction getWebCrypto(): NonNullable<typeof globalThis.crypto> {\n const webCrypto = globalThis.crypto;\n if (!webCrypto || typeof webCrypto.getRandomValues !== \"function\") {\n throw new Error(\"Web Crypto API is required in workflow functions.\");\n }\n return webCrypto;\n}\n\nfunction bytesToBase64(bytes: Uint8Array): string {\n if (bytes.length === 0) {\n return \"\";\n }\n const btoaImpl = typeof globalThis.btoa === \"function\" ? globalThis.btoa.bind(globalThis) : undefined;\n if (btoaImpl) {\n let binary = \"\";\n for (let i = 0; i < bytes.length; i += BASE64_CHUNK_SIZE) {\n const chunk = bytes.subarray(i, i + BASE64_CHUNK_SIZE);\n binary += String.fromCharCode(...chunk);\n }\n return btoaImpl(binary);\n }\n\n let output = \"\";\n for (let i = 0; i < bytes.length; i += 3) {\n const b0 = bytes[i] ?? 0;\n const b1 = bytes[i + 1] ?? 0;\n const b2 = bytes[i + 2] ?? 0;\n const triple = (b0 << 16) | (b1 << 8) | b2;\n output += BASE64_ALPHABET[(triple >> 18) & 63];\n output += BASE64_ALPHABET[(triple >> 12) & 63];\n output += i + 1 < bytes.length ? BASE64_ALPHABET[(triple >> 6) & 63] : \"=\";\n output += i + 2 < bytes.length ? BASE64_ALPHABET[triple & 63] : \"=\";\n }\n return output;\n}\n\nfunction normalizeBase64Input(value: string): string {\n const cleaned = value\n .replace(/\\s+/g, \"\")\n .replace(/-/g, \"+\")\n .replace(/_/g, \"/\");\n\n // Pad with '=' to make the length a multiple of 4 (required by standard base64).\n // When cleaned is empty, length is 0 and 0 % 4 === 0, so it passes through unchanged.\n return cleaned?.length % 4 === 0 ? cleaned : cleaned + \"=\".repeat(4 - (cleaned.length % 4));\n}\n\nfunction base64ToBytes(value: string, label: string): Uint8Array {\n if (!value) {\n throw new Error(`${label} is missing`);\n }\n const normalized = normalizeBase64Input(value);\n const atobImpl = typeof globalThis.atob === \"function\" ? globalThis.atob.bind(globalThis) : undefined;\n if (atobImpl) {\n let binary: string;\n try {\n binary = atobImpl(normalized);\n } catch {\n throw new Error(`${label} is not valid base64`);\n }\n if (!binary) {\n throw new Error(`${label} decoded to empty value`);\n }\n const bytes = new Uint8Array(binary.length);\n for (let i = 0; i < binary.length; i++) {\n bytes[i] = binary.charCodeAt(i);\n }\n return bytes;\n }\n\n if (normalized.length % 4 !== 0) {\n throw new Error(`${label} is not valid base64`);\n }\n\n const padding = normalized.endsWith(\"==\") ? 2 : normalized.endsWith(\"=\") ? 1 : 0;\n const outputLength = (normalized.length / 4) * 3 - padding;\n const bytes = new Uint8Array(outputLength);\n let offset = 0;\n\n for (let i = 0; i < normalized.length; i += 4) {\n const c0 = normalized.charCodeAt(i);\n const c1 = normalized.charCodeAt(i + 1);\n const c2 = normalized.charCodeAt(i + 2);\n const c3 = normalized.charCodeAt(i + 3);\n\n if (c0 === 61 || c1 === 61) {\n throw new Error(`${label} is not valid base64`);\n }\n\n const n0 = BASE64_LOOKUP[c0] ?? 255;\n const n1 = BASE64_LOOKUP[c1] ?? 255;\n const n2 = c2 === 61 ? 0 : (BASE64_LOOKUP[c2] ?? 255);\n const n3 = c3 === 61 ? 0 : (BASE64_LOOKUP[c3] ?? 255);\n\n if (n0 === 255 || n1 === 255 || n2 === 255 || n3 === 255) {\n throw new Error(`${label} is not valid base64`);\n }\n\n const triple = (n0 << 18) | (n1 << 12) | (n2 << 6) | n3;\n bytes[offset++] = (triple >> 16) & 0xFF;\n if (c2 !== 61) {\n bytes[offset++] = (triple >> 8) & 0xFF;\n }\n if (c3 !== 61) {\n bytes[offset++] = triple & 0xFF;\n }\n }\n\n if (!bytes.length) {\n throw new Error(`${label} decoded to empty value`);\n }\n\n return bytes;\n}\n\n/** Wraps base64ToBytes for encrypted payload fields, adding context to errors */\nfunction payloadBase64ToBytes(value: string, field: string): Uint8Array {\n try {\n return base64ToBytes(value, field);\n } catch (error) {\n const detail = error instanceof Error ? error.message : `${field} is invalid`;\n throw new Error(`Invalid encrypted payload: ${detail}`);\n }\n}\n\n/** Converts key to bytes and validates it's exactly 32 bytes (256 bits) */\nfunction normalizeKey(key: Uint8Array | string): Uint8Array {\n let keyBytes: Uint8Array;\n if (typeof key === \"string\") {\n try {\n keyBytes = base64ToBytes(key, \"value\");\n } catch (error) {\n const detail = error instanceof Error ? error.message : \"value is not valid base64\";\n throw new Error(`Invalid workflow secret key: ${detail}. Expected 32-byte base64 value.`);\n }\n } else {\n keyBytes = new Uint8Array(key);\n }\n\n if (keyBytes.length !== 32) {\n throw new Error(`Invalid workflow secret key: expected 32 bytes, got ${keyBytes.length}.`);\n }\n\n return keyBytes;\n}\n\n/** Type guard to check if a value is a valid encrypted payload structure */\nexport function isEncryptedPayload(value: unknown): value is EncryptedPayload {\n if (!value || typeof value !== \"object\") {\n return false;\n }\n\n const payload = value as EncryptedPayload;\n return (\n payload.v === WORKFLOW_ENCRYPTION_VERSION &&\n payload.alg === WORKFLOW_ENCRYPTION_ALGORITHM &&\n typeof payload.iv === \"string\" &&\n typeof payload.tag === \"string\" &&\n typeof payload.ciphertext === \"string\"\n );\n}\n\n/** Validates payload structure and cryptographic parameters before decryption */\nfunction assertEncryptedPayload(payload: EncryptedPayload): void {\n if (payload.v !== WORKFLOW_ENCRYPTION_VERSION) {\n throw new Error(\"Invalid encrypted payload: unsupported version.\");\n }\n\n if (payload.alg !== WORKFLOW_ENCRYPTION_ALGORITHM) {\n throw new Error(\"Invalid encrypted payload: unsupported algorithm.\");\n }\n\n const iv = payloadBase64ToBytes(payload.iv, \"iv\");\n const tag = payloadBase64ToBytes(payload.tag, \"tag\");\n\n if (iv.length !== IV_LENGTH_BYTES) {\n throw new Error(\"Invalid encrypted payload: iv length mismatch.\");\n }\n\n if (tag.length !== AUTH_TAG_LENGTH_BYTES) {\n throw new Error(\"Invalid encrypted payload: tag length mismatch.\");\n }\n\n payloadBase64ToBytes(payload.ciphertext, \"ciphertext\");\n}\n\n/**\n * Encrypts a value for secure transport to a workflow.\n *\n * @param value - Any JSON-serializable value (typically WorkflowCredentials)\n * @param key - 32-byte secret key (base64 string or Uint8Array)\n * @param keyId - Optional key identifier for rotation support (stored in plaintext)\n * @returns Encrypted payload with metadata, safe to pass through untrusted channels\n */\nexport async function encryptForWorkflow<T>(\n value: T,\n key: Uint8Array | string,\n keyId?: string,\n): Promise<Encrypted<T>> {\n const keyBytes = normalizeKey(key);\n const webCrypto = getWebCrypto();\n const iv = new Uint8Array(IV_LENGTH_BYTES);\n webCrypto.getRandomValues(iv); // Fresh IV for each encryption\n\n let serialized: string;\n try {\n serialized = JSON.stringify(value);\n } catch {\n throw new Error(\"Failed to serialize value for encryption.\");\n }\n\n const encoder = new TextEncoder();\n const plaintext = encoder.encode(serialized);\n const encryptedBytes = gcm(keyBytes, iv).encrypt(plaintext);\n const tag = encryptedBytes.slice(encryptedBytes.length - AUTH_TAG_LENGTH_BYTES);\n const ciphertext = encryptedBytes.slice(0, encryptedBytes.length - AUTH_TAG_LENGTH_BYTES);\n\n return {\n v: WORKFLOW_ENCRYPTION_VERSION,\n alg: WORKFLOW_ENCRYPTION_ALGORITHM,\n ...(keyId !== undefined && { kid: keyId }),\n iv: bytesToBase64(iv),\n tag: bytesToBase64(tag),\n ciphertext: bytesToBase64(ciphertext),\n };\n}\n\n/**\n * Decrypts and deserializes a workflow payload.\n *\n * @param payload - Encrypted payload from `encryptForWorkflow`\n * @param key - Same 32-byte secret key used for encryption\n * @returns The original decrypted value\n * @throws If payload is invalid, tampered with, or key is wrong\n */\nexport async function decryptFromWorkflow<T>(\n payload: EncryptedPayload,\n key: Uint8Array | string,\n): Promise<T> {\n if (!isEncryptedPayload(payload)) {\n throw new Error(\"Invalid encrypted payload.\");\n }\n\n assertEncryptedPayload(payload);\n\n const keyBytes = normalizeKey(key);\n const iv = payloadBase64ToBytes(payload.iv, \"iv\");\n const tag = payloadBase64ToBytes(payload.tag, \"tag\");\n const ciphertext = payloadBase64ToBytes(payload.ciphertext, \"ciphertext\");\n\n const combined = new Uint8Array(ciphertext.length + tag.length);\n combined.set(ciphertext);\n combined.set(tag, ciphertext.length);\n\n let plaintext: Uint8Array;\n try {\n plaintext = gcm(keyBytes, iv).decrypt(combined);\n } catch (error) {\n const message = (error as { message?: string } | undefined)?.message;\n throw new Error(`Failed to decrypt workflow payload. ${message ?? String(error)}`);\n }\n\n try {\n const decoder = new TextDecoder();\n return JSON.parse(decoder.decode(plaintext)) as T;\n } catch {\n throw new Error(\"Failed to parse decrypted payload.\");\n }\n}\n","/**\n * Workflow Credentials Management\n *\n * This module provides a unified way to resolve credentials from multiple sources:\n * 1. A custom credentials provider (set via `setWorkflowCredentialsProvider`)\n * 2. Encrypted credentials passed directly to workflow functions\n * 3. Environment variables as fallback\n *\n * Credentials are merged in order of precedence: direct input > provider > environment.\n */\nimport env from \"@mux/ai/env\";\nimport type { Env } from \"@mux/ai/env\";\nimport type { SigningContext } from \"@mux/ai/lib/url-signing\";\nimport { decryptFromWorkflow, isEncryptedPayload } from \"@mux/ai/lib/workflow-crypto\";\nimport type {\n WorkflowCredentials,\n WorkflowCredentialsInput,\n WorkflowMuxClient,\n} from \"@mux/ai/types\";\n\n/**\n * A function that returns workflow credentials, either synchronously or asynchronously.\n * Used to inject credentials from external sources (e.g., a secrets manager).\n */\nexport type WorkflowCredentialsProvider =\n () => Promise<WorkflowCredentials | undefined> | WorkflowCredentials | undefined;\n\n/** Module-level credentials provider, set via `setWorkflowCredentialsProvider` */\nlet workflowCredentialsProvider: WorkflowCredentialsProvider | undefined;\n\n/**\n * Registers a custom credentials provider for the module.\n * The provider will be called whenever credentials need to be resolved.\n */\nexport function setWorkflowCredentialsProvider(provider?: WorkflowCredentialsProvider): void {\n workflowCredentialsProvider = provider;\n}\n\n/**\n * Detects whether code is running inside a Workflow Dev Kit runtime.\n * getWorkflowMetadata throws when invoked outside a workflow.\n */\nasync function isWorkflowRuntime(): Promise<boolean> {\n try {\n const workflowModule = await import(\"workflow\");\n if (typeof workflowModule.getWorkflowMetadata !== \"function\") {\n return false;\n }\n workflowModule.getWorkflowMetadata();\n return true;\n } catch {\n return false;\n }\n}\n\n/**\n * Determines if we should enforce encrypted credentials.\n * This triggers in workflow runtimes or when the workflow secret key is set.\n */\nasync function shouldEnforceEncryptedCredentials(): Promise<boolean> {\n return Boolean(env.MUX_AI_WORKFLOW_SECRET_KEY) || await isWorkflowRuntime();\n}\n\n/**\n * Retrieves the workflow secret key from environment variables.\n * This key is used to decrypt encrypted credential payloads.\n */\nfunction getWorkflowSecretKeyFromEnv(): string {\n const key = env.MUX_AI_WORKFLOW_SECRET_KEY;\n if (!key) {\n throw new Error(\n \"Workflow secret key is required. Set MUX_AI_WORKFLOW_SECRET_KEY environment variable.\",\n );\n }\n return key;\n}\n\n/**\n * Invokes the registered credentials provider (if any) and validates the result.\n */\nasync function resolveProviderCredentials(): Promise<WorkflowCredentials | undefined> {\n if (!workflowCredentialsProvider) {\n return undefined;\n }\n\n const provided = await workflowCredentialsProvider();\n if (!provided) {\n return undefined;\n }\n\n if (typeof provided !== \"object\") {\n throw new TypeError(\"Workflow credentials provider must return an object.\");\n }\n\n return provided;\n}\n\n/**\n * Resolves workflow credentials by merging from multiple sources.\n *\n * Resolution order (later sources override earlier):\n * 1. Credentials from the registered provider\n * 2. Decrypted credentials (if input is an encrypted payload)\n * OR plain credentials object (if input is already decrypted)\n *\n * @param credentials - Optional credentials input\n * @returns Merged credentials object\n */\nexport async function resolveWorkflowCredentials(\n credentials?: WorkflowCredentialsInput,\n): Promise<WorkflowCredentials> {\n // Start with provider credentials as the base\n const providerCredentials = await resolveProviderCredentials();\n const resolved: WorkflowCredentials = providerCredentials ? { ...providerCredentials } : {};\n\n if (!credentials) {\n return resolved;\n }\n\n // Handle encrypted payloads by decrypting them first.\n if (isEncryptedPayload(credentials)) {\n try {\n const decrypted = await decryptFromWorkflow<WorkflowCredentials>(\n credentials,\n getWorkflowSecretKeyFromEnv(),\n );\n return { ...resolved, ...decrypted };\n } catch (error) {\n const detail = error instanceof Error ? error.message : \"Unknown error.\";\n throw new Error(`Failed to decrypt workflow credentials. ${detail}`);\n }\n }\n\n if (await shouldEnforceEncryptedCredentials()) {\n throw new Error(\n \"Plaintext workflow credentials are not allowed when using Workflow Dev Kit.\" +\n \" Pass encrypted credentials (encryptForWorkflow) or resolve secrets via environment variables.\",\n );\n }\n\n // Plain credentials object - merge directly.\n return { ...resolved, ...credentials };\n}\n\ninterface DirectMuxCredentials {\n tokenId?: string;\n tokenSecret?: string;\n authorizationToken?: string;\n signingKey?: string;\n privateKey?: string;\n}\n\nfunction readString(record: Record<string, unknown> | undefined, key: string): string | undefined {\n const value = record?.[key];\n return typeof value === \"string\" && value.length > 0 ? value : undefined;\n}\n\nfunction resolveDirectMuxCredentials(record: Record<string, unknown> | undefined): DirectMuxCredentials | undefined {\n const tokenId = readString(record, \"muxTokenId\");\n const tokenSecret = readString(record, \"muxTokenSecret\");\n const authorizationToken = readString(record, \"muxAuthorizationToken\");\n const signingKey = readString(record, \"muxSigningKey\");\n const privateKey = readString(record, \"muxPrivateKey\");\n\n if (!tokenId && !tokenSecret && !authorizationToken && !signingKey && !privateKey) {\n return undefined;\n }\n\n if ((!tokenId || !tokenSecret) && !authorizationToken) {\n throw new Error(\n \"Both muxTokenId and muxTokenSecret are required when passing direct Mux workflow credentials.\",\n );\n }\n\n return {\n tokenId,\n tokenSecret,\n authorizationToken,\n signingKey,\n privateKey,\n };\n}\n\nfunction createWorkflowMuxClient(options: DirectMuxCredentials): WorkflowMuxClient {\n return {\n async createClient() {\n // Dynamic import to avoid pulling mux-node into workflow VM bundles.\n const { default: MuxClient } = await import(\"@mux/mux-node\");\n return new MuxClient({\n tokenId: options.tokenId,\n tokenSecret: options.tokenSecret,\n authorizationToken: options.authorizationToken,\n });\n },\n getSigningKey() {\n return options.signingKey;\n },\n getPrivateKey() {\n return options.privateKey;\n },\n };\n}\n\n/**\n * Resolves a WorkflowMuxClient from workflow credentials or environment variables.\n *\n * Checks direct workflow credentials first (muxTokenId/muxTokenSecret),\n * then provider credentials, then falls back to MUX_TOKEN_ID / MUX_TOKEN_SECRET\n * (and optional MUX_SIGNING_KEY / MUX_PRIVATE_KEY) environment variables.\n *\n * @param credentials - Optional workflow credentials input\n * @returns A WorkflowMuxClient instance\n * @throws Error if Mux credentials are not available\n */\nexport async function resolveMuxClient(\n credentials?: WorkflowCredentialsInput,\n): Promise<WorkflowMuxClient> {\n const resolved = await resolveWorkflowCredentials(credentials);\n const resolvedRecord = resolved as Record<string, unknown>;\n const resolvedMuxCredentials = resolveDirectMuxCredentials(resolvedRecord);\n if (resolvedMuxCredentials) {\n return createWorkflowMuxClient(resolvedMuxCredentials);\n }\n\n // Fall back to environment variables\n const muxTokenId = env.MUX_TOKEN_ID;\n const muxTokenSecret = env.MUX_TOKEN_SECRET;\n\n if (!muxTokenId || !muxTokenSecret) {\n throw new Error(\n \"Mux credentials are required. Provide muxTokenId/muxTokenSecret via workflow credentials, or set MUX_TOKEN_ID and MUX_TOKEN_SECRET environment variables.\",\n );\n }\n\n return createWorkflowMuxClient({\n tokenId: muxTokenId,\n tokenSecret: muxTokenSecret,\n signingKey: env.MUX_SIGNING_KEY,\n privateKey: env.MUX_PRIVATE_KEY,\n });\n}\n\n/** Supported AI/ML provider identifiers for API key resolution. */\nexport type ApiKeyProvider = \"openai\" | \"anthropic\" | \"google\" | \"hive\" | \"elevenlabs\";\n\nfunction resolveProviderApiKeyFromCredentials(\n provider: ApiKeyProvider,\n resolved: WorkflowCredentials,\n): string {\n const record = resolved as Record<string, unknown>;\n const openaiApiKey = readString(record, \"openaiApiKey\");\n const anthropicApiKey = readString(record, \"anthropicApiKey\");\n const googleApiKey = readString(record, \"googleApiKey\");\n const hiveApiKey = readString(record, \"hiveApiKey\");\n const elevenLabsApiKey = readString(record, \"elevenLabsApiKey\");\n\n // Map each provider to its credential source and env var fallback\n const apiKeyMap: Record<ApiKeyProvider, string | undefined> = {\n openai: openaiApiKey ?? env.OPENAI_API_KEY,\n anthropic: anthropicApiKey ?? env.ANTHROPIC_API_KEY,\n google: googleApiKey ?? env.GOOGLE_GENERATIVE_AI_API_KEY,\n hive: hiveApiKey ?? env.HIVE_API_KEY,\n elevenlabs: elevenLabsApiKey ?? env.ELEVENLABS_API_KEY,\n };\n\n const apiKey = apiKeyMap[provider];\n if (!apiKey) {\n // Provide helpful error message with the correct env var name.\n // Using `satisfies` ensures these stay in sync with the Env schema.\n const envVarNames = {\n openai: \"OPENAI_API_KEY\",\n anthropic: \"ANTHROPIC_API_KEY\",\n google: \"GOOGLE_GENERATIVE_AI_API_KEY\",\n hive: \"HIVE_API_KEY\",\n elevenlabs: \"ELEVENLABS_API_KEY\",\n } as const satisfies Record<ApiKeyProvider, keyof Env>;\n\n throw new Error(\n `${provider} API key is required. Provide ${provider} credentials via workflow credentials or set ${envVarNames[provider]} environment variable.`,\n );\n }\n\n return apiKey;\n}\n/**\n * Resolves an API key for a specific AI/ML provider.\n *\n * Checks resolved workflow credentials first, then falls back to the\n * provider-specific environment variable.\n *\n * @param provider - The provider identifier (e.g., \"openai\", \"anthropic\")\n * @param credentials - Optional workflow credentials input\n * @returns The resolved API key string\n * @throws Error if no API key is available for the specified provider\n */\nexport async function resolveProviderApiKey(\n provider: ApiKeyProvider,\n credentials?: WorkflowCredentialsInput,\n): Promise<string> {\n const resolved = await resolveWorkflowCredentials(credentials);\n return resolveProviderApiKeyFromCredentials(provider, resolved);\n}\n\n/**\n * Resolves Mux URL signing context for generating signed playback URLs.\n *\n * Unlike other resolve functions, this returns undefined if signing keys\n * are not configured (signing is optional for public assets).\n *\n * @param credentials - Optional workflow credentials input\n * @returns SigningContext if keys are available, undefined otherwise\n */\nexport async function resolveMuxSigningContext(\n credentials?: WorkflowCredentialsInput,\n): Promise<SigningContext | undefined> {\n const resolved = await resolveWorkflowCredentials(credentials);\n const resolvedRecord = resolved as Record<string, unknown>;\n\n // Try direct credentials first, then fall back to environment variables.\n const keyId = readString(resolvedRecord, \"muxSigningKey\") ?? env.MUX_SIGNING_KEY;\n const keySecret =\n readString(resolvedRecord, \"muxPrivateKey\") ?? env.MUX_PRIVATE_KEY;\n\n if (!keyId || !keySecret) {\n return undefined;\n }\n\n return { keyId, keySecret };\n}\n","import type {\n ModelIdByProvider,\n ModelRequestOptions,\n SupportedProvider,\n} from \"@mux/ai/lib/providers\";\nimport {\n resolveLanguageModel,\n} from \"@mux/ai/lib/providers\";\nimport type { ApiKeyProvider } from \"@mux/ai/lib/workflow-credentials\";\nimport { resolveMuxClient, resolveProviderApiKey } from \"@mux/ai/lib/workflow-credentials\";\nimport type { WorkflowCredentialsInput, WorkflowMuxClient } from \"@mux/ai/types\";\n\n/**\n * Gets a WorkflowMuxClient from workflow credentials or environment variables.\n * Used internally by workflow steps to avoid passing credentials through step I/O.\n * Throws if Mux credentials are not available.\n */\nexport async function getMuxClientFromEnv(\n credentials?: WorkflowCredentialsInput,\n): Promise<WorkflowMuxClient> {\n return resolveMuxClient(credentials);\n}\n\n/**\n * Gets an API key from workflow credentials or environment variables for the specified provider.\n * Used internally by workflow steps to avoid passing credentials through step I/O.\n * Throws if the API key is not available.\n */\nexport async function getApiKeyFromEnv(\n provider: ApiKeyProvider,\n credentials?: WorkflowCredentialsInput,\n): Promise<string> {\n return resolveProviderApiKey(provider, credentials);\n}\n\nexport interface WorkflowConfig<P extends SupportedProvider = SupportedProvider> {\n muxClient: WorkflowMuxClient;\n provider: P;\n modelId: ModelIdByProvider[P];\n}\n\n/**\n * Resolves Mux client and model configuration for a workflow.\n * This function is NOT a workflow step to avoid exposing credentials in step I/O.\n */\nexport async function createWorkflowConfig<P extends SupportedProvider = SupportedProvider>(\n options: ModelRequestOptions<P>,\n provider?: P,\n): Promise<WorkflowConfig<P>> {\n const providerToUse = provider || options.provider || (\"openai\" as P);\n const muxClient = await resolveMuxClient(options.credentials);\n const resolved = resolveLanguageModel({\n ...options,\n provider: providerToUse,\n });\n\n return {\n muxClient,\n provider: resolved.provider,\n modelId: resolved.modelId,\n };\n}\n","import { getMuxClientFromEnv } from \"@mux/ai/lib/client-factory\";\nimport type { WorkflowCredentialsInput } from \"@mux/ai/types\";\n\nexport interface HeatmapOptions {\n /** Time window for results, e.g., ['7:days'] (default: ['7:days']) */\n timeframe?: string;\n /** Optional workflow credentials */\n credentials?: WorkflowCredentialsInput;\n}\n\n/** Raw API response structure from Mux Data API */\n// To be removed when the Mux Node SDK is updated\ninterface HeatmapApiResponse {\n asset_id?: string;\n video_id?: string;\n playback_id?: string;\n heatmap: number[];\n timeframe: [number, number];\n}\n\nexport interface HeatmapResponse {\n assetId?: string;\n videoId?: string;\n playbackId?: string;\n /** Array of 100 values representing engagement for each 1/100th of the video */\n heatmap: number[];\n timeframe: [number, number];\n}\n\n/**\n * Fetches engagement heatmap for a Mux asset.\n * Returns a length 100 array where each value represents how many times\n * that 1/100th of the video was watched.\n *\n * @param assetId - The Mux asset ID\n * @param options - Heatmap query options\n * @returns Heatmap data with 100 engagement values\n */\nexport async function getHeatmapForAsset(\n assetId: string,\n options: HeatmapOptions = {},\n): Promise<HeatmapResponse> {\n \"use step\";\n return fetchHeatmap(\"assets\", assetId, options);\n}\n\n/**\n * Fetches engagement heatmap for a Mux video ID.\n * Returns a length 100 array where each value represents how many times\n * that 1/100th of the video was watched.\n *\n * @param videoId - The Mux video ID\n * @param options - Heatmap query options\n * @returns Heatmap data with 100 engagement values\n */\nexport async function getHeatmapForVideo(\n videoId: string,\n options: HeatmapOptions = {},\n): Promise<HeatmapResponse> {\n \"use step\";\n return fetchHeatmap(\"videos\", videoId, options);\n}\n\n/**\n * Fetches engagement heatmap for a Mux playback ID.\n * Returns a length 100 array where each value represents how many times\n * that 1/100th of the video was watched.\n *\n * @param playbackId - The Mux playback ID\n * @param options - Heatmap query options\n * @returns Heatmap data with 100 engagement values\n */\nexport async function getHeatmapForPlaybackId(\n playbackId: string,\n options: HeatmapOptions = {},\n): Promise<HeatmapResponse> {\n \"use step\";\n return fetchHeatmap(\"playback-ids\", playbackId, options);\n}\n\n/**\n * Transforms the snake_case API response to camelCase for the public interface.\n * TODO: Remove when the Mux Node SDK is updated\n */\nfunction transformHeatmapResponse(\n response: HeatmapApiResponse,\n): HeatmapResponse {\n return {\n assetId: response.asset_id,\n videoId: response.video_id,\n playbackId: response.playback_id,\n heatmap: response.heatmap,\n timeframe: response.timeframe,\n };\n}\n\n/**\n * Internal helper to fetch heatmap from the Mux Data API.\n * Uses the raw HTTP methods on the Mux client since the SDK doesn't have\n * typed methods for the engagement endpoints yet.\n */\nasync function fetchHeatmap(\n identifierType: \"assets\" | \"videos\" | \"playback-ids\",\n id: string,\n options: HeatmapOptions,\n): Promise<HeatmapResponse> {\n \"use step\";\n const { timeframe = \"[24:hours]\", credentials } = options;\n\n const muxClient = await getMuxClientFromEnv(credentials);\n const mux = await muxClient.createClient();\n\n // Build query parameters\n const queryParams = new URLSearchParams();\n queryParams.append(\"timeframe[]\", timeframe);\n\n // Use the raw HTTP method since the SDK doesn't have typed engagement methods yet\n const path = `/data/v1/engagement/${identifierType}/${id}/heatmap?${queryParams.toString()}`;\n const response = await mux.get<unknown, HeatmapApiResponse>(path);\n\n return transformHeatmapResponse(response);\n}\n","import { getMuxClientFromEnv } from \"@mux/ai/lib/client-factory\";\nimport type { WorkflowCredentialsInput } from \"@mux/ai/types\";\n\nexport interface Hotspot {\n /** Inclusive start time in milliseconds */\n startMs: number;\n /** Exclusive end time in milliseconds */\n endMs: number;\n /** Hotspot score using distribution-based normalization (0-1) */\n score: number;\n}\n\nexport interface HotspotOptions {\n /** Maximum number of hotspots to return (default: 5) */\n limit?: number;\n /** Sort order: 'asc' or 'desc' (default: 'desc') */\n orderDirection?: \"asc\" | \"desc\";\n /** Order by field (default: 'score') */\n orderBy?: \"score\";\n /** Time window for results, e.g., ['7:days'] (default: ['7:days']) */\n timeframe?: string;\n /** Optional workflow credentials */\n credentials?: WorkflowCredentialsInput;\n}\n\n/** Raw API response structure from Mux Data API */\n// To be removed when the Mux Node SDK is updated\ninterface HotspotApiResponse {\n total_row_count: number | null;\n timeframe: [number, number];\n data: {\n asset_id?: string;\n video_id?: string;\n playback_id?: string;\n hotspots: Array<{\n start_ms: number;\n end_ms: number;\n score: number;\n }>;\n };\n}\n\nexport interface HotspotResponse {\n assetId?: string;\n videoId?: string;\n playbackId?: string;\n hotspots: Hotspot[];\n}\n\n/**\n * Fetches engagement hotspots for a Mux asset.\n * Returns the top N \"hot\" time ranges based on engagement data.\n *\n * @param assetId - The Mux asset ID\n * @param options - Hotspot query options\n * @returns Array of hotspots with time ranges and scores\n */\nexport async function getHotspotsForAsset(\n assetId: string,\n options: HotspotOptions = {},\n): Promise<Hotspot[]> {\n \"use step\";\n const response = await fetchHotspots(\"assets\", assetId, options);\n return response.hotspots;\n}\n\n/**\n * Fetches engagement hotspots for a Mux video ID.\n * Returns the top N \"hot\" time ranges based on engagement data.\n *\n * @param videoId - The Mux video ID\n * @param options - Hotspot query options\n * @returns Array of hotspots with time ranges and scores\n */\nexport async function getHotspotsForVideo(\n videoId: string,\n options: HotspotOptions = {},\n): Promise<Hotspot[]> {\n \"use step\";\n const response = await fetchHotspots(\"videos\", videoId, options);\n return response.hotspots;\n}\n\n/**\n * Fetches engagement hotspots for a Mux playback ID.\n * Returns the top N \"hot\" time ranges based on engagement data.\n *\n * @param playbackId - The Mux playback ID\n * @param options - Hotspot query options\n * @returns Array of hotspots with time ranges and scores\n */\nexport async function getHotspotsForPlaybackId(\n playbackId: string,\n options: HotspotOptions = {},\n): Promise<Hotspot[]> {\n \"use step\";\n const response = await fetchHotspots(\"playback-ids\", playbackId, options);\n return response.hotspots;\n}\n\n/**\n * Transforms the snake_case API response to camelCase for the public interface.\n * TODO: Remove when the Mux Node SDK is updated\n */\nfunction transformHotspotResponse(response: HotspotApiResponse): HotspotResponse {\n return {\n assetId: response.data.asset_id,\n videoId: response.data.video_id,\n playbackId: response.data.playback_id,\n hotspots: response.data.hotspots.map(h => ({\n startMs: h.start_ms,\n endMs: h.end_ms,\n score: h.score,\n })),\n };\n}\n\n/**\n * Internal helper to fetch hotspots from the Mux Data API.\n * Uses the raw HTTP methods on the Mux client since the SDK doesn't have\n * typed methods for the engagement endpoints yet.\n */\nasync function fetchHotspots(\n identifierType: \"assets\" | \"videos\" | \"playback-ids\",\n id: string,\n options: HotspotOptions,\n): Promise<HotspotResponse> {\n \"use step\";\n const {\n limit = 5,\n orderDirection = \"desc\",\n orderBy = \"score\",\n timeframe = \"[24:hours]\",\n credentials,\n } = options;\n\n const muxClient = await getMuxClientFromEnv(credentials);\n const mux = await muxClient.createClient();\n\n // Build query parameters\n const queryParams = new URLSearchParams();\n queryParams.append(\"limit\", String(limit));\n queryParams.append(\"order_direction\", orderDirection);\n queryParams.append(\"order_by\", orderBy);\n queryParams.append(\"timeframe[]\", timeframe);\n\n // Use the raw HTTP method since the SDK doesn't have typed engagement methods yet\n const path = `/data/v1/engagement/${identifierType}/${id}/hotspots?${queryParams.toString()}`;\n const response = await mux.get<unknown, HotspotApiResponse>(path);\n\n return transformHotspotResponse(response);\n}\n","import env from \"@mux/ai/env\";\n\nconst DEFAULT_MUX_IMAGE_ORIGIN = \"https://image.mux.com\";\n\nfunction normalizeMuxImageOrigin(value: string): string {\n const trimmed = value.trim();\n const candidate = trimmed.includes(\"://\") ? trimmed : `https://${trimmed}`;\n\n let parsed: URL;\n try {\n parsed = new URL(candidate);\n } catch {\n throw new Error(\n \"Invalid MUX_IMAGE_URL_OVERRIDE. Provide a hostname like \" +\n `\"image.example.mux.com\" (or a URL origin such as \"https://image.example.mux.com\").`,\n );\n }\n\n if (\n parsed.username ||\n parsed.password ||\n parsed.search ||\n parsed.hash ||\n (parsed.pathname && parsed.pathname !== \"/\")\n ) {\n throw new Error(\n \"Invalid MUX_IMAGE_URL_OVERRIDE. Only a hostname/origin is allowed \" +\n \"(no credentials, query params, hash fragments, or path).\",\n );\n }\n\n return parsed.origin;\n}\n\nexport function getMuxImageOrigin(): string {\n const override = env.MUX_IMAGE_URL_OVERRIDE;\n if (!override) {\n return DEFAULT_MUX_IMAGE_ORIGIN;\n }\n\n return normalizeMuxImageOrigin(override);\n}\n\nexport function getMuxImageBaseUrl(playbackId: string, assetType: \"storyboard\" | \"thumbnail\"): string {\n const origin = getMuxImageOrigin();\n return `${origin}/${playbackId}/${assetType}.png`;\n}\n\nexport function getMuxStoryboardBaseUrl(playbackId: string): string {\n return getMuxImageBaseUrl(playbackId, \"storyboard\");\n}\n\nexport function getMuxThumbnailBaseUrl(playbackId: string): string {\n return getMuxImageBaseUrl(playbackId, \"thumbnail\");\n}\n","import env from \"@mux/ai/env\";\nimport { resolveMuxSigningContext } from \"@mux/ai/lib/workflow-credentials\";\nimport type { WorkflowCredentialsInput } from \"@mux/ai/types\";\n\nimport type Mux from \"@mux/mux-node\";\n\n/**\n * Context required to sign URLs for signed playback IDs.\n */\nexport interface SigningContext {\n /** The signing key ID from Mux dashboard. */\n keyId: string;\n /** The base64-encoded private key from Mux dashboard. */\n keySecret: string;\n /** Token expiration time (e.g. '1h', '1d'). Defaults to '1h'. */\n expiration?: string;\n}\n\n/**\n * Token type determines which Mux service the token is valid for.\n */\nexport type TokenType = \"video\" | \"thumbnail\" | \"storyboard\" | \"gif\";\n\n/**\n * Resolves signing context from config or environment variables.\n * Returns undefined if signing keys are not configured.\n */\nexport function getMuxSigningContextFromEnv(): SigningContext | undefined {\n const keyId = env.MUX_SIGNING_KEY;\n const keySecret = env.MUX_PRIVATE_KEY;\n\n if (!keyId || !keySecret) {\n return undefined;\n }\n\n return { keyId, keySecret };\n}\n\n/**\n * Creates a Mux client configured for JWT signing.\n * This client is used internally for signing operations.\n */\nasync function createSigningClient(context: SigningContext): Promise<Mux> {\n // Dynamic import to prevent @mux/mux-node (and its transitive dep jose)\n // from being bundled into workflow VM code where `require` is unavailable.\n const { default: MuxClient } = await import(\"@mux/mux-node\");\n return new MuxClient({\n // These are not needed for signing, but the SDK requires them\n // Using empty strings as we only need the jwt functionality\n tokenId: env.MUX_TOKEN_ID || \"\",\n tokenSecret: env.MUX_TOKEN_SECRET || \"\",\n jwtSigningKey: context.keyId,\n jwtPrivateKey: context.keySecret,\n });\n}\n\n/**\n * Generates a signed token for a playback ID using the Mux SDK.\n *\n * @param playbackId - The Mux playback ID to sign\n * @param context - Signing context with key credentials\n * @param type - Token type (video, thumbnail, storyboard, gif)\n * @param params - Additional parameters for thumbnail/storyboard tokens (values will be stringified)\n * @returns Signed JWT token\n */\nexport async function signPlaybackId(\n playbackId: string,\n context: SigningContext,\n type: TokenType = \"video\",\n params?: Record<string, string | number>,\n): Promise<string> {\n \"use step\";\n const client = await createSigningClient(context);\n\n // Convert params to Record<string, string> as required by the SDK\n const stringParams = params ?\n Object.fromEntries(\n Object.entries(params).map(([key, value]) => [key, String(value)]),\n ) :\n undefined;\n\n return client.jwt.signPlaybackId(playbackId, {\n type,\n expiration: context.expiration || \"1h\",\n params: stringParams,\n });\n}\n\n/**\n * Appends a signed token to a Mux URL.\n *\n * @param url - The base Mux URL (e.g. https://image.mux.com/{playbackId}/thumbnail.png)\n * @param playbackId - The Mux playback ID\n * @param type - Token type for the URL\n * @param params - Additional parameters for the token\n * @returns URL with token query parameter appended\n */\nexport async function signUrl(\n url: string,\n playbackId: string,\n type: TokenType = \"video\",\n params?: Record<string, string | number>,\n credentials?: WorkflowCredentialsInput,\n): Promise<string> {\n \"use step\";\n const resolvedContext = await resolveMuxSigningContext(credentials);\n if (!resolvedContext) {\n throw new Error(\n \"Signed playback ID requires signing credentials. \" +\n \"Provide muxSigningKey and muxPrivateKey via workflow credentials or set MUX_SIGNING_KEY and MUX_PRIVATE_KEY environment variables.\",\n );\n }\n const token = await signPlaybackId(playbackId, resolvedContext, type, params);\n const separator = url.includes(\"?\") ? \"&\" : \"?\";\n return `${url}${separator}token=${token}`;\n}\n","import { getMuxStoryboardBaseUrl } from \"@mux/ai/lib/mux-image-url\";\nimport { signUrl } from \"@mux/ai/lib/url-signing\";\nimport type { WorkflowCredentialsInput } from \"@mux/ai/types\";\n\nexport const DEFAULT_STORYBOARD_WIDTH = 640;\n\n/**\n * Generates a storyboard URL for the given playback ID.\n * If shouldSign is true, the URL will be signed with a token using credentials from environment variables.\n *\n * @param playbackId - The Mux playback ID\n * @param width - Width of the storyboard in pixels (default: 640)\n * @param shouldSign - Flag for whether or not to use signed playback IDs (default: false)\n * @returns Storyboard URL (signed if shouldSign is true)\n */\nexport async function getStoryboardUrl(\n playbackId: string,\n width: number = DEFAULT_STORYBOARD_WIDTH,\n shouldSign: boolean = false,\n credentials?: WorkflowCredentialsInput,\n): Promise<string> {\n \"use step\";\n const baseUrl = getMuxStoryboardBaseUrl(playbackId);\n\n if (shouldSign) {\n return signUrl(baseUrl, playbackId, \"storyboard\", { width }, credentials);\n }\n\n return `${baseUrl}?width=${width}`;\n}\n","import type { VTTCue } from \"@mux/ai/primitives/transcripts\";\nimport type { ChunkingStrategy, TextChunk } from \"@mux/ai/types\";\n\n/**\n * Simple token counter that approximates tokens by word count.\n * For production use with OpenAI, consider using a proper tokenizer like tiktoken.\n * This approximation is generally close enough for chunking purposes (1 token ≈ 0.75 words).\n */\nexport function estimateTokenCount(text: string): number {\n const words = text.trim().split(/\\s+/).length;\n return Math.ceil(words / 0.75);\n}\n\n/**\n * Chunks text into overlapping segments based on token count.\n *\n * @param text - The text to chunk\n * @param maxTokens - Maximum tokens per chunk\n * @param overlapTokens - Number of tokens to overlap between chunks\n * @returns Array of text chunks with metadata\n */\nexport function chunkByTokens(\n text: string,\n maxTokens: number,\n overlapTokens: number = 0,\n): TextChunk[] {\n if (!text.trim()) {\n return [];\n }\n\n const chunks: TextChunk[] = [];\n const words = text.trim().split(/\\s+/);\n\n // Convert tokens to approximate word count\n const wordsPerChunk = Math.floor(maxTokens * 0.75);\n const overlapWords = Math.floor(overlapTokens * 0.75);\n\n let chunkIndex = 0;\n let currentPosition = 0;\n\n while (currentPosition < words.length) {\n const chunkWords = words.slice(\n currentPosition,\n currentPosition + wordsPerChunk,\n );\n const chunkText = chunkWords.join(\" \");\n const tokenCount = estimateTokenCount(chunkText);\n\n chunks.push({\n id: `chunk-${chunkIndex}`,\n text: chunkText,\n tokenCount,\n });\n\n // Move forward by chunk size minus overlap\n currentPosition += wordsPerChunk - overlapWords;\n chunkIndex++;\n\n // Prevent infinite loop if overlap is too large\n if (currentPosition <= (chunkIndex - 1) * (wordsPerChunk - overlapWords)) {\n break;\n }\n }\n\n return chunks;\n}\n\n/**\n * Creates a TextChunk from a group of VTT cues.\n */\nfunction createChunkFromCues(cues: VTTCue[], index: number): TextChunk {\n const text = cues.map(c => c.text).join(\" \");\n return {\n id: `chunk-${index}`,\n text,\n tokenCount: estimateTokenCount(text),\n startTime: cues[0].startTime,\n endTime: cues[cues.length - 1].endTime,\n };\n}\n\n/**\n * Chunks VTT cues into groups that respect natural cue boundaries.\n * Splits at cue boundaries rather than mid-sentence, preserving accurate timestamps.\n *\n * @param cues - Array of VTT cues to chunk\n * @param maxTokens - Maximum tokens per chunk\n * @param overlapCues - Number of cues to overlap between chunks (default: 2)\n * @returns Array of text chunks with accurate start/end times\n */\nexport function chunkVTTCues(\n cues: VTTCue[],\n maxTokens: number,\n overlapCues: number = 2,\n): TextChunk[] {\n if (cues.length === 0)\n return [];\n\n const chunks: TextChunk[] = [];\n let currentCues: VTTCue[] = [];\n let currentTokens = 0;\n let chunkIndex = 0;\n\n for (let i = 0; i < cues.length; i++) {\n const cue = cues[i];\n const cueTokens = estimateTokenCount(cue.text);\n\n // If adding this cue would exceed limit, finalize current chunk\n if (currentTokens + cueTokens > maxTokens && currentCues.length > 0) {\n chunks.push(createChunkFromCues(currentCues, chunkIndex));\n chunkIndex++;\n\n // Start new chunk with overlap from end of previous\n const overlapStart = Math.max(0, currentCues.length - overlapCues);\n currentCues = currentCues.slice(overlapStart);\n currentTokens = currentCues.reduce(\n (sum, c) => sum + estimateTokenCount(c.text),\n 0,\n );\n }\n\n currentCues.push(cue);\n currentTokens += cueTokens;\n }\n\n // Don't forget the last chunk\n if (currentCues.length > 0) {\n chunks.push(createChunkFromCues(currentCues, chunkIndex));\n }\n\n return chunks;\n}\n\n/**\n * Chunks text according to the specified strategy.\n *\n * @param text - The text to chunk\n * @param strategy - The chunking strategy to use\n * @returns Array of text chunks\n */\nexport function chunkText(text: string, strategy: ChunkingStrategy): TextChunk[] {\n switch (strategy.type) {\n case \"token\": {\n return chunkByTokens(text, strategy.maxTokens, strategy.overlap ?? 0);\n }\n default: {\n const exhaustiveCheck: never = strategy as never;\n throw new Error(`Unsupported chunking strategy: ${exhaustiveCheck}`);\n }\n }\n}\n","import { getMuxThumbnailBaseUrl } from \"@mux/ai/lib/mux-image-url\";\nimport { signUrl } from \"@mux/ai/lib/url-signing\";\nimport type { WorkflowCredentialsInput } from \"@mux/ai/types\";\n\nexport interface ThumbnailOptions {\n /** Interval between thumbnails in seconds (default: 10) */\n interval?: number;\n /** Width of the thumbnail in pixels (default: 640) */\n width?: number;\n /** Flag for whether or not to use signed playback IDs (default: false) */\n shouldSign?: boolean;\n /** Maximum number of thumbnails to generate. When set, samples are evenly distributed with first and last frames pinned. */\n maxSamples?: number;\n /** Workflow credentials for signing (optional). */\n credentials?: WorkflowCredentialsInput;\n}\n\n/**\n * Generates thumbnail URLs at regular intervals based on video duration.\n * If shouldSign is true, the URLs will be signed with tokens using credentials from environment variables.\n *\n * @param playbackId - The Mux playback ID\n * @param duration - Video duration in seconds\n * @param options - Thumbnail generation options\n * @returns Array of thumbnail URLs (signed if shouldSign is true)\n */\nexport async function getThumbnailUrls(\n playbackId: string,\n duration: number,\n options: ThumbnailOptions = {},\n): Promise<string[]> {\n \"use step\";\n const { interval = 10, width = 640, shouldSign = false, maxSamples, credentials } = options;\n let timestamps: number[] = [];\n\n if (duration <= 50) {\n const spacing = duration / 6;\n for (let i = 1; i <= 5; i++) {\n timestamps.push(Math.round(i * spacing));\n }\n } else {\n for (let time = 0; time < duration; time += interval) {\n timestamps.push(time);\n }\n }\n\n // Apply maxSamples cap if specified and we have more timestamps than the limit\n if (maxSamples !== undefined && timestamps.length > maxSamples) {\n const newTimestamps: number[] = [];\n\n // Always include first frame\n newTimestamps.push(0);\n\n // If maxSamples >= 2, add evenly distributed middle frames and last frame\n if (maxSamples >= 2) {\n const spacing = duration / (maxSamples - 1);\n for (let i = 1; i < maxSamples - 1; i++) {\n newTimestamps.push(spacing * i);\n }\n // Always include last frame\n newTimestamps.push(duration);\n }\n\n timestamps = newTimestamps;\n }\n\n const baseUrl = getMuxThumbnailBaseUrl(playbackId);\n\n const urlPromises = timestamps.map(async (time) => {\n if (shouldSign) {\n return signUrl(baseUrl, playbackId, \"thumbnail\", { time, width }, credentials);\n }\n\n return `${baseUrl}?time=${time}&width=${width}`;\n });\n\n return Promise.all(urlPromises);\n}\n","import { signUrl } from \"@mux/ai/lib/url-signing\";\nimport type { AssetTextTrack, MuxAsset, WorkflowCredentialsInput } from \"@mux/ai/types\";\n\n/** A single cue from a VTT file with timing info. */\nexport interface VTTCue {\n startTime: number;\n endTime: number;\n text: string;\n}\n\nexport interface TranscriptFetchOptions {\n languageCode?: string;\n cleanTranscript?: boolean;\n /** Optional signing context for signed playback IDs */\n shouldSign?: boolean;\n credentials?: WorkflowCredentialsInput;\n /**\n * When true, throws if no usable transcript can be retrieved (no ready text track,\n * missing track id, fetch error, or empty transcript).\n *\n * Default behavior is non-fatal and returns an empty `transcriptText`.\n */\n required?: boolean;\n}\n\nexport interface TranscriptResult {\n transcriptText: string;\n transcriptUrl?: string;\n track?: AssetTextTrack;\n}\n\nexport function getReadyTextTracks(asset: MuxAsset): AssetTextTrack[] {\n return (asset.tracks || []).filter(\n track => track.type === \"text\" && track.status === \"ready\",\n );\n}\n\nexport function findCaptionTrack(asset: MuxAsset, languageCode?: string): AssetTextTrack | undefined {\n const tracks = getReadyTextTracks(asset);\n if (!tracks.length)\n return undefined;\n\n if (!languageCode) {\n return tracks[0];\n }\n\n return tracks.find(\n track =>\n track.text_type === \"subtitles\" &&\n track.language_code === languageCode,\n );\n}\n\nexport function extractTextFromVTT(vttContent: string): string {\n if (!vttContent.trim()) {\n return \"\";\n }\n\n const lines = vttContent.split(\"\\n\");\n const textLines: string[] = [];\n\n for (let i = 0; i < lines.length; i++) {\n const line = lines[i].trim();\n\n if (!line)\n continue;\n if (line === \"WEBVTT\")\n continue;\n if (line.startsWith(\"NOTE \"))\n continue;\n if (line.includes(\"-->\"))\n continue;\n if (/^[\\w-]+$/.test(line) && !line.includes(\" \"))\n continue;\n if (line.startsWith(\"STYLE\") || line.startsWith(\"REGION\"))\n continue;\n\n const cleanLine = line.replace(/<[^>]*>/g, \"\").trim();\n\n if (cleanLine) {\n textLines.push(cleanLine);\n }\n }\n\n return textLines.join(\" \").replace(/\\s+/g, \" \").trim();\n}\n\nexport function vttTimestampToSeconds(timestamp: string): number {\n const parts = timestamp.split(\":\");\n if (parts.length !== 3)\n return 0;\n\n const hours = Number.parseInt(parts[0], 10) || 0;\n const minutes = Number.parseInt(parts[1], 10) || 0;\n const seconds = Number.parseFloat(parts[2]) || 0;\n\n return hours * 3600 + minutes * 60 + seconds;\n}\n\n/**\n * Converts seconds to a human-readable timestamp.\n * Returns M:SS for durations under an hour, H:MM:SS for an hour or more.\n *\n * @param seconds - The number of seconds to convert\n * @returns A formatted timestamp string (e.g., \"2:05\" or \"01:02:05\")\n */\nexport function secondsToTimestamp(seconds: number): string {\n const rounded = Math.max(0, Math.floor(seconds));\n const hours = Math.floor(rounded / 3600);\n const minutes = Math.floor((rounded % 3600) / 60);\n const remainingSeconds = rounded % 60;\n\n if (hours > 0) {\n return `${hours.toString().padStart(2, \"0\")}:${minutes.toString().padStart(2, \"0\")}:${remainingSeconds.toString().padStart(2, \"0\")}`;\n }\n return `${minutes}:${remainingSeconds.toString().padStart(2, \"0\")}`;\n}\n\nexport function extractTimestampedTranscript(vttContent: string): string {\n if (!vttContent.trim()) {\n return \"\";\n }\n\n const lines = vttContent.split(\"\\n\");\n const segments: Array<{ time: number; text: string }> = [];\n\n for (let i = 0; i < lines.length; i++) {\n const line = lines[i].trim();\n\n if (line.includes(\"-->\")) {\n const startTime = line.split(\" --> \")[0].trim();\n const timeInSeconds = vttTimestampToSeconds(startTime);\n\n let j = i + 1;\n while (j < lines.length && !lines[j].trim()) {\n j++;\n }\n\n if (j < lines.length) {\n const text = lines[j].trim().replace(/<[^>]*>/g, \"\");\n if (text) {\n segments.push({ time: timeInSeconds, text });\n }\n }\n }\n }\n\n return segments\n .map(segment => `[${Math.floor(segment.time)}s] ${segment.text}`)\n .join(\"\\n\");\n}\n\n/**\n * Parses VTT content into structured cues with timing.\n *\n * @param vttContent - Raw VTT file content\n * @returns Array of VTT cues with start/end times and text\n */\nexport function parseVTTCues(vttContent: string): VTTCue[] {\n if (!vttContent.trim())\n return [];\n\n const lines = vttContent.split(\"\\n\");\n const cues: VTTCue[] = [];\n\n for (let i = 0; i < lines.length; i++) {\n const line = lines[i].trim();\n\n if (line.includes(\"-->\")) {\n const [startStr, endStr] = line.split(\" --> \").map(s => s.trim());\n const startTime = vttTimestampToSeconds(startStr);\n const endTime = vttTimestampToSeconds(endStr.split(\" \")[0]); // Handle cue settings\n\n // Collect text lines until empty line or next timestamp\n const textLines: string[] = [];\n let j = i + 1;\n while (j < lines.length && lines[j].trim() && !lines[j].includes(\"-->\")) {\n const cleanLine = lines[j].trim().replace(/<[^>]*>/g, \"\");\n if (cleanLine)\n textLines.push(cleanLine);\n j++;\n }\n\n if (textLines.length > 0) {\n cues.push({\n startTime,\n endTime,\n text: textLines.join(\" \"),\n });\n }\n }\n }\n\n return cues;\n}\n\n/**\n * Builds a transcript URL for the given playback ID and track ID.\n * If a signing context is provided, the URL will be signed with a token.\n *\n * @param playbackId - The Mux playback ID\n * @param trackId - The text track ID\n * @param shouldSign - Flag for whether or not to use signed playback IDs\n * @returns Transcript URL (signed if context provided)\n */\nexport async function buildTranscriptUrl(\n playbackId: string,\n trackId: string,\n shouldSign: boolean = false,\n credentials?: WorkflowCredentialsInput,\n): Promise<string> {\n \"use step\";\n const baseUrl = `https://stream.mux.com/${playbackId}/text/${trackId}.vtt`;\n\n if (shouldSign) {\n return signUrl(baseUrl, playbackId, \"video\", undefined, credentials);\n }\n\n return baseUrl;\n}\n\nexport async function fetchTranscriptForAsset(\n asset: MuxAsset,\n playbackId: string,\n options: TranscriptFetchOptions = {},\n): Promise<TranscriptResult> {\n \"use step\";\n const {\n languageCode,\n cleanTranscript = true,\n shouldSign,\n credentials,\n required = false,\n } = options;\n const track = findCaptionTrack(asset, languageCode);\n\n if (!track) {\n if (required) {\n const availableLanguages = getReadyTextTracks(asset)\n .map(t => t.language_code)\n .filter(Boolean)\n .join(\", \");\n throw new Error(\n `No transcript track found${languageCode ? ` for language '${languageCode}'` : \"\"}. Available languages: ${availableLanguages || \"none\"}`,\n );\n }\n return { transcriptText: \"\" };\n }\n\n if (!track.id) {\n if (required) {\n throw new Error(\"Transcript track is missing an id\");\n }\n return { transcriptText: \"\", track };\n }\n\n const transcriptUrl = await buildTranscriptUrl(playbackId, track.id, shouldSign, credentials);\n\n try {\n const response = await fetch(transcriptUrl);\n if (!response.ok) {\n if (required) {\n throw new Error(`Failed to fetch transcript (HTTP ${response.status})`);\n }\n return { transcriptText: \"\", transcriptUrl, track };\n }\n\n const rawVtt = await response.text();\n const transcriptText = cleanTranscript ? extractTextFromVTT(rawVtt) : rawVtt;\n\n if (required && !transcriptText.trim()) {\n throw new Error(\"Transcript is empty\");\n }\n\n return { transcriptText, transcriptUrl, track };\n } catch (error) {\n if (required) {\n throw new Error(\n `Failed to fetch transcript: ${error instanceof Error ? error.message : \"Unknown error\"}`,\n );\n }\n console.warn(\"Failed to fetch transcript:\", error);\n return { transcriptText: \"\", transcriptUrl, track };\n }\n}\n"],"mappings":"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","names":["env","bytes","chunkText"]}
1
+ {"version":3,"sources":["../../src/lib/providers.ts","../../src/env.ts","../../src/lib/workflow-crypto.ts","../../src/lib/workflow-credentials.ts","../../src/lib/client-factory.ts","../../src/primitives/heatmap.ts","../../src/primitives/hotspots.ts","../../src/lib/mux-image-url.ts","../../src/lib/url-signing.ts","../../src/primitives/storyboards.ts","../../src/primitives/text-chunking.ts","../../src/primitives/thumbnails.ts","../../src/primitives/transcripts.ts"],"sourcesContent":["import { createAnthropic } from \"@ai-sdk/anthropic\";\nimport { createGoogleGenerativeAI } from \"@ai-sdk/google\";\nimport { createOpenAI } from \"@ai-sdk/openai\";\n\nimport type { Env } from \"@mux/ai/env\";\nimport env from \"@mux/ai/env\";\nimport { resolveProviderApiKey } from \"@mux/ai/lib/workflow-credentials\";\nimport type { MuxAIOptions, WorkflowCredentialsInput } from \"@mux/ai/types\";\n\nimport type { EmbeddingModel, LanguageModel } from \"ai\";\n\nexport type SupportedProvider = \"openai\" | \"anthropic\" | \"google\";\nexport type SupportedEmbeddingProvider = \"openai\" | \"google\";\n\n// Model ID unions inferred from ai-sdk provider call signatures\ntype OpenAIModelId = Parameters<ReturnType<typeof createOpenAI>[\"chat\"]>[0];\ntype AnthropicModelId = Parameters<ReturnType<typeof createAnthropic>[\"chat\"]>[0];\ntype GoogleModelId = Parameters<ReturnType<typeof createGoogleGenerativeAI>[\"chat\"]>[0];\n\ntype OpenAIEmbeddingModelId = Parameters<ReturnType<typeof createOpenAI>[\"embedding\"]>[0];\ntype GoogleEmbeddingModelId = Parameters<ReturnType<typeof createGoogleGenerativeAI>[\"textEmbeddingModel\"]>[0];\n\nexport interface ModelIdByProvider {\n openai: OpenAIModelId;\n anthropic: AnthropicModelId;\n google: GoogleModelId;\n}\n\nexport interface EmbeddingModelIdByProvider {\n openai: OpenAIEmbeddingModelId;\n google: GoogleEmbeddingModelId;\n}\n\nexport interface ModelRequestOptions<P extends SupportedProvider = SupportedProvider> extends MuxAIOptions {\n provider?: P;\n model?: ModelIdByProvider[P];\n}\n\nexport interface ResolvedModel<P extends SupportedProvider = SupportedProvider> {\n provider: P;\n modelId: ModelIdByProvider[P];\n model: LanguageModel;\n}\n\nexport const DEFAULT_LANGUAGE_MODELS: { [K in SupportedProvider]: ModelIdByProvider[K] } = {\n openai: \"gpt-5.1\",\n anthropic: \"claude-sonnet-4-5\",\n google: \"gemini-3-flash-preview\",\n};\n\nconst DEFAULT_EMBEDDING_MODELS: { [K in SupportedEmbeddingProvider]: EmbeddingModelIdByProvider[K] } = {\n openai: \"text-embedding-3-small\",\n google: \"gemini-embedding-001\",\n};\n\n/**\n * All language models available per provider.\n * Includes the default model plus any additional models for evaluation and selection.\n * New models are additive — existing defaults are unchanged.\n */\nexport const LANGUAGE_MODELS: { [K in SupportedProvider]: ModelIdByProvider[K][] } = {\n openai: [\"gpt-5.1\", \"gpt-5-mini\"],\n anthropic: [\"claude-sonnet-4-5\"],\n google: [\"gemini-3-flash-preview\", \"gemini-3.1-flash-lite-preview\", \"gemini-2.5-flash\"],\n};\n\nexport type ModelDeprecationPhase = \"warn\" | \"blocked\";\n\n/**\n * Lifecycle metadata for a language model that is being phased out.\n *\n * @remarks\n * - \"warn\": model is still supported, but logs migration guidance.\n * - \"blocked\": model is no longer supported and throws with migration guidance.\n */\nexport interface LanguageModelDeprecation {\n provider: SupportedProvider;\n modelId: string;\n replacementModelId?: string;\n phase: ModelDeprecationPhase;\n deprecatedOn: string;\n sunsetOn?: string;\n reason?: string;\n}\n\n/**\n * Deprecated language models that remain supported during a grace period.\n * This enables gradual migration while clearly signaling planned removal.\n */\nexport const LANGUAGE_MODEL_DEPRECATIONS: LanguageModelDeprecation[] = [\n {\n provider: \"google\",\n modelId: \"gemini-2.5-flash\",\n replacementModelId: \"gemini-3.1-flash-lite-preview\",\n phase: \"warn\",\n deprecatedOn: \"2026-03-03\",\n sunsetOn: \"2026-06-30\",\n reason: \"Gemini 3.1 Flash-Lite Preview offers better quality/latency/cost balance in current evals.\",\n },\n];\n\nconst warnedDeprecatedLanguageModels = new Set<string>();\n\n/**\n * Returns deprecation metadata for a provider/model pair, if any.\n */\nexport function getLanguageModelDeprecation(\n provider: SupportedProvider,\n modelId: string,\n): LanguageModelDeprecation | undefined {\n return LANGUAGE_MODEL_DEPRECATIONS.find(\n deprecation => deprecation.provider === provider && deprecation.modelId === modelId,\n );\n}\n\nfunction maybeWarnOrThrowForDeprecatedLanguageModel(provider: SupportedProvider, modelId: string): void {\n const deprecation = getLanguageModelDeprecation(provider, modelId);\n if (!deprecation) {\n return;\n }\n\n const replacementText = deprecation.replacementModelId ?\n ` Use replacement provider=\"${provider}\" model=\"${deprecation.replacementModelId}\" instead.` :\n \"\";\n const sunsetText = deprecation.sunsetOn ? ` Planned removal date: ${deprecation.sunsetOn}.` : \"\";\n const reasonText = deprecation.reason ? ` Reason: ${deprecation.reason}` : \"\";\n\n const message =\n deprecation.phase === \"blocked\" ?\n `Language model is no longer supported for provider=\"${provider}\" model=\"${modelId}\".${replacementText}${reasonText}` :\n `Language model is deprecated and in a grace period for provider=\"${provider}\" model=\"${modelId}\".${replacementText}${sunsetText}${reasonText}`;\n\n if (deprecation.phase === \"blocked\") {\n throw new Error(message);\n }\n\n const warningKey = `${provider}:${modelId}`;\n if (warnedDeprecatedLanguageModels.has(warningKey)) {\n return;\n }\n\n warnedDeprecatedLanguageModels.add(warningKey);\n console.warn(message);\n}\n\nexport function resetLanguageModelDeprecationWarningsForTests(): void {\n warnedDeprecatedLanguageModels.clear();\n}\n\n/**\n * A (provider, modelId) pair used for evaluation iteration.\n */\nexport interface EvalModelConfig {\n provider: SupportedProvider;\n modelId: ModelIdByProvider[SupportedProvider];\n}\n\nexport type EvalModelSelection = \"default\" | \"all\";\n\nexport interface ResolveEvalModelConfigsOptions {\n selection?: EvalModelSelection;\n modelPairs?: string[];\n}\n\nfunction getDefaultEvalModelConfigs(): EvalModelConfig[] {\n return (Object.entries(DEFAULT_LANGUAGE_MODELS) as [SupportedProvider, ModelIdByProvider[SupportedProvider]][])\n .map(([provider, modelId]) => ({ provider, modelId }));\n}\n\nfunction getAllEvalModelConfigs(): EvalModelConfig[] {\n return (Object.entries(LANGUAGE_MODELS) as [SupportedProvider, ModelIdByProvider[SupportedProvider][]][])\n .flatMap(([provider, models]) => models.map(modelId => ({ provider, modelId })));\n}\n\nfunction isSupportedProvider(value: string): value is SupportedProvider {\n return value === \"openai\" || value === \"anthropic\" || value === \"google\";\n}\n\nfunction parseEvalModelPair(value: string): EvalModelConfig {\n const trimmed = value.trim();\n const [providerRaw, modelIdRaw] = trimmed.split(\":\", 2);\n const provider = providerRaw?.trim();\n const modelId = modelIdRaw?.trim();\n\n if (!provider || !modelId) {\n throw new Error(\n `Invalid eval model pair \"${value}\". Use \"provider:model\" (example: \"openai:gpt-5.1\").`,\n );\n }\n\n if (!isSupportedProvider(provider)) {\n throw new Error(\n `Unsupported eval provider \"${provider}\" in \"${value}\". Supported providers: ${Object.keys(LANGUAGE_MODELS).join(\", \")}.`,\n );\n }\n\n const supportedModels = LANGUAGE_MODELS[provider] as string[];\n if (!supportedModels.includes(modelId)) {\n throw new Error(\n `Unsupported eval model \"${modelId}\" for provider \"${provider}\". Supported models: ${supportedModels.join(\", \")}.`,\n );\n }\n\n maybeWarnOrThrowForDeprecatedLanguageModel(provider, modelId);\n\n return {\n provider,\n modelId: modelId as ModelIdByProvider[SupportedProvider],\n };\n}\n\n/**\n * Resolves eval model configurations.\n *\n * Selection order:\n * 1) Explicit model pairs (provider:model)\n * 2) Selection mode (\"default\" | \"all\")\n * 3) Default mode (\"default\")\n */\nexport function resolveEvalModelConfigs(options: ResolveEvalModelConfigsOptions = {}): EvalModelConfig[] {\n const explicitPairs = options.modelPairs?.map(value => value.trim()).filter(Boolean) ?? [];\n if (explicitPairs.length > 0) {\n const dedupedPairs = Array.from(new Set(explicitPairs));\n return dedupedPairs.map(parseEvalModelPair);\n }\n\n const selection = options.selection ?? \"default\";\n if (selection === \"all\") {\n return getAllEvalModelConfigs();\n }\n\n return getDefaultEvalModelConfigs();\n}\n\n/**\n * Environment variables for selecting eval models at runtime.\n *\n * - MUX_AI_EVAL_MODEL_SET: \"default\" | \"all\" (default: \"default\")\n * - MUX_AI_EVAL_MODELS: comma-separated \"provider:model\" pairs\n * (takes precedence over MUX_AI_EVAL_MODEL_SET)\n */\nexport function resolveEvalModelConfigsFromEnv(environment: Env = env): EvalModelConfig[] {\n const rawSelection = environment.MUX_AI_EVAL_MODEL_SET?.trim();\n const rawModelPairs = environment.MUX_AI_EVAL_MODELS?.trim();\n let selection: EvalModelSelection;\n if (!rawSelection || rawSelection === \"default\") {\n selection = \"default\";\n } else if (rawSelection === \"all\") {\n selection = \"all\";\n } else {\n throw new Error(\n `Invalid MUX_AI_EVAL_MODEL_SET=\"${rawSelection}\". Expected \"default\" or \"all\".`,\n );\n }\n\n let modelPairs: string[] | undefined;\n if (rawModelPairs) {\n modelPairs = rawModelPairs.split(\",\").map(value => value.trim()).filter(Boolean);\n }\n\n return resolveEvalModelConfigs({\n selection,\n modelPairs,\n });\n}\n\n/**\n * Flattened list of (provider, modelId) pairs for evaluation iteration.\n * Resolved from env so eval runs can target default models, all models, or an explicit list.\n */\nexport const EVAL_MODEL_CONFIGS: EvalModelConfig[] = resolveEvalModelConfigsFromEnv();\n\nexport function resolveLanguageModelConfig<P extends SupportedProvider = SupportedProvider>(\n options: ModelRequestOptions<P> = {},\n): { provider: P; modelId: ModelIdByProvider[P] } {\n const provider = options.provider || (\"openai\" as P);\n const modelId = (options.model || DEFAULT_LANGUAGE_MODELS[provider]) as ModelIdByProvider[P];\n maybeWarnOrThrowForDeprecatedLanguageModel(provider, modelId);\n\n return { provider, modelId };\n}\n\nexport function resolveEmbeddingModelConfig<P extends SupportedEmbeddingProvider = \"openai\">(\n options: MuxAIOptions & { provider?: P; model?: EmbeddingModelIdByProvider[P] } = {},\n): { provider: P; modelId: EmbeddingModelIdByProvider[P] } {\n const provider = options.provider || (\"openai\" as P);\n const modelId = (options.model || DEFAULT_EMBEDDING_MODELS[provider]) as EmbeddingModelIdByProvider[P];\n\n return { provider, modelId };\n}\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Model Pricing\n// ─────────────────────────────────────────────────────────────────────────────\n//\n// Pricing is in USD per million tokens. These values are used for cost estimation\n// in evaluations and should be periodically verified against official sources.\n//\n// Sources (verified on 2026-02-17):\n// - OpenAI: https://openai.com/api/pricing\n// - Anthropic: https://www.anthropic.com/pricing\n// - Google: https://ai.google.dev/gemini-api/docs/pricing\n// ─────────────────────────────────────────────────────────────────────────────\n\n/**\n * Pricing structure for a language model.\n * All costs are in USD per million tokens.\n */\nexport interface ModelPricing {\n /** Cost per million input tokens (USD). */\n inputPerMillion: number;\n /** Cost per million output tokens (USD). */\n outputPerMillion: number;\n /** Cost per million cached input tokens (USD), if supported. */\n cachedInputPerMillion?: number;\n /** URL to the official pricing page for verification. */\n pricingUrl: string;\n}\n\n/**\n * Per-model pricing data for all supported language models.\n * Used for model-specific cost estimation in evaluations and expense tracking.\n *\n * @remarks\n * Prices are subject to change. Verify against official sources before production use.\n * When adding a new model to LANGUAGE_MODELS, add its pricing here as well.\n */\nexport const MODEL_PRICING: Record<string, ModelPricing> = {\n // OpenAI models\n // Reference: https://openai.com/api/pricing\n \"gpt-5.1\": {\n inputPerMillion: 1.25,\n outputPerMillion: 10.00,\n cachedInputPerMillion: 0.125,\n pricingUrl: \"https://openai.com/api/pricing\",\n },\n \"gpt-5-mini\": {\n inputPerMillion: 0.25,\n outputPerMillion: 2.00,\n cachedInputPerMillion: 0.025,\n pricingUrl: \"https://openai.com/api/pricing\",\n },\n\n // Anthropic models\n // Reference: https://www.anthropic.com/pricing\n \"claude-sonnet-4-5\": {\n inputPerMillion: 3.00,\n outputPerMillion: 15.00,\n cachedInputPerMillion: 0.30,\n pricingUrl: \"https://www.anthropic.com/pricing\",\n },\n\n // Google models\n // Reference: https://ai.google.dev/pricing\n \"gemini-3.1-flash-lite-preview\": {\n inputPerMillion: 0.25,\n outputPerMillion: 1.50,\n cachedInputPerMillion: 0.025,\n pricingUrl: \"https://ai.google.dev/gemini-api/docs/pricing\",\n },\n \"gemini-3-flash-preview\": {\n inputPerMillion: 0.50,\n outputPerMillion: 3.00,\n cachedInputPerMillion: 0.05,\n pricingUrl: \"https://ai.google.dev/gemini-api/docs/pricing\",\n },\n \"gemini-2.5-flash\": {\n inputPerMillion: 0.30,\n outputPerMillion: 2.50,\n cachedInputPerMillion: 0.03,\n pricingUrl: \"https://ai.google.dev/gemini-api/docs/pricing\",\n },\n};\n\n/**\n * Calculates the estimated cost for a request based on token usage and model-specific pricing.\n *\n * @param modelId - The specific model ID used\n * @param inputTokens - Number of input tokens consumed\n * @param outputTokens - Number of output tokens generated\n * @param cachedInputTokens - Number of input tokens served from cache (optional)\n * @returns Estimated cost in USD\n *\n * @example\n * ```typescript\n * const cost = calculateModelCost('gpt-5-mini', 2000, 500);\n * console.log(`Estimated cost: $${cost.toFixed(6)}`);\n * ```\n */\nexport function calculateModelCost(\n modelId: string,\n inputTokens: number,\n outputTokens: number,\n cachedInputTokens: number = 0,\n): number {\n const pricing = MODEL_PRICING[modelId];\n if (!pricing) {\n throw new Error(`No pricing data for model: ${modelId}. Add pricing to MODEL_PRICING in providers.ts.`);\n }\n\n const uncachedInputTokens = Math.max(0, inputTokens - cachedInputTokens);\n\n const inputCost = (uncachedInputTokens / 1_000_000) * pricing.inputPerMillion;\n const outputCost = (outputTokens / 1_000_000) * pricing.outputPerMillion;\n let cachedCost = 0;\n if (pricing.cachedInputPerMillion) {\n cachedCost = (cachedInputTokens / 1_000_000) * pricing.cachedInputPerMillion;\n }\n\n return inputCost + outputCost + cachedCost;\n}\n\n/**\n * Calculates the estimated cost for a request based on token usage.\n * Uses each provider's default model pricing from MODEL_PRICING.\n *\n * @param provider - The AI provider used\n * @param inputTokens - Number of input tokens consumed\n * @param outputTokens - Number of output tokens generated\n * @param cachedInputTokens - Number of input tokens served from cache (optional)\n * @returns Estimated cost in USD\n *\n * @example\n * ```typescript\n * const cost = calculateCost('openai', 2000, 500);\n * console.log(`Estimated cost: $${cost.toFixed(6)}`);\n * ```\n */\nexport function calculateCost(\n provider: SupportedProvider,\n inputTokens: number,\n outputTokens: number,\n cachedInputTokens: number = 0,\n): number {\n const defaultModelId = DEFAULT_LANGUAGE_MODELS[provider];\n return calculateModelCost(defaultModelId, inputTokens, outputTokens, cachedInputTokens);\n}\n\nfunction requireEnv(value: string | undefined, name: string): string {\n if (!value) {\n throw new Error(`Missing ${name}. Set ${name} in your environment or pass it in options.`);\n }\n return value;\n}\n\n/**\n * Creates a language model instance from serializable config.\n * Use this in steps to instantiate models from config passed through workflow.\n * Fetches credentials internally from environment variables to avoid exposing them in step I/O.\n */\nexport async function createLanguageModelFromConfig<P extends SupportedProvider = SupportedProvider>(\n provider: P,\n modelId: ModelIdByProvider[P],\n credentials?: WorkflowCredentialsInput,\n): Promise<LanguageModel> {\n maybeWarnOrThrowForDeprecatedLanguageModel(provider, modelId);\n\n switch (provider) {\n case \"openai\": {\n const apiKey = await resolveProviderApiKey(\"openai\", credentials);\n const openai = createOpenAI({ apiKey });\n return openai(modelId);\n }\n case \"anthropic\": {\n const apiKey = await resolveProviderApiKey(\"anthropic\", credentials);\n const anthropic = createAnthropic({ apiKey });\n return anthropic(modelId);\n }\n case \"google\": {\n const apiKey = await resolveProviderApiKey(\"google\", credentials);\n const google = createGoogleGenerativeAI({ apiKey });\n return google(modelId);\n }\n default: {\n const exhaustiveCheck: never = provider;\n throw new Error(`Unsupported provider: ${exhaustiveCheck}`);\n }\n }\n}\n\n/**\n * Creates an embedding model instance from serializable config.\n * Use this in steps to instantiate embedding models from config passed through workflow.\n * Fetches credentials internally from environment variables to avoid exposing them in step I/O.\n */\nexport async function createEmbeddingModelFromConfig<\n P extends SupportedEmbeddingProvider = SupportedEmbeddingProvider,\n>(\n provider: P,\n modelId: EmbeddingModelIdByProvider[P],\n credentials?: WorkflowCredentialsInput,\n): Promise<EmbeddingModel> {\n switch (provider) {\n case \"openai\": {\n const apiKey = await resolveProviderApiKey(\"openai\", credentials);\n const openai = createOpenAI({ apiKey });\n return openai.embedding(modelId);\n }\n case \"google\": {\n const apiKey = await resolveProviderApiKey(\"google\", credentials);\n const google = createGoogleGenerativeAI({ apiKey });\n return google.textEmbeddingModel(modelId);\n }\n default: {\n const exhaustiveCheck: never = provider;\n throw new Error(`Unsupported embedding provider: ${exhaustiveCheck}`);\n }\n }\n}\n\n/**\n * Resolves a language model from a suggested provider.\n */\nexport function resolveLanguageModel<P extends SupportedProvider = SupportedProvider>(\n options: ModelRequestOptions<P> = {},\n): ResolvedModel<P> {\n const provider = options.provider || (\"openai\" as P);\n const modelId = (options.model || DEFAULT_LANGUAGE_MODELS[provider]) as ModelIdByProvider[P];\n maybeWarnOrThrowForDeprecatedLanguageModel(provider, modelId);\n\n switch (provider) {\n case \"openai\": {\n const apiKey = env.OPENAI_API_KEY;\n requireEnv(apiKey, \"OPENAI_API_KEY\");\n const openai = createOpenAI({\n apiKey,\n });\n\n return {\n provider,\n modelId,\n model: openai(modelId),\n };\n }\n case \"anthropic\": {\n const apiKey = env.ANTHROPIC_API_KEY;\n requireEnv(apiKey, \"ANTHROPIC_API_KEY\");\n const anthropic = createAnthropic({\n apiKey,\n });\n\n return {\n provider,\n modelId,\n model: anthropic(modelId),\n };\n }\n case \"google\": {\n const apiKey = env.GOOGLE_GENERATIVE_AI_API_KEY;\n requireEnv(apiKey, \"GOOGLE_GENERATIVE_AI_API_KEY\");\n const google = createGoogleGenerativeAI({\n apiKey,\n });\n\n return {\n provider,\n modelId,\n model: google(modelId),\n };\n }\n default: {\n const exhaustiveCheck: never = provider;\n throw new Error(`Unsupported provider: ${exhaustiveCheck}`);\n }\n }\n}\n\n/**\n * Resolves an embedding model from a suggested provider.\n */\nexport function resolveEmbeddingModel<P extends SupportedEmbeddingProvider = \"openai\">(\n options: MuxAIOptions & { provider?: P; model?: EmbeddingModelIdByProvider[P] } = {},\n): { provider: P; modelId: EmbeddingModelIdByProvider[P]; model: EmbeddingModel } {\n const provider = options.provider || (\"openai\" as P);\n const modelId = (options.model || DEFAULT_EMBEDDING_MODELS[provider]) as EmbeddingModelIdByProvider[P];\n\n switch (provider) {\n case \"openai\": {\n const apiKey = env.OPENAI_API_KEY;\n requireEnv(apiKey, \"OPENAI_API_KEY\");\n const openai = createOpenAI({\n apiKey,\n });\n\n return {\n provider,\n modelId,\n model: openai.embedding(modelId),\n };\n }\n case \"google\": {\n const apiKey = env.GOOGLE_GENERATIVE_AI_API_KEY;\n requireEnv(apiKey, \"GOOGLE_GENERATIVE_AI_API_KEY\");\n const google = createGoogleGenerativeAI({\n apiKey,\n });\n\n return {\n provider,\n modelId,\n model: google.textEmbeddingModel(modelId),\n };\n }\n default: {\n const exhaustiveCheck: never = provider;\n throw new Error(`Unsupported embedding provider: ${exhaustiveCheck}`);\n }\n }\n}\n","/* eslint-disable node/no-process-env */\n\nimport { z } from \"zod\";\n\nimport \"dotenv/config\";\n\nfunction optionalString(description: string, message?: string) {\n return z.preprocess(\n value => typeof value === \"string\" && value.trim().length === 0 ? undefined : value,\n z.string().trim().min(1, message).optional(),\n ).describe(description);\n}\n\n// eslint-disable-next-line unused-imports/no-unused-vars\nfunction requiredString(description: string, message?: string) {\n return z.preprocess(\n value => typeof value === \"string\" ? value.trim().length > 0 ? value.trim() : undefined : value,\n z.string().trim().min(1, message),\n ).describe(description);\n}\n\nconst EnvSchema = z.object({\n NODE_ENV: z.string().default(\"development\").describe(\"Runtime environment.\"),\n\n MUX_TOKEN_ID: optionalString(\"Mux access token ID.\", \"Required to access Mux APIs\"),\n MUX_TOKEN_SECRET: optionalString(\"Mux access token secret.\", \"Required to access Mux APIs\"),\n MUX_AI_WORKFLOW_SECRET_KEY: optionalString(\n \"Base64-encoded 32-byte key for workflow encryption/decryption.\",\n \"Workflow secret key\",\n ),\n EVALITE_INGEST_SECRET: optionalString(\n \"Shared secret for posting Evalite results.\",\n \"Evalite ingest secret\",\n ),\n\n MUX_SIGNING_KEY: optionalString(\"Mux signing key ID for signed playback URLs.\", \"Used to sign playback URLs\"),\n MUX_PRIVATE_KEY: optionalString(\"Mux signing private key for signed playback URLs.\", \"Used to sign playback URLs\"),\n MUX_IMAGE_URL_OVERRIDE: optionalString(\n \"Override for Mux image base URL (defaults to https://image.mux.com).\",\n \"Mux image URL override\",\n ),\n\n // Test-only helpers (used by this repo's integration tests)\n MUX_TEST_ASSET_ID: optionalString(\"Mux asset ID used by integration tests.\", \"Mux test asset id\"),\n MUX_TEST_ASSET_ID_CHAPTERS: optionalString(\"Mux asset ID used by integration tests for chapters.\", \"Mux test asset id for chapters\"),\n MUX_TEST_ASSET_ID_VIOLENT: optionalString(\"Mux violent asset ID used by integration tests.\", \"Mux violent test asset id\"),\n MUX_TEST_ASSET_ID_BURNED_IN_CAPTIONS: optionalString(\n \"Mux burned-in captions asset ID used by integration tests.\",\n \"Mux burned-in captions test asset id\",\n ),\n MUX_TEST_ASSET_ID_BURNED_IN_CAPTIONS_2: optionalString(\n \"Mux burned-in captions asset ID 2 (a different asset) used by integration tests.\",\n \"Mux burned-in captions test asset id 2 (a different asset)\",\n ),\n MUX_TEST_ASSET_ID_WITHOUT_BURNED_IN_CAPTIONS: optionalString(\n \"Mux without burned-in captions asset ID used by integration tests.\",\n \"Mux without burned-in captions test asset id\",\n ),\n MUX_TEST_ASSET_ID_AUDIO_ONLY: optionalString(\"Mux test asset ID for audio-only assets.\", \"Mux test asset id for audio-only assets for testing\"),\n MUX_TEST_ASSET_ID_VIOLENT_AUDIO_ONLY: optionalString(\"Mux test asset ID for audio-only assets with violent content.\", \"Mux test asset id for audio-only assets with violent content for testing\"),\n\n // Eval config\n MUX_AI_EVAL_MODEL_SET: optionalString(\"Eval model selection mode.\", \"Choose between 'default' (provider defaults only) or 'all' (all configured models)\"),\n MUX_AI_EVAL_MODELS: optionalString(\"Comma-separated eval model pairs.\", \"Comma-separated provider:model pairs (e.g. 'openai:gpt-5.1,anthropic:claude-sonnet-4-5,google:gemini-3-flash-preview')\"),\n\n // AI Providers\n OPENAI_API_KEY: optionalString(\"OpenAI API key for OpenAI-backed workflows.\", \"OpenAI API key\"),\n ANTHROPIC_API_KEY: optionalString(\"Anthropic API key for Claude-backed workflows.\", \"Anthropic API key\"),\n GOOGLE_GENERATIVE_AI_API_KEY: optionalString(\"Google Generative AI API key for Gemini-backed workflows.\", \"Google Generative AI API key\"),\n\n ELEVENLABS_API_KEY: optionalString(\"ElevenLabs API key for audio translation.\", \"ElevenLabs API key\"),\n HIVE_API_KEY: optionalString(\"Hive Visual Moderation API key.\", \"Hive API key\"),\n\n // S3-Compatible Storage (required for translation & audio dubbing)\n S3_ENDPOINT: optionalString(\"S3-compatible endpoint for uploads.\", \"S3 endpoint\"),\n S3_REGION: optionalString(\"S3 region (defaults to 'auto' when omitted).\"),\n S3_BUCKET: optionalString(\"Bucket used for caption and audio uploads.\", \"S3 bucket\"),\n S3_ACCESS_KEY_ID: optionalString(\"Access key ID for S3-compatible uploads.\", \"S3 access key id\"),\n S3_SECRET_ACCESS_KEY: optionalString(\"Secret access key for S3-compatible uploads.\", \"S3 secret access key\"),\n S3_ALLOWED_ENDPOINT_HOSTS: optionalString(\n \"Comma-separated S3 endpoint allowlist (supports exact hosts and *.suffix patterns).\",\n ),\n\n EVALITE_RESULTS_ENDPOINT: optionalString(\n \"Full URL for posting Evalite results (e.g., https://example.com/api/evalite-results).\",\n \"Evalite results endpoint\",\n ),\n}).refine(\n (env) => {\n const hasMuxCredentials = Boolean(env.MUX_TOKEN_ID && env.MUX_TOKEN_SECRET);\n const hasWorkflowKey = Boolean(env.MUX_AI_WORKFLOW_SECRET_KEY);\n return hasMuxCredentials || hasWorkflowKey;\n },\n {\n message: \"Either MUX_TOKEN_ID + MUX_TOKEN_SECRET or MUX_AI_WORKFLOW_SECRET_KEY must be set.\",\n },\n);\n\nexport type Env = z.infer<typeof EnvSchema>;\n\nfunction parseEnv(): Env {\n const parsedEnv = EnvSchema.safeParse(process.env);\n\n if (!parsedEnv.success) {\n console.error(\"❌ Invalid env:\");\n console.error(JSON.stringify(parsedEnv.error.flatten().fieldErrors, null, 2));\n process.exit(1);\n }\n\n return parsedEnv.data;\n}\n\nconst env: Env = parseEnv();\n\nexport function reloadEnv(): Env {\n const parsed = parseEnv();\n Object.assign(env, parsed);\n return env;\n}\n\nexport { env };\nexport default env;\n","/**\n * Workflow Crypto\n *\n * Provides AES-256-GCM encryption for securely passing credentials to workflows.\n * Encrypted payloads are JSON-serializable and include version/algorithm metadata\n * for forward compatibility.\n */\n\nimport { gcm } from \"@noble/ciphers/aes.js\";\n\nconst BASE64_CHUNK_SIZE = 0x8000;\nconst BASE64_ALPHABET = \"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/\";\nconst BASE64_LOOKUP = (() => {\n const table = new Uint8Array(256);\n table.fill(255);\n for (let i = 0; i < BASE64_ALPHABET.length; i++) {\n table[BASE64_ALPHABET.charCodeAt(i)] = i;\n }\n return table;\n})();\n\n// Encryption parameters (AES-256-GCM with standard IV/tag sizes)\nconst WORKFLOW_ENCRYPTION_VERSION = 1;\nconst WORKFLOW_ENCRYPTION_ALGORITHM = \"aes-256-gcm\" as const;\nconst IV_LENGTH_BYTES = 12; // 96-bit IV per NIST recommendation for GCM\nconst AUTH_TAG_LENGTH_BYTES = 16; // 128-bit authentication tag\n\n/**\n * Structure of an encrypted payload (all fields are base64-encoded where applicable).\n *\n * The optional `kid` (key ID) field supports key rotation scenarios:\n * - Include a `kid` when encrypting to identify which key was used\n * - On decryption, read `payload.kid` to look up the correct key\n * - Keys should be invalidated/deleted after rotation, not kept indefinitely\n *\n * Security notes:\n * - `kid` is stored in plaintext (not encrypted) - don't put sensitive data in it\n * - Tampering with `kid` doesn't weaken security - wrong key = decryption fails\n */\nexport interface EncryptedPayload {\n v: typeof WORKFLOW_ENCRYPTION_VERSION;\n alg: typeof WORKFLOW_ENCRYPTION_ALGORITHM;\n kid?: string;\n iv: string;\n tag: string;\n ciphertext: string;\n}\n\n/** Branded type that preserves the original type information for decryption */\nexport type Encrypted<T> = EncryptedPayload & { __type?: T };\n\nfunction getWebCrypto(): NonNullable<typeof globalThis.crypto> {\n const webCrypto = globalThis.crypto;\n if (!webCrypto || typeof webCrypto.getRandomValues !== \"function\") {\n throw new Error(\"Web Crypto API is required in workflow functions.\");\n }\n return webCrypto;\n}\n\nfunction bytesToBase64(bytes: Uint8Array): string {\n if (bytes.length === 0) {\n return \"\";\n }\n const btoaImpl = typeof globalThis.btoa === \"function\" ? globalThis.btoa.bind(globalThis) : undefined;\n if (btoaImpl) {\n let binary = \"\";\n for (let i = 0; i < bytes.length; i += BASE64_CHUNK_SIZE) {\n const chunk = bytes.subarray(i, i + BASE64_CHUNK_SIZE);\n binary += String.fromCharCode(...chunk);\n }\n return btoaImpl(binary);\n }\n\n let output = \"\";\n for (let i = 0; i < bytes.length; i += 3) {\n const b0 = bytes[i] ?? 0;\n const b1 = bytes[i + 1] ?? 0;\n const b2 = bytes[i + 2] ?? 0;\n const triple = (b0 << 16) | (b1 << 8) | b2;\n output += BASE64_ALPHABET[(triple >> 18) & 63];\n output += BASE64_ALPHABET[(triple >> 12) & 63];\n output += i + 1 < bytes.length ? BASE64_ALPHABET[(triple >> 6) & 63] : \"=\";\n output += i + 2 < bytes.length ? BASE64_ALPHABET[triple & 63] : \"=\";\n }\n return output;\n}\n\nfunction normalizeBase64Input(value: string): string {\n const cleaned = value\n .replace(/\\s+/g, \"\")\n .replace(/-/g, \"+\")\n .replace(/_/g, \"/\");\n\n // Pad with '=' to make the length a multiple of 4 (required by standard base64).\n // When cleaned is empty, length is 0 and 0 % 4 === 0, so it passes through unchanged.\n return cleaned?.length % 4 === 0 ? cleaned : cleaned + \"=\".repeat(4 - (cleaned.length % 4));\n}\n\nfunction base64ToBytes(value: string, label: string): Uint8Array {\n if (!value) {\n throw new Error(`${label} is missing`);\n }\n const normalized = normalizeBase64Input(value);\n const atobImpl = typeof globalThis.atob === \"function\" ? globalThis.atob.bind(globalThis) : undefined;\n if (atobImpl) {\n let binary: string;\n try {\n binary = atobImpl(normalized);\n } catch {\n throw new Error(`${label} is not valid base64`);\n }\n if (!binary) {\n throw new Error(`${label} decoded to empty value`);\n }\n const bytes = new Uint8Array(binary.length);\n for (let i = 0; i < binary.length; i++) {\n bytes[i] = binary.charCodeAt(i);\n }\n return bytes;\n }\n\n if (normalized.length % 4 !== 0) {\n throw new Error(`${label} is not valid base64`);\n }\n\n const padding = normalized.endsWith(\"==\") ? 2 : normalized.endsWith(\"=\") ? 1 : 0;\n const outputLength = (normalized.length / 4) * 3 - padding;\n const bytes = new Uint8Array(outputLength);\n let offset = 0;\n\n for (let i = 0; i < normalized.length; i += 4) {\n const c0 = normalized.charCodeAt(i);\n const c1 = normalized.charCodeAt(i + 1);\n const c2 = normalized.charCodeAt(i + 2);\n const c3 = normalized.charCodeAt(i + 3);\n\n if (c0 === 61 || c1 === 61) {\n throw new Error(`${label} is not valid base64`);\n }\n\n const n0 = BASE64_LOOKUP[c0] ?? 255;\n const n1 = BASE64_LOOKUP[c1] ?? 255;\n const n2 = c2 === 61 ? 0 : (BASE64_LOOKUP[c2] ?? 255);\n const n3 = c3 === 61 ? 0 : (BASE64_LOOKUP[c3] ?? 255);\n\n if (n0 === 255 || n1 === 255 || n2 === 255 || n3 === 255) {\n throw new Error(`${label} is not valid base64`);\n }\n\n const triple = (n0 << 18) | (n1 << 12) | (n2 << 6) | n3;\n bytes[offset++] = (triple >> 16) & 0xFF;\n if (c2 !== 61) {\n bytes[offset++] = (triple >> 8) & 0xFF;\n }\n if (c3 !== 61) {\n bytes[offset++] = triple & 0xFF;\n }\n }\n\n if (!bytes.length) {\n throw new Error(`${label} decoded to empty value`);\n }\n\n return bytes;\n}\n\n/** Wraps base64ToBytes for encrypted payload fields, adding context to errors */\nfunction payloadBase64ToBytes(value: string, field: string): Uint8Array {\n try {\n return base64ToBytes(value, field);\n } catch (error) {\n const detail = error instanceof Error ? error.message : `${field} is invalid`;\n throw new Error(`Invalid encrypted payload: ${detail}`);\n }\n}\n\n/** Converts key to bytes and validates it's exactly 32 bytes (256 bits) */\nfunction normalizeKey(key: Uint8Array | string): Uint8Array {\n let keyBytes: Uint8Array;\n if (typeof key === \"string\") {\n try {\n keyBytes = base64ToBytes(key, \"value\");\n } catch (error) {\n const detail = error instanceof Error ? error.message : \"value is not valid base64\";\n throw new Error(`Invalid workflow secret key: ${detail}. Expected 32-byte base64 value.`);\n }\n } else {\n keyBytes = new Uint8Array(key);\n }\n\n if (keyBytes.length !== 32) {\n throw new Error(`Invalid workflow secret key: expected 32 bytes, got ${keyBytes.length}.`);\n }\n\n return keyBytes;\n}\n\n/** Type guard to check if a value is a valid encrypted payload structure */\nexport function isEncryptedPayload(value: unknown): value is EncryptedPayload {\n if (!value || typeof value !== \"object\") {\n return false;\n }\n\n const payload = value as EncryptedPayload;\n return (\n payload.v === WORKFLOW_ENCRYPTION_VERSION &&\n payload.alg === WORKFLOW_ENCRYPTION_ALGORITHM &&\n typeof payload.iv === \"string\" &&\n typeof payload.tag === \"string\" &&\n typeof payload.ciphertext === \"string\"\n );\n}\n\n/** Validates payload structure and cryptographic parameters before decryption */\nfunction assertEncryptedPayload(payload: EncryptedPayload): void {\n if (payload.v !== WORKFLOW_ENCRYPTION_VERSION) {\n throw new Error(\"Invalid encrypted payload: unsupported version.\");\n }\n\n if (payload.alg !== WORKFLOW_ENCRYPTION_ALGORITHM) {\n throw new Error(\"Invalid encrypted payload: unsupported algorithm.\");\n }\n\n const iv = payloadBase64ToBytes(payload.iv, \"iv\");\n const tag = payloadBase64ToBytes(payload.tag, \"tag\");\n\n if (iv.length !== IV_LENGTH_BYTES) {\n throw new Error(\"Invalid encrypted payload: iv length mismatch.\");\n }\n\n if (tag.length !== AUTH_TAG_LENGTH_BYTES) {\n throw new Error(\"Invalid encrypted payload: tag length mismatch.\");\n }\n\n payloadBase64ToBytes(payload.ciphertext, \"ciphertext\");\n}\n\n/**\n * Encrypts a value for secure transport to a workflow.\n *\n * @param value - Any JSON-serializable value (typically WorkflowCredentials)\n * @param key - 32-byte secret key (base64 string or Uint8Array)\n * @param keyId - Optional key identifier for rotation support (stored in plaintext)\n * @returns Encrypted payload with metadata, safe to pass through untrusted channels\n */\nexport async function encryptForWorkflow<T>(\n value: T,\n key: Uint8Array | string,\n keyId?: string,\n): Promise<Encrypted<T>> {\n const keyBytes = normalizeKey(key);\n const webCrypto = getWebCrypto();\n const iv = new Uint8Array(IV_LENGTH_BYTES);\n webCrypto.getRandomValues(iv); // Fresh IV for each encryption\n\n let serialized: string;\n try {\n serialized = JSON.stringify(value);\n } catch {\n throw new Error(\"Failed to serialize value for encryption.\");\n }\n\n const encoder = new TextEncoder();\n const plaintext = encoder.encode(serialized);\n const encryptedBytes = gcm(keyBytes, iv).encrypt(plaintext);\n const tag = encryptedBytes.slice(encryptedBytes.length - AUTH_TAG_LENGTH_BYTES);\n const ciphertext = encryptedBytes.slice(0, encryptedBytes.length - AUTH_TAG_LENGTH_BYTES);\n\n return {\n v: WORKFLOW_ENCRYPTION_VERSION,\n alg: WORKFLOW_ENCRYPTION_ALGORITHM,\n ...(keyId !== undefined && { kid: keyId }),\n iv: bytesToBase64(iv),\n tag: bytesToBase64(tag),\n ciphertext: bytesToBase64(ciphertext),\n };\n}\n\n/**\n * Decrypts and deserializes a workflow payload.\n *\n * @param payload - Encrypted payload from `encryptForWorkflow`\n * @param key - Same 32-byte secret key used for encryption\n * @returns The original decrypted value\n * @throws If payload is invalid, tampered with, or key is wrong\n */\nexport async function decryptFromWorkflow<T>(\n payload: EncryptedPayload,\n key: Uint8Array | string,\n): Promise<T> {\n if (!isEncryptedPayload(payload)) {\n throw new Error(\"Invalid encrypted payload.\");\n }\n\n assertEncryptedPayload(payload);\n\n const keyBytes = normalizeKey(key);\n const iv = payloadBase64ToBytes(payload.iv, \"iv\");\n const tag = payloadBase64ToBytes(payload.tag, \"tag\");\n const ciphertext = payloadBase64ToBytes(payload.ciphertext, \"ciphertext\");\n\n const combined = new Uint8Array(ciphertext.length + tag.length);\n combined.set(ciphertext);\n combined.set(tag, ciphertext.length);\n\n let plaintext: Uint8Array;\n try {\n plaintext = gcm(keyBytes, iv).decrypt(combined);\n } catch (error) {\n const message = (error as { message?: string } | undefined)?.message;\n throw new Error(`Failed to decrypt workflow payload. ${message ?? String(error)}`);\n }\n\n try {\n const decoder = new TextDecoder();\n return JSON.parse(decoder.decode(plaintext)) as T;\n } catch {\n throw new Error(\"Failed to parse decrypted payload.\");\n }\n}\n","/**\n * Workflow Credentials Management\n *\n * This module provides a unified way to resolve credentials from multiple sources:\n * 1. A custom credentials provider (set via `setWorkflowCredentialsProvider`)\n * 2. Encrypted credentials passed directly to workflow functions\n * 3. Environment variables as fallback\n *\n * Credentials are merged in order of precedence: direct input > provider > environment.\n */\nimport env from \"@mux/ai/env\";\nimport type { Env } from \"@mux/ai/env\";\nimport type { SigningContext } from \"@mux/ai/lib/url-signing\";\nimport { decryptFromWorkflow, isEncryptedPayload } from \"@mux/ai/lib/workflow-crypto\";\nimport type {\n WorkflowCredentials,\n WorkflowCredentialsInput,\n WorkflowMuxClient,\n} from \"@mux/ai/types\";\n\n/**\n * A function that returns workflow credentials, either synchronously or asynchronously.\n * Used to inject credentials from external sources (e.g., a secrets manager).\n */\nexport type WorkflowCredentialsProvider =\n () => Promise<WorkflowCredentials | undefined> | WorkflowCredentials | undefined;\n\n/** Module-level credentials provider, set via `setWorkflowCredentialsProvider` */\nlet workflowCredentialsProvider: WorkflowCredentialsProvider | undefined;\n\n/**\n * Registers a custom credentials provider for the module.\n * The provider will be called whenever credentials need to be resolved.\n */\nexport function setWorkflowCredentialsProvider(provider?: WorkflowCredentialsProvider): void {\n workflowCredentialsProvider = provider;\n}\n\n/**\n * Detects whether code is running inside a Workflow Dev Kit runtime.\n * getWorkflowMetadata throws when invoked outside a workflow.\n */\nasync function isWorkflowRuntime(): Promise<boolean> {\n try {\n const workflowModule = await import(\"workflow\");\n if (typeof workflowModule.getWorkflowMetadata !== \"function\") {\n return false;\n }\n workflowModule.getWorkflowMetadata();\n return true;\n } catch {\n return false;\n }\n}\n\n/**\n * Determines if we should enforce encrypted credentials.\n * This triggers in workflow runtimes or when the workflow secret key is set.\n */\nasync function shouldEnforceEncryptedCredentials(): Promise<boolean> {\n return Boolean(env.MUX_AI_WORKFLOW_SECRET_KEY) || await isWorkflowRuntime();\n}\n\n/**\n * Retrieves the workflow secret key from environment variables.\n * This key is used to decrypt encrypted credential payloads.\n */\nfunction getWorkflowSecretKeyFromEnv(): string {\n const key = env.MUX_AI_WORKFLOW_SECRET_KEY;\n if (!key) {\n throw new Error(\n \"Workflow secret key is required. Set MUX_AI_WORKFLOW_SECRET_KEY environment variable.\",\n );\n }\n return key;\n}\n\n/**\n * Invokes the registered credentials provider (if any) and validates the result.\n */\nasync function resolveProviderCredentials(): Promise<WorkflowCredentials | undefined> {\n if (!workflowCredentialsProvider) {\n return undefined;\n }\n\n const provided = await workflowCredentialsProvider();\n if (!provided) {\n return undefined;\n }\n\n if (typeof provided !== \"object\") {\n throw new TypeError(\"Workflow credentials provider must return an object.\");\n }\n\n return provided;\n}\n\n/**\n * Resolves workflow credentials by merging from multiple sources.\n *\n * Resolution order (later sources override earlier):\n * 1. Credentials from the registered provider\n * 2. Decrypted credentials (if input is an encrypted payload)\n * OR plain credentials object (if input is already decrypted)\n *\n * @param credentials - Optional credentials input\n * @returns Merged credentials object\n */\nexport async function resolveWorkflowCredentials(\n credentials?: WorkflowCredentialsInput,\n): Promise<WorkflowCredentials> {\n // Start with provider credentials as the base\n const providerCredentials = await resolveProviderCredentials();\n const resolved: WorkflowCredentials = providerCredentials ? { ...providerCredentials } : {};\n\n if (!credentials) {\n return resolved;\n }\n\n // Handle encrypted payloads by decrypting them first.\n if (isEncryptedPayload(credentials)) {\n try {\n const decrypted = await decryptFromWorkflow<WorkflowCredentials>(\n credentials,\n getWorkflowSecretKeyFromEnv(),\n );\n return { ...resolved, ...decrypted };\n } catch (error) {\n const detail = error instanceof Error ? error.message : \"Unknown error.\";\n throw new Error(`Failed to decrypt workflow credentials. ${detail}`);\n }\n }\n\n if (await shouldEnforceEncryptedCredentials()) {\n throw new Error(\n \"Plaintext workflow credentials are not allowed when using Workflow Dev Kit.\" +\n \" Pass encrypted credentials (encryptForWorkflow) or resolve secrets via environment variables.\",\n );\n }\n\n // Plain credentials object - merge directly.\n return { ...resolved, ...credentials };\n}\n\ninterface DirectMuxCredentials {\n tokenId?: string;\n tokenSecret?: string;\n authorizationToken?: string;\n signingKey?: string;\n privateKey?: string;\n}\n\nfunction readString(record: Record<string, unknown> | undefined, key: string): string | undefined {\n const value = record?.[key];\n return typeof value === \"string\" && value.length > 0 ? value : undefined;\n}\n\nfunction resolveDirectMuxCredentials(record: Record<string, unknown> | undefined): DirectMuxCredentials | undefined {\n const tokenId = readString(record, \"muxTokenId\");\n const tokenSecret = readString(record, \"muxTokenSecret\");\n const authorizationToken = readString(record, \"muxAuthorizationToken\");\n const signingKey = readString(record, \"muxSigningKey\");\n const privateKey = readString(record, \"muxPrivateKey\");\n\n if (!tokenId && !tokenSecret && !authorizationToken && !signingKey && !privateKey) {\n return undefined;\n }\n\n if ((!tokenId || !tokenSecret) && !authorizationToken) {\n throw new Error(\n \"Both muxTokenId and muxTokenSecret are required when passing direct Mux workflow credentials.\",\n );\n }\n\n return {\n tokenId,\n tokenSecret,\n authorizationToken,\n signingKey,\n privateKey,\n };\n}\n\nfunction createWorkflowMuxClient(options: DirectMuxCredentials): WorkflowMuxClient {\n return {\n async createClient() {\n // Dynamic import to avoid pulling mux-node into workflow VM bundles.\n const { default: MuxClient } = await import(\"@mux/mux-node\");\n return new MuxClient({\n tokenId: options.tokenId,\n tokenSecret: options.tokenSecret,\n authorizationToken: options.authorizationToken,\n });\n },\n getSigningKey() {\n return options.signingKey;\n },\n getPrivateKey() {\n return options.privateKey;\n },\n };\n}\n\n/**\n * Resolves a WorkflowMuxClient from workflow credentials or environment variables.\n *\n * Checks direct workflow credentials first (muxTokenId/muxTokenSecret),\n * then provider credentials, then falls back to MUX_TOKEN_ID / MUX_TOKEN_SECRET\n * (and optional MUX_SIGNING_KEY / MUX_PRIVATE_KEY) environment variables.\n *\n * @param credentials - Optional workflow credentials input\n * @returns A WorkflowMuxClient instance\n * @throws Error if Mux credentials are not available\n */\nexport async function resolveMuxClient(\n credentials?: WorkflowCredentialsInput,\n): Promise<WorkflowMuxClient> {\n const resolved = await resolveWorkflowCredentials(credentials);\n const resolvedRecord = resolved as Record<string, unknown>;\n const resolvedMuxCredentials = resolveDirectMuxCredentials(resolvedRecord);\n if (resolvedMuxCredentials) {\n return createWorkflowMuxClient(resolvedMuxCredentials);\n }\n\n // Fall back to environment variables\n const muxTokenId = env.MUX_TOKEN_ID;\n const muxTokenSecret = env.MUX_TOKEN_SECRET;\n\n if (!muxTokenId || !muxTokenSecret) {\n throw new Error(\n \"Mux credentials are required. Provide muxTokenId/muxTokenSecret via workflow credentials, or set MUX_TOKEN_ID and MUX_TOKEN_SECRET environment variables.\",\n );\n }\n\n return createWorkflowMuxClient({\n tokenId: muxTokenId,\n tokenSecret: muxTokenSecret,\n signingKey: env.MUX_SIGNING_KEY,\n privateKey: env.MUX_PRIVATE_KEY,\n });\n}\n\n/** Supported AI/ML provider identifiers for API key resolution. */\nexport type ApiKeyProvider = \"openai\" | \"anthropic\" | \"google\" | \"hive\" | \"elevenlabs\";\n\nfunction resolveProviderApiKeyFromCredentials(\n provider: ApiKeyProvider,\n resolved: WorkflowCredentials,\n): string {\n const record = resolved as Record<string, unknown>;\n const openaiApiKey = readString(record, \"openaiApiKey\");\n const anthropicApiKey = readString(record, \"anthropicApiKey\");\n const googleApiKey = readString(record, \"googleApiKey\");\n const hiveApiKey = readString(record, \"hiveApiKey\");\n const elevenLabsApiKey = readString(record, \"elevenLabsApiKey\");\n\n // Map each provider to its credential source and env var fallback\n const apiKeyMap: Record<ApiKeyProvider, string | undefined> = {\n openai: openaiApiKey ?? env.OPENAI_API_KEY,\n anthropic: anthropicApiKey ?? env.ANTHROPIC_API_KEY,\n google: googleApiKey ?? env.GOOGLE_GENERATIVE_AI_API_KEY,\n hive: hiveApiKey ?? env.HIVE_API_KEY,\n elevenlabs: elevenLabsApiKey ?? env.ELEVENLABS_API_KEY,\n };\n\n const apiKey = apiKeyMap[provider];\n if (!apiKey) {\n // Provide helpful error message with the correct env var name.\n // Using `satisfies` ensures these stay in sync with the Env schema.\n const envVarNames = {\n openai: \"OPENAI_API_KEY\",\n anthropic: \"ANTHROPIC_API_KEY\",\n google: \"GOOGLE_GENERATIVE_AI_API_KEY\",\n hive: \"HIVE_API_KEY\",\n elevenlabs: \"ELEVENLABS_API_KEY\",\n } as const satisfies Record<ApiKeyProvider, keyof Env>;\n\n throw new Error(\n `${provider} API key is required. Provide ${provider} credentials via workflow credentials or set ${envVarNames[provider]} environment variable.`,\n );\n }\n\n return apiKey;\n}\n/**\n * Resolves an API key for a specific AI/ML provider.\n *\n * Checks resolved workflow credentials first, then falls back to the\n * provider-specific environment variable.\n *\n * @param provider - The provider identifier (e.g., \"openai\", \"anthropic\")\n * @param credentials - Optional workflow credentials input\n * @returns The resolved API key string\n * @throws Error if no API key is available for the specified provider\n */\nexport async function resolveProviderApiKey(\n provider: ApiKeyProvider,\n credentials?: WorkflowCredentialsInput,\n): Promise<string> {\n const resolved = await resolveWorkflowCredentials(credentials);\n return resolveProviderApiKeyFromCredentials(provider, resolved);\n}\n\n/**\n * Resolves Mux URL signing context for generating signed playback URLs.\n *\n * Unlike other resolve functions, this returns undefined if signing keys\n * are not configured (signing is optional for public assets).\n *\n * @param credentials - Optional workflow credentials input\n * @returns SigningContext if keys are available, undefined otherwise\n */\nexport async function resolveMuxSigningContext(\n credentials?: WorkflowCredentialsInput,\n): Promise<SigningContext | undefined> {\n const resolved = await resolveWorkflowCredentials(credentials);\n const resolvedRecord = resolved as Record<string, unknown>;\n\n // Try direct credentials first, then fall back to environment variables.\n const keyId = readString(resolvedRecord, \"muxSigningKey\") ?? env.MUX_SIGNING_KEY;\n const keySecret =\n readString(resolvedRecord, \"muxPrivateKey\") ?? env.MUX_PRIVATE_KEY;\n\n if (!keyId || !keySecret) {\n return undefined;\n }\n\n return { keyId, keySecret };\n}\n","import type {\n ModelIdByProvider,\n ModelRequestOptions,\n SupportedProvider,\n} from \"@mux/ai/lib/providers\";\nimport {\n resolveLanguageModel,\n} from \"@mux/ai/lib/providers\";\nimport type { ApiKeyProvider } from \"@mux/ai/lib/workflow-credentials\";\nimport { resolveMuxClient, resolveProviderApiKey } from \"@mux/ai/lib/workflow-credentials\";\nimport type { WorkflowCredentialsInput, WorkflowMuxClient } from \"@mux/ai/types\";\n\n/**\n * Gets a WorkflowMuxClient from workflow credentials or environment variables.\n * Used internally by workflow steps to avoid passing credentials through step I/O.\n * Throws if Mux credentials are not available.\n */\nexport async function getMuxClientFromEnv(\n credentials?: WorkflowCredentialsInput,\n): Promise<WorkflowMuxClient> {\n return resolveMuxClient(credentials);\n}\n\n/**\n * Gets an API key from workflow credentials or environment variables for the specified provider.\n * Used internally by workflow steps to avoid passing credentials through step I/O.\n * Throws if the API key is not available.\n */\nexport async function getApiKeyFromEnv(\n provider: ApiKeyProvider,\n credentials?: WorkflowCredentialsInput,\n): Promise<string> {\n return resolveProviderApiKey(provider, credentials);\n}\n\nexport interface WorkflowConfig<P extends SupportedProvider = SupportedProvider> {\n muxClient: WorkflowMuxClient;\n provider: P;\n modelId: ModelIdByProvider[P];\n}\n\n/**\n * Resolves Mux client and model configuration for a workflow.\n * This function is NOT a workflow step to avoid exposing credentials in step I/O.\n */\nexport async function createWorkflowConfig<P extends SupportedProvider = SupportedProvider>(\n options: ModelRequestOptions<P>,\n provider?: P,\n): Promise<WorkflowConfig<P>> {\n const providerToUse = provider || options.provider || (\"openai\" as P);\n const muxClient = await resolveMuxClient(options.credentials);\n const resolved = resolveLanguageModel({\n ...options,\n provider: providerToUse,\n });\n\n return {\n muxClient,\n provider: resolved.provider,\n modelId: resolved.modelId,\n };\n}\n","import { getMuxClientFromEnv } from \"@mux/ai/lib/client-factory\";\nimport type { WorkflowCredentialsInput } from \"@mux/ai/types\";\n\nexport interface HeatmapOptions {\n /** Time window for results, e.g., ['7:days'] (default: ['7:days']) */\n timeframe?: string;\n /** Optional workflow credentials */\n credentials?: WorkflowCredentialsInput;\n}\n\n/** Raw API response structure from Mux Data API */\n// To be removed when the Mux Node SDK is updated\ninterface HeatmapApiResponse {\n asset_id?: string;\n video_id?: string;\n playback_id?: string;\n heatmap: number[];\n timeframe: [number, number];\n}\n\nexport interface HeatmapResponse {\n assetId?: string;\n videoId?: string;\n playbackId?: string;\n /** Array of 100 values representing engagement for each 1/100th of the video */\n heatmap: number[];\n timeframe: [number, number];\n}\n\n/**\n * Fetches engagement heatmap for a Mux asset.\n * Returns a length 100 array where each value represents how many times\n * that 1/100th of the video was watched.\n *\n * @param assetId - The Mux asset ID\n * @param options - Heatmap query options\n * @returns Heatmap data with 100 engagement values\n */\nexport async function getHeatmapForAsset(\n assetId: string,\n options: HeatmapOptions = {},\n): Promise<HeatmapResponse> {\n \"use step\";\n return fetchHeatmap(\"assets\", assetId, options);\n}\n\n/**\n * Fetches engagement heatmap for a Mux video ID.\n * Returns a length 100 array where each value represents how many times\n * that 1/100th of the video was watched.\n *\n * @param videoId - The Mux video ID\n * @param options - Heatmap query options\n * @returns Heatmap data with 100 engagement values\n */\nexport async function getHeatmapForVideo(\n videoId: string,\n options: HeatmapOptions = {},\n): Promise<HeatmapResponse> {\n \"use step\";\n return fetchHeatmap(\"videos\", videoId, options);\n}\n\n/**\n * Fetches engagement heatmap for a Mux playback ID.\n * Returns a length 100 array where each value represents how many times\n * that 1/100th of the video was watched.\n *\n * @param playbackId - The Mux playback ID\n * @param options - Heatmap query options\n * @returns Heatmap data with 100 engagement values\n */\nexport async function getHeatmapForPlaybackId(\n playbackId: string,\n options: HeatmapOptions = {},\n): Promise<HeatmapResponse> {\n \"use step\";\n return fetchHeatmap(\"playback-ids\", playbackId, options);\n}\n\n/**\n * Transforms the snake_case API response to camelCase for the public interface.\n * TODO: Remove when the Mux Node SDK is updated\n */\nfunction transformHeatmapResponse(\n response: HeatmapApiResponse,\n): HeatmapResponse {\n return {\n assetId: response.asset_id,\n videoId: response.video_id,\n playbackId: response.playback_id,\n heatmap: response.heatmap,\n timeframe: response.timeframe,\n };\n}\n\n/**\n * Internal helper to fetch heatmap from the Mux Data API.\n * Uses the raw HTTP methods on the Mux client since the SDK doesn't have\n * typed methods for the engagement endpoints yet.\n */\nasync function fetchHeatmap(\n identifierType: \"assets\" | \"videos\" | \"playback-ids\",\n id: string,\n options: HeatmapOptions,\n): Promise<HeatmapResponse> {\n \"use step\";\n const { timeframe = \"[24:hours]\", credentials } = options;\n\n const muxClient = await getMuxClientFromEnv(credentials);\n const mux = await muxClient.createClient();\n\n // Build query parameters\n const queryParams = new URLSearchParams();\n queryParams.append(\"timeframe[]\", timeframe);\n\n // Use the raw HTTP method since the SDK doesn't have typed engagement methods yet\n const path = `/data/v1/engagement/${identifierType}/${id}/heatmap?${queryParams.toString()}`;\n const response = await mux.get<unknown, HeatmapApiResponse>(path);\n\n return transformHeatmapResponse(response);\n}\n","import { getMuxClientFromEnv } from \"@mux/ai/lib/client-factory\";\nimport type { WorkflowCredentialsInput } from \"@mux/ai/types\";\n\nexport interface Hotspot {\n /** Inclusive start time in milliseconds */\n startMs: number;\n /** Exclusive end time in milliseconds */\n endMs: number;\n /** Hotspot score using distribution-based normalization (0-1) */\n score: number;\n}\n\nexport interface HotspotOptions {\n /** Maximum number of hotspots to return (default: 5) */\n limit?: number;\n /** Sort order: 'asc' or 'desc' (default: 'desc') */\n orderDirection?: \"asc\" | \"desc\";\n /** Order by field (default: 'score') */\n orderBy?: \"score\";\n /** Time window for results, e.g., ['7:days'] (default: ['7:days']) */\n timeframe?: string;\n /** Optional workflow credentials */\n credentials?: WorkflowCredentialsInput;\n}\n\n/** Raw API response structure from Mux Data API */\n// To be removed when the Mux Node SDK is updated\ninterface HotspotApiResponse {\n total_row_count: number | null;\n timeframe: [number, number];\n data: {\n asset_id?: string;\n video_id?: string;\n playback_id?: string;\n hotspots: Array<{\n start_ms: number;\n end_ms: number;\n score: number;\n }>;\n };\n}\n\nexport interface HotspotResponse {\n assetId?: string;\n videoId?: string;\n playbackId?: string;\n hotspots: Hotspot[];\n}\n\n/**\n * Fetches engagement hotspots for a Mux asset.\n * Returns the top N \"hot\" time ranges based on engagement data.\n *\n * @param assetId - The Mux asset ID\n * @param options - Hotspot query options\n * @returns Array of hotspots with time ranges and scores\n */\nexport async function getHotspotsForAsset(\n assetId: string,\n options: HotspotOptions = {},\n): Promise<Hotspot[]> {\n \"use step\";\n const response = await fetchHotspots(\"assets\", assetId, options);\n return response.hotspots;\n}\n\n/**\n * Fetches engagement hotspots for a Mux video ID.\n * Returns the top N \"hot\" time ranges based on engagement data.\n *\n * @param videoId - The Mux video ID\n * @param options - Hotspot query options\n * @returns Array of hotspots with time ranges and scores\n */\nexport async function getHotspotsForVideo(\n videoId: string,\n options: HotspotOptions = {},\n): Promise<Hotspot[]> {\n \"use step\";\n const response = await fetchHotspots(\"videos\", videoId, options);\n return response.hotspots;\n}\n\n/**\n * Fetches engagement hotspots for a Mux playback ID.\n * Returns the top N \"hot\" time ranges based on engagement data.\n *\n * @param playbackId - The Mux playback ID\n * @param options - Hotspot query options\n * @returns Array of hotspots with time ranges and scores\n */\nexport async function getHotspotsForPlaybackId(\n playbackId: string,\n options: HotspotOptions = {},\n): Promise<Hotspot[]> {\n \"use step\";\n const response = await fetchHotspots(\"playback-ids\", playbackId, options);\n return response.hotspots;\n}\n\n/**\n * Transforms the snake_case API response to camelCase for the public interface.\n * TODO: Remove when the Mux Node SDK is updated\n */\nfunction transformHotspotResponse(response: HotspotApiResponse): HotspotResponse {\n return {\n assetId: response.data.asset_id,\n videoId: response.data.video_id,\n playbackId: response.data.playback_id,\n hotspots: response.data.hotspots.map(h => ({\n startMs: h.start_ms,\n endMs: h.end_ms,\n score: h.score,\n })),\n };\n}\n\n/**\n * Internal helper to fetch hotspots from the Mux Data API.\n * Uses the raw HTTP methods on the Mux client since the SDK doesn't have\n * typed methods for the engagement endpoints yet.\n */\nasync function fetchHotspots(\n identifierType: \"assets\" | \"videos\" | \"playback-ids\",\n id: string,\n options: HotspotOptions,\n): Promise<HotspotResponse> {\n \"use step\";\n const {\n limit = 5,\n orderDirection = \"desc\",\n orderBy = \"score\",\n timeframe = \"[24:hours]\",\n credentials,\n } = options;\n\n const muxClient = await getMuxClientFromEnv(credentials);\n const mux = await muxClient.createClient();\n\n // Build query parameters\n const queryParams = new URLSearchParams();\n queryParams.append(\"limit\", String(limit));\n queryParams.append(\"order_direction\", orderDirection);\n queryParams.append(\"order_by\", orderBy);\n queryParams.append(\"timeframe[]\", timeframe);\n\n // Use the raw HTTP method since the SDK doesn't have typed engagement methods yet\n const path = `/data/v1/engagement/${identifierType}/${id}/hotspots?${queryParams.toString()}`;\n const response = await mux.get<unknown, HotspotApiResponse>(path);\n\n return transformHotspotResponse(response);\n}\n","import env from \"@mux/ai/env\";\n\nconst DEFAULT_MUX_IMAGE_ORIGIN = \"https://image.mux.com\";\n\nfunction normalizeMuxImageOrigin(value: string): string {\n const trimmed = value.trim();\n const candidate = trimmed.includes(\"://\") ? trimmed : `https://${trimmed}`;\n\n let parsed: URL;\n try {\n parsed = new URL(candidate);\n } catch {\n throw new Error(\n \"Invalid MUX_IMAGE_URL_OVERRIDE. Provide a hostname like \" +\n `\"image.example.mux.com\" (or a URL origin such as \"https://image.example.mux.com\").`,\n );\n }\n\n if (\n parsed.username ||\n parsed.password ||\n parsed.search ||\n parsed.hash ||\n (parsed.pathname && parsed.pathname !== \"/\")\n ) {\n throw new Error(\n \"Invalid MUX_IMAGE_URL_OVERRIDE. Only a hostname/origin is allowed \" +\n \"(no credentials, query params, hash fragments, or path).\",\n );\n }\n\n return parsed.origin;\n}\n\nexport function getMuxImageOrigin(): string {\n const override = env.MUX_IMAGE_URL_OVERRIDE;\n if (!override) {\n return DEFAULT_MUX_IMAGE_ORIGIN;\n }\n\n return normalizeMuxImageOrigin(override);\n}\n\nexport function getMuxImageBaseUrl(playbackId: string, assetType: \"storyboard\" | \"thumbnail\"): string {\n const origin = getMuxImageOrigin();\n return `${origin}/${playbackId}/${assetType}.png`;\n}\n\nexport function getMuxStoryboardBaseUrl(playbackId: string): string {\n return getMuxImageBaseUrl(playbackId, \"storyboard\");\n}\n\nexport function getMuxThumbnailBaseUrl(playbackId: string): string {\n return getMuxImageBaseUrl(playbackId, \"thumbnail\");\n}\n","import env from \"@mux/ai/env\";\nimport { resolveMuxSigningContext } from \"@mux/ai/lib/workflow-credentials\";\nimport type { WorkflowCredentialsInput } from \"@mux/ai/types\";\n\nimport type Mux from \"@mux/mux-node\";\n\n/**\n * Context required to sign URLs for signed playback IDs.\n */\nexport interface SigningContext {\n /** The signing key ID from Mux dashboard. */\n keyId: string;\n /** The base64-encoded private key from Mux dashboard. */\n keySecret: string;\n /** Token expiration time (e.g. '1h', '1d'). Defaults to '1h'. */\n expiration?: string;\n}\n\n/**\n * Token type determines which Mux service the token is valid for.\n */\nexport type TokenType = \"video\" | \"thumbnail\" | \"storyboard\" | \"gif\";\n\n/**\n * Resolves signing context from config or environment variables.\n * Returns undefined if signing keys are not configured.\n */\nexport function getMuxSigningContextFromEnv(): SigningContext | undefined {\n const keyId = env.MUX_SIGNING_KEY;\n const keySecret = env.MUX_PRIVATE_KEY;\n\n if (!keyId || !keySecret) {\n return undefined;\n }\n\n return { keyId, keySecret };\n}\n\n/**\n * Creates a Mux client configured for JWT signing.\n * This client is used internally for signing operations.\n */\nasync function createSigningClient(context: SigningContext): Promise<Mux> {\n // Dynamic import to prevent @mux/mux-node (and its transitive dep jose)\n // from being bundled into workflow VM code where `require` is unavailable.\n const { default: MuxClient } = await import(\"@mux/mux-node\");\n return new MuxClient({\n // These are not needed for signing, but the SDK requires them\n // Using empty strings as we only need the jwt functionality\n tokenId: env.MUX_TOKEN_ID || \"\",\n tokenSecret: env.MUX_TOKEN_SECRET || \"\",\n jwtSigningKey: context.keyId,\n jwtPrivateKey: context.keySecret,\n });\n}\n\n/**\n * Generates a signed token for a playback ID using the Mux SDK.\n *\n * @param playbackId - The Mux playback ID to sign\n * @param context - Signing context with key credentials\n * @param type - Token type (video, thumbnail, storyboard, gif)\n * @param params - Additional parameters for thumbnail/storyboard tokens (values will be stringified)\n * @returns Signed JWT token\n */\nexport async function signPlaybackId(\n playbackId: string,\n context: SigningContext,\n type: TokenType = \"video\",\n params?: Record<string, string | number>,\n): Promise<string> {\n \"use step\";\n const client = await createSigningClient(context);\n\n // Convert params to Record<string, string> as required by the SDK\n const stringParams = params ?\n Object.fromEntries(\n Object.entries(params).map(([key, value]) => [key, String(value)]),\n ) :\n undefined;\n\n return client.jwt.signPlaybackId(playbackId, {\n type,\n expiration: context.expiration || \"1h\",\n params: stringParams,\n });\n}\n\n/**\n * Appends a signed token to a Mux URL.\n *\n * @param url - The base Mux URL (e.g. https://image.mux.com/{playbackId}/thumbnail.png)\n * @param playbackId - The Mux playback ID\n * @param type - Token type for the URL\n * @param params - Additional parameters for the token\n * @returns URL with token query parameter appended\n */\nexport async function signUrl(\n url: string,\n playbackId: string,\n type: TokenType = \"video\",\n params?: Record<string, string | number>,\n credentials?: WorkflowCredentialsInput,\n): Promise<string> {\n \"use step\";\n const resolvedContext = await resolveMuxSigningContext(credentials);\n if (!resolvedContext) {\n throw new Error(\n \"Signed playback ID requires signing credentials. \" +\n \"Provide muxSigningKey and muxPrivateKey via workflow credentials or set MUX_SIGNING_KEY and MUX_PRIVATE_KEY environment variables.\",\n );\n }\n const token = await signPlaybackId(playbackId, resolvedContext, type, params);\n const separator = url.includes(\"?\") ? \"&\" : \"?\";\n return `${url}${separator}token=${token}`;\n}\n","import { getMuxStoryboardBaseUrl } from \"@mux/ai/lib/mux-image-url\";\nimport { signUrl } from \"@mux/ai/lib/url-signing\";\nimport type { WorkflowCredentialsInput } from \"@mux/ai/types\";\n\nexport const DEFAULT_STORYBOARD_WIDTH = 640;\n\n/**\n * Generates a storyboard URL for the given playback ID.\n * If shouldSign is true, the URL will be signed with a token using credentials from environment variables.\n *\n * @param playbackId - The Mux playback ID\n * @param width - Width of the storyboard in pixels (default: 640)\n * @param shouldSign - Flag for whether or not to use signed playback IDs (default: false)\n * @returns Storyboard URL (signed if shouldSign is true)\n */\nexport async function getStoryboardUrl(\n playbackId: string,\n width: number = DEFAULT_STORYBOARD_WIDTH,\n shouldSign: boolean = false,\n credentials?: WorkflowCredentialsInput,\n): Promise<string> {\n \"use step\";\n const baseUrl = getMuxStoryboardBaseUrl(playbackId);\n\n if (shouldSign) {\n return signUrl(baseUrl, playbackId, \"storyboard\", { width }, credentials);\n }\n\n return `${baseUrl}?width=${width}`;\n}\n","import type { VTTCue } from \"@mux/ai/primitives/transcripts\";\nimport type { ChunkingStrategy, TextChunk } from \"@mux/ai/types\";\n\n/**\n * Simple token counter that approximates tokens by word count.\n * For production use with OpenAI, consider using a proper tokenizer like tiktoken.\n * This approximation is generally close enough for chunking purposes (1 token ≈ 0.75 words).\n */\nexport function estimateTokenCount(text: string): number {\n const words = text.trim().split(/\\s+/).length;\n return Math.ceil(words / 0.75);\n}\n\n/**\n * Chunks text into overlapping segments based on token count.\n *\n * @param text - The text to chunk\n * @param maxTokens - Maximum tokens per chunk\n * @param overlapTokens - Number of tokens to overlap between chunks\n * @returns Array of text chunks with metadata\n */\nexport function chunkByTokens(\n text: string,\n maxTokens: number,\n overlapTokens: number = 0,\n): TextChunk[] {\n if (!text.trim()) {\n return [];\n }\n\n const chunks: TextChunk[] = [];\n const words = text.trim().split(/\\s+/);\n\n // Convert tokens to approximate word count\n const wordsPerChunk = Math.floor(maxTokens * 0.75);\n const overlapWords = Math.floor(overlapTokens * 0.75);\n\n let chunkIndex = 0;\n let currentPosition = 0;\n\n while (currentPosition < words.length) {\n const chunkWords = words.slice(\n currentPosition,\n currentPosition + wordsPerChunk,\n );\n const chunkText = chunkWords.join(\" \");\n const tokenCount = estimateTokenCount(chunkText);\n\n chunks.push({\n id: `chunk-${chunkIndex}`,\n text: chunkText,\n tokenCount,\n });\n\n // Move forward by chunk size minus overlap\n currentPosition += wordsPerChunk - overlapWords;\n chunkIndex++;\n\n // Prevent infinite loop if overlap is too large\n if (currentPosition <= (chunkIndex - 1) * (wordsPerChunk - overlapWords)) {\n break;\n }\n }\n\n return chunks;\n}\n\n/**\n * Creates a TextChunk from a group of VTT cues.\n */\nfunction createChunkFromCues(cues: VTTCue[], index: number): TextChunk {\n const text = cues.map(c => c.text).join(\" \");\n return {\n id: `chunk-${index}`,\n text,\n tokenCount: estimateTokenCount(text),\n startTime: cues[0].startTime,\n endTime: cues[cues.length - 1].endTime,\n };\n}\n\n/**\n * Chunks VTT cues into groups that respect natural cue boundaries.\n * Splits at cue boundaries rather than mid-sentence, preserving accurate timestamps.\n *\n * @param cues - Array of VTT cues to chunk\n * @param maxTokens - Maximum tokens per chunk\n * @param overlapCues - Number of cues to overlap between chunks (default: 2)\n * @returns Array of text chunks with accurate start/end times\n */\nexport function chunkVTTCues(\n cues: VTTCue[],\n maxTokens: number,\n overlapCues: number = 2,\n): TextChunk[] {\n if (cues.length === 0)\n return [];\n\n const chunks: TextChunk[] = [];\n let currentCues: VTTCue[] = [];\n let currentTokens = 0;\n let chunkIndex = 0;\n\n for (let i = 0; i < cues.length; i++) {\n const cue = cues[i];\n const cueTokens = estimateTokenCount(cue.text);\n\n // If adding this cue would exceed limit, finalize current chunk\n if (currentTokens + cueTokens > maxTokens && currentCues.length > 0) {\n chunks.push(createChunkFromCues(currentCues, chunkIndex));\n chunkIndex++;\n\n // Start new chunk with overlap from end of previous\n const overlapStart = Math.max(0, currentCues.length - overlapCues);\n currentCues = currentCues.slice(overlapStart);\n currentTokens = currentCues.reduce(\n (sum, c) => sum + estimateTokenCount(c.text),\n 0,\n );\n }\n\n currentCues.push(cue);\n currentTokens += cueTokens;\n }\n\n // Don't forget the last chunk\n if (currentCues.length > 0) {\n chunks.push(createChunkFromCues(currentCues, chunkIndex));\n }\n\n return chunks;\n}\n\n/**\n * Chunks text according to the specified strategy.\n *\n * @param text - The text to chunk\n * @param strategy - The chunking strategy to use\n * @returns Array of text chunks\n */\nexport function chunkText(text: string, strategy: ChunkingStrategy): TextChunk[] {\n switch (strategy.type) {\n case \"token\": {\n return chunkByTokens(text, strategy.maxTokens, strategy.overlap ?? 0);\n }\n default: {\n const exhaustiveCheck: never = strategy as never;\n throw new Error(`Unsupported chunking strategy: ${exhaustiveCheck}`);\n }\n }\n}\n","import { getMuxThumbnailBaseUrl } from \"@mux/ai/lib/mux-image-url\";\nimport { signUrl } from \"@mux/ai/lib/url-signing\";\nimport type { WorkflowCredentialsInput } from \"@mux/ai/types\";\n\nexport interface ThumbnailOptions {\n /** Interval between thumbnails in seconds (default: 10) */\n interval?: number;\n /** Width of the thumbnail in pixels (default: 640) */\n width?: number;\n /** Flag for whether or not to use signed playback IDs (default: false) */\n shouldSign?: boolean;\n /** Maximum number of thumbnails to generate. When set, samples are evenly distributed with first and last frames pinned. */\n maxSamples?: number;\n /** Workflow credentials for signing (optional). */\n credentials?: WorkflowCredentialsInput;\n}\n\n/**\n * Generates thumbnail URLs at regular intervals based on video duration.\n * If shouldSign is true, the URLs will be signed with tokens using credentials from environment variables.\n *\n * @param playbackId - The Mux playback ID\n * @param duration - Video duration in seconds\n * @param options - Thumbnail generation options\n * @returns Array of thumbnail URLs (signed if shouldSign is true)\n */\nexport async function getThumbnailUrls(\n playbackId: string,\n duration: number,\n options: ThumbnailOptions = {},\n): Promise<string[]> {\n \"use step\";\n const { interval = 10, width = 640, shouldSign = false, maxSamples, credentials } = options;\n let timestamps: number[] = [];\n\n if (duration <= 50) {\n const spacing = duration / 6;\n for (let i = 1; i <= 5; i++) {\n timestamps.push(Math.round(i * spacing));\n }\n } else {\n for (let time = 0; time < duration; time += interval) {\n timestamps.push(time);\n }\n }\n\n // Apply maxSamples cap if specified and we have more timestamps than the limit\n if (maxSamples !== undefined && timestamps.length > maxSamples) {\n const newTimestamps: number[] = [];\n\n // Always include first frame\n newTimestamps.push(0);\n\n // If maxSamples >= 2, add evenly distributed middle frames and last frame\n if (maxSamples >= 2) {\n const spacing = duration / (maxSamples - 1);\n for (let i = 1; i < maxSamples - 1; i++) {\n newTimestamps.push(spacing * i);\n }\n // Always include last frame\n newTimestamps.push(duration);\n }\n\n timestamps = newTimestamps;\n }\n\n const baseUrl = getMuxThumbnailBaseUrl(playbackId);\n\n const urlPromises = timestamps.map(async (time) => {\n if (shouldSign) {\n return signUrl(baseUrl, playbackId, \"thumbnail\", { time, width }, credentials);\n }\n\n return `${baseUrl}?time=${time}&width=${width}`;\n });\n\n return Promise.all(urlPromises);\n}\n","import { signUrl } from \"@mux/ai/lib/url-signing\";\nimport type { AssetTextTrack, MuxAsset, WorkflowCredentialsInput } from \"@mux/ai/types\";\n\n/** A single cue from a VTT file with timing info. */\nexport interface VTTCue {\n startTime: number;\n endTime: number;\n text: string;\n}\n\nexport interface TranscriptFetchOptions {\n languageCode?: string;\n cleanTranscript?: boolean;\n /** Optional signing context for signed playback IDs */\n shouldSign?: boolean;\n credentials?: WorkflowCredentialsInput;\n /**\n * When true, throws if no usable transcript can be retrieved (no ready text track,\n * missing track id, fetch error, or empty transcript).\n *\n * Default behavior is non-fatal and returns an empty `transcriptText`.\n */\n required?: boolean;\n}\n\nexport interface TranscriptResult {\n transcriptText: string;\n transcriptUrl?: string;\n track?: AssetTextTrack;\n}\n\nexport function getReadyTextTracks(asset: MuxAsset): AssetTextTrack[] {\n return (asset.tracks || []).filter(\n track => track.type === \"text\" && track.status === \"ready\",\n );\n}\n\nexport function findCaptionTrack(asset: MuxAsset, languageCode?: string): AssetTextTrack | undefined {\n const tracks = getReadyTextTracks(asset);\n if (!tracks.length)\n return undefined;\n\n if (!languageCode) {\n return tracks[0];\n }\n\n return tracks.find(\n track =>\n track.text_type === \"subtitles\" &&\n track.language_code === languageCode,\n );\n}\n\nexport function extractTextFromVTT(vttContent: string): string {\n if (!vttContent.trim()) {\n return \"\";\n }\n\n const lines = vttContent.split(\"\\n\");\n const textLines: string[] = [];\n\n for (let i = 0; i < lines.length; i++) {\n const line = lines[i].trim();\n\n if (!line)\n continue;\n if (line === \"WEBVTT\")\n continue;\n if (line.startsWith(\"NOTE \"))\n continue;\n if (line.includes(\"-->\"))\n continue;\n if (/^[\\w-]+$/.test(line) && !line.includes(\" \"))\n continue;\n if (line.startsWith(\"STYLE\") || line.startsWith(\"REGION\"))\n continue;\n\n const cleanLine = line.replace(/<[^>]*>/g, \"\").trim();\n\n if (cleanLine) {\n textLines.push(cleanLine);\n }\n }\n\n return textLines.join(\" \").replace(/\\s+/g, \" \").trim();\n}\n\nexport function vttTimestampToSeconds(timestamp: string): number {\n const parts = timestamp.split(\":\");\n if (parts.length !== 3)\n return 0;\n\n const hours = Number.parseInt(parts[0], 10) || 0;\n const minutes = Number.parseInt(parts[1], 10) || 0;\n const seconds = Number.parseFloat(parts[2]) || 0;\n\n return hours * 3600 + minutes * 60 + seconds;\n}\n\n/**\n * Converts seconds to a human-readable timestamp.\n * Returns M:SS for durations under an hour, H:MM:SS for an hour or more.\n *\n * @param seconds - The number of seconds to convert\n * @returns A formatted timestamp string (e.g., \"2:05\" or \"01:02:05\")\n */\nexport function secondsToTimestamp(seconds: number): string {\n const rounded = Math.max(0, Math.floor(seconds));\n const hours = Math.floor(rounded / 3600);\n const minutes = Math.floor((rounded % 3600) / 60);\n const remainingSeconds = rounded % 60;\n\n if (hours > 0) {\n return `${hours.toString().padStart(2, \"0\")}:${minutes.toString().padStart(2, \"0\")}:${remainingSeconds.toString().padStart(2, \"0\")}`;\n }\n return `${minutes}:${remainingSeconds.toString().padStart(2, \"0\")}`;\n}\n\nexport function extractTimestampedTranscript(vttContent: string): string {\n if (!vttContent.trim()) {\n return \"\";\n }\n\n const lines = vttContent.split(\"\\n\");\n const segments: Array<{ time: number; text: string }> = [];\n\n for (let i = 0; i < lines.length; i++) {\n const line = lines[i].trim();\n\n if (line.includes(\"-->\")) {\n const startTime = line.split(\" --> \")[0].trim();\n const timeInSeconds = vttTimestampToSeconds(startTime);\n\n let j = i + 1;\n while (j < lines.length && !lines[j].trim()) {\n j++;\n }\n\n if (j < lines.length) {\n const text = lines[j].trim().replace(/<[^>]*>/g, \"\");\n if (text) {\n segments.push({ time: timeInSeconds, text });\n }\n }\n }\n }\n\n return segments\n .map(segment => `[${Math.floor(segment.time)}s] ${segment.text}`)\n .join(\"\\n\");\n}\n\n/**\n * Parses VTT content into structured cues with timing.\n *\n * @param vttContent - Raw VTT file content\n * @returns Array of VTT cues with start/end times and text\n */\nexport function parseVTTCues(vttContent: string): VTTCue[] {\n if (!vttContent.trim())\n return [];\n\n const lines = vttContent.split(\"\\n\");\n const cues: VTTCue[] = [];\n\n for (let i = 0; i < lines.length; i++) {\n const line = lines[i].trim();\n\n if (line.includes(\"-->\")) {\n const [startStr, endStr] = line.split(\" --> \").map(s => s.trim());\n const startTime = vttTimestampToSeconds(startStr);\n const endTime = vttTimestampToSeconds(endStr.split(\" \")[0]); // Handle cue settings\n\n // Collect text lines until empty line or next timestamp\n const textLines: string[] = [];\n let j = i + 1;\n while (j < lines.length && lines[j].trim() && !lines[j].includes(\"-->\")) {\n const cleanLine = lines[j].trim().replace(/<[^>]*>/g, \"\");\n if (cleanLine)\n textLines.push(cleanLine);\n j++;\n }\n\n if (textLines.length > 0) {\n cues.push({\n startTime,\n endTime,\n text: textLines.join(\" \"),\n });\n }\n }\n }\n\n return cues;\n}\n\n/**\n * Builds a transcript URL for the given playback ID and track ID.\n * If a signing context is provided, the URL will be signed with a token.\n *\n * @param playbackId - The Mux playback ID\n * @param trackId - The text track ID\n * @param shouldSign - Flag for whether or not to use signed playback IDs\n * @returns Transcript URL (signed if context provided)\n */\nexport async function buildTranscriptUrl(\n playbackId: string,\n trackId: string,\n shouldSign: boolean = false,\n credentials?: WorkflowCredentialsInput,\n): Promise<string> {\n \"use step\";\n const baseUrl = `https://stream.mux.com/${playbackId}/text/${trackId}.vtt`;\n\n if (shouldSign) {\n return signUrl(baseUrl, playbackId, \"video\", undefined, credentials);\n }\n\n return baseUrl;\n}\n\nexport async function fetchTranscriptForAsset(\n asset: MuxAsset,\n playbackId: string,\n options: TranscriptFetchOptions = {},\n): Promise<TranscriptResult> {\n \"use step\";\n const {\n languageCode,\n cleanTranscript = true,\n shouldSign,\n credentials,\n required = false,\n } = options;\n const track = findCaptionTrack(asset, languageCode);\n\n if (!track) {\n if (required) {\n const availableLanguages = getReadyTextTracks(asset)\n .map(t => t.language_code)\n .filter(Boolean)\n .join(\", \");\n throw new Error(\n `No transcript track found${languageCode ? ` for language '${languageCode}'` : \"\"}. Available languages: ${availableLanguages || \"none\"}`,\n );\n }\n return { transcriptText: \"\" };\n }\n\n if (!track.id) {\n if (required) {\n throw new Error(\"Transcript track is missing an id\");\n }\n return { transcriptText: \"\", track };\n }\n\n const transcriptUrl = await buildTranscriptUrl(playbackId, track.id, shouldSign, credentials);\n\n try {\n const response = await fetch(transcriptUrl);\n if (!response.ok) {\n if (required) {\n throw new Error(`Failed to fetch transcript (HTTP ${response.status})`);\n }\n return { transcriptText: \"\", transcriptUrl, track };\n }\n\n const rawVtt = await response.text();\n const transcriptText = cleanTranscript ? extractTextFromVTT(rawVtt) : rawVtt;\n\n if (required && !transcriptText.trim()) {\n throw new Error(\"Transcript is empty\");\n }\n\n return { transcriptText, transcriptUrl, track };\n } catch (error) {\n if (required) {\n throw new Error(\n `Failed to fetch transcript: ${error instanceof Error ? error.message : \"Unknown error\"}`,\n );\n }\n console.warn(\"Failed to fetch transcript:\", error);\n return { transcriptText: \"\", transcriptUrl, track };\n }\n}\n"],"mappings":";AAAA,SAAS,uBAAuB;AAChC,SAAS,gCAAgC;AACzC,SAAS,oBAAoB;;;ACA7B,SAAS,SAAS;AAElB,OAAO;AAEP,SAAS,eAAe,aAAqB,SAAkB;AAC7D,SAAO,EAAE;AAAA,IACP,WAAS,OAAO,UAAU,YAAY,MAAM,KAAK,EAAE,WAAW,IAAI,SAAY;AAAA,IAC9E,EAAE,OAAO,EAAE,KAAK,EAAE,IAAI,GAAG,OAAO,EAAE,SAAS;AAAA,EAC7C,EAAE,SAAS,WAAW;AACxB;AAUA,IAAM,YAAY,EAAE,OAAO;AAAA,EACzB,UAAU,EAAE,OAAO,EAAE,QAAQ,aAAa,EAAE,SAAS,sBAAsB;AAAA,EAE3E,cAAc,eAAe,wBAAwB,6BAA6B;AAAA,EAClF,kBAAkB,eAAe,4BAA4B,6BAA6B;AAAA,EAC1F,4BAA4B;AAAA,IAC1B;AAAA,IACA;AAAA,EACF;AAAA,EACA,uBAAuB;AAAA,IACrB;AAAA,IACA;AAAA,EACF;AAAA,EAEA,iBAAiB,eAAe,gDAAgD,4BAA4B;AAAA,EAC5G,iBAAiB,eAAe,qDAAqD,4BAA4B;AAAA,EACjH,wBAAwB;AAAA,IACtB;AAAA,IACA;AAAA,EACF;AAAA;AAAA,EAGA,mBAAmB,eAAe,2CAA2C,mBAAmB;AAAA,EAChG,4BAA4B,eAAe,wDAAwD,gCAAgC;AAAA,EACnI,2BAA2B,eAAe,mDAAmD,2BAA2B;AAAA,EACxH,sCAAsC;AAAA,IACpC;AAAA,IACA;AAAA,EACF;AAAA,EACA,wCAAwC;AAAA,IACtC;AAAA,IACA;AAAA,EACF;AAAA,EACA,8CAA8C;AAAA,IAC5C;AAAA,IACA;AAAA,EACF;AAAA,EACA,8BAA8B,eAAe,4CAA4C,qDAAqD;AAAA,EAC9I,sCAAsC,eAAe,iEAAiE,0EAA0E;AAAA;AAAA,EAGhM,uBAAuB,eAAe,8BAA8B,oFAAoF;AAAA,EACxJ,oBAAoB,eAAe,qCAAqC,wHAAwH;AAAA;AAAA,EAGhM,gBAAgB,eAAe,+CAA+C,gBAAgB;AAAA,EAC9F,mBAAmB,eAAe,kDAAkD,mBAAmB;AAAA,EACvG,8BAA8B,eAAe,6DAA6D,8BAA8B;AAAA,EAExI,oBAAoB,eAAe,6CAA6C,oBAAoB;AAAA,EACpG,cAAc,eAAe,mCAAmC,cAAc;AAAA;AAAA,EAG9E,aAAa,eAAe,uCAAuC,aAAa;AAAA,EAChF,WAAW,eAAe,8CAA8C;AAAA,EACxE,WAAW,eAAe,8CAA8C,WAAW;AAAA,EACnF,kBAAkB,eAAe,4CAA4C,kBAAkB;AAAA,EAC/F,sBAAsB,eAAe,gDAAgD,sBAAsB;AAAA,EAC3G,2BAA2B;AAAA,IACzB;AAAA,EACF;AAAA,EAEA,0BAA0B;AAAA,IACxB;AAAA,IACA;AAAA,EACF;AACF,CAAC,EAAE;AAAA,EACD,CAACA,SAAQ;AACP,UAAM,oBAAoB,QAAQA,KAAI,gBAAgBA,KAAI,gBAAgB;AAC1E,UAAM,iBAAiB,QAAQA,KAAI,0BAA0B;AAC7D,WAAO,qBAAqB;AAAA,EAC9B;AAAA,EACA;AAAA,IACE,SAAS;AAAA,EACX;AACF;AAIA,SAAS,WAAgB;AACvB,QAAM,YAAY,UAAU,UAAU,QAAQ,GAAG;AAEjD,MAAI,CAAC,UAAU,SAAS;AACtB,YAAQ,MAAM,qBAAgB;AAC9B,YAAQ,MAAM,KAAK,UAAU,UAAU,MAAM,QAAQ,EAAE,aAAa,MAAM,CAAC,CAAC;AAC5E,YAAQ,KAAK,CAAC;AAAA,EAChB;AAEA,SAAO,UAAU;AACnB;AAEA,IAAM,MAAW,SAAS;AAS1B,IAAO,cAAQ;;;ACjHf,SAAS,WAAW;AAGpB,IAAM,kBAAkB;AACxB,IAAM,iBAAiB,MAAM;AAC3B,QAAM,QAAQ,IAAI,WAAW,GAAG;AAChC,QAAM,KAAK,GAAG;AACd,WAAS,IAAI,GAAG,IAAI,gBAAgB,QAAQ,KAAK;AAC/C,UAAM,gBAAgB,WAAW,CAAC,CAAC,IAAI;AAAA,EACzC;AACA,SAAO;AACT,GAAG;AAGH,IAAM,8BAA8B;AACpC,IAAM,gCAAgC;AACtC,IAAM,kBAAkB;AACxB,IAAM,wBAAwB;AA8D9B,SAAS,qBAAqB,OAAuB;AACnD,QAAM,UAAU,MACb,QAAQ,QAAQ,EAAE,EAClB,QAAQ,MAAM,GAAG,EACjB,QAAQ,MAAM,GAAG;AAIpB,SAAO,SAAS,SAAS,MAAM,IAAI,UAAU,UAAU,IAAI,OAAO,IAAK,QAAQ,SAAS,CAAE;AAC5F;AAEA,SAAS,cAAc,OAAe,OAA2B;AAC/D,MAAI,CAAC,OAAO;AACV,UAAM,IAAI,MAAM,GAAG,KAAK,aAAa;AAAA,EACvC;AACA,QAAM,aAAa,qBAAqB,KAAK;AAC7C,QAAM,WAAW,OAAO,WAAW,SAAS,aAAa,WAAW,KAAK,KAAK,UAAU,IAAI;AAC5F,MAAI,UAAU;AACZ,QAAI;AACJ,QAAI;AACF,eAAS,SAAS,UAAU;AAAA,IAC9B,QAAQ;AACN,YAAM,IAAI,MAAM,GAAG,KAAK,sBAAsB;AAAA,IAChD;AACA,QAAI,CAAC,QAAQ;AACX,YAAM,IAAI,MAAM,GAAG,KAAK,yBAAyB;AAAA,IACnD;AACA,UAAMC,SAAQ,IAAI,WAAW,OAAO,MAAM;AAC1C,aAAS,IAAI,GAAG,IAAI,OAAO,QAAQ,KAAK;AACtC,MAAAA,OAAM,CAAC,IAAI,OAAO,WAAW,CAAC;AAAA,IAChC;AACA,WAAOA;AAAA,EACT;AAEA,MAAI,WAAW,SAAS,MAAM,GAAG;AAC/B,UAAM,IAAI,MAAM,GAAG,KAAK,sBAAsB;AAAA,EAChD;AAEA,QAAM,UAAU,WAAW,SAAS,IAAI,IAAI,IAAI,WAAW,SAAS,GAAG,IAAI,IAAI;AAC/E,QAAM,eAAgB,WAAW,SAAS,IAAK,IAAI;AACnD,QAAM,QAAQ,IAAI,WAAW,YAAY;AACzC,MAAI,SAAS;AAEb,WAAS,IAAI,GAAG,IAAI,WAAW,QAAQ,KAAK,GAAG;AAC7C,UAAM,KAAK,WAAW,WAAW,CAAC;AAClC,UAAM,KAAK,WAAW,WAAW,IAAI,CAAC;AACtC,UAAM,KAAK,WAAW,WAAW,IAAI,CAAC;AACtC,UAAM,KAAK,WAAW,WAAW,IAAI,CAAC;AAEtC,QAAI,OAAO,MAAM,OAAO,IAAI;AAC1B,YAAM,IAAI,MAAM,GAAG,KAAK,sBAAsB;AAAA,IAChD;AAEA,UAAM,KAAK,cAAc,EAAE,KAAK;AAChC,UAAM,KAAK,cAAc,EAAE,KAAK;AAChC,UAAM,KAAK,OAAO,KAAK,IAAK,cAAc,EAAE,KAAK;AACjD,UAAM,KAAK,OAAO,KAAK,IAAK,cAAc,EAAE,KAAK;AAEjD,QAAI,OAAO,OAAO,OAAO,OAAO,OAAO,OAAO,OAAO,KAAK;AACxD,YAAM,IAAI,MAAM,GAAG,KAAK,sBAAsB;AAAA,IAChD;AAEA,UAAM,SAAU,MAAM,KAAO,MAAM,KAAO,MAAM,IAAK;AACrD,UAAM,QAAQ,IAAK,UAAU,KAAM;AACnC,QAAI,OAAO,IAAI;AACb,YAAM,QAAQ,IAAK,UAAU,IAAK;AAAA,IACpC;AACA,QAAI,OAAO,IAAI;AACb,YAAM,QAAQ,IAAI,SAAS;AAAA,IAC7B;AAAA,EACF;AAEA,MAAI,CAAC,MAAM,QAAQ;AACjB,UAAM,IAAI,MAAM,GAAG,KAAK,yBAAyB;AAAA,EACnD;AAEA,SAAO;AACT;AAGA,SAAS,qBAAqB,OAAe,OAA2B;AACtE,MAAI;AACF,WAAO,cAAc,OAAO,KAAK;AAAA,EACnC,SAAS,OAAO;AACd,UAAM,SAAS,iBAAiB,QAAQ,MAAM,UAAU,GAAG,KAAK;AAChE,UAAM,IAAI,MAAM,8BAA8B,MAAM,EAAE;AAAA,EACxD;AACF;AAGA,SAAS,aAAa,KAAsC;AAC1D,MAAI;AACJ,MAAI,OAAO,QAAQ,UAAU;AAC3B,QAAI;AACF,iBAAW,cAAc,KAAK,OAAO;AAAA,IACvC,SAAS,OAAO;AACd,YAAM,SAAS,iBAAiB,QAAQ,MAAM,UAAU;AACxD,YAAM,IAAI,MAAM,gCAAgC,MAAM,kCAAkC;AAAA,IAC1F;AAAA,EACF,OAAO;AACL,eAAW,IAAI,WAAW,GAAG;AAAA,EAC/B;AAEA,MAAI,SAAS,WAAW,IAAI;AAC1B,UAAM,IAAI,MAAM,uDAAuD,SAAS,MAAM,GAAG;AAAA,EAC3F;AAEA,SAAO;AACT;AAGO,SAAS,mBAAmB,OAA2C;AAC5E,MAAI,CAAC,SAAS,OAAO,UAAU,UAAU;AACvC,WAAO;AAAA,EACT;AAEA,QAAM,UAAU;AAChB,SACE,QAAQ,MAAM,+BACd,QAAQ,QAAQ,iCAChB,OAAO,QAAQ,OAAO,YACtB,OAAO,QAAQ,QAAQ,YACvB,OAAO,QAAQ,eAAe;AAElC;AAGA,SAAS,uBAAuB,SAAiC;AAC/D,MAAI,QAAQ,MAAM,6BAA6B;AAC7C,UAAM,IAAI,MAAM,iDAAiD;AAAA,EACnE;AAEA,MAAI,QAAQ,QAAQ,+BAA+B;AACjD,UAAM,IAAI,MAAM,mDAAmD;AAAA,EACrE;AAEA,QAAM,KAAK,qBAAqB,QAAQ,IAAI,IAAI;AAChD,QAAM,MAAM,qBAAqB,QAAQ,KAAK,KAAK;AAEnD,MAAI,GAAG,WAAW,iBAAiB;AACjC,UAAM,IAAI,MAAM,gDAAgD;AAAA,EAClE;AAEA,MAAI,IAAI,WAAW,uBAAuB;AACxC,UAAM,IAAI,MAAM,iDAAiD;AAAA,EACnE;AAEA,uBAAqB,QAAQ,YAAY,YAAY;AACvD;AAmDA,eAAsB,oBACpB,SACA,KACY;AACZ,MAAI,CAAC,mBAAmB,OAAO,GAAG;AAChC,UAAM,IAAI,MAAM,4BAA4B;AAAA,EAC9C;AAEA,yBAAuB,OAAO;AAE9B,QAAM,WAAW,aAAa,GAAG;AACjC,QAAM,KAAK,qBAAqB,QAAQ,IAAI,IAAI;AAChD,QAAM,MAAM,qBAAqB,QAAQ,KAAK,KAAK;AACnD,QAAM,aAAa,qBAAqB,QAAQ,YAAY,YAAY;AAExE,QAAM,WAAW,IAAI,WAAW,WAAW,SAAS,IAAI,MAAM;AAC9D,WAAS,IAAI,UAAU;AACvB,WAAS,IAAI,KAAK,WAAW,MAAM;AAEnC,MAAI;AACJ,MAAI;AACF,gBAAY,IAAI,UAAU,EAAE,EAAE,QAAQ,QAAQ;AAAA,EAChD,SAAS,OAAO;AACd,UAAM,UAAW,OAA4C;AAC7D,UAAM,IAAI,MAAM,uCAAuC,WAAW,OAAO,KAAK,CAAC,EAAE;AAAA,EACnF;AAEA,MAAI;AACF,UAAM,UAAU,IAAI,YAAY;AAChC,WAAO,KAAK,MAAM,QAAQ,OAAO,SAAS,CAAC;AAAA,EAC7C,QAAQ;AACN,UAAM,IAAI,MAAM,oCAAoC;AAAA,EACtD;AACF;;;ACnSA,IAAI;AAcJ,eAAe,oBAAsC;AACnD,MAAI;AACF,UAAM,iBAAiB,MAAM,OAAO,UAAU;AAC9C,QAAI,OAAO,eAAe,wBAAwB,YAAY;AAC5D,aAAO;AAAA,IACT;AACA,mBAAe,oBAAoB;AACnC,WAAO;AAAA,EACT,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAMA,eAAe,oCAAsD;AACnE,SAAO,QAAQ,YAAI,0BAA0B,KAAK,MAAM,kBAAkB;AAC5E;AAMA,SAAS,8BAAsC;AAC7C,QAAM,MAAM,YAAI;AAChB,MAAI,CAAC,KAAK;AACR,UAAM,IAAI;AAAA,MACR;AAAA,IACF;AAAA,EACF;AACA,SAAO;AACT;AAKA,eAAe,6BAAuE;AACpF,MAAI,CAAC,6BAA6B;AAChC,WAAO;AAAA,EACT;AAEA,QAAM,WAAW,MAAM,4BAA4B;AACnD,MAAI,CAAC,UAAU;AACb,WAAO;AAAA,EACT;AAEA,MAAI,OAAO,aAAa,UAAU;AAChC,UAAM,IAAI,UAAU,sDAAsD;AAAA,EAC5E;AAEA,SAAO;AACT;AAaA,eAAsB,2BACpB,aAC8B;AAE9B,QAAM,sBAAsB,MAAM,2BAA2B;AAC7D,QAAM,WAAgC,sBAAsB,EAAE,GAAG,oBAAoB,IAAI,CAAC;AAE1F,MAAI,CAAC,aAAa;AAChB,WAAO;AAAA,EACT;AAGA,MAAI,mBAAmB,WAAW,GAAG;AACnC,QAAI;AACF,YAAM,YAAY,MAAM;AAAA,QACtB;AAAA,QACA,4BAA4B;AAAA,MAC9B;AACA,aAAO,EAAE,GAAG,UAAU,GAAG,UAAU;AAAA,IACrC,SAAS,OAAO;AACd,YAAM,SAAS,iBAAiB,QAAQ,MAAM,UAAU;AACxD,YAAM,IAAI,MAAM,2CAA2C,MAAM,EAAE;AAAA,IACrE;AAAA,EACF;AAEA,MAAI,MAAM,kCAAkC,GAAG;AAC7C,UAAM,IAAI;AAAA,MACR;AAAA,IAEF;AAAA,EACF;AAGA,SAAO,EAAE,GAAG,UAAU,GAAG,YAAY;AACvC;AAUA,SAAS,WAAW,QAA6C,KAAiC;AAChG,QAAM,QAAQ,SAAS,GAAG;AAC1B,SAAO,OAAO,UAAU,YAAY,MAAM,SAAS,IAAI,QAAQ;AACjE;AAEA,SAAS,4BAA4B,QAA+E;AAClH,QAAM,UAAU,WAAW,QAAQ,YAAY;AAC/C,QAAM,cAAc,WAAW,QAAQ,gBAAgB;AACvD,QAAM,qBAAqB,WAAW,QAAQ,uBAAuB;AACrE,QAAM,aAAa,WAAW,QAAQ,eAAe;AACrD,QAAM,aAAa,WAAW,QAAQ,eAAe;AAErD,MAAI,CAAC,WAAW,CAAC,eAAe,CAAC,sBAAsB,CAAC,cAAc,CAAC,YAAY;AACjF,WAAO;AAAA,EACT;AAEA,OAAK,CAAC,WAAW,CAAC,gBAAgB,CAAC,oBAAoB;AACrD,UAAM,IAAI;AAAA,MACR;AAAA,IACF;AAAA,EACF;AAEA,SAAO;AAAA,IACL;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF;AACF;AAEA,SAAS,wBAAwB,SAAkD;AACjF,SAAO;AAAA,IACL,MAAM,eAAe;AAEnB,YAAM,EAAE,SAAS,UAAU,IAAI,MAAM,OAAO,eAAe;AAC3D,aAAO,IAAI,UAAU;AAAA,QACnB,SAAS,QAAQ;AAAA,QACjB,aAAa,QAAQ;AAAA,QACrB,oBAAoB,QAAQ;AAAA,MAC9B,CAAC;AAAA,IACH;AAAA,IACA,gBAAgB;AACd,aAAO,QAAQ;AAAA,IACjB;AAAA,IACA,gBAAgB;AACd,aAAO,QAAQ;AAAA,IACjB;AAAA,EACF;AACF;AAaA,eAAsB,iBACpB,aAC4B;AAC5B,QAAM,WAAW,MAAM,2BAA2B,WAAW;AAC7D,QAAM,iBAAiB;AACvB,QAAM,yBAAyB,4BAA4B,cAAc;AACzE,MAAI,wBAAwB;AAC1B,WAAO,wBAAwB,sBAAsB;AAAA,EACvD;AAGA,QAAM,aAAa,YAAI;AACvB,QAAM,iBAAiB,YAAI;AAE3B,MAAI,CAAC,cAAc,CAAC,gBAAgB;AAClC,UAAM,IAAI;AAAA,MACR;AAAA,IACF;AAAA,EACF;AAEA,SAAO,wBAAwB;AAAA,IAC7B,SAAS;AAAA,IACT,aAAa;AAAA,IACb,YAAY,YAAI;AAAA,IAChB,YAAY,YAAI;AAAA,EAClB,CAAC;AACH;AAwEA,eAAsB,yBACpB,aACqC;AACrC,QAAM,WAAW,MAAM,2BAA2B,WAAW;AAC7D,QAAM,iBAAiB;AAGvB,QAAM,QAAQ,WAAW,gBAAgB,eAAe,KAAK,YAAI;AACjE,QAAM,YACJ,WAAW,gBAAgB,eAAe,KAAK,YAAI;AAErD,MAAI,CAAC,SAAS,CAAC,WAAW;AACxB,WAAO;AAAA,EACT;AAEA,SAAO,EAAE,OAAO,UAAU;AAC5B;;;AH5RO,IAAM,0BAA8E;AAAA,EACzF,QAAQ;AAAA,EACR,WAAW;AAAA,EACX,QAAQ;AACV;AAYO,IAAM,kBAAwE;AAAA,EACnF,QAAQ,CAAC,WAAW,YAAY;AAAA,EAChC,WAAW,CAAC,mBAAmB;AAAA,EAC/B,QAAQ,CAAC,0BAA0B,iCAAiC,kBAAkB;AACxF;AAyBO,IAAM,8BAA0D;AAAA,EACrE;AAAA,IACE,UAAU;AAAA,IACV,SAAS;AAAA,IACT,oBAAoB;AAAA,IACpB,OAAO;AAAA,IACP,cAAc;AAAA,IACd,UAAU;AAAA,IACV,QAAQ;AAAA,EACV;AACF;AAEA,IAAM,iCAAiC,oBAAI,IAAY;AAKhD,SAAS,4BACd,UACA,SACsC;AACtC,SAAO,4BAA4B;AAAA,IACjC,iBAAe,YAAY,aAAa,YAAY,YAAY,YAAY;AAAA,EAC9E;AACF;AAEA,SAAS,2CAA2C,UAA6B,SAAuB;AACtG,QAAM,cAAc,4BAA4B,UAAU,OAAO;AACjE,MAAI,CAAC,aAAa;AAChB;AAAA,EACF;AAEA,QAAM,kBAAkB,YAAY,qBAClC,8BAA8B,QAAQ,YAAY,YAAY,kBAAkB,eAChF;AACF,QAAM,aAAa,YAAY,WAAW,0BAA0B,YAAY,QAAQ,MAAM;AAC9F,QAAM,aAAa,YAAY,SAAS,YAAY,YAAY,MAAM,KAAK;AAE3E,QAAM,UACJ,YAAY,UAAU,YACpB,uDAAuD,QAAQ,YAAY,OAAO,KAAK,eAAe,GAAG,UAAU,KACnH,oEAAoE,QAAQ,YAAY,OAAO,KAAK,eAAe,GAAG,UAAU,GAAG,UAAU;AAEjJ,MAAI,YAAY,UAAU,WAAW;AACnC,UAAM,IAAI,MAAM,OAAO;AAAA,EACzB;AAEA,QAAM,aAAa,GAAG,QAAQ,IAAI,OAAO;AACzC,MAAI,+BAA+B,IAAI,UAAU,GAAG;AAClD;AAAA,EACF;AAEA,iCAA+B,IAAI,UAAU;AAC7C,UAAQ,KAAK,OAAO;AACtB;AAqBA,SAAS,6BAAgD;AACvD,SAAQ,OAAO,QAAQ,uBAAuB,EAC3C,IAAI,CAAC,CAAC,UAAU,OAAO,OAAO,EAAE,UAAU,QAAQ,EAAE;AACzD;AAEA,SAAS,yBAA4C;AACnD,SAAQ,OAAO,QAAQ,eAAe,EACnC,QAAQ,CAAC,CAAC,UAAU,MAAM,MAAM,OAAO,IAAI,cAAY,EAAE,UAAU,QAAQ,EAAE,CAAC;AACnF;AAEA,SAAS,oBAAoB,OAA2C;AACtE,SAAO,UAAU,YAAY,UAAU,eAAe,UAAU;AAClE;AAEA,SAAS,mBAAmB,OAAgC;AAC1D,QAAM,UAAU,MAAM,KAAK;AAC3B,QAAM,CAAC,aAAa,UAAU,IAAI,QAAQ,MAAM,KAAK,CAAC;AACtD,QAAM,WAAW,aAAa,KAAK;AACnC,QAAM,UAAU,YAAY,KAAK;AAEjC,MAAI,CAAC,YAAY,CAAC,SAAS;AACzB,UAAM,IAAI;AAAA,MACR,4BAA4B,KAAK;AAAA,IACnC;AAAA,EACF;AAEA,MAAI,CAAC,oBAAoB,QAAQ,GAAG;AAClC,UAAM,IAAI;AAAA,MACR,8BAA8B,QAAQ,SAAS,KAAK,2BAA2B,OAAO,KAAK,eAAe,EAAE,KAAK,IAAI,CAAC;AAAA,IACxH;AAAA,EACF;AAEA,QAAM,kBAAkB,gBAAgB,QAAQ;AAChD,MAAI,CAAC,gBAAgB,SAAS,OAAO,GAAG;AACtC,UAAM,IAAI;AAAA,MACR,2BAA2B,OAAO,mBAAmB,QAAQ,wBAAwB,gBAAgB,KAAK,IAAI,CAAC;AAAA,IACjH;AAAA,EACF;AAEA,6CAA2C,UAAU,OAAO;AAE5D,SAAO;AAAA,IACL;AAAA,IACA;AAAA,EACF;AACF;AAUO,SAAS,wBAAwB,UAA0C,CAAC,GAAsB;AACvG,QAAM,gBAAgB,QAAQ,YAAY,IAAI,WAAS,MAAM,KAAK,CAAC,EAAE,OAAO,OAAO,KAAK,CAAC;AACzF,MAAI,cAAc,SAAS,GAAG;AAC5B,UAAM,eAAe,MAAM,KAAK,IAAI,IAAI,aAAa,CAAC;AACtD,WAAO,aAAa,IAAI,kBAAkB;AAAA,EAC5C;AAEA,QAAM,YAAY,QAAQ,aAAa;AACvC,MAAI,cAAc,OAAO;AACvB,WAAO,uBAAuB;AAAA,EAChC;AAEA,SAAO,2BAA2B;AACpC;AASO,SAAS,+BAA+B,cAAmB,aAAwB;AACxF,QAAM,eAAe,YAAY,uBAAuB,KAAK;AAC7D,QAAM,gBAAgB,YAAY,oBAAoB,KAAK;AAC3D,MAAI;AACJ,MAAI,CAAC,gBAAgB,iBAAiB,WAAW;AAC/C,gBAAY;AAAA,EACd,WAAW,iBAAiB,OAAO;AACjC,gBAAY;AAAA,EACd,OAAO;AACL,UAAM,IAAI;AAAA,MACR,kCAAkC,YAAY;AAAA,IAChD;AAAA,EACF;AAEA,MAAI;AACJ,MAAI,eAAe;AACjB,iBAAa,cAAc,MAAM,GAAG,EAAE,IAAI,WAAS,MAAM,KAAK,CAAC,EAAE,OAAO,OAAO;AAAA,EACjF;AAEA,SAAO,wBAAwB;AAAA,IAC7B;AAAA,IACA;AAAA,EACF,CAAC;AACH;AAMO,IAAM,qBAAwC,+BAA+B;;;AI7PpF,eAAsB,oBACpB,aAC4B;AAC5B,SAAO,iBAAiB,WAAW;AACrC;;;ACiBA,eAAsB,mBACpB,SACA,UAA0B,CAAC,GACD;AAC1B;AACA,SAAO,aAAa,UAAU,SAAS,OAAO;AAChD;AAWA,eAAsB,mBACpB,SACA,UAA0B,CAAC,GACD;AAC1B;AACA,SAAO,aAAa,UAAU,SAAS,OAAO;AAChD;AAWA,eAAsB,wBACpB,YACA,UAA0B,CAAC,GACD;AAC1B;AACA,SAAO,aAAa,gBAAgB,YAAY,OAAO;AACzD;AAMA,SAAS,yBACP,UACiB;AACjB,SAAO;AAAA,IACL,SAAS,SAAS;AAAA,IAClB,SAAS,SAAS;AAAA,IAClB,YAAY,SAAS;AAAA,IACrB,SAAS,SAAS;AAAA,IAClB,WAAW,SAAS;AAAA,EACtB;AACF;AAOA,eAAe,aACb,gBACA,IACA,SAC0B;AAC1B;AACA,QAAM,EAAE,YAAY,cAAc,YAAY,IAAI;AAElD,QAAM,YAAY,MAAM,oBAAoB,WAAW;AACvD,QAAM,MAAM,MAAM,UAAU,aAAa;AAGzC,QAAM,cAAc,IAAI,gBAAgB;AACxC,cAAY,OAAO,eAAe,SAAS;AAG3C,QAAM,OAAO,uBAAuB,cAAc,IAAI,EAAE,YAAY,YAAY,SAAS,CAAC;AAC1F,QAAM,WAAW,MAAM,IAAI,IAAiC,IAAI;AAEhE,SAAO,yBAAyB,QAAQ;AAC1C;;;AChEA,eAAsB,oBACpB,SACA,UAA0B,CAAC,GACP;AACpB;AACA,QAAM,WAAW,MAAM,cAAc,UAAU,SAAS,OAAO;AAC/D,SAAO,SAAS;AAClB;AAUA,eAAsB,oBACpB,SACA,UAA0B,CAAC,GACP;AACpB;AACA,QAAM,WAAW,MAAM,cAAc,UAAU,SAAS,OAAO;AAC/D,SAAO,SAAS;AAClB;AAUA,eAAsB,yBACpB,YACA,UAA0B,CAAC,GACP;AACpB;AACA,QAAM,WAAW,MAAM,cAAc,gBAAgB,YAAY,OAAO;AACxE,SAAO,SAAS;AAClB;AAMA,SAAS,yBAAyB,UAA+C;AAC/E,SAAO;AAAA,IACL,SAAS,SAAS,KAAK;AAAA,IACvB,SAAS,SAAS,KAAK;AAAA,IACvB,YAAY,SAAS,KAAK;AAAA,IAC1B,UAAU,SAAS,KAAK,SAAS,IAAI,QAAM;AAAA,MACzC,SAAS,EAAE;AAAA,MACX,OAAO,EAAE;AAAA,MACT,OAAO,EAAE;AAAA,IACX,EAAE;AAAA,EACJ;AACF;AAOA,eAAe,cACb,gBACA,IACA,SAC0B;AAC1B;AACA,QAAM;AAAA,IACJ,QAAQ;AAAA,IACR,iBAAiB;AAAA,IACjB,UAAU;AAAA,IACV,YAAY;AAAA,IACZ;AAAA,EACF,IAAI;AAEJ,QAAM,YAAY,MAAM,oBAAoB,WAAW;AACvD,QAAM,MAAM,MAAM,UAAU,aAAa;AAGzC,QAAM,cAAc,IAAI,gBAAgB;AACxC,cAAY,OAAO,SAAS,OAAO,KAAK,CAAC;AACzC,cAAY,OAAO,mBAAmB,cAAc;AACpD,cAAY,OAAO,YAAY,OAAO;AACtC,cAAY,OAAO,eAAe,SAAS;AAG3C,QAAM,OAAO,uBAAuB,cAAc,IAAI,EAAE,aAAa,YAAY,SAAS,CAAC;AAC3F,QAAM,WAAW,MAAM,IAAI,IAAiC,IAAI;AAEhE,SAAO,yBAAyB,QAAQ;AAC1C;;;ACrJA,IAAM,2BAA2B;AAEjC,SAAS,wBAAwB,OAAuB;AACtD,QAAM,UAAU,MAAM,KAAK;AAC3B,QAAM,YAAY,QAAQ,SAAS,KAAK,IAAI,UAAU,WAAW,OAAO;AAExE,MAAI;AACJ,MAAI;AACF,aAAS,IAAI,IAAI,SAAS;AAAA,EAC5B,QAAQ;AACN,UAAM,IAAI;AAAA,MACR;AAAA,IAEF;AAAA,EACF;AAEA,MACE,OAAO,YACP,OAAO,YACP,OAAO,UACP,OAAO,QACN,OAAO,YAAY,OAAO,aAAa,KACxC;AACA,UAAM,IAAI;AAAA,MACR;AAAA,IAEF;AAAA,EACF;AAEA,SAAO,OAAO;AAChB;AAEO,SAAS,oBAA4B;AAC1C,QAAM,WAAW,YAAI;AACrB,MAAI,CAAC,UAAU;AACb,WAAO;AAAA,EACT;AAEA,SAAO,wBAAwB,QAAQ;AACzC;AAEO,SAAS,mBAAmB,YAAoB,WAA+C;AACpG,QAAM,SAAS,kBAAkB;AACjC,SAAO,GAAG,MAAM,IAAI,UAAU,IAAI,SAAS;AAC7C;AAEO,SAAS,wBAAwB,YAA4B;AAClE,SAAO,mBAAmB,YAAY,YAAY;AACpD;AAEO,SAAS,uBAAuB,YAA4B;AACjE,SAAO,mBAAmB,YAAY,WAAW;AACnD;;;ACZA,eAAe,oBAAoB,SAAuC;AAGxE,QAAM,EAAE,SAAS,UAAU,IAAI,MAAM,OAAO,eAAe;AAC3D,SAAO,IAAI,UAAU;AAAA;AAAA;AAAA,IAGnB,SAAS,YAAI,gBAAgB;AAAA,IAC7B,aAAa,YAAI,oBAAoB;AAAA,IACrC,eAAe,QAAQ;AAAA,IACvB,eAAe,QAAQ;AAAA,EACzB,CAAC;AACH;AAWA,eAAsB,eACpB,YACA,SACA,OAAkB,SAClB,QACiB;AACjB;AACA,QAAM,SAAS,MAAM,oBAAoB,OAAO;AAGhD,QAAM,eAAe,SACjB,OAAO;AAAA,IACL,OAAO,QAAQ,MAAM,EAAE,IAAI,CAAC,CAAC,KAAK,KAAK,MAAM,CAAC,KAAK,OAAO,KAAK,CAAC,CAAC;AAAA,EACnE,IACF;AAEF,SAAO,OAAO,IAAI,eAAe,YAAY;AAAA,IAC3C;AAAA,IACA,YAAY,QAAQ,cAAc;AAAA,IAClC,QAAQ;AAAA,EACV,CAAC;AACH;AAWA,eAAsB,QACpB,KACA,YACA,OAAkB,SAClB,QACA,aACiB;AACjB;AACA,QAAM,kBAAkB,MAAM,yBAAyB,WAAW;AAClE,MAAI,CAAC,iBAAiB;AACpB,UAAM,IAAI;AAAA,MACR;AAAA,IAEF;AAAA,EACF;AACA,QAAM,QAAQ,MAAM,eAAe,YAAY,iBAAiB,MAAM,MAAM;AAC5E,QAAM,YAAY,IAAI,SAAS,GAAG,IAAI,MAAM;AAC5C,SAAO,GAAG,GAAG,GAAG,SAAS,SAAS,KAAK;AACzC;;;AC/GO,IAAM,2BAA2B;AAWxC,eAAsB,iBACpB,YACA,QAAgB,0BAChB,aAAsB,OACtB,aACiB;AACjB;AACA,QAAM,UAAU,wBAAwB,UAAU;AAElD,MAAI,YAAY;AACd,WAAO,QAAQ,SAAS,YAAY,cAAc,EAAE,MAAM,GAAG,WAAW;AAAA,EAC1E;AAEA,SAAO,GAAG,OAAO,UAAU,KAAK;AAClC;;;ACrBO,SAAS,mBAAmB,MAAsB;AACvD,QAAM,QAAQ,KAAK,KAAK,EAAE,MAAM,KAAK,EAAE;AACvC,SAAO,KAAK,KAAK,QAAQ,IAAI;AAC/B;AAUO,SAAS,cACd,MACA,WACA,gBAAwB,GACX;AACb,MAAI,CAAC,KAAK,KAAK,GAAG;AAChB,WAAO,CAAC;AAAA,EACV;AAEA,QAAM,SAAsB,CAAC;AAC7B,QAAM,QAAQ,KAAK,KAAK,EAAE,MAAM,KAAK;AAGrC,QAAM,gBAAgB,KAAK,MAAM,YAAY,IAAI;AACjD,QAAM,eAAe,KAAK,MAAM,gBAAgB,IAAI;AAEpD,MAAI,aAAa;AACjB,MAAI,kBAAkB;AAEtB,SAAO,kBAAkB,MAAM,QAAQ;AACrC,UAAM,aAAa,MAAM;AAAA,MACvB;AAAA,MACA,kBAAkB;AAAA,IACpB;AACA,UAAMC,aAAY,WAAW,KAAK,GAAG;AACrC,UAAM,aAAa,mBAAmBA,UAAS;AAE/C,WAAO,KAAK;AAAA,MACV,IAAI,SAAS,UAAU;AAAA,MACvB,MAAMA;AAAA,MACN;AAAA,IACF,CAAC;AAGD,uBAAmB,gBAAgB;AACnC;AAGA,QAAI,oBAAoB,aAAa,MAAM,gBAAgB,eAAe;AACxE;AAAA,IACF;AAAA,EACF;AAEA,SAAO;AACT;AAKA,SAAS,oBAAoB,MAAgB,OAA0B;AACrE,QAAM,OAAO,KAAK,IAAI,OAAK,EAAE,IAAI,EAAE,KAAK,GAAG;AAC3C,SAAO;AAAA,IACL,IAAI,SAAS,KAAK;AAAA,IAClB;AAAA,IACA,YAAY,mBAAmB,IAAI;AAAA,IACnC,WAAW,KAAK,CAAC,EAAE;AAAA,IACnB,SAAS,KAAK,KAAK,SAAS,CAAC,EAAE;AAAA,EACjC;AACF;AAWO,SAAS,aACd,MACA,WACA,cAAsB,GACT;AACb,MAAI,KAAK,WAAW;AAClB,WAAO,CAAC;AAEV,QAAM,SAAsB,CAAC;AAC7B,MAAI,cAAwB,CAAC;AAC7B,MAAI,gBAAgB;AACpB,MAAI,aAAa;AAEjB,WAAS,IAAI,GAAG,IAAI,KAAK,QAAQ,KAAK;AACpC,UAAM,MAAM,KAAK,CAAC;AAClB,UAAM,YAAY,mBAAmB,IAAI,IAAI;AAG7C,QAAI,gBAAgB,YAAY,aAAa,YAAY,SAAS,GAAG;AACnE,aAAO,KAAK,oBAAoB,aAAa,UAAU,CAAC;AACxD;AAGA,YAAM,eAAe,KAAK,IAAI,GAAG,YAAY,SAAS,WAAW;AACjE,oBAAc,YAAY,MAAM,YAAY;AAC5C,sBAAgB,YAAY;AAAA,QAC1B,CAAC,KAAK,MAAM,MAAM,mBAAmB,EAAE,IAAI;AAAA,QAC3C;AAAA,MACF;AAAA,IACF;AAEA,gBAAY,KAAK,GAAG;AACpB,qBAAiB;AAAA,EACnB;AAGA,MAAI,YAAY,SAAS,GAAG;AAC1B,WAAO,KAAK,oBAAoB,aAAa,UAAU,CAAC;AAAA,EAC1D;AAEA,SAAO;AACT;AASO,SAAS,UAAU,MAAc,UAAyC;AAC/E,UAAQ,SAAS,MAAM;AAAA,IACrB,KAAK,SAAS;AACZ,aAAO,cAAc,MAAM,SAAS,WAAW,SAAS,WAAW,CAAC;AAAA,IACtE;AAAA,IACA,SAAS;AACP,YAAM,kBAAyB;AAC/B,YAAM,IAAI,MAAM,kCAAkC,eAAe,EAAE;AAAA,IACrE;AAAA,EACF;AACF;;;AC5HA,eAAsB,iBACpB,YACA,UACA,UAA4B,CAAC,GACV;AACnB;AACA,QAAM,EAAE,WAAW,IAAI,QAAQ,KAAK,aAAa,OAAO,YAAY,YAAY,IAAI;AACpF,MAAI,aAAuB,CAAC;AAE5B,MAAI,YAAY,IAAI;AAClB,UAAM,UAAU,WAAW;AAC3B,aAAS,IAAI,GAAG,KAAK,GAAG,KAAK;AAC3B,iBAAW,KAAK,KAAK,MAAM,IAAI,OAAO,CAAC;AAAA,IACzC;AAAA,EACF,OAAO;AACL,aAAS,OAAO,GAAG,OAAO,UAAU,QAAQ,UAAU;AACpD,iBAAW,KAAK,IAAI;AAAA,IACtB;AAAA,EACF;AAGA,MAAI,eAAe,UAAa,WAAW,SAAS,YAAY;AAC9D,UAAM,gBAA0B,CAAC;AAGjC,kBAAc,KAAK,CAAC;AAGpB,QAAI,cAAc,GAAG;AACnB,YAAM,UAAU,YAAY,aAAa;AACzC,eAAS,IAAI,GAAG,IAAI,aAAa,GAAG,KAAK;AACvC,sBAAc,KAAK,UAAU,CAAC;AAAA,MAChC;AAEA,oBAAc,KAAK,QAAQ;AAAA,IAC7B;AAEA,iBAAa;AAAA,EACf;AAEA,QAAM,UAAU,uBAAuB,UAAU;AAEjD,QAAM,cAAc,WAAW,IAAI,OAAO,SAAS;AACjD,QAAI,YAAY;AACd,aAAO,QAAQ,SAAS,YAAY,aAAa,EAAE,MAAM,MAAM,GAAG,WAAW;AAAA,IAC/E;AAEA,WAAO,GAAG,OAAO,SAAS,IAAI,UAAU,KAAK;AAAA,EAC/C,CAAC;AAED,SAAO,QAAQ,IAAI,WAAW;AAChC;;;AC9CO,SAAS,mBAAmB,OAAmC;AACpE,UAAQ,MAAM,UAAU,CAAC,GAAG;AAAA,IAC1B,WAAS,MAAM,SAAS,UAAU,MAAM,WAAW;AAAA,EACrD;AACF;AAEO,SAAS,iBAAiB,OAAiB,cAAmD;AACnG,QAAM,SAAS,mBAAmB,KAAK;AACvC,MAAI,CAAC,OAAO;AACV,WAAO;AAET,MAAI,CAAC,cAAc;AACjB,WAAO,OAAO,CAAC;AAAA,EACjB;AAEA,SAAO,OAAO;AAAA,IACZ,WACE,MAAM,cAAc,eACpB,MAAM,kBAAkB;AAAA,EAC5B;AACF;AAEO,SAAS,mBAAmB,YAA4B;AAC7D,MAAI,CAAC,WAAW,KAAK,GAAG;AACtB,WAAO;AAAA,EACT;AAEA,QAAM,QAAQ,WAAW,MAAM,IAAI;AACnC,QAAM,YAAsB,CAAC;AAE7B,WAAS,IAAI,GAAG,IAAI,MAAM,QAAQ,KAAK;AACrC,UAAM,OAAO,MAAM,CAAC,EAAE,KAAK;AAE3B,QAAI,CAAC;AACH;AACF,QAAI,SAAS;AACX;AACF,QAAI,KAAK,WAAW,OAAO;AACzB;AACF,QAAI,KAAK,SAAS,KAAK;AACrB;AACF,QAAI,WAAW,KAAK,IAAI,KAAK,CAAC,KAAK,SAAS,GAAG;AAC7C;AACF,QAAI,KAAK,WAAW,OAAO,KAAK,KAAK,WAAW,QAAQ;AACtD;AAEF,UAAM,YAAY,KAAK,QAAQ,YAAY,EAAE,EAAE,KAAK;AAEpD,QAAI,WAAW;AACb,gBAAU,KAAK,SAAS;AAAA,IAC1B;AAAA,EACF;AAEA,SAAO,UAAU,KAAK,GAAG,EAAE,QAAQ,QAAQ,GAAG,EAAE,KAAK;AACvD;AAEO,SAAS,sBAAsB,WAA2B;AAC/D,QAAM,QAAQ,UAAU,MAAM,GAAG;AACjC,MAAI,MAAM,WAAW;AACnB,WAAO;AAET,QAAM,QAAQ,OAAO,SAAS,MAAM,CAAC,GAAG,EAAE,KAAK;AAC/C,QAAM,UAAU,OAAO,SAAS,MAAM,CAAC,GAAG,EAAE,KAAK;AACjD,QAAM,UAAU,OAAO,WAAW,MAAM,CAAC,CAAC,KAAK;AAE/C,SAAO,QAAQ,OAAO,UAAU,KAAK;AACvC;AASO,SAAS,mBAAmB,SAAyB;AAC1D,QAAM,UAAU,KAAK,IAAI,GAAG,KAAK,MAAM,OAAO,CAAC;AAC/C,QAAM,QAAQ,KAAK,MAAM,UAAU,IAAI;AACvC,QAAM,UAAU,KAAK,MAAO,UAAU,OAAQ,EAAE;AAChD,QAAM,mBAAmB,UAAU;AAEnC,MAAI,QAAQ,GAAG;AACb,WAAO,GAAG,MAAM,SAAS,EAAE,SAAS,GAAG,GAAG,CAAC,IAAI,QAAQ,SAAS,EAAE,SAAS,GAAG,GAAG,CAAC,IAAI,iBAAiB,SAAS,EAAE,SAAS,GAAG,GAAG,CAAC;AAAA,EACpI;AACA,SAAO,GAAG,OAAO,IAAI,iBAAiB,SAAS,EAAE,SAAS,GAAG,GAAG,CAAC;AACnE;AAEO,SAAS,6BAA6B,YAA4B;AACvE,MAAI,CAAC,WAAW,KAAK,GAAG;AACtB,WAAO;AAAA,EACT;AAEA,QAAM,QAAQ,WAAW,MAAM,IAAI;AACnC,QAAM,WAAkD,CAAC;AAEzD,WAAS,IAAI,GAAG,IAAI,MAAM,QAAQ,KAAK;AACrC,UAAM,OAAO,MAAM,CAAC,EAAE,KAAK;AAE3B,QAAI,KAAK,SAAS,KAAK,GAAG;AACxB,YAAM,YAAY,KAAK,MAAM,OAAO,EAAE,CAAC,EAAE,KAAK;AAC9C,YAAM,gBAAgB,sBAAsB,SAAS;AAErD,UAAI,IAAI,IAAI;AACZ,aAAO,IAAI,MAAM,UAAU,CAAC,MAAM,CAAC,EAAE,KAAK,GAAG;AAC3C;AAAA,MACF;AAEA,UAAI,IAAI,MAAM,QAAQ;AACpB,cAAM,OAAO,MAAM,CAAC,EAAE,KAAK,EAAE,QAAQ,YAAY,EAAE;AACnD,YAAI,MAAM;AACR,mBAAS,KAAK,EAAE,MAAM,eAAe,KAAK,CAAC;AAAA,QAC7C;AAAA,MACF;AAAA,IACF;AAAA,EACF;AAEA,SAAO,SACJ,IAAI,aAAW,IAAI,KAAK,MAAM,QAAQ,IAAI,CAAC,MAAM,QAAQ,IAAI,EAAE,EAC/D,KAAK,IAAI;AACd;AAQO,SAAS,aAAa,YAA8B;AACzD,MAAI,CAAC,WAAW,KAAK;AACnB,WAAO,CAAC;AAEV,QAAM,QAAQ,WAAW,MAAM,IAAI;AACnC,QAAM,OAAiB,CAAC;AAExB,WAAS,IAAI,GAAG,IAAI,MAAM,QAAQ,KAAK;AACrC,UAAM,OAAO,MAAM,CAAC,EAAE,KAAK;AAE3B,QAAI,KAAK,SAAS,KAAK,GAAG;AACxB,YAAM,CAAC,UAAU,MAAM,IAAI,KAAK,MAAM,OAAO,EAAE,IAAI,OAAK,EAAE,KAAK,CAAC;AAChE,YAAM,YAAY,sBAAsB,QAAQ;AAChD,YAAM,UAAU,sBAAsB,OAAO,MAAM,GAAG,EAAE,CAAC,CAAC;AAG1D,YAAM,YAAsB,CAAC;AAC7B,UAAI,IAAI,IAAI;AACZ,aAAO,IAAI,MAAM,UAAU,MAAM,CAAC,EAAE,KAAK,KAAK,CAAC,MAAM,CAAC,EAAE,SAAS,KAAK,GAAG;AACvE,cAAM,YAAY,MAAM,CAAC,EAAE,KAAK,EAAE,QAAQ,YAAY,EAAE;AACxD,YAAI;AACF,oBAAU,KAAK,SAAS;AAC1B;AAAA,MACF;AAEA,UAAI,UAAU,SAAS,GAAG;AACxB,aAAK,KAAK;AAAA,UACR;AAAA,UACA;AAAA,UACA,MAAM,UAAU,KAAK,GAAG;AAAA,QAC1B,CAAC;AAAA,MACH;AAAA,IACF;AAAA,EACF;AAEA,SAAO;AACT;AAWA,eAAsB,mBACpB,YACA,SACA,aAAsB,OACtB,aACiB;AACjB;AACA,QAAM,UAAU,0BAA0B,UAAU,SAAS,OAAO;AAEpE,MAAI,YAAY;AACd,WAAO,QAAQ,SAAS,YAAY,SAAS,QAAW,WAAW;AAAA,EACrE;AAEA,SAAO;AACT;AAEA,eAAsB,wBACpB,OACA,YACA,UAAkC,CAAC,GACR;AAC3B;AACA,QAAM;AAAA,IACJ;AAAA,IACA,kBAAkB;AAAA,IAClB;AAAA,IACA;AAAA,IACA,WAAW;AAAA,EACb,IAAI;AACJ,QAAM,QAAQ,iBAAiB,OAAO,YAAY;AAElD,MAAI,CAAC,OAAO;AACV,QAAI,UAAU;AACZ,YAAM,qBAAqB,mBAAmB,KAAK,EAChD,IAAI,OAAK,EAAE,aAAa,EACxB,OAAO,OAAO,EACd,KAAK,IAAI;AACZ,YAAM,IAAI;AAAA,QACR,4BAA4B,eAAe,kBAAkB,YAAY,MAAM,EAAE,0BAA0B,sBAAsB,MAAM;AAAA,MACzI;AAAA,IACF;AACA,WAAO,EAAE,gBAAgB,GAAG;AAAA,EAC9B;AAEA,MAAI,CAAC,MAAM,IAAI;AACb,QAAI,UAAU;AACZ,YAAM,IAAI,MAAM,mCAAmC;AAAA,IACrD;AACA,WAAO,EAAE,gBAAgB,IAAI,MAAM;AAAA,EACrC;AAEA,QAAM,gBAAgB,MAAM,mBAAmB,YAAY,MAAM,IAAI,YAAY,WAAW;AAE5F,MAAI;AACF,UAAM,WAAW,MAAM,MAAM,aAAa;AAC1C,QAAI,CAAC,SAAS,IAAI;AAChB,UAAI,UAAU;AACZ,cAAM,IAAI,MAAM,oCAAoC,SAAS,MAAM,GAAG;AAAA,MACxE;AACA,aAAO,EAAE,gBAAgB,IAAI,eAAe,MAAM;AAAA,IACpD;AAEA,UAAM,SAAS,MAAM,SAAS,KAAK;AACnC,UAAM,iBAAiB,kBAAkB,mBAAmB,MAAM,IAAI;AAEtE,QAAI,YAAY,CAAC,eAAe,KAAK,GAAG;AACtC,YAAM,IAAI,MAAM,qBAAqB;AAAA,IACvC;AAEA,WAAO,EAAE,gBAAgB,eAAe,MAAM;AAAA,EAChD,SAAS,OAAO;AACd,QAAI,UAAU;AACZ,YAAM,IAAI;AAAA,QACR,+BAA+B,iBAAiB,QAAQ,MAAM,UAAU,eAAe;AAAA,MACzF;AAAA,IACF;AACA,YAAQ,KAAK,+BAA+B,KAAK;AACjD,WAAO,EAAE,gBAAgB,IAAI,eAAe,MAAM;AAAA,EACpD;AACF;","names":["env","bytes","chunkText"]}