{"version":3,"file":"fal.cjs","names":["getTestId","flattenHeaders","crypto","getContext","matchFixtureDiagnostic","resolveStrictMode","strictNoMatchMessage","strictNoMatchLogLine","strictOverrideField","proxyAndRecord","resolveResponse","isErrorResponse","serializeErrorResponse","isJSONResponse","isAudioResponse","audioToFalFile","clampTimeout","resolveUpstreamUrl","buildForwardHeaders","buildFixtureMatch","persistFixture","sanitizeHeaderValue"],"sources":["../src/fal.ts"],"sourcesContent":["import type http from \"node:http\";\nimport crypto from \"node:crypto\";\nimport type {\n  ChatCompletionRequest,\n  FalQueueConfig,\n  Fixture,\n  HandlerDefaults,\n  ImageItem,\n  ImageResponse,\n  RawJSONResponse,\n  VideoResponse,\n} from \"./types.js\";\nimport {\n  isAudioResponse,\n  isErrorResponse,\n  serializeErrorResponse,\n  isJSONResponse,\n  flattenHeaders,\n  getContext,\n  getTestId,\n  resolveResponse,\n  resolveStrictMode,\n  strictOverrideField,\n  strictNoMatchMessage,\n  strictNoMatchLogLine,\n} from \"./helpers.js\";\nimport { writeErrorResponse } from \"./sse-writer.js\";\nimport { matchFixtureDiagnostic } from \"./router.js\";\nimport type { Logger } from \"./logger.js\";\nimport {\n  buildFixtureMatch,\n  buildForwardHeaders,\n  clampTimeout,\n  persistFixture,\n  proxyAndRecord,\n  sanitizeHeaderValue,\n} from \"./recorder.js\";\nimport { resolveUpstreamUrl } from \"./url.js\";\nimport type { Journal } from \"./journal.js\";\nimport { audioToFalFile } from \"./fal-audio.js\";\n\n// ─── FalQueueState (TTL + bounded) ───────────────────────────────────────\n\nconst FAL_QUEUE_MAX_ENTRIES = 10_000;\nconst FAL_QUEUE_TTL_MS = 3_600_000; // 1 hour\n\ntype FalQueueStatus = \"IN_QUEUE\" | \"IN_PROGRESS\" | \"COMPLETED\" | \"CANCELLED\";\n\ninterface FalQueueJob {\n  requestId: string;\n  modelId: string;\n  status: FalQueueStatus;\n  result: unknown;\n  /**\n   * Billed quantity emitted as the `x-fal-billable-units` header on the\n   * completed `queue-result` response. Sourced from the fixture's\n   * `response.billableUnits`; `null` when the fixture omitted it (no header).\n   */\n  billableUnits: number | null;\n  /** Number of `/status` (or `/{id}`) polls the caller has made against this job. */\n  pollCount: number;\n  /** Poll-count threshold for `IN_QUEUE → IN_PROGRESS` transition. */\n  pollsBeforeInProgress: number;\n  /** Poll-count threshold for `IN_PROGRESS → COMPLETED` transition. */\n  pollsBeforeCompleted: number;\n  submittedAt: number;\n  completedAt: number | null;\n  /** State-transition log entries surfaced in the `/status` response. */\n  logs: Array<{ timestamp: string; level: string; message: string }>;\n  createdAt: number;\n}\n\ninterface FalQueueEntry {\n  job: FalQueueJob;\n  createdAt: number;\n}\n\n/**\n * Per-testId queue state for the general fal handler. Mirrors FalJobMap from\n * fal-audio.ts but stores arbitrary JSON payloads instead of audio file\n * objects, so it can serve any fal model (image, video, motion, music, etc.).\n */\nexport class FalQueueStateMap {\n  private readonly entries = new Map<string, FalQueueEntry>();\n\n  get(key: string): FalQueueJob | undefined {\n    const entry = this.entries.get(key);\n    if (!entry) return undefined;\n    if (Date.now() - entry.createdAt > FAL_QUEUE_TTL_MS) {\n      this.entries.delete(key);\n      return undefined;\n    }\n    return entry.job;\n  }\n\n  set(key: string, job: FalQueueJob): void {\n    this.entries.set(key, { job, createdAt: Date.now() });\n    if (this.entries.size > FAL_QUEUE_MAX_ENTRIES) {\n      const excess = this.entries.size - FAL_QUEUE_MAX_ENTRIES;\n      const iter = this.entries.keys();\n      for (let i = 0; i < excess; i++) {\n        const next = iter.next();\n        if (!next.done) this.entries.delete(next.value);\n      }\n    }\n  }\n\n  delete(key: string): boolean {\n    return this.entries.delete(key);\n  }\n\n  clear(): void {\n    this.entries.clear();\n  }\n\n  get size(): number {\n    return this.entries.size;\n  }\n}\n\nexport const falQueueStates = new FalQueueStateMap();\n\n// ─── Typed-response → fal envelope converters ───────────────────────────\n\nfunction extractExtension(url: string, fallback: string): { fileName: string; ext: string } {\n  const segment = url.split(\"?\")[0].split(\"#\")[0].split(\"/\").pop() ?? \"\";\n  const fileName = segment.length > 0 ? segment : \"\";\n  const dotIdx = fileName.lastIndexOf(\".\");\n  const ext = dotIdx >= 0 ? fileName.slice(dotIdx + 1).toLowerCase() : fallback;\n  return { fileName, ext };\n}\n\nfunction imageItemToFalImage(item: ImageItem, index: number): Record<string, unknown> {\n  const url = item.url ?? `https://mock.fal.media/files/generated_image_${index}.png`;\n  const { ext } = extractExtension(url, \"png\");\n  const contentType = ext === \"jpg\" || ext === \"jpeg\" ? \"image/jpeg\" : `image/${ext}`;\n  return {\n    url,\n    width: 1024,\n    height: 1024,\n    content_type: contentType,\n  };\n}\n\n/**\n * Translate an `ImageResponse` fixture into fal's image envelope shape:\n * `{ images: [...], timings, seed, has_nsfw_concepts, prompt }`.\n * Used by `LLMock.onFalImage` to keep callers from re-deriving the wire shape.\n */\nexport function imageResponseToFalJson(response: ImageResponse): Record<string, unknown> {\n  const items = response.images ?? (response.image ? [response.image] : []);\n  const images = items.map((item, i) => imageItemToFalImage(item, i));\n  return {\n    images,\n    timings: { inference: 0 },\n    seed: 0,\n    has_nsfw_concepts: images.map(() => false),\n    prompt: \"\",\n  };\n}\n\n/**\n * Translate a `VideoResponse` fixture into fal's video envelope shape:\n * `{ video: { url, content_type, file_name, file_size }, seed }`.\n */\nexport function videoResponseToFalJson(response: VideoResponse): Record<string, unknown> {\n  const url = response.video.url ?? \"https://mock.fal.media/files/generated_video.mp4\";\n  const { fileName, ext } = extractExtension(url, \"mp4\");\n  return {\n    video: {\n      url,\n      content_type: `video/${ext}`,\n      file_name: fileName || \"generated_video.mp4\",\n      file_size: 0,\n    },\n    seed: 0,\n  };\n}\n\n// ─── Queue progression ─────────────────────────────────────────────────\n\nexport function resolveProgression(config: FalQueueConfig | undefined): {\n  pollsBeforeInProgress: number;\n  pollsBeforeCompleted: number;\n} {\n  // Thresholds must resolve to non-negative integers: a NaN would make\n  // advanceJob's `pollCount >= threshold` comparison permanently false (a\n  // polling client never reaches terminal), and a negative would break the\n  // documented \"explicit 0 enables progression\" contract. Non-finite values\n  // are treated as unset; anything else is floored and clamped to >= 0.\n  const sanitize = (value: number | undefined): number | undefined =>\n    typeof value === \"number\" && Number.isFinite(value)\n      ? Math.max(0, Math.floor(value))\n      : undefined;\n  const explicitInProgress = sanitize(config?.pollsBeforeInProgress);\n  const explicitCompleted = sanitize(config?.pollsBeforeCompleted);\n  const pollsBeforeInProgress = explicitInProgress ?? 0;\n  // When only pollsBeforeInProgress is set, default pollsBeforeCompleted to one\n  // poll later so the job actually passes through IN_PROGRESS. When the caller\n  // sets both explicitly, clamp completed >= inProgress so a misconfigured\n  // pair (e.g. completed < inProgress) can't silently skip the IN_PROGRESS\n  // transition. When neither is set, both stay 0 (completes on submit).\n  let pollsBeforeCompleted: number;\n  if (explicitCompleted !== undefined) {\n    pollsBeforeCompleted = Math.max(pollsBeforeInProgress, explicitCompleted);\n  } else if (explicitInProgress !== undefined) {\n    pollsBeforeCompleted = pollsBeforeInProgress + 1;\n  } else {\n    pollsBeforeCompleted = 0;\n  }\n  return { pollsBeforeInProgress, pollsBeforeCompleted };\n}\n\n/**\n * Mutates a job in place to advance its state on a status/result poll.\n * IN_QUEUE → IN_PROGRESS → COMPLETED based on poll-count thresholds. No-op\n * once COMPLETED or CANCELLED.\n */\nfunction advanceJob(job: FalQueueJob): void {\n  if (job.status === \"COMPLETED\" || job.status === \"CANCELLED\") return;\n\n  job.pollCount += 1;\n  // Check IN_PROGRESS before COMPLETED so a job whose thresholds are equal\n  // still spends one poll in IN_PROGRESS instead of jumping straight to\n  // COMPLETED.\n  if (job.status === \"IN_QUEUE\" && job.pollCount >= job.pollsBeforeInProgress) {\n    job.status = \"IN_PROGRESS\";\n    job.logs.push({\n      timestamp: new Date().toISOString(),\n      level: \"INFO\",\n      message: \"Job started processing.\",\n    });\n  } else if (job.pollCount >= job.pollsBeforeCompleted) {\n    job.status = \"COMPLETED\";\n    job.completedAt = Date.now();\n    job.logs.push({\n      timestamp: new Date().toISOString(),\n      level: \"INFO\",\n      message: \"Job completed.\",\n    });\n  }\n}\n\nfunction queuePosition(job: FalQueueJob): number {\n  if (job.status !== \"IN_QUEUE\") return 0;\n  return Math.max(0, job.pollsBeforeInProgress - job.pollCount);\n}\n\nfunction statusResponseBody(job: FalQueueJob): Record<string, unknown> {\n  const body: Record<string, unknown> = {\n    status: job.status,\n    request_id: job.requestId,\n    response_url: `https://${FAL_HOSTS.queue}/${job.modelId}/requests/${job.requestId}`,\n    logs: job.logs,\n  };\n  if (job.status === \"IN_QUEUE\" || job.status === \"IN_PROGRESS\") {\n    body.queue_position = queuePosition(job);\n  }\n  if (job.status === \"COMPLETED\" && job.completedAt != null) {\n    body.metrics = {\n      inference_time: (job.completedAt - job.submittedAt) / 1000,\n    };\n  }\n  return body;\n}\n\n// ─── Hosts and routing ──────────────────────────────────────────────────\n\nconst FAL_HOSTS = {\n  queue: \"queue.fal.run\",\n  sync: \"fal.run\",\n  storage: \"rest.fal.ai\",\n  storageAlpha: \"rest.alpha.fal.ai\",\n  gateway: \"gateway.fal.ai\",\n} as const;\n\n// Headers real fal sets on its queue responses. `@tanstack/ai-fal` (and\n// likely other adapters) read both to correlate a billed quantity with the\n// originating request: the request-id keys the lookup, the billable-units\n// value is the quantity. aimock emits the request-id on the `status` and\n// `result` queue responses (where adapters perform the billing lookup); the\n// submit envelope already carries `request_id` in its JSON body, and the\n// cancel / not-found responses omit it. The billable-units header rides only\n// the completed `result`, and only when a fixture opts in via\n// `response.billableUnits`.\nconst FAL_REQUEST_ID_HEADER = \"x-fal-request-id\";\nconst FAL_BILLABLE_UNITS_HEADER = \"x-fal-billable-units\";\n\n/**\n * Read an optional `billableUnits` off a resolved fixture response. Returns the\n * finite numeric value or `null` (absent, non-numeric, or non-finite) — the\n * `x-fal-billable-units` header is emitted only for a non-null result.\n */\nfunction extractBillableUnits(response: unknown): number | null {\n  if (response && typeof response === \"object\" && \"billableUnits\" in response) {\n    // The `in` narrowing types `response.billableUnits` as `unknown` — no cast.\n    const value: unknown = response.billableUnits;\n    if (typeof value === \"number\" && Number.isFinite(value)) return value;\n  }\n  return null;\n}\n\nconst QUEUE_REQUESTS_RE = /^(.+)\\/requests\\/([^/]+)(\\/status|\\/cancel)?$/;\nconst STORAGE_INITIATE_PATH = \"/storage/upload/initiate\";\n\nfunction stripFalPrefix(pathname: string): string {\n  const stripped = pathname.replace(/^\\/fal/, \"\");\n  return stripped.length > 0 ? stripped : \"/\";\n}\n\nfunction extractPromptFromBody(body: unknown): string {\n  if (!body || typeof body !== \"object\") return \"\";\n  const obj = body as Record<string, unknown>;\n  if (typeof obj.prompt === \"string\") return obj.prompt;\n  if (typeof obj.text === \"string\") return obj.text;\n  const input = obj.input;\n  if (input && typeof input === \"object\") {\n    const inputObj = input as Record<string, unknown>;\n    if (typeof inputObj.prompt === \"string\") return inputObj.prompt;\n    if (typeof inputObj.text === \"string\") return inputObj.text;\n  }\n  return \"\";\n}\n\ninterface ParsedFalPath {\n  modelId: string;\n  requestId?: string;\n  action?: \"status\" | \"cancel\" | \"result\";\n}\n\nfunction parseFalPath(stripped: string): ParsedFalPath | null {\n  if (!stripped.startsWith(\"/\")) return null;\n  const trimmed = stripped.replace(/^\\/+/, \"\");\n  if (!trimmed) return null;\n\n  const m = QUEUE_REQUESTS_RE.exec(`/${trimmed}`);\n  if (m) {\n    const modelId = m[1].replace(/^\\/+/, \"\");\n    const action = m[3] === \"/status\" ? \"status\" : m[3] === \"/cancel\" ? \"cancel\" : \"result\";\n    return { modelId, requestId: m[2], action };\n  }\n  return { modelId: trimmed };\n}\n\nexport type HandleFalOutcome = \"handled\" | \"passthrough\";\n\ninterface FalRouteInfo {\n  kind: \"queue-submit\" | \"queue-status\" | \"queue-result\" | \"queue-cancel\" | \"sync-run\" | \"storage\";\n  modelId?: string;\n  requestId?: string;\n  targetHost: string;\n}\n\nfunction classifyRoute(\n  req: http.IncomingMessage,\n  pathname: string,\n  targetHost: string,\n): FalRouteInfo | null {\n  const stripped = stripFalPrefix(pathname);\n\n  if (targetHost === FAL_HOSTS.storage || targetHost === FAL_HOSTS.storageAlpha) {\n    if (req.method === \"POST\" && stripped === STORAGE_INITIATE_PATH) {\n      return { kind: \"storage\", targetHost };\n    }\n    return null;\n  }\n\n  const parsed = parseFalPath(stripped);\n  if (!parsed) return null;\n\n  if (targetHost === FAL_HOSTS.queue) {\n    if (parsed.requestId) {\n      if (parsed.action === \"status\" && req.method === \"GET\") {\n        return {\n          kind: \"queue-status\",\n          modelId: parsed.modelId,\n          requestId: parsed.requestId,\n          targetHost,\n        };\n      }\n      if (parsed.action === \"cancel\" && req.method === \"PUT\") {\n        return {\n          kind: \"queue-cancel\",\n          modelId: parsed.modelId,\n          requestId: parsed.requestId,\n          targetHost,\n        };\n      }\n      if (parsed.action === \"result\" && req.method === \"GET\") {\n        return {\n          kind: \"queue-result\",\n          modelId: parsed.modelId,\n          requestId: parsed.requestId,\n          targetHost,\n        };\n      }\n      return null;\n    }\n    if (req.method === \"POST\") {\n      return { kind: \"queue-submit\", modelId: parsed.modelId, targetHost };\n    }\n    return null;\n  }\n\n  if (targetHost === FAL_HOSTS.sync) {\n    if (req.method === \"POST\" && parsed.modelId) {\n      return { kind: \"sync-run\", modelId: parsed.modelId, targetHost };\n    }\n    return null;\n  }\n\n  return null;\n}\n\n/**\n * General fal.ai handler. Routes by `x-fal-target-host` header (the convention\n * used by `@fal-ai/client`'s server-side requestMiddleware workaround for the\n * fact that `proxyUrl` is browser-only).\n *\n * Returns `\"passthrough\"` when the request does not look like a host-mirrored\n * fal call, so the caller can fall back to the legacy `/fal/queue/...` and\n * `/fal/run/...` audio routes.\n */\nexport async function handleFal(\n  req: http.IncomingMessage,\n  res: http.ServerResponse,\n  body: string,\n  pathname: string,\n  fixtures: Fixture[],\n  defaults: HandlerDefaults,\n  journal: Journal,\n): Promise<HandleFalOutcome> {\n  const targetHostHeader = req.headers[\"x-fal-target-host\"];\n  const targetHost = Array.isArray(targetHostHeader) ? targetHostHeader[0] : targetHostHeader;\n  if (!targetHost) return \"passthrough\";\n\n  const route = classifyRoute(req, pathname, targetHost);\n  if (!route) return \"passthrough\";\n\n  const testId = getTestId(req);\n  const stateKey = (id: string) => `${testId}:${id}`;\n\n  switch (route.kind) {\n    case \"queue-status\": {\n      const job = falQueueStates.get(stateKey(route.requestId!));\n      if (!job) {\n        respondNotFound(req, res, pathname, journal, route.requestId!);\n        return \"handled\";\n      }\n      advanceJob(job);\n      writeJson(req, res, 200, statusResponseBody(job), pathname, journal, {\n        [FAL_REQUEST_ID_HEADER]: job.requestId,\n      });\n      return \"handled\";\n    }\n\n    case \"queue-result\": {\n      const job = falQueueStates.get(stateKey(route.requestId!));\n      if (!job) {\n        respondNotFound(req, res, pathname, journal, route.requestId!);\n        return \"handled\";\n      }\n      // Callers may fetch result without first polling status — advance so\n      // tests that skip the status check still reach completion.\n      advanceJob(job);\n      // Emit x-fal-request-id on both the in-progress (202) and completed (200)\n      // result; adapters key their billing lookup off it.\n      const resultHeaders: Record<string, string> = { [FAL_REQUEST_ID_HEADER]: job.requestId };\n      if (job.status !== \"COMPLETED\") {\n        writeJson(req, res, 202, statusResponseBody(job), pathname, journal, resultHeaders);\n        return \"handled\";\n      }\n      // x-fal-billable-units rides only the completed result, and only when the\n      // fixture opted into a billed quantity.\n      if (job.billableUnits != null) {\n        resultHeaders[FAL_BILLABLE_UNITS_HEADER] = String(job.billableUnits);\n      }\n      writeJson(req, res, 200, job.result, pathname, journal, resultHeaders);\n      return \"handled\";\n    }\n\n    case \"queue-cancel\": {\n      const job = falQueueStates.get(stateKey(route.requestId!));\n      if (!job) {\n        journal.add({\n          method: req.method ?? \"PUT\",\n          path: pathname,\n          headers: flattenHeaders(req.headers),\n          body: null,\n          response: { status: 404, fixture: null },\n        });\n        res.writeHead(404, { \"Content-Type\": \"application/json\" });\n        res.end(JSON.stringify({ status: \"NOT_FOUND\" }));\n        return \"handled\";\n      }\n      if (job.status === \"COMPLETED\") {\n        journal.add({\n          method: req.method ?? \"PUT\",\n          path: pathname,\n          headers: flattenHeaders(req.headers),\n          body: null,\n          response: { status: 400, fixture: null },\n        });\n        res.writeHead(400, { \"Content-Type\": \"application/json\" });\n        res.end(JSON.stringify({ status: \"ALREADY_COMPLETED\" }));\n        return \"handled\";\n      }\n      if (job.status === \"CANCELLED\") {\n        journal.add({\n          method: req.method ?? \"PUT\",\n          path: pathname,\n          headers: flattenHeaders(req.headers),\n          body: null,\n          response: { status: 200, fixture: null },\n        });\n        res.writeHead(200, { \"Content-Type\": \"application/json\" });\n        res.end(JSON.stringify({ status: \"CANCELLED\" }));\n        return \"handled\";\n      }\n      job.status = \"CANCELLED\";\n      job.logs.push({\n        timestamp: new Date().toISOString(),\n        level: \"INFO\",\n        message: \"Job cancelled.\",\n      });\n      journal.add({\n        method: req.method ?? \"PUT\",\n        path: pathname,\n        headers: flattenHeaders(req.headers),\n        body: null,\n        response: { status: 200, fixture: null },\n      });\n      res.writeHead(200, { \"Content-Type\": \"application/json\" });\n      res.end(JSON.stringify({ status: \"CANCELLED\" }));\n      return \"handled\";\n    }\n\n    case \"storage\": {\n      let filename = \"upload.bin\";\n      try {\n        const parsed = body ? (JSON.parse(body) as Record<string, unknown>) : {};\n        if (typeof parsed.filename === \"string\") filename = parsed.filename;\n        if (typeof parsed.file_name === \"string\") filename = parsed.file_name;\n      } catch {\n        // ignore — stub doesn't require a structured body\n      }\n      const fileId = crypto.randomUUID();\n      const responseBody = {\n        upload_url: `https://${route.targetHost}/storage/upload/${fileId}`,\n        file_url: `https://${route.targetHost}/files/${fileId}/${filename}`,\n      };\n      writeJson(req, res, 200, responseBody, pathname, journal);\n      return \"handled\";\n    }\n\n    case \"queue-submit\":\n    case \"sync-run\": {\n      const modelId = route.modelId!;\n      let parsedBody: Record<string, unknown> | null;\n      try {\n        parsedBody = parseBody(body);\n      } catch (err) {\n        const detail = err instanceof Error ? err.message : \"Invalid JSON body\";\n        journal.add({\n          method: req.method ?? \"POST\",\n          path: pathname,\n          headers: flattenHeaders(req.headers),\n          body: null,\n          response: { status: 400, fixture: null },\n        });\n        res.writeHead(400, { \"Content-Type\": \"application/json\" });\n        res.end(\n          JSON.stringify({\n            error: {\n              message: detail,\n              type: \"invalid_request_error\",\n              code: \"invalid_json\",\n            },\n          }),\n        );\n        return \"handled\";\n      }\n      const prompt = extractPromptFromBody(parsedBody);\n      const syntheticReq: ChatCompletionRequest = {\n        model: modelId,\n        messages: [{ role: \"user\", content: prompt || JSON.stringify(parsedBody ?? {}) }],\n        _endpointType: \"fal\",\n        _context: getContext(req),\n      };\n\n      const matchCounts = journal.getFixtureMatchCountsForTest(testId);\n      const { fixture, skippedBySequenceOrTurn } = matchFixtureDiagnostic(\n        fixtures,\n        syntheticReq,\n        matchCounts,\n        defaults.requestTransform,\n      );\n\n      if (!fixture) {\n        const effectiveStrict = resolveStrictMode(defaults.strict, req.headers);\n        if (effectiveStrict) {\n          const strictMessage = strictNoMatchMessage(skippedBySequenceOrTurn);\n          defaults.logger.error(\n            strictNoMatchLogLine(req.method ?? \"POST\", pathname, skippedBySequenceOrTurn),\n          );\n          journal.add({\n            method: req.method ?? \"POST\",\n            path: pathname,\n            headers: flattenHeaders(req.headers),\n            body: syntheticReq,\n            response: {\n              status: 503,\n              fixture: null,\n              ...strictOverrideField(defaults.strict, req.headers),\n            },\n          });\n          res.writeHead(503, { \"Content-Type\": \"application/json\" });\n          res.end(\n            JSON.stringify({\n              error: {\n                message: strictMessage,\n                type: \"invalid_request_error\",\n                code: \"no_fixture_match\",\n              },\n            }),\n          );\n          return \"handled\";\n        }\n        if (defaults.record) {\n          const effectiveDefaults = withFalUpstream(defaults, route.targetHost);\n          // queue-submit must walk the queue upstream (submit → poll status →\n          // get result) before persisting, so the fixture stores the FINAL job\n          // body, not the IN_QUEUE envelope. sync-run is already a single\n          // request/response cycle and the generic recorder handles it.\n          if (route.kind === \"queue-submit\") {\n            const outcome = await proxyAndRecordFalQueueSubmit({\n              req,\n              res,\n              syntheticReq,\n              modelId,\n              pathname,\n              strippedPath: stripFalPrefix(pathname),\n              body,\n              fixtures,\n              defaults: effectiveDefaults,\n              stateKey,\n              journal,\n            });\n            if (outcome === \"handled\") return \"handled\";\n            // outcome === \"no_upstream\" — fall through to 404 (strict was\n            // already handled above)\n          } else {\n            const outcome = await proxyAndRecord(\n              req,\n              res,\n              syntheticReq,\n              \"fal\",\n              stripFalPrefix(pathname),\n              fixtures,\n              effectiveDefaults,\n              body,\n            );\n            if (outcome === \"handled_by_hook\") return \"handled\";\n            if (outcome !== \"not_configured\") {\n              journal.add({\n                method: req.method ?? \"POST\",\n                path: pathname,\n                headers: flattenHeaders(req.headers),\n                body: syntheticReq,\n                response: { status: res.statusCode ?? 200, fixture: null, source: \"proxy\" },\n              });\n              return \"handled\";\n            }\n          }\n        }\n\n        journal.add({\n          method: req.method ?? \"POST\",\n          path: pathname,\n          headers: flattenHeaders(req.headers),\n          body: syntheticReq,\n          response: {\n            status: 404,\n            fixture: null,\n            ...strictOverrideField(defaults.strict, req.headers),\n          },\n        });\n        res.writeHead(404, { \"Content-Type\": \"application/json\" });\n        res.end(\n          JSON.stringify({\n            error: {\n              message: \"No fixture matched\",\n              type: \"invalid_request_error\",\n              code: \"no_fixture_match\",\n            },\n          }),\n        );\n        return \"handled\";\n      }\n\n      journal.incrementFixtureMatchCount(fixture, fixtures, testId);\n      const response = await resolveResponse(fixture, syntheticReq);\n\n      if (isErrorResponse(response)) {\n        const status = response.status ?? 500;\n        journal.add({\n          method: req.method ?? \"POST\",\n          path: pathname,\n          headers: flattenHeaders(req.headers),\n          body: syntheticReq,\n          response: { status, fixture },\n        });\n        writeErrorResponse(res, status, serializeErrorResponse(response), {\n          retryAfter: response.retryAfter,\n        });\n        return \"handled\";\n      }\n\n      let payload: unknown;\n      if (isJSONResponse(response)) {\n        payload = (response as RawJSONResponse).json;\n      } else if (isAudioResponse(response)) {\n        payload = audioToFalFile(response);\n      } else {\n        journal.add({\n          method: req.method ?? \"POST\",\n          path: pathname,\n          headers: flattenHeaders(req.headers),\n          body: syntheticReq,\n          response: { status: 500, fixture },\n        });\n        res.writeHead(500, { \"Content-Type\": \"application/json\" });\n        res.end(\n          JSON.stringify({\n            error: {\n              message: \"Fixture response is not JSON or audio for fal endpoint\",\n              type: \"server_error\",\n            },\n          }),\n        );\n        return \"handled\";\n      }\n\n      if (route.kind === \"sync-run\") {\n        journal.add({\n          method: req.method ?? \"POST\",\n          path: pathname,\n          headers: flattenHeaders(req.headers),\n          body: syntheticReq,\n          response: { status: 200, fixture },\n        });\n        res.writeHead(200, { \"Content-Type\": \"application/json\" });\n        res.end(JSON.stringify(payload));\n        return \"handled\";\n      }\n\n      const requestId = crypto.randomUUID();\n      const progression = resolveProgression(defaults.falQueue);\n      const now = Date.now();\n      const initialStatus: FalQueueStatus =\n        progression.pollsBeforeCompleted === 0 ? \"COMPLETED\" : \"IN_QUEUE\";\n      const job: FalQueueJob = {\n        requestId,\n        modelId,\n        status: initialStatus,\n        result: payload,\n        billableUnits: extractBillableUnits(response),\n        pollCount: 0,\n        pollsBeforeInProgress: progression.pollsBeforeInProgress,\n        pollsBeforeCompleted: progression.pollsBeforeCompleted,\n        submittedAt: now,\n        completedAt: initialStatus === \"COMPLETED\" ? now : null,\n        logs: [\n          {\n            timestamp: new Date(now).toISOString(),\n            level: \"INFO\",\n            message: \"Job enqueued.\",\n          },\n        ],\n        createdAt: now,\n      };\n      falQueueStates.set(stateKey(requestId), job);\n      const envelope = {\n        request_id: requestId,\n        response_url: `https://${FAL_HOSTS.queue}/${modelId}/requests/${requestId}`,\n        status_url: `https://${FAL_HOSTS.queue}/${modelId}/requests/${requestId}/status`,\n        cancel_url: `https://${FAL_HOSTS.queue}/${modelId}/requests/${requestId}/cancel`,\n        queue_position: queuePosition(job),\n      };\n      journal.add({\n        method: req.method ?? \"POST\",\n        path: pathname,\n        headers: flattenHeaders(req.headers),\n        body: syntheticReq,\n        response: { status: 200, fixture },\n      });\n      res.writeHead(200, { \"Content-Type\": \"application/json\" });\n      res.end(JSON.stringify(envelope));\n      return \"handled\";\n    }\n  }\n}\n\nfunction parseBody(raw: string): Record<string, unknown> | null {\n  if (!raw.trim()) return null;\n  try {\n    return JSON.parse(raw) as Record<string, unknown>;\n  } catch (err) {\n    const detail = err instanceof Error ? err.message : \"unknown\";\n    throw new Error(`Malformed JSON: ${detail}`);\n  }\n}\n\n// ─── Queue-walk recording ──────────────────────────────────────────────\n//\n// The fal queue protocol surfaces three endpoints — submit (POST), status\n// (GET, polled), and result (GET) — but at the fixture layer we only store ONE\n// thing: the FINAL job body. A naive `proxyAndRecord` against submit would\n// persist the IN_QUEUE envelope, which is useless to replay (the SDK polls\n// status and then reads the result body, expecting `{ images: [...] }`-shaped\n// model output, not an envelope). So during recording we walk the upstream\n// queue ourselves, capture the result body, and write THAT as the fixture —\n// then synthesise the local envelope the same way the replay path does.\n\nconst DEFAULT_FAL_POLL_INTERVAL_MS = 1000;\n// Video generations (kling, veo, runway, etc.) routinely take 5–10 minutes\n// on the upstream queue; 15 min gives headroom without trapping a genuinely\n// hung job indefinitely.\nconst DEFAULT_FAL_TIMEOUT_MS = 900_000;\n// Per-fetch upstream timeout default for the walk's submit/status/result\n// fetches — same value and clamp conventions as the rest of the recording\n// surfaces (recorder.ts / openrouter-video.ts).\nconst DEFAULT_FAL_FETCH_TIMEOUT_MS = 30_000;\n\n// Upstream header forwarding uses buildForwardHeaders (recorder.ts) — the\n// shared strip list (hop-by-hop, client-set, and the mock-internal x-test-id\n// / x-aimock-strict / x-aimock-context / x-aimock-chaos-* family), so the\n// fal queue walk never leaks control headers onto a real provider's wire\n// (one shared list, not a per-surface copy, so the surfaces cannot drift).\n\n/**\n * The result of a queue walk: the parsed final body plus the billed quantity\n * captured from the result fetch's `x-fal-billable-units` header (`null` when\n * upstream didn't set it). The caller persists `body` as the fixture and seeds\n * local queue state; `billableUnits` round-trips real fal billing so a recorded\n * fixture replays `x-fal-billable-units` without hand-editing.\n */\nexport interface FalQueueWalkResult {\n  body: unknown;\n  billableUnits: number | null;\n}\n\n/**\n * Walk a fal-shaped queue protocol upstream: POST submit, poll status until\n * COMPLETED, GET final result. Returns the parsed final body and the result\n * response's billed quantity so the caller can persist the fixture and seed\n * local queue state.\n *\n * Decoupled from the route layer so the legacy `/fal/queue/submit/{model}`\n * audio path (`fal-audio.ts`) can reuse the same logic.\n */\nexport async function walkFalQueue(args: {\n  upstreamBase: string;\n  submitPath: string;\n  body: string;\n  headers: Record<string, string>;\n  pollIntervalMs?: number;\n  timeoutMs?: number;\n  /**\n   * Per-fetch upstream timeout (`record.upstreamTimeoutMs` clamp conventions,\n   * 30s default) applied to each of the walk's submit/status/result fetches\n   * via AbortSignal — a hung upstream socket must not pin the walk past its\n   * budget. Each fetch's signal is additionally clamped to the walk's\n   * remaining deadline; an abort surfaces through the caller's existing\n   * failure handling (502, no fixture persisted).\n   */\n  upstreamTimeoutMs?: number;\n  /**\n   * Build the status-poll URL from `request_id` when upstream's submit\n   * response doesn't return a usable `status_url`. The legacy path uses\n   * aimock-internal `/fal/queue/requests/<id>/status` rather than fal.ai's\n   * `/<model>/requests/<id>/status` layout.\n   */\n  fallbackStatusPath: (requestId: string) => string;\n  fallbackResultPath: (requestId: string) => string;\n  /** Warn sink for the same-origin envelope-URL gate (omitting it only mutes the warns). */\n  logger?: Logger;\n}): Promise<FalQueueWalkResult> {\n  const {\n    upstreamBase,\n    submitPath,\n    body,\n    headers,\n    pollIntervalMs = DEFAULT_FAL_POLL_INTERVAL_MS,\n    timeoutMs = DEFAULT_FAL_TIMEOUT_MS,\n    upstreamTimeoutMs,\n    fallbackStatusPath,\n    fallbackResultPath,\n    logger,\n  } = args;\n\n  const deadline = Date.now() + timeoutMs;\n  const perFetchTimeoutMs = clampTimeout(upstreamTimeoutMs, DEFAULT_FAL_FETCH_TIMEOUT_MS);\n  // Bound every upstream fetch: the per-fetch timeout, additionally clamped\n  // to the walk's remaining budget so a fetch can never outlive the deadline\n  // (floored at 1ms — AbortSignal.timeout rejects non-positive values; the\n  // deadline check in the poll loop is the authoritative expiry).\n  const fetchSignal = (): AbortSignal =>\n    AbortSignal.timeout(Math.max(1, Math.min(perFetchTimeoutMs, deadline - Date.now())));\n\n  // ── 1. POST submit ────────────────────────────────────────────────\n  const submitUrl = resolveUpstreamUrl(upstreamBase, submitPath);\n  const submitRes = await fetch(submitUrl, {\n    method: \"POST\",\n    headers,\n    body,\n    signal: fetchSignal(),\n  });\n  const submitText = await submitRes.text();\n  if (!submitRes.ok) {\n    throw new Error(`Submit ${submitRes.status}: ${submitText.slice(0, 200)}`);\n  }\n  const submitJson = parseJsonOrThrow(submitText, \"Submit\");\n  // JSON.parse admits null/arrays/scalars — reject non-object envelopes with\n  // a clean error instead of TypeError-ing on the field reads below (the\n  // OpenRouter video proxies apply the same guard).\n  if (submitJson === null || typeof submitJson !== \"object\" || Array.isArray(submitJson)) {\n    throw new Error(\"Submit response is not a JSON object\");\n  }\n  const env = submitJson as Record<string, unknown>;\n  const upstreamRequestId = String(env.request_id ?? \"\").trim();\n  if (!upstreamRequestId) {\n    throw new Error(\"Submit response missing request_id\");\n  }\n\n  // Prefer the URLs upstream returned — but ONLY same-origin with the\n  // configured upstream (mirroring the OpenRouter video proxy's\n  // polling_url gate): every status/result fetch below forwards the client's\n  // headers — including Authorization — so an envelope nominating a foreign\n  // host must never receive them. Same-origin still covers the documented\n  // proxy-in-front case (the configured upstream IS that proxy); off-origin,\n  // unparseable, or absent envelope URLs fall back to the constructed\n  // canonical paths on the upstream origin, with a warn.\n  const upstreamOrigin = submitUrl.origin;\n  const adoptSameOrigin = (value: unknown, label: string, fallbackPath: string): URL => {\n    if (typeof value === \"string\" && value) {\n      try {\n        const parsed = new URL(value);\n        if (parsed.origin === upstreamOrigin) return parsed;\n        logger?.warn(\n          `Upstream ${label} origin ${parsed.origin} differs from the upstream origin ${upstreamOrigin} — using the constructed canonical path instead`,\n        );\n      } catch {\n        logger?.warn(\n          `Upstream ${label} is not a valid URL (${value.slice(0, 100)}) — using the constructed canonical path instead`,\n        );\n      }\n    }\n    return resolveUpstreamUrl(upstreamBase, fallbackPath);\n  };\n  const statusUrl = adoptSameOrigin(\n    env.status_url,\n    \"status_url\",\n    fallbackStatusPath(upstreamRequestId),\n  );\n  const resultUrl = adoptSameOrigin(\n    env.response_url,\n    \"response_url\",\n    fallbackResultPath(upstreamRequestId),\n  );\n\n  // ── 2. Poll status until COMPLETED ───────────────────────────────\n  while (true) {\n    if (Date.now() > deadline) throw new Error(`Queue walk timed out after ${timeoutMs}ms`);\n    const statusRes = await fetch(statusUrl, { headers, signal: fetchSignal() });\n    const statusText = await statusRes.text();\n    if (!statusRes.ok) {\n      throw new Error(`Status ${statusRes.status}: ${statusText.slice(0, 200)}`);\n    }\n    const statusParsed = parseJsonOrThrow(statusText, \"Status\");\n    // Same non-object guard as the submit envelope above.\n    if (statusParsed === null || typeof statusParsed !== \"object\" || Array.isArray(statusParsed)) {\n      throw new Error(\"Status response is not a JSON object\");\n    }\n    const statusJson = statusParsed as Record<string, unknown>;\n    const s = String(statusJson.status ?? \"\");\n    if (s === \"COMPLETED\") break;\n    if (s === \"FAILED\" || s === \"ERROR\" || s === \"CANCELLED\") {\n      throw new Error(`Upstream job terminated with status ${s}`);\n    }\n    const remaining = deadline - Date.now();\n    // Time out only when the budget is actually exhausted — a zero\n    // pollIntervalMs is a valid \"poll as fast as possible\" configuration,\n    // not an instant expiry (setTimeout(0) still yields the event loop).\n    if (remaining <= 0) throw new Error(`Queue walk timed out after ${timeoutMs}ms`);\n    const sleep = Math.min(pollIntervalMs, remaining);\n    await new Promise<void>((r) => setTimeout(r, Math.max(0, sleep)));\n  }\n\n  // ── 3. GET final result ──────────────────────────────────────────\n  const resultRes = await fetch(resultUrl, { headers, signal: fetchSignal() });\n  const resultText = await resultRes.text();\n  if (!resultRes.ok) {\n    throw new Error(`Result ${resultRes.status}: ${resultText.slice(0, 200)}`);\n  }\n  return {\n    body: parseJsonOrThrow(resultText, \"Result\"),\n    billableUnits: parseBillableUnitsHeader(resultRes.headers.get(FAL_BILLABLE_UNITS_HEADER)),\n  };\n}\n\n/**\n * Parse the upstream `x-fal-billable-units` header into a finite number, or\n * `null` when absent/unparseable — mirrors the consumer-side parse so a\n * recorded fixture's captured units match what the live adapter would have read.\n */\nfunction parseBillableUnitsHeader(raw: string | null): number | null {\n  if (raw == null) return null;\n  const value = Number(raw.trim());\n  return Number.isFinite(value) ? value : null;\n}\n\nasync function proxyAndRecordFalQueueSubmit(args: {\n  req: http.IncomingMessage;\n  res: http.ServerResponse;\n  syntheticReq: ChatCompletionRequest;\n  modelId: string;\n  pathname: string;\n  strippedPath: string;\n  body: string;\n  fixtures: Fixture[];\n  defaults: HandlerDefaults;\n  stateKey: (id: string) => string;\n  journal: Journal;\n}): Promise<\"handled\" | \"no_upstream\"> {\n  const {\n    req,\n    res,\n    syntheticReq,\n    modelId,\n    pathname,\n    strippedPath,\n    body,\n    fixtures,\n    defaults,\n    stateKey,\n    journal,\n  } = args;\n\n  const record = defaults.record;\n  if (!record) return \"no_upstream\";\n  const upstreamBase = record.providers.fal;\n  if (!upstreamBase) {\n    defaults.logger.warn(`No upstream URL configured for provider \"fal\" — cannot proxy`);\n    return \"no_upstream\";\n  }\n\n  defaults.logger.warn(`NO FIXTURE MATCH — walking fal queue at ${upstreamBase}${strippedPath}`);\n\n  let finalBody: unknown;\n  let billableUnits: number | null;\n  try {\n    const walk = await walkFalQueue({\n      upstreamBase,\n      submitPath: strippedPath,\n      body,\n      headers: buildForwardHeaders(req),\n      pollIntervalMs: record.fal?.pollIntervalMs,\n      timeoutMs: record.fal?.timeoutMs,\n      upstreamTimeoutMs: record.upstreamTimeoutMs,\n      fallbackStatusPath: (id) => `${modelId}/requests/${id}/status`,\n      fallbackResultPath: (id) => `${modelId}/requests/${id}`,\n      logger: defaults.logger,\n    });\n    finalBody = walk.body;\n    billableUnits = walk.billableUnits;\n  } catch (err) {\n    const msg = err instanceof Error ? err.message : \"Unknown queue-walk error\";\n    defaults.logger.error(`fal queue-walk proxy failed: ${msg}`);\n    // Guard BEFORE journaling (openrouter-video convention): a client that\n    // disconnected during the multi-second walk gets neither a write nor a\n    // journal entry.\n    if (res.destroyed || res.writableEnded) return \"handled\";\n    journal.add({\n      method: req.method ?? \"POST\",\n      path: pathname,\n      headers: flattenHeaders(req.headers),\n      body: syntheticReq,\n      response: { status: 502, fixture: null, source: \"proxy\" },\n    });\n    res.writeHead(502, { \"Content-Type\": \"application/json\" });\n    res.end(\n      JSON.stringify({\n        error: { message: `Proxy to upstream failed: ${msg}`, type: \"proxy_error\" },\n      }),\n    );\n    return \"handled\";\n  }\n\n  // ── 4. Persist fixture using the FINAL body, not the submit envelope ──\n  const matchRequest = defaults.requestTransform\n    ? defaults.requestTransform(syntheticReq)\n    : syntheticReq;\n  const fixture: Fixture = {\n    match: buildFixtureMatch(matchRequest, record),\n    // Persist the captured billed quantity so the recorded fixture replays\n    // x-fal-billable-units verbatim; omit the field entirely when upstream\n    // didn't bill (keeps recorded fixtures clean).\n    response: {\n      json: finalBody,\n      status: 200,\n      ...(billableUnits != null ? { billableUnits } : {}),\n    },\n  };\n  const persistResult = persistFixture({\n    record,\n    providerKey: \"fal\",\n    testId: getTestId(req),\n    fixture,\n    fixtures,\n    logger: defaults.logger,\n  });\n  // Surface a persist failure on the envelope (parity with the generic\n  // recorder relay and the OpenRouter failed branch) — the synthesized\n  // envelope below has not been written yet, so the header can still ride it.\n  if (persistResult.kind === \"failed\" && !res.headersSent) {\n    res.setHeader(\"X-AIMock-Record-Error\", sanitizeHeaderValue(persistResult.error));\n  }\n\n  // ── 5. Synthesise envelope + seed state (same shape as the replay path) ──\n  const newRequestId = crypto.randomUUID();\n  const progression = resolveProgression(defaults.falQueue);\n  const now = Date.now();\n  const initialStatus: FalQueueStatus =\n    progression.pollsBeforeCompleted === 0 ? \"COMPLETED\" : \"IN_QUEUE\";\n  const job: FalQueueJob = {\n    requestId: newRequestId,\n    modelId,\n    status: initialStatus,\n    result: finalBody,\n    // Captured from the upstream result's x-fal-billable-units header so the\n    // same-session replay (before the persisted fixture is reloaded) already\n    // surfaces the billed quantity.\n    billableUnits,\n    pollCount: 0,\n    pollsBeforeInProgress: progression.pollsBeforeInProgress,\n    pollsBeforeCompleted: progression.pollsBeforeCompleted,\n    submittedAt: now,\n    completedAt: initialStatus === \"COMPLETED\" ? now : null,\n    logs: [{ timestamp: new Date(now).toISOString(), level: \"INFO\", message: \"Job enqueued.\" }],\n    createdAt: now,\n  };\n  falQueueStates.set(stateKey(newRequestId), job);\n  const envelope = {\n    request_id: newRequestId,\n    response_url: `https://${FAL_HOSTS.queue}/${modelId}/requests/${newRequestId}`,\n    status_url: `https://${FAL_HOSTS.queue}/${modelId}/requests/${newRequestId}/status`,\n    cancel_url: `https://${FAL_HOSTS.queue}/${modelId}/requests/${newRequestId}/cancel`,\n    queue_position: queuePosition(job),\n  };\n  // Guard BEFORE journaling (openrouter-video convention): a client that\n  // disconnected during the multi-second walk gets neither a write nor a\n  // journal entry. The persisted fixture and seeded state above stay — the\n  // captured upstream response is valuable regardless.\n  if (res.destroyed || res.writableEnded) return \"handled\";\n  journal.add({\n    method: req.method ?? \"POST\",\n    path: pathname,\n    headers: flattenHeaders(req.headers),\n    body: syntheticReq,\n    response: { status: 200, fixture: null, source: \"proxy\" },\n  });\n  res.writeHead(200, { \"Content-Type\": \"application/json\" });\n  res.end(JSON.stringify(envelope));\n  return \"handled\";\n}\n\nfunction parseJsonOrThrow(text: string, label: string): unknown {\n  try {\n    return JSON.parse(text);\n  } catch {\n    throw new Error(`${label} returned non-JSON: ${text.slice(0, 200)}`);\n  }\n}\n\nfunction withFalUpstream(defaults: HandlerDefaults, targetHost: string): HandlerDefaults {\n  if (!defaults.record) return defaults;\n  // Respect an explicit record.providers.fal — tests and dev configs need to\n  // point at a stub upstream. Only synthesise from the header when the user\n  // didn't configure one (the \"or omit upstream URL — it's in the request\n  // hostname\" mode from the issue).\n  if (defaults.record.providers.fal) return defaults;\n  return {\n    ...defaults,\n    record: {\n      ...defaults.record,\n      providers: {\n        ...defaults.record.providers,\n        fal: `https://${targetHost}`,\n      },\n    },\n  };\n}\n\nfunction writeJson(\n  req: http.IncomingMessage,\n  res: http.ServerResponse,\n  status: number,\n  payload: unknown,\n  pathname: string,\n  journal: Journal,\n  extraHeaders?: Record<string, string>,\n): void {\n  journal.add({\n    method: req.method ?? \"GET\",\n    path: pathname,\n    headers: flattenHeaders(req.headers),\n    body: null,\n    response: { status, fixture: null },\n  });\n  res.writeHead(status, { \"Content-Type\": \"application/json\", ...extraHeaders });\n  res.end(JSON.stringify(payload));\n}\n\nfunction respondNotFound(\n  req: http.IncomingMessage,\n  res: http.ServerResponse,\n  pathname: string,\n  journal: Journal,\n  requestId: string,\n): void {\n  journal.add({\n    method: req.method ?? \"GET\",\n    path: pathname,\n    headers: flattenHeaders(req.headers),\n    body: null,\n    response: { status: 404, fixture: null },\n  });\n  res.writeHead(404, { \"Content-Type\": \"application/json\" });\n  res.end(\n    JSON.stringify({\n      error: { message: `Request ${requestId} not found`, type: \"not_found\" },\n    }),\n  );\n}\n"],"mappings":"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"}