@copilotkit/aimock 1.42.1 → 1.43.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude-plugin/marketplace.json +1 -1
- package/.claude-plugin/plugin.json +1 -1
- package/CHANGELOG.md +50 -0
- package/README.md +102 -16
- package/dist/agui-mock.cjs +1 -1
- package/dist/agui-mock.cjs.map +1 -1
- package/dist/agui-mock.d.cts.map +1 -1
- package/dist/agui-mock.d.ts.map +1 -1
- package/dist/agui-mock.js +1 -1
- package/dist/agui-mock.js.map +1 -1
- package/dist/agui-recorder.cjs +33 -11
- package/dist/agui-recorder.cjs.map +1 -1
- package/dist/agui-recorder.d.cts +10 -1
- package/dist/agui-recorder.d.cts.map +1 -1
- package/dist/agui-recorder.d.ts +10 -1
- package/dist/agui-recorder.d.ts.map +1 -1
- package/dist/agui-recorder.js +33 -11
- package/dist/agui-recorder.js.map +1 -1
- package/dist/agui-types.d.cts +1 -0
- package/dist/agui-types.d.cts.map +1 -1
- package/dist/agui-types.d.ts +1 -0
- package/dist/agui-types.d.ts.map +1 -1
- package/dist/aimock-cli.cjs +38 -10
- package/dist/aimock-cli.cjs.map +1 -1
- package/dist/aimock-cli.d.cts +5 -0
- package/dist/aimock-cli.d.cts.map +1 -1
- package/dist/aimock-cli.d.ts +5 -0
- package/dist/aimock-cli.d.ts.map +1 -1
- package/dist/aimock-cli.js +38 -10
- package/dist/aimock-cli.js.map +1 -1
- package/dist/batches.cjs +458 -0
- package/dist/batches.cjs.map +1 -0
- package/dist/batches.d.cts +57 -0
- package/dist/batches.d.cts.map +1 -0
- package/dist/batches.d.ts +57 -0
- package/dist/batches.d.ts.map +1 -0
- package/dist/batches.js +453 -0
- package/dist/batches.js.map +1 -0
- package/dist/bedrock-converse.cjs +12 -8
- package/dist/bedrock-converse.cjs.map +1 -1
- package/dist/bedrock-converse.d.cts.map +1 -1
- package/dist/bedrock-converse.d.ts.map +1 -1
- package/dist/bedrock-converse.js +14 -10
- package/dist/bedrock-converse.js.map +1 -1
- package/dist/bedrock.cjs +10 -8
- package/dist/bedrock.cjs.map +1 -1
- package/dist/bedrock.d.cts.map +1 -1
- package/dist/bedrock.d.ts.map +1 -1
- package/dist/bedrock.js +12 -10
- package/dist/bedrock.js.map +1 -1
- package/dist/byteplus-video.cjs +3 -3
- package/dist/byteplus-video.cjs.map +1 -1
- package/dist/byteplus-video.js +4 -4
- package/dist/byteplus-video.js.map +1 -1
- package/dist/chaos.cjs +622 -72
- package/dist/chaos.cjs.map +1 -1
- package/dist/chaos.d.cts +164 -7
- package/dist/chaos.d.cts.map +1 -1
- package/dist/chaos.d.ts +164 -7
- package/dist/chaos.d.ts.map +1 -1
- package/dist/chaos.js +613 -74
- package/dist/chaos.js.map +1 -1
- package/dist/cli.cjs +42 -23
- package/dist/cli.cjs.map +1 -1
- package/dist/cli.js +42 -23
- package/dist/cli.js.map +1 -1
- package/dist/cohere.cjs +10 -8
- package/dist/cohere.cjs.map +1 -1
- package/dist/cohere.d.cts.map +1 -1
- package/dist/cohere.d.ts.map +1 -1
- package/dist/cohere.js +12 -10
- package/dist/cohere.js.map +1 -1
- package/dist/config-loader.d.ts.map +1 -1
- package/dist/convert.cjs +1 -1
- package/dist/convert.cjs.map +1 -1
- package/dist/convert.js +1 -1
- package/dist/convert.js.map +1 -1
- package/dist/elevenlabs-audio.cjs +3 -3
- package/dist/elevenlabs-audio.cjs.map +1 -1
- package/dist/elevenlabs-audio.js +4 -4
- package/dist/elevenlabs-audio.js.map +1 -1
- package/dist/elevenlabs-voice.cjs +1053 -0
- package/dist/elevenlabs-voice.cjs.map +1 -0
- package/dist/elevenlabs-voice.d.cts +72 -0
- package/dist/elevenlabs-voice.d.cts.map +1 -0
- package/dist/elevenlabs-voice.d.ts +72 -0
- package/dist/elevenlabs-voice.d.ts.map +1 -0
- package/dist/elevenlabs-voice.js +1048 -0
- package/dist/elevenlabs-voice.js.map +1 -0
- package/dist/embeddings.cjs +8 -4
- package/dist/embeddings.cjs.map +1 -1
- package/dist/embeddings.d.cts.map +1 -1
- package/dist/embeddings.d.ts.map +1 -1
- package/dist/embeddings.js +9 -5
- package/dist/embeddings.js.map +1 -1
- package/dist/fal-audio.cjs +3 -3
- package/dist/fal-audio.cjs.map +1 -1
- package/dist/fal-audio.js +4 -4
- package/dist/fal-audio.js.map +1 -1
- package/dist/fal.cjs +28 -4
- package/dist/fal.cjs.map +1 -1
- package/dist/fal.d.cts +16 -1
- package/dist/fal.d.cts.map +1 -1
- package/dist/fal.d.ts +16 -1
- package/dist/fal.d.ts.map +1 -1
- package/dist/fal.js +28 -5
- package/dist/fal.js.map +1 -1
- package/dist/files.cjs +1071 -0
- package/dist/files.cjs.map +1 -0
- package/dist/files.d.cts +68 -0
- package/dist/files.d.cts.map +1 -0
- package/dist/files.d.ts +68 -0
- package/dist/files.d.ts.map +1 -0
- package/dist/files.js +1059 -0
- package/dist/files.js.map +1 -0
- package/dist/fine-tuning.cjs +1051 -0
- package/dist/fine-tuning.cjs.map +1 -0
- package/dist/fine-tuning.d.cts +96 -0
- package/dist/fine-tuning.d.cts.map +1 -0
- package/dist/fine-tuning.d.ts +96 -0
- package/dist/fine-tuning.d.ts.map +1 -0
- package/dist/fine-tuning.js +1044 -0
- package/dist/fine-tuning.js.map +1 -0
- package/dist/fixture-loader.cjs +0 -0
- package/dist/fixture-loader.cjs.map +1 -1
- package/dist/fixture-loader.d.cts +37 -5
- package/dist/fixture-loader.d.cts.map +1 -1
- package/dist/fixture-loader.d.ts +37 -5
- package/dist/fixture-loader.d.ts.map +1 -1
- package/dist/fixture-loader.js +0 -0
- package/dist/fixture-loader.js.map +1 -1
- package/dist/gemini-embeddings.cjs +2 -2
- package/dist/gemini-embeddings.cjs.map +1 -1
- package/dist/gemini-embeddings.js +3 -3
- package/dist/gemini-embeddings.js.map +1 -1
- package/dist/gemini-interactions.cjs +13 -7
- package/dist/gemini-interactions.cjs.map +1 -1
- package/dist/gemini-interactions.d.cts.map +1 -1
- package/dist/gemini-interactions.d.ts.map +1 -1
- package/dist/gemini-interactions.js +15 -9
- package/dist/gemini-interactions.js.map +1 -1
- package/dist/gemini.cjs +11 -7
- package/dist/gemini.cjs.map +1 -1
- package/dist/gemini.d.cts.map +1 -1
- package/dist/gemini.d.ts.map +1 -1
- package/dist/gemini.js +13 -9
- package/dist/gemini.js.map +1 -1
- package/dist/grok-video.cjs +14 -14
- package/dist/grok-video.cjs.map +1 -1
- package/dist/grok-video.js +15 -15
- package/dist/grok-video.js.map +1 -1
- package/dist/helpers.cjs +221 -5
- package/dist/helpers.cjs.map +1 -1
- package/dist/helpers.d.cts +38 -2
- package/dist/helpers.d.cts.map +1 -1
- package/dist/helpers.d.ts +38 -2
- package/dist/helpers.d.ts.map +1 -1
- package/dist/helpers.js +213 -6
- package/dist/helpers.js.map +1 -1
- package/dist/images.cjs +54 -123
- package/dist/images.cjs.map +1 -1
- package/dist/images.d.cts.map +1 -1
- package/dist/images.d.ts.map +1 -1
- package/dist/images.js +55 -124
- package/dist/images.js.map +1 -1
- package/dist/index.cjs +41 -8
- package/dist/index.d.cts +12 -7
- package/dist/index.d.ts +12 -7
- package/dist/index.js +14 -10
- package/dist/jest.cjs +14 -7
- package/dist/jest.cjs.map +1 -1
- package/dist/jest.d.cts +3 -2
- package/dist/jest.d.cts.map +1 -1
- package/dist/jest.d.ts +3 -2
- package/dist/jest.d.ts.map +1 -1
- package/dist/jest.js +14 -7
- package/dist/jest.js.map +1 -1
- package/dist/journal.cjs +41 -4
- package/dist/journal.cjs.map +1 -1
- package/dist/journal.d.cts +44 -3
- package/dist/journal.d.cts.map +1 -1
- package/dist/journal.d.ts +44 -3
- package/dist/journal.d.ts.map +1 -1
- package/dist/journal.js +41 -5
- package/dist/journal.js.map +1 -1
- package/dist/live-fixture.cjs +1192 -0
- package/dist/live-fixture.cjs.map +1 -0
- package/dist/live-fixture.js +1187 -0
- package/dist/live-fixture.js.map +1 -0
- package/dist/live-recorder.cjs +256 -0
- package/dist/live-recorder.cjs.map +1 -0
- package/dist/live-recorder.js +254 -0
- package/dist/live-recorder.js.map +1 -0
- package/dist/live-replay.cjs +286 -0
- package/dist/live-replay.cjs.map +1 -0
- package/dist/live-replay.js +285 -0
- package/dist/live-replay.js.map +1 -0
- package/dist/live-sanitize.cjs +196 -0
- package/dist/live-sanitize.cjs.map +1 -0
- package/dist/live-sanitize.js +196 -0
- package/dist/live-sanitize.js.map +1 -0
- package/dist/live-types.d.cts +71 -0
- package/dist/live-types.d.cts.map +1 -0
- package/dist/live-types.d.ts +71 -0
- package/dist/live-types.d.ts.map +1 -0
- package/dist/llmock.cjs +52 -41
- package/dist/llmock.cjs.map +1 -1
- package/dist/llmock.d.cts +16 -5
- package/dist/llmock.d.cts.map +1 -1
- package/dist/llmock.d.ts +16 -5
- package/dist/llmock.d.ts.map +1 -1
- package/dist/llmock.js +53 -42
- package/dist/llmock.js.map +1 -1
- package/dist/messages.cjs +11 -7
- package/dist/messages.cjs.map +1 -1
- package/dist/messages.d.cts.map +1 -1
- package/dist/messages.d.ts.map +1 -1
- package/dist/messages.js +13 -9
- package/dist/messages.js.map +1 -1
- package/dist/metrics.cjs +258 -5
- package/dist/metrics.cjs.map +1 -1
- package/dist/metrics.d.cts +3 -1
- package/dist/metrics.d.cts.map +1 -1
- package/dist/metrics.d.ts +3 -1
- package/dist/metrics.d.ts.map +1 -1
- package/dist/metrics.js +255 -6
- package/dist/metrics.js.map +1 -1
- package/dist/moderation.cjs +10 -1
- package/dist/moderation.cjs.map +1 -1
- package/dist/moderation.d.cts.map +1 -1
- package/dist/moderation.d.ts.map +1 -1
- package/dist/moderation.js +10 -1
- package/dist/moderation.js.map +1 -1
- package/dist/ollama.cjs +5 -5
- package/dist/ollama.cjs.map +1 -1
- package/dist/ollama.js +6 -6
- package/dist/ollama.js.map +1 -1
- package/dist/openrouter-video.cjs +5 -5
- package/dist/openrouter-video.cjs.map +1 -1
- package/dist/openrouter-video.js +6 -6
- package/dist/openrouter-video.js.map +1 -1
- package/dist/recorder.cjs +45 -9
- package/dist/recorder.cjs.map +1 -1
- package/dist/recorder.d.cts.map +1 -1
- package/dist/recorder.d.ts.map +1 -1
- package/dist/recorder.js +43 -11
- package/dist/recorder.js.map +1 -1
- package/dist/responses.cjs +4 -3
- package/dist/responses.cjs.map +1 -1
- package/dist/responses.js +5 -5
- package/dist/responses.js.map +1 -1
- package/dist/router.cjs +20 -33
- package/dist/router.cjs.map +1 -1
- package/dist/router.d.cts +4 -1
- package/dist/router.d.cts.map +1 -1
- package/dist/router.d.ts +4 -1
- package/dist/router.d.ts.map +1 -1
- package/dist/router.js +20 -34
- package/dist/router.js.map +1 -1
- package/dist/server.cjs +701 -625
- package/dist/server.cjs.map +1 -1
- package/dist/server.d.cts +1 -0
- package/dist/server.d.cts.map +1 -1
- package/dist/server.d.ts +1 -0
- package/dist/server.d.ts.map +1 -1
- package/dist/server.js +704 -628
- package/dist/server.js.map +1 -1
- package/dist/speech.cjs +2 -2
- package/dist/speech.cjs.map +1 -1
- package/dist/speech.js +3 -3
- package/dist/speech.js.map +1 -1
- package/dist/transcription.cjs +4 -4
- package/dist/transcription.cjs.map +1 -1
- package/dist/transcription.js +5 -5
- package/dist/transcription.js.map +1 -1
- package/dist/types.d.cts +119 -16
- package/dist/types.d.cts.map +1 -1
- package/dist/types.d.ts +119 -16
- package/dist/types.d.ts.map +1 -1
- package/dist/validate-cli.cjs +925 -0
- package/dist/validate-cli.cjs.map +1 -0
- package/dist/validate-cli.js +924 -0
- package/dist/validate-cli.js.map +1 -0
- package/dist/veo-video.cjs +13 -13
- package/dist/veo-video.cjs.map +1 -1
- package/dist/veo-video.js +14 -14
- package/dist/veo-video.js.map +1 -1
- package/dist/video.cjs +4 -4
- package/dist/video.cjs.map +1 -1
- package/dist/video.d.cts +1 -1
- package/dist/video.d.cts.map +1 -1
- package/dist/video.d.ts +1 -1
- package/dist/video.d.ts.map +1 -1
- package/dist/video.js +5 -5
- package/dist/video.js.map +1 -1
- package/dist/vitest.cjs +13 -6
- package/dist/vitest.cjs.map +1 -1
- package/dist/vitest.d.cts +2 -1
- package/dist/vitest.d.cts.map +1 -1
- package/dist/vitest.d.ts +2 -1
- package/dist/vitest.d.ts.map +1 -1
- package/dist/vitest.js +14 -7
- package/dist/vitest.js.map +1 -1
- package/dist/watcher.cjs +2 -1
- package/dist/watcher.cjs.map +1 -1
- package/dist/watcher.js +2 -1
- package/dist/watcher.js.map +1 -1
- package/dist/ws-framing.cjs +150 -35
- package/dist/ws-framing.cjs.map +1 -1
- package/dist/ws-framing.d.cts +18 -3
- package/dist/ws-framing.d.cts.map +1 -1
- package/dist/ws-framing.d.ts +18 -3
- package/dist/ws-framing.d.ts.map +1 -1
- package/dist/ws-framing.js +151 -36
- package/dist/ws-framing.js.map +1 -1
- package/dist/ws-gemini-live.cjs +2 -2
- package/dist/ws-gemini-live.d.ts +2 -2
- package/dist/ws-gemini-live.js +2 -2
- package/dist/ws-live.cjs +401 -0
- package/dist/ws-live.cjs.map +1 -0
- package/dist/ws-live.js +400 -0
- package/dist/ws-live.js.map +1 -0
- package/dist/ws-realtime.cjs +46 -2
- package/dist/ws-realtime.cjs.map +1 -1
- package/dist/ws-realtime.d.cts.map +1 -1
- package/dist/ws-realtime.d.ts +2 -2
- package/dist/ws-realtime.d.ts.map +1 -1
- package/dist/ws-realtime.js +46 -2
- package/dist/ws-realtime.js.map +1 -1
- package/dist/ws-responses.cjs +2 -2
- package/dist/ws-responses.js +2 -2
- package/dist/ws-upstream.cjs +118 -0
- package/dist/ws-upstream.cjs.map +1 -0
- package/dist/ws-upstream.js +115 -0
- package/dist/ws-upstream.js.map +1 -0
- package/fixtures/openai-live-client.json +390 -0
- package/fixtures/openai-live-managed.json +709 -0
- package/package.json +1 -1
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"validate-cli.js","names":[],"sources":["../src/validate-cli.ts"],"sourcesContent":["/**\n * `aimock validate` subcommand — offline fixture lint.\n *\n * Usage: `aimock validate [--strict] [--json] [--] <path> [more paths ...]`\n *\n * Each path is a fixture file or a directory; directories are walked\n * recursively for `*.json` in the same order `--fixtures <dir>` loads them.\n * For each file: read bytes, JSON-parse, require a top-level\n * `{ \"fixtures\": [...] }` envelope, convert the entries (surfacing the\n * per-entry conversion warnings the server logs), then run the\n * `validateFixtures` rules over that file's entries alone, so its findings\n * are attributed to it.\n *\n * `validateFixtures` is then run a SECOND time over the union of every\n * successfully-parsed file's entries, in load order. Its rules are not all\n * per-fixture: `duplicate userMessage` and the catch-all ordering check look\n * at the other fixtures in the array they are given. Run per file those two\n * cannot see across files, so the findings the union pass adds — a\n * `userMessage` duplicated in two files, a catch-all that is last in its own\n * file but not last overall — are the cross-file ones. They are reported\n * against the file that owns the fixture, with the other file named in the\n * message; findings the per-file pass already made are not repeated.\n *\n * How this compares to the server's `--validate-on-load`, in both directions:\n * - Stricter: there an unreadable, unparseable or wrong-shape file is\n * non-fatal — it warns on stderr, contributes zero fixtures, and startup\n * continues as long as the run loaded at least one fixture from somewhere.\n * Here every such file fails the run — with one deliberate exception, for\n * parity: a `*.json` file the WALK turned up that parses but carries no\n * top-level `fixtures` array is not a fixture file (this package's own\n * `fixtures/` tree holds aimock config files alongside fixtures), and is\n * passed over with a note, exactly as the loader passes over it. A file\n * the caller NAMES is still validated, wrong shape and all. Two walk\n * hazards the server does not\n * survive are refused outright rather than mirrored: a `*.json` path that\n * is not a regular file (a FIFO, a socket, a device) is a per-file error\n * here, where the server's `readFileSync` blocks on it forever; and a\n * directory symlink that points back at a directory already being walked\n * is reported once as a cycle, where the server recurses through it until\n * the kernel returns ELOOP (~32 levels deep) and loads those levels'\n * fixtures over and over. One diagnostic here has no counterpart there at\n * all: an entry whose `match` is a string, a number, a boolean or an array\n * converts, on both sides, into an EMPTY match — a catch-all answering\n * every request — and the server loads it without a word. That is a server\n * bug, not a rule this lint may quietly invent an error for, so it is a\n * `[warning]` (which `--strict` promotes to a failure) rather than an\n * error, and the fixture still goes on to the rules exactly as the server\n * would build it.\n * - Equal: the `validateFixtures` rules themselves, because of the union\n * pass above, and the zero-fixture rule — a run whose inputs yield no\n * fixtures at all is an error here, matching the server's \"No fixtures\n * loaded and validation/strict mode is enabled — aborting.\" A single\n * `{\"fixtures\": []}` file alongside files that do load fixtures is an\n * error in neither place. Equal too in what the inputs expand to: a path\n * named twice, and a directory reached both directly and through an\n * acyclic symlink to it, are validated once per mention, exactly as the\n * server loads one `--fixtures` source per mention — so the duplicate\n * fixtures that produces are reported here as the server reports them,\n * one finding per (fixture, rule), the same count the server logs.\n *\n * A path mentioned more than once in one run is therefore several DIFFERENT\n * loads that happen to share a name, and printing that bare name for each of\n * them made them indistinguishable: a warning read as if a fixture shadowed\n * itself. Each mention is numbered, and every rendering surface carries that\n * number — human lines and their summary, cross-file references, and a\n * `mention` field on each `--json` `files[]` entry. The `<path>[<n>]` suffix\n * is printed only for a path that IS mentioned more than once, because for\n * every other path the name alone already names one load; the `--json` field\n * is always present so a consumer never has to infer it.\n * - Weaker: this is a static lint. It never starts a server, so it covers\n * nothing about binding, remote `--fixtures <url>` sources (it reads local\n * paths only), `--watch` reloads, or any runtime behaviour.\n *\n * Nothing the filesystem or a fixture entry can do is allowed to escape this\n * function: a malformed entry, an unreadable path, a stat failure or a symlink\n * cycle each becomes a per-file `[error]` in the report, and a crash inside\n * either `validateFixtures` pass becomes a file-level or run-level `[error]`,\n * so the run still emits a report (valid JSON under `--json`) and still exits\n * 1. Run-level errors print after the per-file output, and the \"no fixtures\n * loaded\" one is withheld when a file already reported why it loaded none.\n *\n * Prints human lines by default (`file: [error|warning] #i message`) or a\n * JSON document with `--json`. Everything that contributes to a non-zero exit\n * goes to stderr, so a CI log's stderr always shows the reason; `--json` keeps\n * stdout to the report alone and puts the reason on stderr. The two modes\n * report the same facts: a fatal file is one record that both the stdout\n * tally and `files[].errors` count, and `--json` emits a document for a usage\n * error too — stdout is never empty on a `--json` run, whatever failed it.\n * A path naming a remote source (`https://...`) is refused as unsupported\n * rather than read as a filename. Exit codes:\n * 0 = clean (warnings allowed unless `--strict`), 1 = everything that fails\n * the run — errors / unreadable / unparseable / wrong shape / unwalkable path\n * / no fixtures loaded at all, and a usage error (no paths or an unknown\n * option) too, because every other CLI surface in this package exits 1 on a\n * usage error and nothing here uses sysexits codes. `--help` is handled\n * separately and exits 0.\n */\n\nimport { readFileSync, readdirSync, realpathSync, statSync } from \"node:fs\";\nimport type { Stats } from \"node:fs\";\nimport { join, resolve } from \"node:path\";\nimport { format, parseArgs } from \"node:util\";\nimport { entryToFixture, renderValidationRef, validateFixtures } from \"./fixture-loader.js\";\nimport type { ValidationRef } from \"./fixture-loader.js\";\nimport { Logger } from \"./logger.js\";\nimport type { Fixture, FixtureFile } from \"./types.js\";\n\nexport const VALIDATE_HELP = `\nUsage: aimock validate [options] [--] <path> [more paths ...]\n\nValidate aimock fixture files offline. Each path is a fixture file or a\ndirectory of them (walked recursively for *.json, like --fixtures <dir>).\nFiles are checked one at a time and then as one combined set, so the rules\nthat span files -- a userMessage duplicated across two files, a catch-all\nthat is not last overall -- are reported here as the server reports them.\n\nWhere the server's --validate-on-load differs: there an unreadable,\nunparseable or wrong-shape file only warns and startup continues, while here\nevery such file fails the run. The one exception, which matches the loader: a\n*.json file found by WALKING a directory that has no top-level \"fixtures\"\narray is not a fixture file (an aimock config, say) and is skipped with a\nnote -- a file you name on the command line is still validated. As on the\nserver, a run that loads no fixtures at all is an error. This is a static lint: it starts no server and reads\nlocal paths only (no remote --fixtures URL).\n\nA path given twice is validated twice, because the server loads one set of\nfixtures per --fixtures it is given; the duplicate fixtures that produces are\nreported as the server reports them, one finding per fixture and rule. Such a\npath is printed as <path>[<n>], numbering its mentions from 1, so each load\nand every reference to its fixtures names one of them (--json reports the\nnumber as \"mention\" on every file). A directory symlink pointing back at a\ndirectory already being walked is a symlink cycle and fails the run; one\npointing anywhere else is followed, as the server follows it.\n\nOptions:\n --strict Treat warnings as errors (exit 1 on warnings)\n --json Emit a JSON report instead of human lines\n -h, --help Show this help message\n -- Stop option parsing; every later argument is a path\n (use this for a path that begins with \"-\")\n\nExit codes:\n 0 all files valid (warnings allowed unless --strict)\n 1 anything that failed the run: validation errors; a malformed fixture\n entry; an unreadable or unparseable file; a named path of the wrong\n shape (a walked one is skipped instead); a path that\n cannot be stat'd, read or walked (including a symlink cycle, or a\n *.json path that is not a regular file); a directory holding no *.json;\n no fixtures loaded from any input; --strict warnings; or a usage error\n (no paths given, or an unknown option)\n`.trim();\n\nexport interface ValidateCliDeps {\n argv?: string[];\n log?: (msg: string) => void;\n logError?: (msg: string) => void;\n exit?: (code: number) => void;\n}\n\n/**\n * One finding against a file. `index` is the fixture entry it belongs to and is\n * ABSENT when the finding is about the file as a whole (a crash inside the\n * validator, say) rather than an entry — attributing such a finding to entry #0\n * blames a fixture that may be perfectly valid. `detail` carries the raw\n * underlying text (a thrown error's message) for a finding whose `message` is a\n * human-facing rephrasing of it; the `--json` report carries it as its own\n * field and the human report prints it on an indented continuation line.\n */\ninterface Finding {\n index?: number;\n message: string;\n detail?: string;\n}\n\ninterface FileReport {\n file: string;\n /**\n * Which mention of `file` this report is, 1-based, in the order the run\n * expanded its paths. It is the only thing that tells two reports of the\n * same path apart — they are separate loads with separate findings.\n */\n mention: number;\n fixtures: number;\n errors: Finding[];\n warnings: Finding[];\n fatal?: string;\n /** Set when the walk passed the file over; see `nonFixtureSkipReason`. */\n skipped?: string;\n}\n\n/**\n * A rule finding the per-file pass made, with what the cross-file pass needs\n * to recognise its own restatement of it.\n *\n * Both passes run the same rules, and each rule fires at most once per fixture\n * — which is why the server, whose single pass IS the union pass, logs exactly\n * one line per (fixture, rule). So the SITE identifies the finding: the file\n * (supplied by the caller, the only thing that knows the file id), the entry\n * index, the severity and the rule. Two findings at one site can differ only\n * in which other fixtures they name, and the union pass's answer to that is\n * the authoritative one — it sees every file — so its wording REPLACES the\n * per-file wording rather than printing beside it, which is what made a\n * repeated path report one duplicate twice.\n *\n * `refs` holds the entry indices this finding names, so a restatement that\n * names the same fixtures can be recognised as adding nothing and leave the\n * per-file wording alone. Matching on the rendered message with its digits\n * masked (a previous scheme) both collapsed distinct findings that happened to\n * mask alike and hid genuine cross-file ones, because a union index and a\n * file-local index mask to the same \"#\".\n *\n * `finding` is the very object in the report, so an upgrade is an in-place\n * rewrite that keeps the finding in the position it was reported at.\n */\ninterface FindingIdentity {\n severity: \"error\" | \"warning\";\n index: number;\n /** The rule id for a ref-carrying finding, else its verbatim message. */\n kind: string;\n /** Entry indices this finding names, within the SAME file. */\n refs: number[];\n /** The finding as it sits in the file's report, for an in-place upgrade. */\n finding: Finding;\n}\n\n/** The indices a ref names, in a fixed order, for identity purposes. */\nfunction refIndices(ref: ValidationRef): number[] {\n return ref.rule === \"duplicate-user-message\"\n ? [ref.shadows]\n : [ref.shadowsFrom, ref.shadowsThrough];\n}\n\n/**\n * A path argument that names a remote source rather than a local file: any\n * `scheme://` prefix, not just http(s), so `ftp://` and friends get the same\n * explanation instead of a filesystem error. A Windows drive letter (\"C:\\\\x\")\n * has no `//` and is not matched.\n */\nconst REMOTE_SOURCE = /^[a-z][a-z0-9+.-]*:\\/\\//i;\n\n/** Render an unknown thrown value as a one-line message. */\nfunction errText(err: unknown): string {\n return err instanceof Error ? err.message : String(err);\n}\n\n/**\n * Record why a file produced no report at all. A fatal is ONE fact, so it is\n * written once, here, into both shapes that report it: `fatal`, which names\n * the reason, and `errors`, which is what every tally — the human summary\n * line, the `--json` consumer counting `files[].errors` — actually counts.\n * Set on `fatal` alone, the two modes disagreed: stdout said \"1 error(s)\"\n * while the same run's JSON said `\"errors\": []`, so a CI script adding up the\n * JSON saw a clean run that had exited 1.\n *\n * The finding carries no `index`: a fatal is about the file, not an entry\n * (see `Finding.index`).\n */\nfunction setFatal(report: FileReport, message: string): void {\n report.fatal = message;\n report.errors.push({ message });\n}\n\n/** A file that never got as far as a report, with its fatal already recorded. */\nfunction fatalReport(file: string, mention: number, message: string): FileReport {\n const report: FileReport = { file, mention, fixtures: 0, errors: [], warnings: [] };\n setFatal(report, message);\n return report;\n}\n\n/**\n * Logger that routes `warn` AND `error` into caller-supplied sinks instead of\n * the console, so the per-entry conversion diagnostics `entryToFixture` emits\n * land in the report rather than being dropped on the floor — and, under\n * `--json`, never interleave with the JSON document on stdout/stderr.\n * Exported for tests only; it is not part of the package's public API.\n * `info`/`debug` are inert: they are already below this logger's \"warn\" level,\n * and silencing them explicitly keeps future call sites off stdout too.\n */\nexport class CollectingLogger extends Logger {\n private readonly warnSink: (msg: string) => void;\n private readonly errorSink: (msg: string) => void;\n\n constructor(warnSink: (msg: string) => void, errorSink: (msg: string) => void) {\n super(\"warn\");\n this.warnSink = warnSink;\n this.errorSink = errorSink;\n }\n\n /**\n * Serialise exactly as the sink this logger replaces does — byte for byte.\n * `Logger` writes `console.warn(\"[aimock]\", ...args)`, and console's\n * formatting IS `util.format`, so the call it makes is\n * `format(\"[aimock]\", ...args)`: `\"[aimock]\"` is the FORMAT STRING and holds\n * no specifiers, which is what makes a caller's own `%s`/`%d` inert and its\n * arguments plain trailing values. Calling `format(...args)` instead\n * promoted the caller's message into the format slot — so a message that\n * happened to contain `%s` (a fixture path, say) swallowed the next\n * argument — and dropped the `[aimock]` prefix, leaving the report and the\n * server's own stderr disagreeing about the same diagnostic.\n * `util.inspect` still renders object arguments readably, which is what\n * `String(a)` could not do.\n */\n private static format(args: unknown[]): string {\n return format(\"[aimock]\", ...args);\n }\n\n override warn(...args: unknown[]): void {\n this.warnSink(CollectingLogger.format(args));\n }\n\n override error(...args: unknown[]): void {\n this.errorSink(CollectingLogger.format(args));\n }\n\n override info(...args: unknown[]): void {\n void args; /* suppressed: never write to stdout from inside a --json run */\n }\n\n override debug(...args: unknown[]): void {\n void args; /* suppressed: never write to stdout from inside a --json run */\n }\n}\n\n/**\n * One product of the walk: a `*.json` file to validate, or — with `fatal`\n * set — a path that could not be walked, reported as its own per-file error,\n * or — with `skipped` set — a `*.json` file the walk found that is not a\n * fixture file at all and is passed over (see `nonFixtureSkipReason`).\n */\ninterface CollectEntry {\n file: string;\n fatal?: string;\n skipped?: string;\n}\n\n/**\n * Why a `*.json` file the WALK turned up is not a fixture file, or\n * `undefined` if it is one (or if the question cannot be answered cheaply).\n *\n * A directory is not a fixture directory: this package ships `fixtures/`\n * holding both fixture files and eight aimock CONFIG files — the six\n * `fixtures/examples/**\\/*-config.json`, `full-suite.json`, and\n * `agui/agui-text-response.json`, whose AG-UI fixtures sit NESTED under\n * `agui` where `--fixtures` cannot see them — which is why the documented\n * `aimock validate ./fixtures/` exited 1 on the package's own tree. The\n * server has the same mixture to deal with and passes over it:\n * `loadFixtureFile` warns \"Missing or invalid \\\"fixtures\\\" array\", returns no\n * fixtures, and startup continues. This mirrors that skip, so a walked\n * directory reports on the fixture files in it and says what it passed over.\n *\n * Non-fixture means the file carries no top-level `fixtures` key at all (or\n * is an aimock config). A `fixtures` key that is present but not an array is\n * a MALFORMED fixture file and still fails the run, as the loader would refuse\n * it. The skip applies ONLY to files the walk discovered — a path the caller named explicitly is still validated, and a\n * wrong-shape file there still fails the run, because naming a file is\n * asking about that file.\n *\n * A file carrying an aimock config section (`configSections`) is named as\n * what it is rather than by what it lacks, because \"not a fixture file\" on\n * its own is read as \"holds no fixtures\" — false of `agui`-sectioned configs,\n * which hold AG-UI fixtures that `aimock --config`, not `--fixtures`, serves.\n *\n * Unreadable and unparseable are deliberately NOT skips. Returning\n * `undefined` for them hands the file to `validateOneFile`, which reports the\n * real read error or JSON syntax error — a defect in any `*.json` file, and\n * one no config file has an excuse for either.\n */\nfunction nonFixtureSkipReason(file: string): string | undefined {\n let parsed: unknown;\n try {\n parsed = JSON.parse(readFileSync(file, \"utf-8\")) as unknown;\n } catch {\n return undefined;\n }\n const asConfig = aimockConfigDescription(parsed);\n if (asConfig !== undefined) return `not a fixture file — ${asConfig}`;\n // A present `fixtures` key of the wrong shape is a MALFORMED fixture file,\n // not a non-fixture file: hand it to `validateOneFile`, which fails it the\n // way the same file fails when named.\n if (typeof parsed === \"object\" && parsed !== null && \"fixtures\" in parsed) return undefined;\n return 'not a fixture file — no top-level \"fixtures\" key';\n}\n\n/**\n * The mock sections of `AimockConfig` (config-loader.ts). An object carrying\n * any of them is an aimock config file: `aimock --config <path>` starts a\n * server from it, while `--fixtures <path>` loads nothing from it.\n */\nconst CONFIG_SECTIONS = [\"llm\", \"mcp\", \"a2a\", \"agui\", \"vector\", \"services\"] as const;\n\n/**\n * What a parsed-but-not-fixture-shaped JSON document IS, when it is an aimock\n * config file, or `undefined` when it is not one. Used by both the walk's skip\n * note and the fatal a NAMED file of that shape gets, so the two surfaces\n * describe the same file the same way. The sections found are listed, so an\n * `agui` config is never described as a file holding no fixtures — it holds\n * AG-UI ones, nested where the fixture loader does not look.\n */\nfunction aimockConfigDescription(parsed: unknown): string | undefined {\n if (typeof parsed !== \"object\" || parsed === null) return undefined;\n const doc = parsed as Record<string, unknown>;\n const sections = CONFIG_SECTIONS.filter((key) => typeof doc[key] === \"object\" && doc[key]);\n if (sections.length === 0) return undefined;\n return `an aimock config file (${sections.join(\", \")}), which \"aimock --config\" serves and \"--fixtures\" does not load`;\n}\n\n/**\n * Collect `*.json` files under `dirPath`, recursing into subdirectories.\n * Mirrors `loadFixturesFromDir`: within a directory the `*.json` files come\n * first in sorted order, then its subdirectories in sorted order, so the\n * reported order matches the server's load order, and the same stat-error\n * handling — a vanished entry (ENOENT) is skipped silently, every other\n * failure is surfaced. Unlike the loader, which only warns, each surfaced\n * failure becomes a `fatal` entry that fails the run, and it is emitted in\n * walk position (an unreadable subdirectory is reported where that\n * subdirectory was reached, not after every file in the tree) so the report\n * reads in encounter order. Entries that are neither directories nor\n * `*.json` files are ignored, as they are by the loader.\n *\n * `ancestors` holds the real paths of the directories this call is nested\n * inside. A directory whose real path is one of them closes a symlink cycle\n * and is reported instead of walked; the server has no such guard and\n * recurses through the cycle until the kernel returns ELOOP. A repeat visit\n * that is NOT an ancestor — `root/link -> root/sub` alongside `root/sub` — is\n * no cycle: it is walked again, because the server walks it again and loads\n * those fixtures twice.\n *\n * A `*.json` entry that is not a regular file (a FIFO, a socket, a device)\n * is a `fatal` entry rather than a target, because reading it is what the\n * containment promise above cannot keep: `readFileSync` on a FIFO with no\n * writer blocks forever, as it does on the server.\n */\nfunction collectJsonFiles(\n dirPath: string,\n ancestors: string[],\n out: CollectEntry[] = [],\n): CollectEntry[] {\n let realPath: string;\n try {\n realPath = realpathSync(dirPath);\n } catch (err) {\n out.push({ file: dirPath, fatal: `Could not resolve directory: ${errText(err)}` });\n return out;\n }\n if (ancestors.includes(realPath)) {\n out.push({\n file: dirPath,\n fatal: `Symlink cycle detected: already walking ${realPath}`,\n });\n return out;\n }\n\n let names: string[];\n try {\n names = readdirSync(dirPath);\n } catch (err) {\n out.push({ file: dirPath, fatal: `Could not read directory: ${errText(err)}` });\n return out;\n }\n\n // Sort the whole entry list, not just the `*.json` names, so a stat failure\n // or a non-regular `*.json` lands at a deterministic place in the report\n // whatever order the filesystem hands entries back in.\n const subdirs: string[] = [];\n for (const name of [...names].sort()) {\n const fullPath = join(dirPath, name);\n let stats: Stats;\n try {\n stats = statSync(fullPath);\n } catch (err) {\n if ((err as NodeJS.ErrnoException).code !== \"ENOENT\") {\n out.push({ file: fullPath, fatal: `Could not stat: ${errText(err)}` });\n }\n continue;\n }\n if (stats.isDirectory()) subdirs.push(name);\n else if (!name.endsWith(\".json\")) continue;\n else if (stats.isFile()) {\n const skipped = nonFixtureSkipReason(fullPath);\n out.push(skipped === undefined ? { file: fullPath } : { file: fullPath, skipped });\n } else out.push({ file: fullPath, fatal: \"Not a regular file\" });\n }\n\n const chain = [...ancestors, realPath];\n for (const sub of subdirs) collectJsonFiles(join(dirPath, sub), chain, out);\n return out;\n}\n\n/** The entry shape the loader and the server both assume, quoted verbatim. */\nconst ENTRY_SHAPE = 'every entry needs { \"match\": { ... }, \"response\": { ... } }';\n\n/** Name a value the way a fixture author would read it back in their JSON. */\nfunction gotText(value: unknown): string {\n if (value === null) return \"null\";\n if (value === undefined) return \"absent\";\n if (Array.isArray(value)) return \"an array\";\n return `a ${typeof value}`;\n}\n\n/**\n * A shape defect in a raw fixture entry, classified BEFORE conversion.\n *\n * `convertible` is what the SERVER does with the same entry, and it is the\n * whole reason this is a classifier and not a catch handler. `entryToFixture`\n * only reads `entry.match.userMessage` and friends, so it throws for exactly\n * two shapes: a non-object `entry`, and an `entry.match` that is null or\n * absent. A `match` that is a STRING, a NUMBER, a BOOLEAN or an ARRAY has no\n * such properties either, but reading them is perfectly legal — every field\n * comes back `undefined`, the entry converts into an EMPTY match, and an empty\n * match is a catch-all that answers every request. Classifying after the throw\n * therefore never saw that case at all, and `validate` printed `OK`.\n */\ntype EntryDefect =\n /** `entryToFixture` would throw: report it and skip the entry. */\n | { severity: \"error\"; convertible: false; why: string }\n /** Converts, but not into the fixture the author meant: report and convert. */\n | { severity: \"warning\"; convertible: true; why: string };\n\n/**\n * Classify a raw entry's shape, or return `undefined` when nothing about it is\n * worth saying. Runs before `entryToFixture`, so every branch below is driven\n * by the entry itself rather than by whichever exception the conversion\n * happened to throw.\n *\n * A missing or non-object `response` is deliberately NOT classified here: the\n * conversion accepts it (`{ ...undefined }` is `{}`), the server accepts it,\n * and the `validateFixtures` rules already reject it by name — \"response is\n * not a recognized type (must have content, toolCalls, ...)\" — which is the\n * more actionable message of the two. Saying it here as well would report one\n * defect twice. It is still named alongside `match` when `match` is fatal,\n * because then the rules never run on the entry at all and an author fixing\n * `{}` would otherwise need one round trip per missing key.\n */\nfunction classifyEntry(entry: unknown): EntryDefect | undefined {\n if (entry === null || typeof entry !== \"object\" || Array.isArray(entry)) {\n return {\n severity: \"error\",\n convertible: false,\n why: `entry is ${gotText(entry)}, expected an object — ${ENTRY_SHAPE}`,\n };\n }\n const match = (entry as Record<string, unknown>).match;\n if (match === null || match === undefined) {\n const missing = ([\"match\", \"response\"] as const).filter((key) => {\n const value = (entry as Record<string, unknown>)[key];\n return value === null || typeof value !== \"object\" || Array.isArray(value);\n });\n return {\n severity: \"error\",\n convertible: false,\n why: `missing or non-object ${missing.map((k) => `\"${k}\"`).join(\" and \")} — ${ENTRY_SHAPE}`,\n };\n }\n if (typeof match !== \"object\" || Array.isArray(match)) {\n return {\n severity: \"warning\",\n convertible: true,\n why:\n `\"match\" is ${gotText(match)}, not an object — every match field reads as ` +\n `undefined, so this entry is an empty match: a CATCH-ALL answering every ` +\n `request. The server loads it the same way, so this warns rather than ` +\n `errors; --strict fails on it. ${ENTRY_SHAPE}`,\n };\n }\n return undefined;\n}\n\n/**\n * The backstop for an entry `classifyEntry` passed and `entryToFixture` threw\n * on anyway (a `toolCalls: [null]`, say, which `normalizeResponse` walks).\n * Which entry it was is the actionable half, so that leads; the raw thrown\n * text follows as `detail`, which `--json` carries as its own field and the\n * human report prints on an indented continuation line. It used to say\n * `see \"detail\"`, naming a field only the `--json` report has — in human mode\n * that pointed at nothing at all.\n */\nfunction unexpectedEntryFailure(index: number, err: unknown): Finding {\n return {\n index,\n message: `Invalid fixture entry #${index}: could not be converted to a fixture`,\n detail: errText(err),\n };\n}\n\n/**\n * Validate one fixture file into a report. Every failure mode — unreadable,\n * unparseable, wrong shape, or a single malformed entry — is recorded in the\n * returned report rather than thrown.\n *\n * The converted fixtures are handed back alongside the report, with\n * `indices[i]` giving the entry index `fixtures[i]` was converted from, so the\n * cross-file union pass can append them and map its findings back to a file\n * and an entry index.\n */\nfunction validateOneFile(\n file: string,\n mention: number,\n): {\n report: FileReport;\n fixtures: Fixture[];\n indices: number[];\n identities: FindingIdentity[];\n runErrors: string[];\n} {\n const report: FileReport = { file, mention, fixtures: 0, errors: [], warnings: [] };\n const fixtures: Fixture[] = [];\n const sourceIndex: number[] = [];\n // Findings that belong to no entry because an index could not be resolved.\n // They are raised at run level rather than filed against a guessed entry.\n const runErrors: string[] = [];\n // Identities of the findings the RULES produced (not the conversion\n // diagnostics, which the union pass cannot produce and so cannot repeat).\n const identities: FindingIdentity[] = [];\n\n let raw: string;\n try {\n raw = readFileSync(file, \"utf-8\");\n } catch (err) {\n setFatal(report, `Could not read file: ${errText(err)}`);\n return { report, fixtures, indices: sourceIndex, identities, runErrors };\n }\n let parsed: unknown;\n try {\n parsed = JSON.parse(raw) as unknown;\n } catch (err) {\n setFatal(report, `Invalid JSON: ${errText(err)}`);\n return { report, fixtures, indices: sourceIndex, identities, runErrors };\n }\n if (\n typeof parsed !== \"object\" ||\n parsed === null ||\n !Array.isArray((parsed as { fixtures?: unknown }).fixtures)\n ) {\n // Naming a file is asking about that file, so the wrong shape is still\n // fatal — the loader refuses it too. Say WHICH wrong shape when the file\n // is an aimock config, so the answer names the file for what it is rather\n // than implying it holds nothing.\n const asConfig = aimockConfigDescription(parsed);\n setFatal(\n report,\n asConfig === undefined\n ? 'Missing or invalid \"fixtures\" array (expected { \"fixtures\": [...] })'\n : `Missing or invalid \"fixtures\" array (expected { \"fixtures\": [...] }) — this is ${asConfig}`,\n );\n return { report, fixtures, indices: sourceIndex, identities, runErrors };\n }\n\n // Convert the entries we already parsed rather than re-reading the file,\n // and give the conversion a logger so its per-entry diagnostics are\n // attributed to the entry that produced them instead of being discarded.\n let entryIndex = 0;\n const entryLogger = new CollectingLogger(\n (message) => report.warnings.push({ index: entryIndex, message }),\n (message) => report.errors.push({ index: entryIndex, message }),\n );\n\n // Classify each entry's shape FIRST, then convert. A `{}`, `null` or\n // match-less entry makes `entryToFixture` throw, so it is reported and\n // skipped; a convertible-but-wrong entry (a non-object `match`) is reported\n // and still converted, so the rules go on seeing exactly the fixture array\n // the server would build. The `try` stays as a backstop for a throw the\n // classifier did not predict. Which entry each converted fixture came from\n // is remembered so `validateFixtures` indices stay entry indices.\n const entries = (parsed as FixtureFile).fixtures;\n for (let index = 0; index < entries.length; index++) {\n entryIndex = index;\n const defect = classifyEntry(entries[index]);\n if (defect !== undefined) {\n const label = defect.severity === \"error\" ? \"Invalid fixture entry\" : \"Fixture entry\";\n const finding: Finding = { index, message: `${label} #${index}: ${defect.why}` };\n if (!defect.convertible) {\n report.errors.push(finding);\n continue;\n }\n report.warnings.push(finding);\n }\n try {\n fixtures.push(entryToFixture(entries[index], entryLogger));\n sourceIndex.push(index);\n } catch (err) {\n report.errors.push(unexpectedEntryFailure(index, err));\n }\n }\n\n report.fixtures = fixtures.length;\n // `validateFixtures` reports against the COMPACTED array, which skips the\n // entries that failed to convert. The finding's own index is mapped back to\n // an entry index — and so are the indices its message NAMES, or one line\n // would print two different index spaces: \"#2 ... shadows fixture 0\" where\n // \"#2\" is an entry and \"0\" is a compacted slot.\n // `?? fixtureIndex` here USED to mask an unresolvable index by printing a\n // compacted slot as if it were an entry index — two different index spaces\n // in one line, with nothing to say so. It is unreachable while `sourceIndex`\n // is pushed in lockstep with `fixtures`, so if it ever fires the run is\n // wrong, not merely imprecise: raise it at run level (which fails the run)\n // instead of quietly printing a number that means something else.\n const unresolved = new Set<number>();\n const toEntryIndex = (fixtureIndex: number): number => {\n const entry = sourceIndex[fixtureIndex];\n if (entry !== undefined) return entry;\n if (!unresolved.has(fixtureIndex)) {\n unresolved.add(fixtureIndex);\n runErrors.push(\n `finding for fixture index ${fixtureIndex}, which maps to no entry in this file`,\n );\n }\n return fixtureIndex;\n };\n try {\n for (const r of validateFixtures(fixtures)) {\n const index = toEntryIndex(r.fixtureIndex);\n const refs = r.ref === undefined ? [] : refIndices(r.ref).map(toEntryIndex);\n const message =\n r.ref === undefined\n ? r.message\n : renderValidationRef(r.ref, (i) => String(toEntryIndex(i)));\n const finding: Finding = { index, message };\n identities.push({\n severity: r.severity,\n index,\n kind: r.ref === undefined ? `message:${r.message}` : r.ref.rule,\n refs,\n finding,\n });\n if (r.severity === \"error\") report.errors.push(finding);\n else report.warnings.push(finding);\n }\n } catch (err) {\n // File-level: the validator walks every entry, so which one tripped it is\n // unknown. Filing it at #0 would accuse the file's first fixture, which is\n // very likely innocent, so this finding carries no entry index at all.\n report.errors.push({ message: `Validation failed for this file: ${errText(err)}` });\n }\n return { report, fixtures, indices: sourceIndex, identities, runErrors };\n}\n\n/**\n * `#<index> ` for a finding that belongs to one entry; empty for a file-level\n * finding, which names no entry (see Finding.index). Shared by the human lines\n * and the failure summary so a reader of either can locate the same entry.\n */\nconst findingAt = (f: Finding): string => (f.index === undefined ? \"\" : `#${f.index} `);\n\n/**\n * One-line reason a run failed, for the stderr side of `--json` output.\n *\n * It NAMES the failure first — the file and the finding that caused the\n * non-zero exit — and only then totals what else failed, because the line is\n * often the single line a CI log shows. Leading with a fixed list of counts\n * put two zeros (\"0 unreadable/invalid file(s), 0 file(s) with errors\") in\n * front of every strict-warning failure, where the reader has to get past the\n * things that did NOT happen to reach the one that did. Counts that are zero\n * are omitted for the same reason, and a file whose fatal is already the lead\n * is counted as unreadable/invalid only, not a second time under \"with\n * errors\" — its fatal now rides in `errors` as well (see `setFatal`).\n */\nfunction summarizeFailure(\n reports: FileReport[],\n strict: boolean,\n runErrors: string[],\n name: (r: FileReport) => string,\n): string {\n const fatalFiles = reports.filter((r) => r.fatal !== undefined);\n const erroredFiles = reports.filter((r) => r.fatal === undefined && r.errors.length > 0);\n const warnedFiles = reports.filter((r) => r.warnings.length > 0);\n\n // First failure in report order: a fatal file, else a file with errors, else\n // a run-level error, else (under --strict) the warning that failed the run.\n let lead: string;\n if (fatalFiles[0] !== undefined) lead = `${name(fatalFiles[0])}: ${fatalFiles[0].fatal}`;\n else if (erroredFiles[0] !== undefined)\n lead = `${name(erroredFiles[0])}: ${findingAt(erroredFiles[0].errors[0])}${erroredFiles[0].errors[0].message}`;\n else if (runErrors[0] !== undefined) lead = runErrors[0];\n else if (warnedFiles[0] !== undefined)\n lead = `${name(warnedFiles[0])}: ${findingAt(warnedFiles[0].warnings[0])}${warnedFiles[0].warnings[0].message} (--strict)`;\n /* v8 ignore next -- a failed run always has one of the four above */ else\n lead = \"the run failed\";\n\n const counts: string[] = [];\n if (fatalFiles.length > 0) counts.push(`${fatalFiles.length} unreadable/invalid file(s)`);\n if (erroredFiles.length > 0) counts.push(`${erroredFiles.length} file(s) with errors`);\n // A run-level error (no fixtures loaded from any input) belongs to no single\n // file, so it is counted on its own.\n if (runErrors.length > 0) counts.push(`${runErrors.length} run-level error(s)`);\n if (strict && warnedFiles.length > 0)\n counts.push(`${warnedFiles.length} file(s) with warnings (--strict)`);\n\n return `Error: fixture validation failed — ${lead} [${counts.join(\", \")}].`;\n}\n\nexport function runValidateCli(deps: ValidateCliDeps = {}): void {\n const argv = deps.argv ?? process.argv.slice(2);\n const log = deps.log ?? console.log.bind(console);\n const logError = deps.logError ?? console.error.bind(console);\n const exit = deps.exit ?? process.exit.bind(process);\n\n /**\n * Is `flag` present as an OPTION in argv — i.e. before the `--` terminator,\n * after which every argument is a path? Needed because a usage error can be\n * a `parseArgs` throw, which leaves no parsed `values` to consult, and the\n * output mode still has to be honoured. `--flag=value` counts: these are all\n * boolean options, so giving one a value IS the usage error being reported,\n * and matching the bare token alone answered \"no `--json` here\" for the one\n * argv that most obviously asked for JSON — leaving stdout empty.\n */\n const flagInArgv = (flag: string): boolean => {\n for (const arg of argv) {\n if (arg === \"--\") return false;\n if (arg === flag || arg.startsWith(`${flag}=`)) return true;\n }\n return false;\n };\n\n /**\n * A usage error, in whichever mode the argv asked for. `--json` promises\n * \"one document on stdout\" (README), and a run that fails on its own argv is\n * still a run: emitting nothing there handed a CI job parsing stdout an\n * empty buffer — a JSON parse error standing in for a perfectly ordinary\n * \"no paths given\". The document reports the usage error the same way a\n * run-level error is reported, so one consumer shape reads both. The\n * human-readable reason plus the help text stay on stderr in both modes, so\n * `--json` stdout is still the document alone.\n */\n const usageError = (message: string): void => {\n logError(`Error: ${message}\\n\\n${VALIDATE_HELP}`);\n if (flagInArgv(\"--json\")) {\n log(\n JSON.stringify(\n {\n strict: flagInArgv(\"--strict\"),\n failed: true,\n files: [],\n run: { fixtures: 0, errors: [message] },\n },\n null,\n 2,\n ),\n );\n }\n exit(1);\n };\n\n // Parse with node:util parseArgs, like the main CLI (src/cli.ts) and the\n // server path in src/aimock-cli.ts: strict mode rejects unknown options and\n // the \"--\" terminator falls out for free, so a fixture path that begins with\n // \"-\" is still reachable.\n let values: { strict?: boolean; json?: boolean; help?: boolean };\n let paths: string[];\n try {\n const parsed = parseArgs({\n args: argv,\n options: {\n strict: { type: \"boolean\", default: false },\n json: { type: \"boolean\", default: false },\n help: { type: \"boolean\", short: \"h\", default: false },\n },\n strict: true,\n allowPositionals: true,\n });\n values = parsed.values;\n paths = parsed.positionals;\n } catch (err) {\n /* v8 ignore next -- parseArgs always throws Error subclasses */\n const msg = err instanceof Error ? err.message : String(err);\n usageError(msg);\n return;\n }\n\n if (values.help === true) {\n // `--json --help` still has to leave one JSON document on stdout and\n // nothing else (README, docs): a consumer that forwards its own argv to\n // `validate --json \"$@\"` and pipes stdout to `jq` gets a parse error the\n // moment a user asks for help. The help text is the document's one field.\n log(values.json === true ? JSON.stringify({ help: VALIDATE_HELP }, null, 2) : VALIDATE_HELP);\n exit(0);\n return;\n }\n const strict = values.strict === true;\n const json = values.json === true;\n\n if (paths.length === 0) {\n usageError(\"no fixture paths given.\");\n return;\n }\n\n // Expand directory arguments into their `*.json` members. A missing path is\n // passed through untouched so the read below reports the real per-file\n // error; any other stat failure (ELOOP, EACCES, ...) is its own error, and\n // a path that exists but is neither a directory nor a regular file is\n // refused rather than read (see `collectJsonFiles`).\n //\n // Targets are NOT de-duplicated: the server loads one set of fixtures per\n // `--fixtures` it is given, so a path named twice — or a file named both\n // directly and via its parent directory — is loaded twice there, and the\n // duplicate-`userMessage` findings that produces are exactly what this lint\n // exists to surface. Validating it twice here is what makes the union pass\n // see what the server sees.\n const targets: CollectEntry[] = [];\n for (const path of paths) {\n // A remote source is refused by NAME. `--fixtures <url>` is a runtime\n // feature of the server; this lint reads local paths only, and handing a\n // URL to `statSync` produced a bare ENOENT quoting the URL back — which\n // reads as \"you typed the path wrong\", not \"this input is unsupported\".\n if (REMOTE_SOURCE.test(path)) {\n targets.push({\n file: path,\n fatal:\n \"Remote fixture source: this lint reads local paths only (the server's --fixtures accepts a URL; validate does not) — download the file and validate the local copy\",\n });\n continue;\n }\n let stats: Stats;\n try {\n stats = statSync(path);\n } catch (err) {\n if ((err as NodeJS.ErrnoException).code === \"ENOENT\") targets.push({ file: path });\n else targets.push({ file: path, fatal: `Could not stat: ${errText(err)}` });\n continue;\n }\n if (!stats.isDirectory()) {\n if (stats.isFile()) targets.push({ file: path });\n else targets.push({ file: path, fatal: \"Not a regular file\" });\n continue;\n }\n const entries = collectJsonFiles(path, []);\n // Only an actually-walkable, actually-empty directory is \"no fixtures\n // found\" — an unreadable one reports why it could not be read.\n if (entries.length === 0) {\n targets.push({ file: path, fatal: \"No .json fixture files found in directory\" });\n continue;\n }\n targets.push(...entries);\n }\n\n // How many times each path is mentioned by the expanded target list, and a\n // running count that hands each target its own 1-based mention number. Both\n // are needed before the first report is printed: whether a name is ambiguous\n // is a property of the whole run, not of the file being reported. Both key\n // on the resolved path, not its spelling: a walked entry is `join`-normalised\n // while a named one is verbatim, and `./d/a.json` named beside the walk of\n // `./d` is the same load twice. The spelling is still what gets displayed.\n const mentionKey = (file: string): string => resolve(file);\n const mentionCount = new Map<string, number>();\n for (const target of targets)\n mentionCount.set(mentionKey(target.file), (mentionCount.get(mentionKey(target.file)) ?? 0) + 1);\n const mentionsSoFar = new Map<string, number>();\n const nextMention = (file: string): number => {\n const mention = (mentionsSoFar.get(mentionKey(file)) ?? 0) + 1;\n mentionsSoFar.set(mentionKey(file), mention);\n return mention;\n };\n /**\n * How a file is NAMED in human output and in cross-file references. A path\n * this run loads more than once gets its mention number, because the bare\n * path names all of those loads at once: without it a second load's findings\n * read as a file shadowing itself. A path loaded once is printed unadorned —\n * there is nothing to tell it apart from, and \"[1]\" on every line of an\n * ordinary run is noise. The `--json` report carries `mention` regardless.\n */\n const displayName = (report: FileReport): string =>\n (mentionCount.get(mentionKey(report.file)) ?? 1) > 1\n ? `${report.file}[${report.mention}]`\n : report.file;\n\n const reports: FileReport[] = [];\n // Every successfully-parsed entry, in load order, plus the file and local\n // index each one came from. `validateFixtures` reports by array index, so\n // the union pass below needs this to map its findings back to a file.\n const union: Fixture[] = [];\n const provenance: { report: FileReport; index: number }[] = [];\n // The rule findings the per-file pass already reported, by SITE — the file\n // (by id, so two mentions of one path are two sites), the entry index, the\n // severity and the rule. Each rule fires at most once per fixture in either\n // pass, so a union finding that lands on an occupied site is that same\n // finding seen with every file in view: it never prints a second line, and\n // it rewrites the first one only when it names different fixtures. Keying on\n // the rendered text with digits masked (a previous scheme) instead made every\n // \"shadows fixtures <n>+\" look alike, which both collapsed distinct findings\n // and silently swallowed genuine cross-file ones.\n const reportedBySite = new Map<string, { finding: Finding; refs: string[] }[]>();\n const fileIds = new Map<FileReport, number>();\n // Declared before the per-file loop because that loop contributes to it: an\n // index a file could not resolve belongs to no entry and no file section.\n const runErrors: string[] = [];\n const siteKey = (fileId: number, index: number, severity: string, kind: string): string =>\n [fileId, index, severity, kind].join(\"\\u0000\");\n\n for (const target of targets) {\n // Passed over by the walk: it appears in the report so nothing the walk\n // touched vanishes, but it contributes no fixtures and no findings.\n if (target.skipped !== undefined) {\n reports.push({\n file: target.file,\n mention: nextMention(target.file),\n fixtures: 0,\n errors: [],\n warnings: [],\n skipped: target.skipped,\n });\n continue;\n }\n if (target.fatal !== undefined) {\n reports.push(fatalReport(target.file, nextMention(target.file), target.fatal));\n continue;\n }\n\n let report: FileReport;\n let fixtures: Fixture[] = [];\n let indices: number[] = [];\n let identities: FindingIdentity[] = [];\n let fileRunErrors: string[] = [];\n const mention = nextMention(target.file);\n try {\n ({\n report,\n fixtures,\n indices,\n identities,\n runErrors: fileRunErrors,\n } = validateOneFile(target.file, mention));\n } catch (err) {\n // Backstop: nothing unexpected gets to abandon the remaining files.\n report = fatalReport(target.file, mention, `Validation failed: ${errText(err)}`);\n }\n reports.push(report);\n // These are raised inside `validateOneFile`, which knows the path but not\n // how many times this run loads it, so they are named here instead.\n runErrors.push(...fileRunErrors.map((e) => `${displayName(report)}: ${e}`));\n const fileId = fileIds.size;\n fileIds.set(report, fileId);\n\n // Only RULE findings are registered: a conversion diagnostic or a\n // file-level finding (no entry index) can never be restated by the union\n // pass, so it has no identity here.\n for (const identity of identities) {\n const key = siteKey(fileId, identity.index, identity.severity, identity.kind);\n const atSite = reportedBySite.get(key) ?? [];\n atSite.push({\n finding: identity.finding,\n refs: identity.refs.map((ref) => `${fileId}:${ref}`),\n });\n reportedBySite.set(key, atSite);\n }\n for (let i = 0; i < fixtures.length; i++) {\n // Same masking as `toEntryIndex` above, one level out: `?? i` silently\n // relabelled a union fixture with a compacted slot number. Keep the\n // union aligned with what the server loads (so the cross-file rules see\n // the same array), but fail the run rather than print a wrong index.\n const index = indices[i];\n if (index === undefined) {\n runErrors.push(\n `${displayName(report)}: converted fixture ${i} has no source entry index; ` +\n `cross-file findings against it are reported at its array position`,\n );\n }\n union.push(fixtures[i]);\n provenance.push({ report, index: index ?? i });\n }\n }\n\n // Cross-file pass. The server validates every loaded fixture as one array,\n // so its duplicate-userMessage and catch-all-ordering rules see across\n // files; run per file they never can. Validate the union and keep only the\n // findings the per-file pass did not already make.\n // Set when the run failed because it loaded nothing at all, so the human\n // printer can withhold \"OK\" from the files that loaded nothing — see below.\n let noFixturesLoaded = false;\n // When a file already said WHY it produced no fixtures (unreadable, invalid\n // JSON, wrong shape, a bad entry), the run-level line below would repeat that\n // same cause as a second, causeless error. Report it only when nothing else\n // has; the run still fails either way, on the per-file finding.\n const causeAlreadyReported = reports.some((r) => r.fatal !== undefined || r.errors.length > 0);\n if (union.length === 0) {\n // The server aborts startup on this exact condition\n // (\"No fixtures loaded and validation/strict mode is enabled\"), so a run\n // whose inputs yield nothing — `{\"fixtures\": []}` and nothing else —\n // fails here too. Parsed-but-empty files alongside files that do load\n // fixtures are fine in both places.\n if (!causeAlreadyReported) {\n runErrors.push(\n \"No fixtures loaded from any input — the server aborts startup on this under --validate-on-load/--strict\",\n );\n noFixturesLoaded = true;\n }\n } else {\n // A union index as the reader sees it: the load that owns the fixture —\n // named by path, plus its mention number when that path is loaded more\n // than once — and its index inside that load.\n const describe = (unionIndex: number): string => {\n const origin = provenance[unionIndex];\n return origin === undefined\n ? String(unionIndex)\n : `${displayName(origin.report)} #${origin.index}`;\n };\n // The same index as an identity for de-duplication. An unresolvable one\n // gets its own namespace rather than aliasing onto a real file.\n const targetOf = (unionIndex: number): string => {\n const origin = provenance[unionIndex];\n if (origin === undefined) return `union:${unionIndex}`;\n const fileId = fileIds.get(origin.report);\n return fileId === undefined ? `union:${unionIndex}` : `${fileId}:${origin.index}`;\n };\n // Cross-file wording is rendered from the finding's REF DATA, never by\n // rewriting its prose: `duplicate userMessage` quotes the fixture's own\n // text, and text that itself reads \"fixture 3\" is not an index.\n const renderCrossFile = (ref: ValidationRef): string => {\n if (ref.rule === \"duplicate-user-message\") {\n return `duplicate userMessage '${ref.userMessage}' — shadows fixture ${describe(ref.shadows)}`;\n }\n // Both ends are named because the point of the cross-file pass is that\n // the shadowed range reaches past the catch-all's own file; a bare\n // \"<file> #0+\" would read as if \"+\" were part of the index.\n const head = \"empty match acts as catch-all but is not the last fixture\";\n return ref.shadowsFrom === ref.shadowsThrough\n ? `${head} — shadows ${describe(ref.shadowsFrom)}`\n : `${head} — shadows every later fixture, from ${describe(ref.shadowsFrom)} through ${describe(ref.shadowsThrough)}`;\n };\n // The per-file pass above is wrapped per file; this one is the run's only\n // other call into `validateFixtures`, and an uncontained throw here killed\n // the process with no report at all and a truncated (invalid) `--json`\n // stdout. Contain it as a run-level error so the report is still emitted,\n // is still valid JSON, and the run still exits 1.\n try {\n for (const r of validateFixtures(union)) {\n const origin = provenance[r.fixtureIndex];\n if (origin === undefined) {\n // Unreachable while `provenance` is pushed in lockstep with `union`.\n // A bare `continue` here DROPPED the finding: an error finding could\n // vanish and leave the run exiting 0, reporting a clean bill of\n // health for fixtures the server would refuse. Raise it at run level,\n // as the adjacent unrecognised-file case already does.\n runErrors.push(\n `Cross-file finding for an unknown fixture index ${r.fixtureIndex}: ${r.message}`,\n );\n continue;\n }\n const fileId = fileIds.get(origin.report);\n if (fileId === undefined) {\n // Unreachable while provenance is built from reported files; recorded\n // as a run-level error rather than defaulted onto file 0, which would\n // attribute the finding to an unrelated file.\n runErrors.push(`Cross-file finding for an unrecognised file: ${r.message}`);\n continue;\n }\n const kind = r.ref === undefined ? `message:${r.message}` : r.ref.rule;\n const refTargets = r.ref === undefined ? [] : refIndices(r.ref).map(targetOf);\n const message = `cross-file: ${r.ref === undefined ? r.message : renderCrossFile(r.ref)}`;\n const already = reportedBySite\n .get(siteKey(fileId, origin.index, r.severity, kind))\n ?.shift();\n if (already !== undefined) {\n // The per-file pass already reported this rule against this fixture,\n // and the server would log it once. If the union names the same\n // fixtures it adds nothing and the file-local wording stands; if it\n // names others — a shadow that reaches into another load — that is\n // the fuller truth, so it replaces the wording in place rather than\n // printing a second line about the same fixture.\n const same =\n already.refs.length === refTargets.length &&\n already.refs.every((target, i) => target === refTargets[i]);\n if (!same) already.finding.message = message;\n continue;\n }\n if (r.severity === \"error\") origin.report.errors.push({ index: origin.index, message });\n else origin.report.warnings.push({ index: origin.index, message });\n }\n } catch (err) {\n runErrors.push(`Cross-file validation failed: ${errText(err)}`);\n }\n }\n\n const hasFatal = reports.some((r) => r.fatal !== undefined);\n const hasErrors = reports.some((r) => r.errors.length > 0) || runErrors.length > 0;\n const hasWarnings = reports.some((r) => r.warnings.length > 0);\n const failed = hasFatal || hasErrors || (strict && hasWarnings);\n\n // Anything that CAUSES the non-zero exit must reach stderr, or a CI log shows\n // a failure with no reason. Warnings are informational by default and are the\n // cause under --strict, so they follow the exit they produce.\n const logWarning = strict ? logError : log;\n\n if (json) {\n log(\n JSON.stringify(\n { strict, failed, files: reports, run: { fixtures: union.length, errors: runErrors } },\n null,\n 2,\n ),\n );\n // Keep stdout to the JSON document alone, but still say on stderr why the\n // run failed.\n if (failed) logError(summarizeFailure(reports, strict, runErrors, displayName));\n } else {\n const at = findingAt;\n // `detail` (the raw thrown text behind a rephrased message) is a field of\n // the `--json` document, so human mode used to lose it entirely. Print it\n // on an indented continuation line under its own finding, where it reads\n // as belonging to that line rather than as another finding.\n const emitDetail = (f: Finding, sink: (msg: string) => void): void => {\n if (f.detail !== undefined) sink(` ${f.detail}`);\n };\n for (const r of reports) {\n const name = displayName(r);\n // A file the WALK turned up that is not fixture-shaped is passed over,\n // exactly as the server passes over it, and says so on its one line.\n if (r.skipped !== undefined) {\n log(`${name}: skipped (${r.skipped})`);\n continue;\n }\n // A fatal file needs no branch of its own: its reason rides in `errors`\n // (see `setFatal`), so the loop below prints the same `[error]` line and\n // the counting summary below tallies it — the stdout count and the\n // `--json` `errors` array can no longer drift apart. Every file still\n // gets exactly one summary line on stdout (README), including this one:\n // a reader tallying stdout must not silently lose a file.\n for (const e of r.errors) {\n logError(`${name}: [error] ${at(e)}${e.message}`);\n emitDetail(e, logError);\n }\n for (const w of r.warnings) {\n logWarning(`${name}: [warning] ${at(w)}${w.message}`);\n emitDetail(w, logWarning);\n }\n if (r.errors.length === 0 && r.warnings.length === 0) {\n // \"OK\" is a claim about the RUN as well as the file: a reader tallying\n // stdout on a run that failed would otherwise read \"OK\" against the very\n // file that loaded none, on an exit of 1. Keying this on the RUN-level\n // \"no fixtures loaded\" error was not enough — that error is withheld as\n // soon as another file reports a cause, which is exactly when a failed\n // run still has a zero-fixture file to print.\n if (r.fixtures === 0 && failed) {\n const why = noFixturesLoaded ? \"see the run-level error\" : \"the run failed elsewhere\";\n log(`${name}: ${r.fixtures} fixture(s), loaded nothing — ${why}`);\n } else {\n log(`${name}: OK (${r.fixtures} fixture(s))`);\n }\n } else {\n log(\n `${name}: ${r.fixtures} fixture(s), ${r.errors.length} error(s), ${r.warnings.length} warning(s)`,\n );\n }\n }\n // Run-level errors last: they are consequences of (or additions to) the\n // per-file output above, and printing them first put the summary before\n // the detail it summarises.\n for (const e of runErrors) logError(`[error] ${e}`);\n }\n\n exit(failed ? 1 : 0);\n}\n"],"mappings":"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"}
|
package/dist/veo-video.cjs
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
const require_runtime = require('./_virtual/_rolldown/runtime.cjs');
|
|
2
2
|
const require_helpers = require('./helpers.cjs');
|
|
3
|
-
const require_router = require('./router.cjs');
|
|
4
3
|
const require_sse_writer = require('./sse-writer.cjs');
|
|
5
4
|
const require_chaos = require('./chaos.cjs');
|
|
5
|
+
const require_router = require('./router.cjs');
|
|
6
6
|
const require_url = require('./url.cjs');
|
|
7
7
|
const require_recorder = require('./recorder.cjs');
|
|
8
8
|
const require_fal = require('./fal.cjs');
|
|
@@ -192,7 +192,7 @@ async function handleVeoVideoCreate(req, res, raw, model, fixtures, journal, def
|
|
|
192
192
|
journal.incrementFixtureMatchCount(fixture, fixtures, testId);
|
|
193
193
|
defaults.logger.debug(`Fixture matched: ${JSON.stringify(fixture.match).slice(0, 120)}`);
|
|
194
194
|
} else defaults.logger.debug(`No fixture matched for Veo request (model=${model}, msg="${prompt.slice(0, 80)}")`);
|
|
195
|
-
if (require_chaos.
|
|
195
|
+
if (await require_chaos.applyChaosAsync(res, fixture, defaults.chaos, req.headers, req.url, journal, {
|
|
196
196
|
method,
|
|
197
197
|
path,
|
|
198
198
|
headers: require_helpers.flattenHeaders(req.headers),
|
|
@@ -247,7 +247,7 @@ async function handleVeoVideoCreate(req, res, raw, model, fixtures, journal, def
|
|
|
247
247
|
const worldGeneration = jobs.generation;
|
|
248
248
|
const response = await require_helpers.resolveResponse(fixture, syntheticReq);
|
|
249
249
|
if (require_helpers.isErrorResponse(response)) {
|
|
250
|
-
if (
|
|
250
|
+
if (require_chaos.responseGone(res)) return;
|
|
251
251
|
const status = response.status ?? 500;
|
|
252
252
|
journal.add({
|
|
253
253
|
method,
|
|
@@ -263,7 +263,7 @@ async function handleVeoVideoCreate(req, res, raw, model, fixtures, journal, def
|
|
|
263
263
|
return;
|
|
264
264
|
}
|
|
265
265
|
if (!require_helpers.isVideoResponse(response)) {
|
|
266
|
-
if (
|
|
266
|
+
if (require_chaos.responseGone(res)) return;
|
|
267
267
|
journal.add({
|
|
268
268
|
method,
|
|
269
269
|
path,
|
|
@@ -277,7 +277,7 @@ async function handleVeoVideoCreate(req, res, raw, model, fixtures, journal, def
|
|
|
277
277
|
require_sse_writer.writeErrorResponse(res, 500, geminiError(500, "Fixture response is not a video type", "INTERNAL"));
|
|
278
278
|
return;
|
|
279
279
|
}
|
|
280
|
-
if (
|
|
280
|
+
if (require_chaos.responseGone(res)) return;
|
|
281
281
|
journal.add({
|
|
282
282
|
method,
|
|
283
283
|
path,
|
|
@@ -312,7 +312,7 @@ async function handleVeoVideoStatus(req, res, operationName, fixtures, journal,
|
|
|
312
312
|
setCorsHeaders(res);
|
|
313
313
|
const path = req.url ?? `/v1beta/${operationName}`;
|
|
314
314
|
const method = req.method ?? "GET";
|
|
315
|
-
if (require_chaos.
|
|
315
|
+
if (await require_chaos.applyChaosAsync(res, null, defaults.chaos, req.headers, req.url, journal, {
|
|
316
316
|
method,
|
|
317
317
|
path,
|
|
318
318
|
headers: require_helpers.flattenHeaders(req.headers),
|
|
@@ -367,7 +367,7 @@ async function handleVeoVideoStatus(req, res, operationName, fixtures, journal,
|
|
|
367
367
|
});
|
|
368
368
|
return;
|
|
369
369
|
}
|
|
370
|
-
if (
|
|
370
|
+
if (require_chaos.responseGone(res)) return;
|
|
371
371
|
advanceVeoJob(job);
|
|
372
372
|
jobs.set(key, job);
|
|
373
373
|
journal.add({
|
|
@@ -421,7 +421,7 @@ async function proxyVeoVideoSubmit(args) {
|
|
|
421
421
|
}
|
|
422
422
|
const proxyError = (msg) => {
|
|
423
423
|
defaults.logger.error(`Veo video submit proxy failed: ${msg}`);
|
|
424
|
-
if (
|
|
424
|
+
if (require_chaos.responseGone(res)) return "handled";
|
|
425
425
|
journal.add({
|
|
426
426
|
method,
|
|
427
427
|
path,
|
|
@@ -471,7 +471,7 @@ async function proxyVeoVideoSubmit(args) {
|
|
|
471
471
|
}
|
|
472
472
|
if (fetched.status === 401 || fetched.status === 403) {
|
|
473
473
|
defaults.logger.warn(`Upstream rejected the Veo video submit (${fetched.status}) — relaying the upstream status`);
|
|
474
|
-
if (
|
|
474
|
+
if (require_chaos.responseGone(res)) return "handled";
|
|
475
475
|
journal.add({
|
|
476
476
|
method,
|
|
477
477
|
path,
|
|
@@ -526,7 +526,7 @@ async function proxyVeoVideoSubmit(args) {
|
|
|
526
526
|
};
|
|
527
527
|
if (jobs.generation === worldGeneration) jobs.set(`${testId}:${operationName}`, job);
|
|
528
528
|
else defaults.logger.warn(`Veo video submit for upstream operation ${upstreamOperationName} completed after a fixtures reset — not inserting the job into the new world (its polls will 404)`);
|
|
529
|
-
if (
|
|
529
|
+
if (require_chaos.responseGone(res)) return "handled";
|
|
530
530
|
journal.add({
|
|
531
531
|
method,
|
|
532
532
|
path,
|
|
@@ -573,7 +573,7 @@ async function proxyVeoVideoRecordPoll(args) {
|
|
|
573
573
|
};
|
|
574
574
|
const proxyError = (msg) => {
|
|
575
575
|
logger.error(`Veo video poll proxy failed: ${msg}`);
|
|
576
|
-
if (
|
|
576
|
+
if (require_chaos.responseGone(res)) return;
|
|
577
577
|
journalProxy(502);
|
|
578
578
|
require_sse_writer.writeErrorResponse(res, 502, JSON.stringify({ error: {
|
|
579
579
|
message: `Proxy to upstream failed: ${msg}`,
|
|
@@ -607,7 +607,7 @@ async function proxyVeoVideoRecordPoll(args) {
|
|
|
607
607
|
}
|
|
608
608
|
if (fetched.status === 401 || fetched.status === 403) {
|
|
609
609
|
logger.warn(`Upstream rejected the Veo status poll for operation ${job.upstreamOperationName} (${fetched.status}) — relaying the upstream status`);
|
|
610
|
-
if (
|
|
610
|
+
if (require_chaos.responseGone(res)) return;
|
|
611
611
|
journalProxy(fetched.status);
|
|
612
612
|
res.writeHead(fetched.status, { "Content-Type": fetched.contentType ?? "application/json" });
|
|
613
613
|
res.end(fetched.text);
|
|
@@ -637,7 +637,7 @@ async function proxyVeoVideoRecordPoll(args) {
|
|
|
637
637
|
name: job.operationName
|
|
638
638
|
};
|
|
639
639
|
const relayJson = () => {
|
|
640
|
-
if (
|
|
640
|
+
if (require_chaos.responseGone(res)) return;
|
|
641
641
|
journalProxy(200);
|
|
642
642
|
res.writeHead(200, { "Content-Type": "application/json" });
|
|
643
643
|
res.end(JSON.stringify(relayBody));
|
package/dist/veo-video.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"veo-video.cjs","names":["flattenHeaders","getContext","getTestId","matchFixtureDiagnostic","applyChaos","resolveStrictMode","strictNoMatchMessage","strictNoMatchLogLine","strictOverrideField","resolveResponse","isErrorResponse","serializeErrorResponse","isVideoResponse","crypto","resolveProgression","testIdSuffix","resolveUpstreamUrl","prepareEgressHeaders","upstreamTimeoutSignal","readEnvelopeText","buildFixtureMatch","persistFixture","sanitizeHeaderValue"],"sources":["../src/veo-video.ts"],"sourcesContent":["import type * as http from \"node:http\";\nimport crypto from \"node:crypto\";\nimport type {\n ChatCompletionRequest,\n Fixture,\n HandlerDefaults,\n RecordConfig,\n VideoResponse,\n} 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 { resolveProgression } from \"./fal.js\";\nimport {\n buildFixtureMatch,\n prepareEgressHeaders,\n persistFixture,\n sanitizeHeaderValue,\n} from \"./recorder.js\";\nimport { resolveUpstreamUrl } from \"./url.js\";\nimport { readEnvelopeText, testIdSuffix, upstreamTimeoutSignal } from \"./video-proxy-shared.js\";\n\n/**\n * Google Veo async video lifecycle mock. Submit\n * `POST /v1beta/models/{model}:predictLongRunning` returns\n * `{ name: \"operations/...\" }`; status `GET /v1beta/operations/{name}` polls\n * `done:false → done:true`. The Files-API `uri` is served AS-IS — aimock does\n * NOT proxy or capture video bytes. With `record.providers.veo` configured an\n * unmatched submit becomes a live interactive proxy (submit + poll forwarded\n * 1:1, eager fixture capture on terminal status). Strict mode still wins.\n *\n * ASSUMED Veo create body shape (the `@google/genai` :predictLongRunning\n * envelope from issue #278): `{ instances: [{ prompt, image? }], parameters }`.\n * Match surface is `instances[0].prompt` only.\n */\n\n/** Files-API-shaped placeholder served when a completed fixture omits a uri. */\nconst DEFAULT_VEO_FILES_URI = \"https://generativelanguage.googleapis.com/v1beta/files/placeholder\";\n\n// The `:predictLongRunning` envelope nests the prompt under instances[].\ninterface VeoVideoRequest {\n instances?: Array<{ prompt?: unknown; [key: string]: unknown }>;\n parameters?: Record<string, unknown>;\n [key: string]: unknown;\n}\n\n// ─── VeoVideoJobMap (TTL + bounded) ─────────────────────────────────────────\n\nexport const VEO_VIDEO_MAX_ENTRIES = 10_000;\nconst VEO_VIDEO_TTL_MS = 3_600_000; // 1 hour\n\ntype VeoVideoStatus = \"pending\" | \"in_progress\" | \"completed\" | \"failed\";\n\ninterface VeoVideoReplayJob {\n kind: \"replay\";\n operationName: string;\n status: VeoVideoStatus;\n pollCount: number;\n pollsBeforeInProgress: number;\n pollsBeforeCompleted: number;\n video: VideoResponse[\"video\"];\n /** Latch for the empty-`error` authoring warn on failed polls (once/job). */\n emptyErrorWarned?: boolean;\n}\n\ninterface VeoVideoRecordJob {\n kind: \"record\";\n operationName: string;\n status: VeoVideoStatus;\n upstreamOperationName: string;\n upstreamPollingUrl: string;\n match: Fixture[\"match\"];\n capturing?: boolean;\n}\n\nexport type VeoVideoJob = VeoVideoReplayJob | VeoVideoRecordJob;\n\ninterface VeoVideoEntry {\n job: VeoVideoJob;\n createdAt: number;\n}\n\n/**\n * Per-testId job state for the Veo video handler. Mirrors OpenRouterVideoJobMap\n * (openrouter-video.ts): lazy TTL eviction on `get`, FIFO eviction of the\n * oldest entries on `set` when over capacity, delete-before-set TTL refresh,\n * monotonic world-generation counter for reset-mid-flight detection, no\n * background sweep timer. Keys are `${testId}:${operationName}`.\n */\nexport class VeoVideoJobMap {\n private readonly entries = new Map<string, VeoVideoEntry>();\n private worldGeneration = 0;\n\n get generation(): number {\n return this.worldGeneration;\n }\n\n get(key: string): VeoVideoJob | undefined {\n const entry = this.entries.get(key);\n if (!entry) return undefined;\n if (Date.now() - entry.createdAt > VEO_VIDEO_TTL_MS) {\n this.entries.delete(key);\n return undefined;\n }\n return entry.job;\n }\n\n set(key: string, job: VeoVideoJob): void {\n this.entries.delete(key);\n this.entries.set(key, { job, createdAt: Date.now() });\n if (this.entries.size > VEO_VIDEO_MAX_ENTRIES) {\n const excess = this.entries.size - VEO_VIDEO_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 this.worldGeneration++;\n }\n\n get size(): number {\n return this.entries.size;\n }\n}\n\n// ─── Job progression ────────────────────────────────────────────────────────\n\n/**\n * Maps the fixture's terminal video status onto the job lifecycle. Anything\n * that is not \"failed\" — including a fixture authored as \"processing\" — is\n * treated as completed, since this surface always drives jobs to a terminal\n * state.\n */\nfunction terminalStatus(job: VeoVideoReplayJob): VeoVideoStatus {\n return job.video.status === \"failed\" ? \"failed\" : \"completed\";\n}\n\n/**\n * Mutates a job in place to advance its state on a status poll.\n * `pending → in_progress → completed | failed` based on poll-count thresholds.\n * No-op once terminal. The in_progress threshold is checked first so a job\n * whose thresholds are equal still spends one poll in in_progress instead of\n * jumping straight to the terminal status (fal advanceJob semantics). The wire\n * value is derived from this internal status (pending/in_progress → done:false,\n * completed/failed → done:true).\n */\nfunction advanceVeoJob(job: VeoVideoReplayJob): void {\n if (job.status === \"completed\" || job.status === \"failed\") return;\n\n job.pollCount += 1;\n if (job.status === \"pending\" && job.pollCount >= job.pollsBeforeInProgress) {\n job.status = \"in_progress\";\n } else if (job.pollCount >= job.pollsBeforeCompleted) {\n job.status = terminalStatus(job);\n }\n}\n\n/** Gemini-style error envelope used by every Veo error response. */\nfunction geminiError(code: number, message: string, status: string): string {\n return JSON.stringify({ error: { code, message, status } });\n}\n\n/**\n * The done:true operation body for a completed replay job — the Files-API uri\n * is served as-is (the fixture's stored `video.url`, or the documented\n * placeholder when omitted).\n */\nfunction completedOperationBody(operationName: string, uri: string): Record<string, unknown> {\n return {\n name: operationName,\n done: true,\n response: {\n generateVideoResponse: {\n generatedSamples: [{ video: { uri } }],\n },\n },\n };\n}\n\n// ─── POST /v1beta/models/{model}:predictLongRunning — submit ─────────────────\n\nexport async function handleVeoVideoCreate(\n req: http.IncomingMessage,\n res: http.ServerResponse,\n raw: string,\n model: string,\n fixtures: Fixture[],\n journal: Journal,\n defaults: HandlerDefaults,\n setCorsHeaders: (res: http.ServerResponse) => void,\n jobs: VeoVideoJobMap,\n): Promise<void> {\n setCorsHeaders(res);\n const path = req.url ?? `/v1beta/models/${model}:predictLongRunning`;\n const method = req.method ?? \"POST\";\n\n let videoReq: VeoVideoRequest;\n try {\n videoReq = JSON.parse(raw) as VeoVideoRequest;\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(res, 400, geminiError(400, `Malformed JSON: ${detail}`, \"INVALID_ARGUMENT\"));\n return;\n }\n\n // Reject bodies that parsed but are not a JSON object before touching fields.\n if (videoReq === null || typeof videoReq !== \"object\" || Array.isArray(videoReq)) {\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 geminiError(400, \"Request body must be a JSON object\", \"INVALID_ARGUMENT\"),\n );\n return;\n }\n\n // Prompt lives at instances[0].prompt (the @google/genai envelope).\n const firstInstance = Array.isArray(videoReq.instances) ? videoReq.instances[0] : undefined;\n const prompt = firstInstance?.prompt;\n // Synthesize a structurally valid journal body for field-validation 400s\n // (JournalEntry.body is ChatCompletionRequest | null). Strip reserved\n // underscore-prefixed keys so a request cannot spoof handler discriminators.\n const sanitized = Object.fromEntries(\n Object.entries(videoReq).filter(([key]) => !key.startsWith(\"_\")),\n );\n const parsedBody: ChatCompletionRequest = { ...sanitized, model, messages: [] };\n\n if (typeof prompt !== \"string\" || !prompt) {\n journal.add({\n method,\n path,\n headers: flattenHeaders(req.headers),\n body: parsedBody,\n response: { status: 400, fixture: null },\n });\n const message =\n prompt === undefined\n ? \"Missing required parameter: 'instances[0].prompt'\"\n : \"Invalid type for parameter: 'instances[0].prompt' must be a non-empty string\";\n writeErrorResponse(res, 400, geminiError(400, message, \"INVALID_ARGUMENT\"));\n return;\n }\n\n const syntheticReq: ChatCompletionRequest = {\n model,\n messages: [{ role: \"user\", content: prompt }],\n _endpointType: \"video\",\n _videoProvider: \"veo\",\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(\n `No fixture matched for Veo request (model=${model}, msg=\"${prompt.slice(0, 80)}\")`,\n );\n }\n\n // Chaos rolls AFTER body validation and fixture matching (mirrors the\n // OpenRouter submit). An unmatched submit is proxied upstream when record\n // mode has a veo provider AND strict would not win — label that roll \"proxy\".\n if (\n applyChaos(\n res,\n fixture,\n defaults.chaos,\n req.headers,\n req.url,\n journal,\n { method, path, headers: flattenHeaders(req.headers), body: syntheticReq },\n fixture\n ? \"fixture\"\n : resolveStrictMode(defaults.strict, req.headers)\n ? \"internal\"\n : defaults.record?.providers.veo\n ? \"proxy\"\n : \"internal\",\n defaults.registry,\n defaults.logger,\n )\n )\n return;\n\n if (!fixture) {\n // Strict mode wins over record: a strict no-match fails loudly with 503.\n if (resolveStrictMode(defaults.strict, req.headers)) {\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(res, 503, geminiError(503, strictMessage, \"UNAVAILABLE\"));\n return;\n }\n\n if (defaults.record) {\n const outcome = await proxyVeoVideoSubmit({\n req,\n res,\n raw,\n syntheticReq,\n record: defaults.record,\n journal,\n defaults,\n jobs,\n method,\n path,\n });\n if (outcome === \"handled\") return;\n // outcome === \"no_upstream\" — fall through to 404 (fal convention).\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(res, 404, geminiError(404, \"No fixture matched\", \"NOT_FOUND\"));\n return;\n }\n\n // World-generation snapshot (mirrors the record-submit guard): a fixtures\n // reset can land while the fixture's ResponseFactory below is awaited, and\n // the job insertion at the bottom must not seed the NEW world.\n const worldGeneration = jobs.generation;\n const response = await resolveResponse(fixture, syntheticReq);\n\n if (isErrorResponse(response)) {\n if (res.destroyed || res.writableEnded) return;\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 if (res.destroyed || res.writableEnded) return;\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 geminiError(500, \"Fixture response is not a video type\", \"INTERNAL\"),\n );\n return;\n }\n\n if (res.destroyed || res.writableEnded) return;\n journal.add({\n method,\n path,\n headers: flattenHeaders(req.headers),\n body: syntheticReq,\n response: { status: 200, fixture },\n });\n\n const fixtureStatus: string = response.video.status;\n if (fixtureStatus === \"processing\") {\n defaults.logger.warn(\n `Veo video fixture has status \"processing\" — treated as completed for the operations lifecycle`,\n );\n } else if (fixtureStatus !== \"completed\" && fixtureStatus !== \"failed\") {\n defaults.logger.warn(\n `Veo video fixture has unknown status \"${fixtureStatus}\" — treating as completed`,\n );\n }\n\n const operationName = `operations/${crypto.randomUUID()}`;\n const progression = resolveProgression(defaults.veoVideo);\n const job: VeoVideoReplayJob = {\n kind: \"replay\",\n operationName,\n status: \"pending\",\n pollCount: 0,\n pollsBeforeInProgress: progression.pollsBeforeInProgress,\n pollsBeforeCompleted: progression.pollsBeforeCompleted,\n video: { ...response.video },\n };\n // Default 0/0 progression seeds the job terminal at submit; the submit\n // envelope still reports a not-done operation.\n if (progression.pollsBeforeCompleted === 0) {\n job.status = terminalStatus(job);\n }\n if (jobs.generation === worldGeneration) {\n jobs.set(`${testId}:${operationName}`, job);\n } else {\n defaults.logger.warn(\n `Veo video submit resolved after a fixtures reset — not inserting operation ${operationName} into the new world (its polls will 404)`,\n );\n }\n\n // Embed the testId in the RETURNED name (not the stored key) so a multi-tenant\n // client can poll the opaque name without an x-test-id header — the testId\n // travels via getTestId's `?testId=` fallback. Mirrors OpenRouter's polling_url\n // treatment; testIdSuffix is \"\" for the default testId, keeping the name bare.\n res.writeHead(200, { \"Content-Type\": \"application/json\" });\n res.end(JSON.stringify({ name: operationName + testIdSuffix(testId, \"?\") }));\n}\n\n// ─── GET /v1beta/operations/{name} — status poll ─────────────────────────────\n\nexport async function handleVeoVideoStatus(\n req: http.IncomingMessage,\n res: http.ServerResponse,\n operationName: string,\n fixtures: Fixture[],\n journal: Journal,\n defaults: HandlerDefaults,\n setCorsHeaders: (res: http.ServerResponse) => void,\n jobs: VeoVideoJobMap,\n): Promise<void> {\n setCorsHeaders(res);\n const path = req.url ?? `/v1beta/${operationName}`;\n const method = req.method ?? \"GET\";\n\n // Chaos rolls BEFORE the job lookup — the label stays \"internal\" even in\n // record mode, and a chaos-dropped poll never reaches the upstream.\n if (\n applyChaos(\n res,\n null,\n defaults.chaos,\n req.headers,\n req.url,\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 key = `${testId}:${operationName}`;\n const job = jobs.get(key);\n\n if (!job) {\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 geminiError(404, `Operation ${operationName} not found`, \"NOT_FOUND\"),\n );\n return;\n }\n\n if (job.kind === \"record\") {\n // Strict means nothing reaches an upstream — a record job's polls are pure\n // upstream proxies, so an effective-strict request is refused with 503.\n if (resolveStrictMode(defaults.strict, req.headers)) {\n defaults.logger.error(\n `STRICT: Veo operation ${operationName} is proxied live upstream (record mode) — refusing the upstream poll`,\n );\n journal.add({\n method,\n path,\n headers: flattenHeaders(req.headers),\n body: null,\n response: {\n status: 503,\n fixture: null,\n ...strictOverrideField(defaults.strict, req.headers),\n },\n });\n writeErrorResponse(\n res,\n 503,\n geminiError(\n 503,\n `Strict mode: Veo operation ${operationName} is proxied live upstream (record mode) — nothing reaches an upstream under strict mode`,\n \"UNAVAILABLE\",\n ),\n );\n return;\n }\n await proxyVeoVideoRecordPoll({\n req,\n res,\n job,\n key,\n testId,\n fixtures,\n journal,\n defaults,\n jobs,\n method,\n path,\n });\n return;\n }\n\n // Guard BEFORE advancing or journaling (file convention): a disconnected\n // client consumes no progression step or TTL refresh.\n if (res.destroyed || res.writableEnded) return;\n advanceVeoJob(job);\n // Refresh the TTL on every replay poll (delete-before-set also moves the\n // entry to the back of the FIFO eviction order).\n jobs.set(key, job);\n journal.add({\n method,\n path,\n headers: flattenHeaders(req.headers),\n body: null,\n response: { status: 200, fixture: null },\n });\n\n let body: Record<string, unknown>;\n if (job.status === \"completed\") {\n // Truthy guard (not `??`): an empty-string fixture url must still fall back\n // to the placeholder Files-API uri rather than being served as an empty uri.\n const rawUrl = job.video.url;\n body = completedOperationBody(\n operationName,\n typeof rawUrl === \"string\" && rawUrl ? rawUrl : DEFAULT_VEO_FILES_URI,\n );\n } else if (job.status === \"failed\") {\n if (job.video.error === \"\" && !job.emptyErrorWarned) {\n job.emptyErrorWarned = true;\n defaults.logger.warn(\n `Veo video fixture for operation ${operationName} has an empty error message — using the default`,\n );\n }\n body = {\n name: operationName,\n done: true,\n error: { code: 3, message: job.video.error || \"Video generation failed\" },\n };\n } else {\n body = { name: operationName, done: false };\n }\n\n res.writeHead(200, { \"Content-Type\": \"application/json\" });\n res.end(JSON.stringify(body));\n}\n\n// ─── Record mode: live interactive proxy (submit) ───────────────────────────\n\n/**\n * Proxy an unmatched Veo submit to the configured upstream and answer the\n * client with a mock-rewritten envelope: a fresh aimock operation name. The\n * upstream lifecycle is driven interactively by the client's own polls.\n *\n * Returns \"no_upstream\" when record mode has no veo provider URL — the caller\n * falls through to its 404 branch (fal convention).\n */\nasync function proxyVeoVideoSubmit(args: {\n req: http.IncomingMessage;\n res: http.ServerResponse;\n raw: string;\n syntheticReq: ChatCompletionRequest;\n record: RecordConfig;\n journal: Journal;\n defaults: HandlerDefaults;\n jobs: VeoVideoJobMap;\n method: string;\n path: string;\n}): Promise<\"handled\" | \"no_upstream\"> {\n const { req, res, raw, syntheticReq, record, journal, defaults, jobs, method, path } = args;\n\n const upstreamBase = record.providers.veo;\n if (!upstreamBase) {\n defaults.logger.warn(`No upstream URL configured for provider \"veo\" — cannot proxy`);\n return \"no_upstream\";\n }\n\n const proxyError = (msg: string): \"handled\" => {\n defaults.logger.error(`Veo video submit proxy failed: ${msg}`);\n if (res.destroyed || res.writableEnded) return \"handled\";\n journal.add({\n method,\n path,\n headers: flattenHeaders(req.headers),\n body: syntheticReq,\n response: {\n status: 502,\n fixture: null,\n source: \"proxy\",\n ...strictOverrideField(defaults.strict, req.headers),\n },\n });\n writeErrorResponse(\n res,\n 502,\n JSON.stringify({\n error: { message: `Proxy to upstream failed: ${msg}`, type: \"proxy_error\" },\n }),\n );\n return \"handled\";\n };\n\n const submitPath = `/v1beta/models/${syntheticReq.model}:predictLongRunning`;\n let submitUrl: URL;\n let upstreamOrigin: string;\n try {\n submitUrl = resolveUpstreamUrl(upstreamBase, submitPath);\n upstreamOrigin = new URL(upstreamBase).origin;\n } catch (err) {\n const msg = err instanceof Error ? err.message : String(err);\n return proxyError(`Invalid upstream URL: ${upstreamBase} (${msg})`);\n }\n\n defaults.logger.warn(\n `NO FIXTURE MATCH — proxying Veo video submit to ${upstreamBase}${submitPath}`,\n );\n\n // World-generation snapshot: a fixtures reset landing during the upstream\n // fetch clears the job map — the insertion guard compares against this.\n const worldGeneration = jobs.generation;\n\n let fetched: { status: number; contentType: string | null; text: string };\n try {\n const headers = prepareEgressHeaders(req, submitUrl, \"veo\", record.providerKeys?.veo);\n if (!headers) return proxyError(\"No configured provider credential\");\n const upstreamRes = await fetch(submitUrl, {\n method: \"POST\",\n headers,\n body: raw,\n signal: upstreamTimeoutSignal(record),\n });\n fetched = {\n status: upstreamRes.status,\n contentType: upstreamRes.headers.get(\"content-type\"),\n text: await readEnvelopeText(upstreamRes, record),\n };\n } catch (err) {\n return proxyError(err instanceof Error ? err.message : \"Unknown proxy error\");\n }\n\n if (fetched.status === 401 || fetched.status === 403) {\n // Real-API fidelity: relay an upstream auth rejection verbatim.\n defaults.logger.warn(\n `Upstream rejected the Veo video submit (${fetched.status}) — relaying the upstream status`,\n );\n if (res.destroyed || res.writableEnded) return \"handled\";\n journal.add({\n method,\n path,\n headers: flattenHeaders(req.headers),\n body: syntheticReq,\n response: {\n status: fetched.status,\n fixture: null,\n source: \"proxy\",\n ...strictOverrideField(defaults.strict, req.headers),\n },\n });\n res.writeHead(fetched.status, { \"Content-Type\": fetched.contentType ?? \"application/json\" });\n res.end(fetched.text);\n return \"handled\";\n }\n\n let upstreamOperationName: string;\n {\n if (fetched.status < 200 || fetched.status >= 300) {\n return proxyError(`Submit ${fetched.status}: ${fetched.text.slice(0, 200)}`);\n }\n let parsed: unknown;\n try {\n parsed = JSON.parse(fetched.text);\n } catch {\n return proxyError(`Submit returned non-JSON: ${fetched.text.slice(0, 200)}`);\n }\n if (parsed === null || typeof parsed !== \"object\" || Array.isArray(parsed)) {\n return proxyError(\"Submit response is not a JSON object\");\n }\n upstreamOperationName = String((parsed as Record<string, unknown>).name ?? \"\").trim();\n if (!upstreamOperationName) {\n return proxyError(\"Submit response missing name\");\n }\n }\n\n // Veo has no polling_url field — the poll URL is constructed from the\n // provider base + `/v1beta/{operationName}`. Origin-validate the constructed\n // URL (the client's Authorization travels to it on every poll); fall back to\n // a string on the provider base on any parse failure.\n let upstreamPollingUrl: string;\n try {\n const constructed = resolveUpstreamUrl(\n upstreamBase,\n `/v1beta/${upstreamOperationName.replace(/^\\/+/, \"\")}`,\n );\n if (constructed.origin === upstreamOrigin) {\n upstreamPollingUrl = constructed.toString();\n } else {\n defaults.logger.warn(\n `Constructed Veo poll URL origin ${constructed.origin} differs from the configured provider origin ${upstreamOrigin} — using the provider-origin URL`,\n );\n upstreamPollingUrl = `${upstreamOrigin}/v1beta/${upstreamOperationName.replace(/^\\/+/, \"\")}`;\n }\n } catch {\n defaults.logger.warn(\n `Could not construct the Veo poll URL for ${upstreamOperationName} — using the provider-origin URL`,\n );\n upstreamPollingUrl = `${upstreamOrigin}/v1beta/${upstreamOperationName.replace(/^\\/+/, \"\")}`;\n }\n\n const testId = getTestId(req);\n const matchRequest = defaults.requestTransform\n ? defaults.requestTransform(syntheticReq)\n : syntheticReq;\n const operationName = `operations/${crypto.randomUUID()}`;\n const job: VeoVideoRecordJob = {\n kind: \"record\",\n operationName,\n status: \"pending\",\n upstreamOperationName,\n upstreamPollingUrl,\n match: buildFixtureMatch(matchRequest, record),\n };\n if (jobs.generation === worldGeneration) {\n jobs.set(`${testId}:${operationName}`, job);\n } else {\n defaults.logger.warn(\n `Veo video submit for upstream operation ${upstreamOperationName} completed after a fixtures reset — not inserting the job into the new world (its polls will 404)`,\n );\n }\n\n if (res.destroyed || res.writableEnded) return \"handled\";\n journal.add({\n method,\n path,\n headers: flattenHeaders(req.headers),\n body: syntheticReq,\n response: {\n status: 200,\n fixture: null,\n source: \"proxy\",\n ...strictOverrideField(defaults.strict, req.headers),\n },\n });\n\n // Embed the testId in the RETURNED name (see the replay submit) so record-mode\n // multi-tenant polls resolve via `?testId=`; the upstream poll still uses the\n // bare job.operationName / job.upstreamPollingUrl, untouched by the suffix.\n res.writeHead(200, { \"Content-Type\": \"application/json\" });\n res.end(JSON.stringify({ name: operationName + testIdSuffix(testId, \"?\") }));\n return \"handled\";\n}\n\n// ─── Record mode: live interactive proxy (poll + eager capture) ─────────────\n\n/**\n * Proxy a status poll for a record-mode Veo job 1:1 to the upstream and relay\n * the result with the operation name rewritten to the mock operation (the\n * Files-API uri inside `response` passes through verbatim — aimock never\n * downloads bytes). When the upstream reports `done:true` with a `response`,\n * the rewritten body is relayed IMMEDIATELY and the eager capture (persist the\n * uri as a fixture, mutate to a terminal replay job) runs DETACHED. A\n * `done:true` with an `error` persists a failed fixture synchronously. Under\n * `record.proxyOnly` nothing is captured, persisted, or mutated. Every\n * post-await map mutation is identity-guarded.\n */\nasync function proxyVeoVideoRecordPoll(args: {\n req: http.IncomingMessage;\n res: http.ServerResponse;\n job: VeoVideoRecordJob;\n key: string;\n testId: string;\n fixtures: Fixture[];\n journal: Journal;\n defaults: HandlerDefaults;\n jobs: VeoVideoJobMap;\n method: string;\n path: string;\n}): Promise<void> {\n const { req, res, job, key, testId, fixtures, journal, defaults, jobs, method, path } = args;\n const logger = defaults.logger;\n\n const journalProxy = (status: number): void => {\n journal.add({\n method,\n path,\n headers: flattenHeaders(req.headers),\n body: null,\n response: {\n status,\n fixture: null,\n source: \"proxy\",\n ...strictOverrideField(defaults.strict, req.headers),\n },\n });\n };\n\n const proxyError = (msg: string): void => {\n logger.error(`Veo video poll proxy failed: ${msg}`);\n if (res.destroyed || res.writableEnded) return;\n journalProxy(502);\n writeErrorResponse(\n res,\n 502,\n JSON.stringify({\n error: { message: `Proxy to upstream failed: ${msg}`, type: \"proxy_error\" },\n }),\n );\n };\n\n // Recording can be disabled mid-flight — an orphaned record job must fail\n // loudly before contacting the upstream.\n const record = defaults.record;\n if (!record) {\n proxyError(\"record mode is no longer configured for an in-flight record job\");\n return;\n }\n\n let fetched: { status: number; contentType: string | null; text: string };\n try {\n const target = new URL(job.upstreamPollingUrl);\n const headers = prepareEgressHeaders(req, target, \"veo\", record.providerKeys?.veo);\n if (!headers) {\n proxyError(\"No configured provider credential\");\n return;\n }\n const upstreamRes = await fetch(job.upstreamPollingUrl, {\n headers,\n signal: upstreamTimeoutSignal(record),\n });\n fetched = {\n status: upstreamRes.status,\n contentType: upstreamRes.headers.get(\"content-type\"),\n text: await readEnvelopeText(upstreamRes, record),\n };\n } catch (err) {\n proxyError(err instanceof Error ? err.message : \"Unknown proxy error\");\n return;\n }\n\n if (fetched.status === 401 || fetched.status === 403) {\n logger.warn(\n `Upstream rejected the Veo status poll for operation ${job.upstreamOperationName} (${fetched.status}) — relaying the upstream status`,\n );\n if (res.destroyed || res.writableEnded) return;\n journalProxy(fetched.status);\n res.writeHead(fetched.status, { \"Content-Type\": fetched.contentType ?? \"application/json\" });\n res.end(fetched.text);\n return;\n }\n\n let upstreamBody: Record<string, unknown>;\n {\n if (fetched.status < 200 || fetched.status >= 300) {\n proxyError(`Status ${fetched.status}: ${fetched.text.slice(0, 200)}`);\n return;\n }\n let parsed: unknown;\n try {\n parsed = JSON.parse(fetched.text);\n } catch {\n proxyError(`Status returned non-JSON: ${fetched.text.slice(0, 200)}`);\n return;\n }\n if (parsed === null || typeof parsed !== \"object\" || Array.isArray(parsed)) {\n proxyError(\"Status response is not a JSON object\");\n return;\n }\n upstreamBody = parsed as Record<string, unknown>;\n }\n\n // Rewrite ONLY the top-level operation name so no upstream identifier\n // escapes — the `response` (carrying the Files-API uri the client needs) and\n // every other field pass through verbatim.\n const relayBody: Record<string, unknown> = { ...upstreamBody, name: job.operationName };\n\n const relayJson = (): void => {\n if (res.destroyed || res.writableEnded) return;\n journalProxy(200);\n res.writeHead(200, { \"Content-Type\": \"application/json\" });\n res.end(JSON.stringify(relayBody));\n };\n\n const done = upstreamBody.done === true;\n const upstreamError = upstreamBody.error;\n const hasError =\n upstreamError !== undefined && upstreamError !== null && typeof upstreamError === \"object\";\n\n if (!done) {\n // Non-terminal: identity-guarded status write + TTL refresh, relay.\n if (jobs.get(key) === job) {\n if (!job.capturing && job.status !== \"completed\" && job.status !== \"failed\") {\n job.status = \"in_progress\";\n }\n jobs.set(key, job);\n }\n relayJson();\n return;\n }\n\n if (hasError) {\n // Terminal failure: persist a failed fixture synchronously (mirrors the\n // OpenRouter failed branch), then relay.\n if (record.proxyOnly) {\n if (jobs.get(key) === job) {\n job.status = \"failed\";\n jobs.set(key, job);\n }\n relayJson();\n return;\n }\n if (jobs.get(key) !== job) {\n // A concurrent terminal poll already persisted/replaced the entry, or a\n // fixtures reset cleared the world — relay without a duplicate fixture.\n relayJson();\n return;\n }\n const errObj = upstreamError as Record<string, unknown>;\n const rawMessage = errObj.message;\n const error = typeof rawMessage === \"string\" && rawMessage ? rawMessage : undefined;\n const video: VideoResponse[\"video\"] = {\n id: job.upstreamOperationName,\n status: \"failed\",\n ...(error !== undefined ? { error } : {}),\n };\n const persistResult = persistFixture({\n record,\n providerKey: \"veo\",\n testId,\n fixture: { match: job.match, response: { video } },\n fixtures,\n logger,\n });\n if (persistResult.kind === \"failed\" && !res.headersSent) {\n res.setHeader(\"X-AIMock-Record-Error\", sanitizeHeaderValue(persistResult.error));\n }\n jobs.set(key, {\n kind: \"replay\",\n operationName: job.operationName,\n status: \"failed\",\n pollCount: 0,\n pollsBeforeInProgress: 0,\n pollsBeforeCompleted: 0,\n video: { ...video },\n });\n relayJson();\n return;\n }\n\n // Terminal success (done:true with a response).\n if (record.proxyOnly) {\n if (jobs.get(key) === job) {\n job.status = \"completed\";\n jobs.set(key, job);\n }\n relayJson();\n return;\n }\n\n if (job.capturing || jobs.get(key) !== job) {\n // A concurrent poll already entered the capture sequence, or this\n // reference is detached — relay without starting a second capture and\n // never re-insert the stale object.\n if (jobs.get(key) === job) {\n jobs.set(key, job); // TTL refresh\n }\n relayJson();\n return;\n }\n\n // Open the capturing window SYNCHRONOUSLY before the first await.\n job.capturing = true;\n job.status = \"completed\";\n jobs.set(key, job); // TTL refresh — identity-checked above, no await since\n\n // Relay IMMEDIATELY; the persist + replay mutation runs DETACHED.\n relayJson();\n\n void captureVeoVideoRecordFixture({\n job,\n key,\n testId,\n fixtures,\n defaults,\n jobs,\n record,\n upstreamBody,\n });\n}\n\n/**\n * Detached eager-capture for a completed Veo record job. Extracts the\n * Files-API uri from the terminal poll body (NO download), persists a\n * fixture, and mutates the map entry into a terminal replay job. Every failure\n * is handled internally; the returned promise NEVER rejects. World-generation\n * guarded immediately before persist.\n */\nasync function captureVeoVideoRecordFixture(args: {\n job: VeoVideoRecordJob;\n key: string;\n testId: string;\n fixtures: Fixture[];\n defaults: HandlerDefaults;\n jobs: VeoVideoJobMap;\n record: RecordConfig;\n upstreamBody: Record<string, unknown>;\n}): Promise<void> {\n const { job, key, testId, fixtures, defaults, jobs, record, upstreamBody } = args;\n const logger = defaults.logger;\n\n try {\n // Drill into response.generateVideoResponse.generatedSamples[0].video.uri.\n let uri: string | undefined;\n const response = upstreamBody.response;\n if (response !== null && typeof response === \"object\" && !Array.isArray(response)) {\n const gvr = (response as Record<string, unknown>).generateVideoResponse;\n if (gvr !== null && typeof gvr === \"object\" && !Array.isArray(gvr)) {\n const samples = (gvr as Record<string, unknown>).generatedSamples;\n const first = Array.isArray(samples) ? samples[0] : undefined;\n if (first !== null && typeof first === \"object\" && !Array.isArray(first)) {\n const video = (first as Record<string, unknown>).video;\n if (video !== null && typeof video === \"object\" && !Array.isArray(video)) {\n const rawUri = (video as Record<string, unknown>).uri;\n if (typeof rawUri === \"string\" && rawUri) uri = rawUri;\n }\n }\n }\n }\n if (!uri) {\n logger.warn(\n `Upstream Veo operation ${job.upstreamOperationName} completed without a usable Files-API uri — capture skipped, nothing persisted; the next completed poll retries`,\n );\n return;\n }\n\n const video: VideoResponse[\"video\"] = {\n id: job.upstreamOperationName,\n status: \"completed\",\n url: uri,\n };\n\n // World-generation guard: a fixtures reset landing during the await above\n // clears the job map — map identity is a valid proxy for \"same world\".\n if (jobs.get(key) !== job) {\n logger.warn(\n `Veo video capture for operation ${job.upstreamOperationName} discarded: the job map no longer holds this job (fixtures reset or TTL eviction) — nothing persisted`,\n );\n return;\n }\n persistFixture({\n record,\n providerKey: \"veo\",\n testId,\n fixture: { match: job.match, response: { video } },\n fixtures,\n logger,\n });\n\n // Mutate the entry into a terminal replay job: later polls serve locally.\n if (jobs.get(key) === job) {\n jobs.set(key, {\n kind: \"replay\",\n operationName: job.operationName,\n status: \"completed\",\n pollCount: 0,\n pollsBeforeInProgress: 0,\n pollsBeforeCompleted: 0,\n video: { ...video },\n });\n }\n } catch (err) {\n const msg = err instanceof Error ? err.message : String(err);\n logger.error(\n `Veo video capture for operation ${job.upstreamOperationName} failed unexpectedly (${msg}) — fixture not persisted; the job keeps proxying live`,\n );\n } finally {\n // Close the capturing window when the map still holds this record job (a\n // step threw before the replay mutation). On the success path the entry\n // was just replaced; the detached object deliberately keeps capturing=true.\n if (jobs.get(key) === job) {\n job.capturing = false;\n }\n }\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"veo-video.cjs","names":["flattenHeaders","getContext","getTestId","matchFixtureDiagnostic","applyChaosAsync","resolveStrictMode","strictNoMatchMessage","strictNoMatchLogLine","strictOverrideField","resolveResponse","isErrorResponse","responseGone","serializeErrorResponse","isVideoResponse","crypto","resolveProgression","testIdSuffix","resolveUpstreamUrl","prepareEgressHeaders","upstreamTimeoutSignal","readEnvelopeText","buildFixtureMatch","persistFixture","sanitizeHeaderValue"],"sources":["../src/veo-video.ts"],"sourcesContent":["import type * as http from \"node:http\";\nimport crypto from \"node:crypto\";\nimport type {\n ChatCompletionRequest,\n Fixture,\n HandlerDefaults,\n RecordConfig,\n VideoResponse,\n} 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 { applyChaosAsync, responseGone } from \"./chaos.js\";\nimport { resolveProgression } from \"./fal.js\";\nimport {\n buildFixtureMatch,\n prepareEgressHeaders,\n persistFixture,\n sanitizeHeaderValue,\n} from \"./recorder.js\";\nimport { resolveUpstreamUrl } from \"./url.js\";\nimport { readEnvelopeText, testIdSuffix, upstreamTimeoutSignal } from \"./video-proxy-shared.js\";\n\n/**\n * Google Veo async video lifecycle mock. Submit\n * `POST /v1beta/models/{model}:predictLongRunning` returns\n * `{ name: \"operations/...\" }`; status `GET /v1beta/operations/{name}` polls\n * `done:false → done:true`. The Files-API `uri` is served AS-IS — aimock does\n * NOT proxy or capture video bytes. With `record.providers.veo` configured an\n * unmatched submit becomes a live interactive proxy (submit + poll forwarded\n * 1:1, eager fixture capture on terminal status). Strict mode still wins.\n *\n * ASSUMED Veo create body shape (the `@google/genai` :predictLongRunning\n * envelope from issue #278): `{ instances: [{ prompt, image? }], parameters }`.\n * Match surface is `instances[0].prompt` only.\n */\n\n/** Files-API-shaped placeholder served when a completed fixture omits a uri. */\nconst DEFAULT_VEO_FILES_URI = \"https://generativelanguage.googleapis.com/v1beta/files/placeholder\";\n\n// The `:predictLongRunning` envelope nests the prompt under instances[].\ninterface VeoVideoRequest {\n instances?: Array<{ prompt?: unknown; [key: string]: unknown }>;\n parameters?: Record<string, unknown>;\n [key: string]: unknown;\n}\n\n// ─── VeoVideoJobMap (TTL + bounded) ─────────────────────────────────────────\n\nexport const VEO_VIDEO_MAX_ENTRIES = 10_000;\nconst VEO_VIDEO_TTL_MS = 3_600_000; // 1 hour\n\ntype VeoVideoStatus = \"pending\" | \"in_progress\" | \"completed\" | \"failed\";\n\ninterface VeoVideoReplayJob {\n kind: \"replay\";\n operationName: string;\n status: VeoVideoStatus;\n pollCount: number;\n pollsBeforeInProgress: number;\n pollsBeforeCompleted: number;\n video: VideoResponse[\"video\"];\n /** Latch for the empty-`error` authoring warn on failed polls (once/job). */\n emptyErrorWarned?: boolean;\n}\n\ninterface VeoVideoRecordJob {\n kind: \"record\";\n operationName: string;\n status: VeoVideoStatus;\n upstreamOperationName: string;\n upstreamPollingUrl: string;\n match: Fixture[\"match\"];\n capturing?: boolean;\n}\n\nexport type VeoVideoJob = VeoVideoReplayJob | VeoVideoRecordJob;\n\ninterface VeoVideoEntry {\n job: VeoVideoJob;\n createdAt: number;\n}\n\n/**\n * Per-testId job state for the Veo video handler. Mirrors OpenRouterVideoJobMap\n * (openrouter-video.ts): lazy TTL eviction on `get`, FIFO eviction of the\n * oldest entries on `set` when over capacity, delete-before-set TTL refresh,\n * monotonic world-generation counter for reset-mid-flight detection, no\n * background sweep timer. Keys are `${testId}:${operationName}`.\n */\nexport class VeoVideoJobMap {\n private readonly entries = new Map<string, VeoVideoEntry>();\n private worldGeneration = 0;\n\n get generation(): number {\n return this.worldGeneration;\n }\n\n get(key: string): VeoVideoJob | undefined {\n const entry = this.entries.get(key);\n if (!entry) return undefined;\n if (Date.now() - entry.createdAt > VEO_VIDEO_TTL_MS) {\n this.entries.delete(key);\n return undefined;\n }\n return entry.job;\n }\n\n set(key: string, job: VeoVideoJob): void {\n this.entries.delete(key);\n this.entries.set(key, { job, createdAt: Date.now() });\n if (this.entries.size > VEO_VIDEO_MAX_ENTRIES) {\n const excess = this.entries.size - VEO_VIDEO_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 this.worldGeneration++;\n }\n\n get size(): number {\n return this.entries.size;\n }\n}\n\n// ─── Job progression ────────────────────────────────────────────────────────\n\n/**\n * Maps the fixture's terminal video status onto the job lifecycle. Anything\n * that is not \"failed\" — including a fixture authored as \"processing\" — is\n * treated as completed, since this surface always drives jobs to a terminal\n * state.\n */\nfunction terminalStatus(job: VeoVideoReplayJob): VeoVideoStatus {\n return job.video.status === \"failed\" ? \"failed\" : \"completed\";\n}\n\n/**\n * Mutates a job in place to advance its state on a status poll.\n * `pending → in_progress → completed | failed` based on poll-count thresholds.\n * No-op once terminal. The in_progress threshold is checked first so a job\n * whose thresholds are equal still spends one poll in in_progress instead of\n * jumping straight to the terminal status (fal advanceJob semantics). The wire\n * value is derived from this internal status (pending/in_progress → done:false,\n * completed/failed → done:true).\n */\nfunction advanceVeoJob(job: VeoVideoReplayJob): void {\n if (job.status === \"completed\" || job.status === \"failed\") return;\n\n job.pollCount += 1;\n if (job.status === \"pending\" && job.pollCount >= job.pollsBeforeInProgress) {\n job.status = \"in_progress\";\n } else if (job.pollCount >= job.pollsBeforeCompleted) {\n job.status = terminalStatus(job);\n }\n}\n\n/** Gemini-style error envelope used by every Veo error response. */\nfunction geminiError(code: number, message: string, status: string): string {\n return JSON.stringify({ error: { code, message, status } });\n}\n\n/**\n * The done:true operation body for a completed replay job — the Files-API uri\n * is served as-is (the fixture's stored `video.url`, or the documented\n * placeholder when omitted).\n */\nfunction completedOperationBody(operationName: string, uri: string): Record<string, unknown> {\n return {\n name: operationName,\n done: true,\n response: {\n generateVideoResponse: {\n generatedSamples: [{ video: { uri } }],\n },\n },\n };\n}\n\n// ─── POST /v1beta/models/{model}:predictLongRunning — submit ─────────────────\n\nexport async function handleVeoVideoCreate(\n req: http.IncomingMessage,\n res: http.ServerResponse,\n raw: string,\n model: string,\n fixtures: Fixture[],\n journal: Journal,\n defaults: HandlerDefaults,\n setCorsHeaders: (res: http.ServerResponse) => void,\n jobs: VeoVideoJobMap,\n): Promise<void> {\n setCorsHeaders(res);\n const path = req.url ?? `/v1beta/models/${model}:predictLongRunning`;\n const method = req.method ?? \"POST\";\n\n let videoReq: VeoVideoRequest;\n try {\n videoReq = JSON.parse(raw) as VeoVideoRequest;\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(res, 400, geminiError(400, `Malformed JSON: ${detail}`, \"INVALID_ARGUMENT\"));\n return;\n }\n\n // Reject bodies that parsed but are not a JSON object before touching fields.\n if (videoReq === null || typeof videoReq !== \"object\" || Array.isArray(videoReq)) {\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 geminiError(400, \"Request body must be a JSON object\", \"INVALID_ARGUMENT\"),\n );\n return;\n }\n\n // Prompt lives at instances[0].prompt (the @google/genai envelope).\n const firstInstance = Array.isArray(videoReq.instances) ? videoReq.instances[0] : undefined;\n const prompt = firstInstance?.prompt;\n // Synthesize a structurally valid journal body for field-validation 400s\n // (JournalEntry.body is ChatCompletionRequest | null). Strip reserved\n // underscore-prefixed keys so a request cannot spoof handler discriminators.\n const sanitized = Object.fromEntries(\n Object.entries(videoReq).filter(([key]) => !key.startsWith(\"_\")),\n );\n const parsedBody: ChatCompletionRequest = { ...sanitized, model, messages: [] };\n\n if (typeof prompt !== \"string\" || !prompt) {\n journal.add({\n method,\n path,\n headers: flattenHeaders(req.headers),\n body: parsedBody,\n response: { status: 400, fixture: null },\n });\n const message =\n prompt === undefined\n ? \"Missing required parameter: 'instances[0].prompt'\"\n : \"Invalid type for parameter: 'instances[0].prompt' must be a non-empty string\";\n writeErrorResponse(res, 400, geminiError(400, message, \"INVALID_ARGUMENT\"));\n return;\n }\n\n const syntheticReq: ChatCompletionRequest = {\n model,\n messages: [{ role: \"user\", content: prompt }],\n _endpointType: \"video\",\n _videoProvider: \"veo\",\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(\n `No fixture matched for Veo request (model=${model}, msg=\"${prompt.slice(0, 80)}\")`,\n );\n }\n\n // Chaos rolls AFTER body validation and fixture matching (mirrors the\n // OpenRouter submit). An unmatched submit is proxied upstream when record\n // mode has a veo provider AND strict would not win — label that roll \"proxy\".\n if (\n await applyChaosAsync(\n res,\n fixture,\n defaults.chaos,\n req.headers,\n req.url,\n journal,\n { method, path, headers: flattenHeaders(req.headers), body: syntheticReq },\n fixture\n ? \"fixture\"\n : resolveStrictMode(defaults.strict, req.headers)\n ? \"internal\"\n : defaults.record?.providers.veo\n ? \"proxy\"\n : \"internal\",\n defaults.registry,\n defaults.logger,\n )\n )\n return;\n\n if (!fixture) {\n // Strict mode wins over record: a strict no-match fails loudly with 503.\n if (resolveStrictMode(defaults.strict, req.headers)) {\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(res, 503, geminiError(503, strictMessage, \"UNAVAILABLE\"));\n return;\n }\n\n if (defaults.record) {\n const outcome = await proxyVeoVideoSubmit({\n req,\n res,\n raw,\n syntheticReq,\n record: defaults.record,\n journal,\n defaults,\n jobs,\n method,\n path,\n });\n if (outcome === \"handled\") return;\n // outcome === \"no_upstream\" — fall through to 404 (fal convention).\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(res, 404, geminiError(404, \"No fixture matched\", \"NOT_FOUND\"));\n return;\n }\n\n // World-generation snapshot (mirrors the record-submit guard): a fixtures\n // reset can land while the fixture's ResponseFactory below is awaited, and\n // the job insertion at the bottom must not seed the NEW world.\n const worldGeneration = jobs.generation;\n const response = await resolveResponse(fixture, syntheticReq);\n\n if (isErrorResponse(response)) {\n if (responseGone(res)) return;\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 if (responseGone(res)) return;\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 geminiError(500, \"Fixture response is not a video type\", \"INTERNAL\"),\n );\n return;\n }\n\n if (responseGone(res)) return;\n journal.add({\n method,\n path,\n headers: flattenHeaders(req.headers),\n body: syntheticReq,\n response: { status: 200, fixture },\n });\n\n const fixtureStatus: string = response.video.status;\n if (fixtureStatus === \"processing\") {\n defaults.logger.warn(\n `Veo video fixture has status \"processing\" — treated as completed for the operations lifecycle`,\n );\n } else if (fixtureStatus !== \"completed\" && fixtureStatus !== \"failed\") {\n defaults.logger.warn(\n `Veo video fixture has unknown status \"${fixtureStatus}\" — treating as completed`,\n );\n }\n\n const operationName = `operations/${crypto.randomUUID()}`;\n const progression = resolveProgression(defaults.veoVideo);\n const job: VeoVideoReplayJob = {\n kind: \"replay\",\n operationName,\n status: \"pending\",\n pollCount: 0,\n pollsBeforeInProgress: progression.pollsBeforeInProgress,\n pollsBeforeCompleted: progression.pollsBeforeCompleted,\n video: { ...response.video },\n };\n // Default 0/0 progression seeds the job terminal at submit; the submit\n // envelope still reports a not-done operation.\n if (progression.pollsBeforeCompleted === 0) {\n job.status = terminalStatus(job);\n }\n if (jobs.generation === worldGeneration) {\n jobs.set(`${testId}:${operationName}`, job);\n } else {\n defaults.logger.warn(\n `Veo video submit resolved after a fixtures reset — not inserting operation ${operationName} into the new world (its polls will 404)`,\n );\n }\n\n // Embed the testId in the RETURNED name (not the stored key) so a multi-tenant\n // client can poll the opaque name without an x-test-id header — the testId\n // travels via getTestId's `?testId=` fallback. Mirrors OpenRouter's polling_url\n // treatment; testIdSuffix is \"\" for the default testId, keeping the name bare.\n res.writeHead(200, { \"Content-Type\": \"application/json\" });\n res.end(JSON.stringify({ name: operationName + testIdSuffix(testId, \"?\") }));\n}\n\n// ─── GET /v1beta/operations/{name} — status poll ─────────────────────────────\n\nexport async function handleVeoVideoStatus(\n req: http.IncomingMessage,\n res: http.ServerResponse,\n operationName: string,\n fixtures: Fixture[],\n journal: Journal,\n defaults: HandlerDefaults,\n setCorsHeaders: (res: http.ServerResponse) => void,\n jobs: VeoVideoJobMap,\n): Promise<void> {\n setCorsHeaders(res);\n const path = req.url ?? `/v1beta/${operationName}`;\n const method = req.method ?? \"GET\";\n\n // Chaos rolls BEFORE the job lookup — the label stays \"internal\" even in\n // record mode, and a chaos-dropped poll never reaches the upstream.\n if (\n await applyChaosAsync(\n res,\n null,\n defaults.chaos,\n req.headers,\n req.url,\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 key = `${testId}:${operationName}`;\n const job = jobs.get(key);\n\n if (!job) {\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 geminiError(404, `Operation ${operationName} not found`, \"NOT_FOUND\"),\n );\n return;\n }\n\n if (job.kind === \"record\") {\n // Strict means nothing reaches an upstream — a record job's polls are pure\n // upstream proxies, so an effective-strict request is refused with 503.\n if (resolveStrictMode(defaults.strict, req.headers)) {\n defaults.logger.error(\n `STRICT: Veo operation ${operationName} is proxied live upstream (record mode) — refusing the upstream poll`,\n );\n journal.add({\n method,\n path,\n headers: flattenHeaders(req.headers),\n body: null,\n response: {\n status: 503,\n fixture: null,\n ...strictOverrideField(defaults.strict, req.headers),\n },\n });\n writeErrorResponse(\n res,\n 503,\n geminiError(\n 503,\n `Strict mode: Veo operation ${operationName} is proxied live upstream (record mode) — nothing reaches an upstream under strict mode`,\n \"UNAVAILABLE\",\n ),\n );\n return;\n }\n await proxyVeoVideoRecordPoll({\n req,\n res,\n job,\n key,\n testId,\n fixtures,\n journal,\n defaults,\n jobs,\n method,\n path,\n });\n return;\n }\n\n // Guard BEFORE advancing or journaling (file convention): a disconnected\n // client consumes no progression step or TTL refresh.\n if (responseGone(res)) return;\n advanceVeoJob(job);\n // Refresh the TTL on every replay poll (delete-before-set also moves the\n // entry to the back of the FIFO eviction order).\n jobs.set(key, job);\n journal.add({\n method,\n path,\n headers: flattenHeaders(req.headers),\n body: null,\n response: { status: 200, fixture: null },\n });\n\n let body: Record<string, unknown>;\n if (job.status === \"completed\") {\n // Truthy guard (not `??`): an empty-string fixture url must still fall back\n // to the placeholder Files-API uri rather than being served as an empty uri.\n const rawUrl = job.video.url;\n body = completedOperationBody(\n operationName,\n typeof rawUrl === \"string\" && rawUrl ? rawUrl : DEFAULT_VEO_FILES_URI,\n );\n } else if (job.status === \"failed\") {\n if (job.video.error === \"\" && !job.emptyErrorWarned) {\n job.emptyErrorWarned = true;\n defaults.logger.warn(\n `Veo video fixture for operation ${operationName} has an empty error message — using the default`,\n );\n }\n body = {\n name: operationName,\n done: true,\n error: { code: 3, message: job.video.error || \"Video generation failed\" },\n };\n } else {\n body = { name: operationName, done: false };\n }\n\n res.writeHead(200, { \"Content-Type\": \"application/json\" });\n res.end(JSON.stringify(body));\n}\n\n// ─── Record mode: live interactive proxy (submit) ───────────────────────────\n\n/**\n * Proxy an unmatched Veo submit to the configured upstream and answer the\n * client with a mock-rewritten envelope: a fresh aimock operation name. The\n * upstream lifecycle is driven interactively by the client's own polls.\n *\n * Returns \"no_upstream\" when record mode has no veo provider URL — the caller\n * falls through to its 404 branch (fal convention).\n */\nasync function proxyVeoVideoSubmit(args: {\n req: http.IncomingMessage;\n res: http.ServerResponse;\n raw: string;\n syntheticReq: ChatCompletionRequest;\n record: RecordConfig;\n journal: Journal;\n defaults: HandlerDefaults;\n jobs: VeoVideoJobMap;\n method: string;\n path: string;\n}): Promise<\"handled\" | \"no_upstream\"> {\n const { req, res, raw, syntheticReq, record, journal, defaults, jobs, method, path } = args;\n\n const upstreamBase = record.providers.veo;\n if (!upstreamBase) {\n defaults.logger.warn(`No upstream URL configured for provider \"veo\" — cannot proxy`);\n return \"no_upstream\";\n }\n\n const proxyError = (msg: string): \"handled\" => {\n defaults.logger.error(`Veo video submit proxy failed: ${msg}`);\n if (responseGone(res)) return \"handled\";\n journal.add({\n method,\n path,\n headers: flattenHeaders(req.headers),\n body: syntheticReq,\n response: {\n status: 502,\n fixture: null,\n source: \"proxy\",\n ...strictOverrideField(defaults.strict, req.headers),\n },\n });\n writeErrorResponse(\n res,\n 502,\n JSON.stringify({\n error: { message: `Proxy to upstream failed: ${msg}`, type: \"proxy_error\" },\n }),\n );\n return \"handled\";\n };\n\n const submitPath = `/v1beta/models/${syntheticReq.model}:predictLongRunning`;\n let submitUrl: URL;\n let upstreamOrigin: string;\n try {\n submitUrl = resolveUpstreamUrl(upstreamBase, submitPath);\n upstreamOrigin = new URL(upstreamBase).origin;\n } catch (err) {\n const msg = err instanceof Error ? err.message : String(err);\n return proxyError(`Invalid upstream URL: ${upstreamBase} (${msg})`);\n }\n\n defaults.logger.warn(\n `NO FIXTURE MATCH — proxying Veo video submit to ${upstreamBase}${submitPath}`,\n );\n\n // World-generation snapshot: a fixtures reset landing during the upstream\n // fetch clears the job map — the insertion guard compares against this.\n const worldGeneration = jobs.generation;\n\n let fetched: { status: number; contentType: string | null; text: string };\n try {\n const headers = prepareEgressHeaders(req, submitUrl, \"veo\", record.providerKeys?.veo);\n if (!headers) return proxyError(\"No configured provider credential\");\n const upstreamRes = await fetch(submitUrl, {\n method: \"POST\",\n headers,\n body: raw,\n signal: upstreamTimeoutSignal(record),\n });\n fetched = {\n status: upstreamRes.status,\n contentType: upstreamRes.headers.get(\"content-type\"),\n text: await readEnvelopeText(upstreamRes, record),\n };\n } catch (err) {\n return proxyError(err instanceof Error ? err.message : \"Unknown proxy error\");\n }\n\n if (fetched.status === 401 || fetched.status === 403) {\n // Real-API fidelity: relay an upstream auth rejection verbatim.\n defaults.logger.warn(\n `Upstream rejected the Veo video submit (${fetched.status}) — relaying the upstream status`,\n );\n if (responseGone(res)) return \"handled\";\n journal.add({\n method,\n path,\n headers: flattenHeaders(req.headers),\n body: syntheticReq,\n response: {\n status: fetched.status,\n fixture: null,\n source: \"proxy\",\n ...strictOverrideField(defaults.strict, req.headers),\n },\n });\n res.writeHead(fetched.status, { \"Content-Type\": fetched.contentType ?? \"application/json\" });\n res.end(fetched.text);\n return \"handled\";\n }\n\n let upstreamOperationName: string;\n {\n if (fetched.status < 200 || fetched.status >= 300) {\n return proxyError(`Submit ${fetched.status}: ${fetched.text.slice(0, 200)}`);\n }\n let parsed: unknown;\n try {\n parsed = JSON.parse(fetched.text);\n } catch {\n return proxyError(`Submit returned non-JSON: ${fetched.text.slice(0, 200)}`);\n }\n if (parsed === null || typeof parsed !== \"object\" || Array.isArray(parsed)) {\n return proxyError(\"Submit response is not a JSON object\");\n }\n upstreamOperationName = String((parsed as Record<string, unknown>).name ?? \"\").trim();\n if (!upstreamOperationName) {\n return proxyError(\"Submit response missing name\");\n }\n }\n\n // Veo has no polling_url field — the poll URL is constructed from the\n // provider base + `/v1beta/{operationName}`. Origin-validate the constructed\n // URL (the client's Authorization travels to it on every poll); fall back to\n // a string on the provider base on any parse failure.\n let upstreamPollingUrl: string;\n try {\n const constructed = resolveUpstreamUrl(\n upstreamBase,\n `/v1beta/${upstreamOperationName.replace(/^\\/+/, \"\")}`,\n );\n if (constructed.origin === upstreamOrigin) {\n upstreamPollingUrl = constructed.toString();\n } else {\n defaults.logger.warn(\n `Constructed Veo poll URL origin ${constructed.origin} differs from the configured provider origin ${upstreamOrigin} — using the provider-origin URL`,\n );\n upstreamPollingUrl = `${upstreamOrigin}/v1beta/${upstreamOperationName.replace(/^\\/+/, \"\")}`;\n }\n } catch {\n defaults.logger.warn(\n `Could not construct the Veo poll URL for ${upstreamOperationName} — using the provider-origin URL`,\n );\n upstreamPollingUrl = `${upstreamOrigin}/v1beta/${upstreamOperationName.replace(/^\\/+/, \"\")}`;\n }\n\n const testId = getTestId(req);\n const matchRequest = defaults.requestTransform\n ? defaults.requestTransform(syntheticReq)\n : syntheticReq;\n const operationName = `operations/${crypto.randomUUID()}`;\n const job: VeoVideoRecordJob = {\n kind: \"record\",\n operationName,\n status: \"pending\",\n upstreamOperationName,\n upstreamPollingUrl,\n match: buildFixtureMatch(matchRequest, record),\n };\n if (jobs.generation === worldGeneration) {\n jobs.set(`${testId}:${operationName}`, job);\n } else {\n defaults.logger.warn(\n `Veo video submit for upstream operation ${upstreamOperationName} completed after a fixtures reset — not inserting the job into the new world (its polls will 404)`,\n );\n }\n\n if (responseGone(res)) return \"handled\";\n journal.add({\n method,\n path,\n headers: flattenHeaders(req.headers),\n body: syntheticReq,\n response: {\n status: 200,\n fixture: null,\n source: \"proxy\",\n ...strictOverrideField(defaults.strict, req.headers),\n },\n });\n\n // Embed the testId in the RETURNED name (see the replay submit) so record-mode\n // multi-tenant polls resolve via `?testId=`; the upstream poll still uses the\n // bare job.operationName / job.upstreamPollingUrl, untouched by the suffix.\n res.writeHead(200, { \"Content-Type\": \"application/json\" });\n res.end(JSON.stringify({ name: operationName + testIdSuffix(testId, \"?\") }));\n return \"handled\";\n}\n\n// ─── Record mode: live interactive proxy (poll + eager capture) ─────────────\n\n/**\n * Proxy a status poll for a record-mode Veo job 1:1 to the upstream and relay\n * the result with the operation name rewritten to the mock operation (the\n * Files-API uri inside `response` passes through verbatim — aimock never\n * downloads bytes). When the upstream reports `done:true` with a `response`,\n * the rewritten body is relayed IMMEDIATELY and the eager capture (persist the\n * uri as a fixture, mutate to a terminal replay job) runs DETACHED. A\n * `done:true` with an `error` persists a failed fixture synchronously. Under\n * `record.proxyOnly` nothing is captured, persisted, or mutated. Every\n * post-await map mutation is identity-guarded.\n */\nasync function proxyVeoVideoRecordPoll(args: {\n req: http.IncomingMessage;\n res: http.ServerResponse;\n job: VeoVideoRecordJob;\n key: string;\n testId: string;\n fixtures: Fixture[];\n journal: Journal;\n defaults: HandlerDefaults;\n jobs: VeoVideoJobMap;\n method: string;\n path: string;\n}): Promise<void> {\n const { req, res, job, key, testId, fixtures, journal, defaults, jobs, method, path } = args;\n const logger = defaults.logger;\n\n const journalProxy = (status: number): void => {\n journal.add({\n method,\n path,\n headers: flattenHeaders(req.headers),\n body: null,\n response: {\n status,\n fixture: null,\n source: \"proxy\",\n ...strictOverrideField(defaults.strict, req.headers),\n },\n });\n };\n\n const proxyError = (msg: string): void => {\n logger.error(`Veo video poll proxy failed: ${msg}`);\n if (responseGone(res)) return;\n journalProxy(502);\n writeErrorResponse(\n res,\n 502,\n JSON.stringify({\n error: { message: `Proxy to upstream failed: ${msg}`, type: \"proxy_error\" },\n }),\n );\n };\n\n // Recording can be disabled mid-flight — an orphaned record job must fail\n // loudly before contacting the upstream.\n const record = defaults.record;\n if (!record) {\n proxyError(\"record mode is no longer configured for an in-flight record job\");\n return;\n }\n\n let fetched: { status: number; contentType: string | null; text: string };\n try {\n const target = new URL(job.upstreamPollingUrl);\n const headers = prepareEgressHeaders(req, target, \"veo\", record.providerKeys?.veo);\n if (!headers) {\n proxyError(\"No configured provider credential\");\n return;\n }\n const upstreamRes = await fetch(job.upstreamPollingUrl, {\n headers,\n signal: upstreamTimeoutSignal(record),\n });\n fetched = {\n status: upstreamRes.status,\n contentType: upstreamRes.headers.get(\"content-type\"),\n text: await readEnvelopeText(upstreamRes, record),\n };\n } catch (err) {\n proxyError(err instanceof Error ? err.message : \"Unknown proxy error\");\n return;\n }\n\n if (fetched.status === 401 || fetched.status === 403) {\n logger.warn(\n `Upstream rejected the Veo status poll for operation ${job.upstreamOperationName} (${fetched.status}) — relaying the upstream status`,\n );\n if (responseGone(res)) return;\n journalProxy(fetched.status);\n res.writeHead(fetched.status, { \"Content-Type\": fetched.contentType ?? \"application/json\" });\n res.end(fetched.text);\n return;\n }\n\n let upstreamBody: Record<string, unknown>;\n {\n if (fetched.status < 200 || fetched.status >= 300) {\n proxyError(`Status ${fetched.status}: ${fetched.text.slice(0, 200)}`);\n return;\n }\n let parsed: unknown;\n try {\n parsed = JSON.parse(fetched.text);\n } catch {\n proxyError(`Status returned non-JSON: ${fetched.text.slice(0, 200)}`);\n return;\n }\n if (parsed === null || typeof parsed !== \"object\" || Array.isArray(parsed)) {\n proxyError(\"Status response is not a JSON object\");\n return;\n }\n upstreamBody = parsed as Record<string, unknown>;\n }\n\n // Rewrite ONLY the top-level operation name so no upstream identifier\n // escapes — the `response` (carrying the Files-API uri the client needs) and\n // every other field pass through verbatim.\n const relayBody: Record<string, unknown> = { ...upstreamBody, name: job.operationName };\n\n const relayJson = (): void => {\n if (responseGone(res)) return;\n journalProxy(200);\n res.writeHead(200, { \"Content-Type\": \"application/json\" });\n res.end(JSON.stringify(relayBody));\n };\n\n const done = upstreamBody.done === true;\n const upstreamError = upstreamBody.error;\n const hasError =\n upstreamError !== undefined && upstreamError !== null && typeof upstreamError === \"object\";\n\n if (!done) {\n // Non-terminal: identity-guarded status write + TTL refresh, relay.\n if (jobs.get(key) === job) {\n if (!job.capturing && job.status !== \"completed\" && job.status !== \"failed\") {\n job.status = \"in_progress\";\n }\n jobs.set(key, job);\n }\n relayJson();\n return;\n }\n\n if (hasError) {\n // Terminal failure: persist a failed fixture synchronously (mirrors the\n // OpenRouter failed branch), then relay.\n if (record.proxyOnly) {\n if (jobs.get(key) === job) {\n job.status = \"failed\";\n jobs.set(key, job);\n }\n relayJson();\n return;\n }\n if (jobs.get(key) !== job) {\n // A concurrent terminal poll already persisted/replaced the entry, or a\n // fixtures reset cleared the world — relay without a duplicate fixture.\n relayJson();\n return;\n }\n const errObj = upstreamError as Record<string, unknown>;\n const rawMessage = errObj.message;\n const error = typeof rawMessage === \"string\" && rawMessage ? rawMessage : undefined;\n const video: VideoResponse[\"video\"] = {\n id: job.upstreamOperationName,\n status: \"failed\",\n ...(error !== undefined ? { error } : {}),\n };\n const persistResult = persistFixture({\n record,\n providerKey: \"veo\",\n testId,\n fixture: { match: job.match, response: { video } },\n fixtures,\n logger,\n });\n if (persistResult.kind === \"failed\" && !res.headersSent) {\n res.setHeader(\"X-AIMock-Record-Error\", sanitizeHeaderValue(persistResult.error));\n }\n jobs.set(key, {\n kind: \"replay\",\n operationName: job.operationName,\n status: \"failed\",\n pollCount: 0,\n pollsBeforeInProgress: 0,\n pollsBeforeCompleted: 0,\n video: { ...video },\n });\n relayJson();\n return;\n }\n\n // Terminal success (done:true with a response).\n if (record.proxyOnly) {\n if (jobs.get(key) === job) {\n job.status = \"completed\";\n jobs.set(key, job);\n }\n relayJson();\n return;\n }\n\n if (job.capturing || jobs.get(key) !== job) {\n // A concurrent poll already entered the capture sequence, or this\n // reference is detached — relay without starting a second capture and\n // never re-insert the stale object.\n if (jobs.get(key) === job) {\n jobs.set(key, job); // TTL refresh\n }\n relayJson();\n return;\n }\n\n // Open the capturing window SYNCHRONOUSLY before the first await.\n job.capturing = true;\n job.status = \"completed\";\n jobs.set(key, job); // TTL refresh — identity-checked above, no await since\n\n // Relay IMMEDIATELY; the persist + replay mutation runs DETACHED.\n relayJson();\n\n void captureVeoVideoRecordFixture({\n job,\n key,\n testId,\n fixtures,\n defaults,\n jobs,\n record,\n upstreamBody,\n });\n}\n\n/**\n * Detached eager-capture for a completed Veo record job. Extracts the\n * Files-API uri from the terminal poll body (NO download), persists a\n * fixture, and mutates the map entry into a terminal replay job. Every failure\n * is handled internally; the returned promise NEVER rejects. World-generation\n * guarded immediately before persist.\n */\nasync function captureVeoVideoRecordFixture(args: {\n job: VeoVideoRecordJob;\n key: string;\n testId: string;\n fixtures: Fixture[];\n defaults: HandlerDefaults;\n jobs: VeoVideoJobMap;\n record: RecordConfig;\n upstreamBody: Record<string, unknown>;\n}): Promise<void> {\n const { job, key, testId, fixtures, defaults, jobs, record, upstreamBody } = args;\n const logger = defaults.logger;\n\n try {\n // Drill into response.generateVideoResponse.generatedSamples[0].video.uri.\n let uri: string | undefined;\n const response = upstreamBody.response;\n if (response !== null && typeof response === \"object\" && !Array.isArray(response)) {\n const gvr = (response as Record<string, unknown>).generateVideoResponse;\n if (gvr !== null && typeof gvr === \"object\" && !Array.isArray(gvr)) {\n const samples = (gvr as Record<string, unknown>).generatedSamples;\n const first = Array.isArray(samples) ? samples[0] : undefined;\n if (first !== null && typeof first === \"object\" && !Array.isArray(first)) {\n const video = (first as Record<string, unknown>).video;\n if (video !== null && typeof video === \"object\" && !Array.isArray(video)) {\n const rawUri = (video as Record<string, unknown>).uri;\n if (typeof rawUri === \"string\" && rawUri) uri = rawUri;\n }\n }\n }\n }\n if (!uri) {\n logger.warn(\n `Upstream Veo operation ${job.upstreamOperationName} completed without a usable Files-API uri — capture skipped, nothing persisted; the next completed poll retries`,\n );\n return;\n }\n\n const video: VideoResponse[\"video\"] = {\n id: job.upstreamOperationName,\n status: \"completed\",\n url: uri,\n };\n\n // World-generation guard: a fixtures reset landing during the await above\n // clears the job map — map identity is a valid proxy for \"same world\".\n if (jobs.get(key) !== job) {\n logger.warn(\n `Veo video capture for operation ${job.upstreamOperationName} discarded: the job map no longer holds this job (fixtures reset or TTL eviction) — nothing persisted`,\n );\n return;\n }\n persistFixture({\n record,\n providerKey: \"veo\",\n testId,\n fixture: { match: job.match, response: { video } },\n fixtures,\n logger,\n });\n\n // Mutate the entry into a terminal replay job: later polls serve locally.\n if (jobs.get(key) === job) {\n jobs.set(key, {\n kind: \"replay\",\n operationName: job.operationName,\n status: \"completed\",\n pollCount: 0,\n pollsBeforeInProgress: 0,\n pollsBeforeCompleted: 0,\n video: { ...video },\n });\n }\n } catch (err) {\n const msg = err instanceof Error ? err.message : String(err);\n logger.error(\n `Veo video capture for operation ${job.upstreamOperationName} failed unexpectedly (${msg}) — fixture not persisted; the job keeps proxying live`,\n );\n } finally {\n // Close the capturing window when the map still holds this record job (a\n // step threw before the replay mutation). On the success path the entry\n // was just replaced; the detached object deliberately keeps capturing=true.\n if (jobs.get(key) === job) {\n job.capturing = false;\n }\n }\n}\n"],"mappings":"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"}
|