{"version":3,"file":"video.cjs","names":["extractBoundary","extractFormField","flattenHeaders","getContext","getTestId","matchFixtureDiagnostic","applyChaos","resolveStrictMode","strictNoMatchMessage","strictNoMatchLogLine","strictOverrideField","proxyAndRecord","resolveResponse","isErrorResponse","serializeErrorResponse","isVideoResponse"],"sources":["../src/video.ts"],"sourcesContent":["import type * as http from \"node:http\";\nimport type { ChatCompletionRequest, Fixture, HandlerDefaults, VideoResponse } from \"./types.js\";\nimport {\n  isVideoResponse,\n  isErrorResponse,\n  serializeErrorResponse,\n  flattenHeaders,\n  getTestId,\n  resolveResponse,\n  resolveStrictMode,\n  strictOverrideField,\n  getContext,\n  strictNoMatchMessage,\n  strictNoMatchLogLine,\n} from \"./helpers.js\";\nimport { matchFixtureDiagnostic } from \"./router.js\";\nimport { writeErrorResponse } from \"./sse-writer.js\";\nimport type { Journal } from \"./journal.js\";\nimport { applyChaos } from \"./chaos.js\";\nimport { proxyAndRecord } from \"./recorder.js\";\nimport { extractBoundary, extractFormField } from \"./transcription.js\";\n\ninterface VideoRequest {\n  model?: string;\n  prompt: string;\n  [key: string]: unknown;\n}\n\n// ─── VideoStateMap with TTL and size bound ────────────────────────────────\n\nconst VIDEO_STATE_MAX_ENTRIES = 10_000;\nconst VIDEO_STATE_TTL_MS = 3_600_000; // 1 hour\n\ninterface VideoStateEntry {\n  video: VideoResponse[\"video\"];\n  createdAt: number;\n}\n\n/**\n * A Map wrapper for video state that enforces a maximum size and per-entry TTL.\n * Entries older than VIDEO_STATE_TTL_MS are lazily evicted on `get`.\n * When the map exceeds VIDEO_STATE_MAX_ENTRIES on `set`, the oldest entries\n * are removed to stay within bounds.\n */\nexport class VideoStateMap {\n  private readonly entries = new Map<string, VideoStateEntry>();\n  private readonly sweepTimer: ReturnType<typeof setInterval>;\n\n  constructor() {\n    // Proactive sweep every 60 seconds to evict expired entries\n    this.sweepTimer = setInterval(() => {\n      const now = Date.now();\n      for (const [key, entry] of this.entries) {\n        if (now - entry.createdAt > VIDEO_STATE_TTL_MS) {\n          this.entries.delete(key);\n        }\n      }\n    }, 60_000);\n    // Allow the process to exit even if the timer is still running\n    if (this.sweepTimer.unref) {\n      this.sweepTimer.unref();\n    }\n  }\n\n  getEntry(key: string): { video: VideoResponse[\"video\"]; createdAtUnix: number } | undefined {\n    const entry = this.entries.get(key);\n    if (!entry) return undefined;\n    if (Date.now() - entry.createdAt > VIDEO_STATE_TTL_MS) {\n      this.entries.delete(key);\n      return undefined;\n    }\n    return { video: entry.video, createdAtUnix: Math.floor(entry.createdAt / 1000) };\n  }\n\n  getCreatedAtUnix(key: string): number | undefined {\n    const e = this.getEntry(key);\n    return e?.createdAtUnix;\n  }\n\n  set(key: string, video: VideoResponse[\"video\"]): void {\n    this.entries.set(key, { video, createdAt: Date.now() });\n    // Evict oldest entries if over capacity\n    if (this.entries.size > VIDEO_STATE_MAX_ENTRIES) {\n      const excess = this.entries.size - VIDEO_STATE_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  destroy(): void {\n    clearInterval(this.sweepTimer);\n    this.entries.clear();\n  }\n\n  get size(): number {\n    return this.entries.size;\n  }\n}\n\nexport async function handleVideoCreate(\n  req: http.IncomingMessage,\n  res: http.ServerResponse,\n  raw: string,\n  fixtures: Fixture[],\n  journal: Journal,\n  defaults: HandlerDefaults,\n  setCorsHeaders: (res: http.ServerResponse) => void,\n  videoStates: VideoStateMap,\n): Promise<void> {\n  setCorsHeaders(res);\n  const path = req.url ?? \"/v1/videos\";\n  const method = req.method ?? \"POST\";\n\n  const contentType = Array.isArray(req.headers[\"content-type\"])\n    ? req.headers[\"content-type\"][0]\n    : req.headers[\"content-type\"];\n  const isMultipart = (contentType ?? \"\").toLowerCase().includes(\"multipart/form-data\");\n\n  let videoReq: VideoRequest;\n  if (isMultipart) {\n    // The OpenAI SDK (6.28.0+) sends POST /v1/videos as multipart/form-data;\n    // older SDKs sent JSON for a File-less body. Parse the form fields into the\n    // same shape the JSON path produces, reusing the transcription multipart\n    // helpers. Numeric fields (e.g. `seconds`) arrive as strings and are\n    // coerced to numbers to match the JSON body's types.\n    const boundary = extractBoundary(contentType);\n    const prompt = extractFormField(raw, \"prompt\", boundary);\n    const model = extractFormField(raw, \"model\", boundary);\n    const size = extractFormField(raw, \"size\", boundary);\n    const secondsRaw = extractFormField(raw, \"seconds\", boundary);\n    videoReq = { prompt: prompt ?? \"\" };\n    if (model !== undefined) videoReq.model = model;\n    if (size !== undefined) videoReq.size = size;\n    if (secondsRaw !== undefined) {\n      const secondsNum = Number(secondsRaw);\n      videoReq.seconds = Number.isNaN(secondsNum) ? secondsRaw : secondsNum;\n    }\n  } else {\n    try {\n      videoReq = JSON.parse(raw) as VideoRequest;\n    } catch (parseErr) {\n      const detail = parseErr instanceof Error ? parseErr.message : \"unknown\";\n      journal.add({\n        method,\n        path,\n        headers: flattenHeaders(req.headers),\n        body: null,\n        response: { status: 400, fixture: null },\n      });\n      writeErrorResponse(\n        res,\n        400,\n        JSON.stringify({\n          error: {\n            message: `Malformed JSON: ${detail}`,\n            type: \"invalid_request_error\",\n            code: \"invalid_json\",\n          },\n        }),\n      );\n      return;\n    }\n  }\n\n  if (!videoReq.prompt) {\n    journal.add({\n      method,\n      path,\n      headers: flattenHeaders(req.headers),\n      body: null,\n      response: { status: 400, fixture: null },\n    });\n    writeErrorResponse(\n      res,\n      400,\n      JSON.stringify({\n        error: { message: \"Missing required parameter: 'prompt'\", type: \"invalid_request_error\" },\n      }),\n    );\n    return;\n  }\n\n  const syntheticReq: ChatCompletionRequest = {\n    model: videoReq.model ?? \"sora-2\",\n    messages: [{ role: \"user\", content: videoReq.prompt }],\n    _endpointType: \"video\",\n    _context: getContext(req),\n  };\n\n  const testId = getTestId(req);\n  const { fixture, skippedBySequenceOrTurn } = matchFixtureDiagnostic(\n    fixtures,\n    syntheticReq,\n    journal.getFixtureMatchCountsForTest(testId),\n    defaults.requestTransform,\n  );\n\n  if (fixture) {\n    journal.incrementFixtureMatchCount(fixture, fixtures, testId);\n    defaults.logger.debug(`Fixture matched: ${JSON.stringify(fixture.match).slice(0, 120)}`);\n  } else {\n    defaults.logger.debug(`No fixture matched for request`);\n  }\n\n  if (\n    applyChaos(\n      res,\n      fixture,\n      defaults.chaos,\n      req.headers,\n      journal,\n      { method, path, headers: flattenHeaders(req.headers), body: syntheticReq },\n      fixture ? \"fixture\" : \"proxy\",\n      defaults.registry,\n      defaults.logger,\n    )\n  )\n    return;\n\n  if (!fixture) {\n    const effectiveStrict = resolveStrictMode(defaults.strict, req.headers);\n    if (effectiveStrict) {\n      const strictMessage = strictNoMatchMessage(skippedBySequenceOrTurn);\n      defaults.logger.error(strictNoMatchLogLine(method, path, skippedBySequenceOrTurn));\n      journal.add({\n        method,\n        path,\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      writeErrorResponse(\n        res,\n        503,\n        JSON.stringify({\n          error: {\n            message: strictMessage,\n            type: \"invalid_request_error\",\n            code: \"no_fixture_match\",\n          },\n        }),\n      );\n      return;\n    }\n    if (defaults.record) {\n      const outcome = await proxyAndRecord(\n        req,\n        res,\n        syntheticReq,\n        \"openai\",\n        req.url ?? \"/v1/videos\",\n        fixtures,\n        defaults,\n        raw,\n      );\n      if (outcome === \"handled_by_hook\") return;\n      if (outcome !== \"not_configured\") {\n        journal.add({\n          method,\n          path,\n          headers: flattenHeaders(req.headers),\n          body: syntheticReq,\n          response: { status: res.statusCode ?? 200, fixture: null, source: \"proxy\" },\n        });\n        return;\n      }\n    }\n\n    journal.add({\n      method,\n      path,\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    writeErrorResponse(\n      res,\n      404,\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;\n  }\n\n  const response = await resolveResponse(fixture, syntheticReq);\n\n  if (isErrorResponse(response)) {\n    const status = response.status ?? 500;\n    journal.add({\n      method,\n      path,\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;\n  }\n\n  if (!isVideoResponse(response)) {\n    journal.add({\n      method,\n      path,\n      headers: flattenHeaders(req.headers),\n      body: syntheticReq,\n      response: { status: 500, fixture },\n    });\n    writeErrorResponse(\n      res,\n      500,\n      JSON.stringify({\n        error: { message: \"Fixture response is not a video type\", type: \"server_error\" },\n      }),\n    );\n    return;\n  }\n\n  journal.add({\n    method,\n    path,\n    headers: flattenHeaders(req.headers),\n    body: syntheticReq,\n    response: { status: 200, fixture },\n  });\n\n  const video = response.video;\n\n  // Store for GET status checks\n  const stateKey = `${testId}:${video.id}`;\n  videoStates.set(stateKey, video);\n  const created_at = videoStates.getCreatedAtUnix(stateKey)!;\n\n  if (video.status === \"completed\") {\n    res.writeHead(200, { \"Content-Type\": \"application/json\" });\n    res.end(JSON.stringify({ id: video.id, status: video.status, url: video.url, created_at }));\n  } else {\n    res.writeHead(200, { \"Content-Type\": \"application/json\" });\n    res.end(JSON.stringify({ id: video.id, status: video.status, created_at }));\n  }\n}\n\nexport function handleVideoStatus(\n  req: http.IncomingMessage,\n  res: http.ServerResponse,\n  videoId: string,\n  journal: Journal,\n  defaults: HandlerDefaults,\n  setCorsHeaders: (res: http.ServerResponse) => void,\n  videoStates: VideoStateMap,\n): void {\n  setCorsHeaders(res);\n  const path = req.url ?? `/v1/videos/${videoId}`;\n  const method = req.method ?? \"GET\";\n\n  if (\n    applyChaos(\n      res,\n      null,\n      defaults.chaos,\n      req.headers,\n      journal,\n      { method, path, headers: flattenHeaders(req.headers), body: null },\n      \"internal\",\n      defaults.registry,\n      defaults.logger,\n    )\n  )\n    return;\n\n  const testId = getTestId(req);\n  const stateKey = `${testId}:${videoId}`;\n  const entry = videoStates.getEntry(stateKey);\n\n  if (!entry) {\n    journal.add({\n      method,\n      path,\n      headers: flattenHeaders(req.headers),\n      body: null,\n      response: { status: 404, fixture: null },\n    });\n    writeErrorResponse(\n      res,\n      404,\n      JSON.stringify({ error: { message: `Video ${videoId} not found`, type: \"not_found\" } }),\n    );\n    return;\n  }\n\n  const { video, createdAtUnix: created_at } = entry;\n\n  journal.add({\n    method,\n    path,\n    headers: flattenHeaders(req.headers),\n    body: null,\n    response: { status: 200, fixture: null },\n  });\n\n  const body: Record<string, unknown> = {\n    id: video.id,\n    status: video.status,\n    created_at,\n  };\n  if (video.url) body.url = video.url;\n\n  res.writeHead(200, { \"Content-Type\": \"application/json\" });\n  res.end(JSON.stringify(body));\n}\n"],"mappings":"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"}