@copilotkit/aimock 1.21.0 → 1.22.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.
Files changed (199) hide show
  1. package/.claude-plugin/plugin.json +1 -1
  2. package/CHANGELOG.md +41 -0
  3. package/README.md +1 -0
  4. package/dist/a2a-mock.cjs +1 -1
  5. package/dist/a2a-mock.cjs.map +1 -1
  6. package/dist/a2a-mock.d.cts.map +1 -1
  7. package/dist/a2a-mock.d.ts.map +1 -1
  8. package/dist/a2a-mock.js +1 -1
  9. package/dist/a2a-mock.js.map +1 -1
  10. package/dist/agui-recorder.cjs +25 -12
  11. package/dist/agui-recorder.cjs.map +1 -1
  12. package/dist/agui-recorder.js +25 -12
  13. package/dist/agui-recorder.js.map +1 -1
  14. package/dist/agui-types.d.ts.map +1 -1
  15. package/dist/bedrock-converse.cjs +18 -12
  16. package/dist/bedrock-converse.cjs.map +1 -1
  17. package/dist/bedrock-converse.d.cts.map +1 -1
  18. package/dist/bedrock-converse.d.ts.map +1 -1
  19. package/dist/bedrock-converse.js +19 -13
  20. package/dist/bedrock-converse.js.map +1 -1
  21. package/dist/bedrock.cjs +18 -12
  22. package/dist/bedrock.cjs.map +1 -1
  23. package/dist/bedrock.d.cts.map +1 -1
  24. package/dist/bedrock.d.ts.map +1 -1
  25. package/dist/bedrock.js +19 -13
  26. package/dist/bedrock.js.map +1 -1
  27. package/dist/cli.cjs +1 -1
  28. package/dist/cli.cjs.map +1 -1
  29. package/dist/cli.js +1 -1
  30. package/dist/cli.js.map +1 -1
  31. package/dist/cohere.cjs +9 -6
  32. package/dist/cohere.cjs.map +1 -1
  33. package/dist/cohere.d.cts.map +1 -1
  34. package/dist/cohere.d.ts.map +1 -1
  35. package/dist/cohere.js +10 -7
  36. package/dist/cohere.js.map +1 -1
  37. package/dist/config-loader.d.cts.map +1 -1
  38. package/dist/config-loader.d.ts.map +1 -1
  39. package/dist/elevenlabs-audio.cjs +8 -5
  40. package/dist/elevenlabs-audio.cjs.map +1 -1
  41. package/dist/elevenlabs-audio.d.cts.map +1 -1
  42. package/dist/elevenlabs-audio.d.ts.map +1 -1
  43. package/dist/elevenlabs-audio.js +9 -6
  44. package/dist/elevenlabs-audio.js.map +1 -1
  45. package/dist/embeddings.cjs +6 -4
  46. package/dist/embeddings.cjs.map +1 -1
  47. package/dist/embeddings.d.cts.map +1 -1
  48. package/dist/embeddings.d.ts.map +1 -1
  49. package/dist/embeddings.js +7 -5
  50. package/dist/embeddings.js.map +1 -1
  51. package/dist/fal-audio.cjs +16 -10
  52. package/dist/fal-audio.cjs.map +1 -1
  53. package/dist/fal-audio.d.cts.map +1 -1
  54. package/dist/fal-audio.d.ts.map +1 -1
  55. package/dist/fal-audio.js +17 -11
  56. package/dist/fal-audio.js.map +1 -1
  57. package/dist/fal.cjs +5 -3
  58. package/dist/fal.cjs.map +1 -1
  59. package/dist/fal.d.cts.map +1 -1
  60. package/dist/fal.d.ts.map +1 -1
  61. package/dist/fal.js +6 -4
  62. package/dist/fal.js.map +1 -1
  63. package/dist/gemini-interactions.cjs +10 -7
  64. package/dist/gemini-interactions.cjs.map +1 -1
  65. package/dist/gemini-interactions.d.cts.map +1 -1
  66. package/dist/gemini-interactions.d.ts.map +1 -1
  67. package/dist/gemini-interactions.js +11 -8
  68. package/dist/gemini-interactions.js.map +1 -1
  69. package/dist/gemini.cjs +10 -7
  70. package/dist/gemini.cjs.map +1 -1
  71. package/dist/gemini.d.cts.map +1 -1
  72. package/dist/gemini.d.ts.map +1 -1
  73. package/dist/gemini.js +11 -8
  74. package/dist/gemini.js.map +1 -1
  75. package/dist/helpers.cjs +31 -0
  76. package/dist/helpers.cjs.map +1 -1
  77. package/dist/helpers.d.cts +1 -0
  78. package/dist/helpers.d.cts.map +1 -1
  79. package/dist/helpers.d.ts +1 -0
  80. package/dist/helpers.d.ts.map +1 -1
  81. package/dist/helpers.js +30 -1
  82. package/dist/helpers.js.map +1 -1
  83. package/dist/images.cjs +8 -5
  84. package/dist/images.cjs.map +1 -1
  85. package/dist/images.d.cts.map +1 -1
  86. package/dist/images.d.ts.map +1 -1
  87. package/dist/images.js +9 -6
  88. package/dist/images.js.map +1 -1
  89. package/dist/mcp-mock.cjs +1 -1
  90. package/dist/mcp-mock.cjs.map +1 -1
  91. package/dist/mcp-mock.d.cts.map +1 -1
  92. package/dist/mcp-mock.d.ts.map +1 -1
  93. package/dist/mcp-mock.js +1 -1
  94. package/dist/mcp-mock.js.map +1 -1
  95. package/dist/messages.cjs +9 -6
  96. package/dist/messages.cjs.map +1 -1
  97. package/dist/messages.d.cts.map +1 -1
  98. package/dist/messages.d.ts.map +1 -1
  99. package/dist/messages.js +10 -7
  100. package/dist/messages.js.map +1 -1
  101. package/dist/moderation.cjs +3 -2
  102. package/dist/moderation.cjs.map +1 -1
  103. package/dist/moderation.js +3 -2
  104. package/dist/moderation.js.map +1 -1
  105. package/dist/ollama.cjs +18 -12
  106. package/dist/ollama.cjs.map +1 -1
  107. package/dist/ollama.d.cts.map +1 -1
  108. package/dist/ollama.d.ts.map +1 -1
  109. package/dist/ollama.js +19 -13
  110. package/dist/ollama.js.map +1 -1
  111. package/dist/recorder.cjs +82 -38
  112. package/dist/recorder.cjs.map +1 -1
  113. package/dist/recorder.d.cts +3 -2
  114. package/dist/recorder.d.cts.map +1 -1
  115. package/dist/recorder.d.ts +3 -2
  116. package/dist/recorder.d.ts.map +1 -1
  117. package/dist/recorder.js +82 -38
  118. package/dist/recorder.js.map +1 -1
  119. package/dist/rerank.cjs +3 -2
  120. package/dist/rerank.cjs.map +1 -1
  121. package/dist/rerank.js +3 -2
  122. package/dist/rerank.js.map +1 -1
  123. package/dist/responses.cjs +9 -6
  124. package/dist/responses.cjs.map +1 -1
  125. package/dist/responses.d.cts.map +1 -1
  126. package/dist/responses.d.ts.map +1 -1
  127. package/dist/responses.js +10 -7
  128. package/dist/responses.js.map +1 -1
  129. package/dist/search.cjs +3 -2
  130. package/dist/search.cjs.map +1 -1
  131. package/dist/search.js +3 -2
  132. package/dist/search.js.map +1 -1
  133. package/dist/server.cjs +135 -73
  134. package/dist/server.cjs.map +1 -1
  135. package/dist/server.d.cts.map +1 -1
  136. package/dist/server.d.ts.map +1 -1
  137. package/dist/server.js +136 -74
  138. package/dist/server.js.map +1 -1
  139. package/dist/speech.cjs +8 -5
  140. package/dist/speech.cjs.map +1 -1
  141. package/dist/speech.d.cts.map +1 -1
  142. package/dist/speech.d.ts.map +1 -1
  143. package/dist/speech.js +9 -6
  144. package/dist/speech.js.map +1 -1
  145. package/dist/stream-collapse.cjs +51 -21
  146. package/dist/stream-collapse.cjs.map +1 -1
  147. package/dist/stream-collapse.d.cts +1 -0
  148. package/dist/stream-collapse.d.cts.map +1 -1
  149. package/dist/stream-collapse.d.ts +1 -0
  150. package/dist/stream-collapse.d.ts.map +1 -1
  151. package/dist/stream-collapse.js +51 -21
  152. package/dist/stream-collapse.js.map +1 -1
  153. package/dist/transcription.cjs +5 -3
  154. package/dist/transcription.cjs.map +1 -1
  155. package/dist/transcription.d.cts.map +1 -1
  156. package/dist/transcription.d.ts.map +1 -1
  157. package/dist/transcription.js +6 -4
  158. package/dist/transcription.js.map +1 -1
  159. package/dist/types.d.cts +2 -0
  160. package/dist/types.d.cts.map +1 -1
  161. package/dist/types.d.ts +2 -0
  162. package/dist/types.d.ts.map +1 -1
  163. package/dist/vector-mock.cjs +10 -8
  164. package/dist/vector-mock.cjs.map +1 -1
  165. package/dist/vector-mock.d.cts.map +1 -1
  166. package/dist/vector-mock.d.ts.map +1 -1
  167. package/dist/vector-mock.js +10 -8
  168. package/dist/vector-mock.js.map +1 -1
  169. package/dist/video.cjs +8 -5
  170. package/dist/video.cjs.map +1 -1
  171. package/dist/video.d.cts.map +1 -1
  172. package/dist/video.d.ts.map +1 -1
  173. package/dist/video.js +9 -6
  174. package/dist/video.js.map +1 -1
  175. package/dist/ws-gemini-live.cjs +6 -4
  176. package/dist/ws-gemini-live.cjs.map +1 -1
  177. package/dist/ws-gemini-live.d.cts +2 -0
  178. package/dist/ws-gemini-live.d.cts.map +1 -1
  179. package/dist/ws-gemini-live.d.ts +2 -0
  180. package/dist/ws-gemini-live.d.ts.map +1 -1
  181. package/dist/ws-gemini-live.js +7 -5
  182. package/dist/ws-gemini-live.js.map +1 -1
  183. package/dist/ws-realtime.cjs +6 -4
  184. package/dist/ws-realtime.cjs.map +1 -1
  185. package/dist/ws-realtime.d.cts +2 -0
  186. package/dist/ws-realtime.d.cts.map +1 -1
  187. package/dist/ws-realtime.d.ts +2 -0
  188. package/dist/ws-realtime.d.ts.map +1 -1
  189. package/dist/ws-realtime.js +7 -5
  190. package/dist/ws-realtime.js.map +1 -1
  191. package/dist/ws-responses.cjs +6 -4
  192. package/dist/ws-responses.cjs.map +1 -1
  193. package/dist/ws-responses.d.cts +2 -0
  194. package/dist/ws-responses.d.cts.map +1 -1
  195. package/dist/ws-responses.d.ts +2 -0
  196. package/dist/ws-responses.d.ts.map +1 -1
  197. package/dist/ws-responses.js +7 -5
  198. package/dist/ws-responses.js.map +1 -1
  199. package/package.json +1 -1
@@ -1 +1 @@
1
- {"version":3,"file":"search.cjs","names":["flattenHeaders","matchesPattern"],"sources":["../src/search.ts"],"sourcesContent":["/**\n * Web Search API support for LLMock.\n *\n * Handles POST /search requests (Tavily-compatible). Matches fixtures by\n * comparing the request `query` field against registered patterns. First\n * match wins; no match returns empty results.\n */\n\nimport type * as http from \"node:http\";\nimport { flattenHeaders, matchesPattern } from \"./helpers.js\";\nimport type { Journal } from \"./journal.js\";\nimport type { Logger } from \"./logger.js\";\n\n// ─── Search types ─────────────────────────────────────────────────────────\n\nexport interface SearchResult {\n title: string;\n url: string;\n content: string;\n score?: number;\n}\n\nexport interface SearchFixture {\n match: string | RegExp;\n results: SearchResult[];\n}\n\n// ─── Request handler ──────────────────────────────────────────────────────\n\nexport async function handleSearch(\n req: http.IncomingMessage,\n res: http.ServerResponse,\n raw: string,\n fixtures: SearchFixture[],\n journal: Journal,\n defaults: { logger: Logger },\n setCorsHeaders: (res: http.ServerResponse) => void,\n): Promise<void> {\n const { logger } = defaults;\n setCorsHeaders(res);\n\n let body: { query?: string; max_results?: number };\n try {\n body = JSON.parse(raw) as { query?: string; max_results?: number };\n } catch {\n journal.add({\n method: req.method ?? \"POST\",\n path: req.url ?? \"/search\",\n headers: flattenHeaders(req.headers),\n body: null,\n service: \"search\",\n response: { status: 400, fixture: null },\n });\n res.writeHead(400, { \"Content-Type\": \"application/json\" });\n res.end(\n JSON.stringify({\n error: {\n message: \"Malformed JSON\",\n type: \"invalid_request_error\",\n code: \"invalid_json\",\n },\n }),\n );\n return;\n }\n\n const query = body.query ?? \"\";\n const maxResults = body.max_results;\n\n // Find first matching fixture\n let matchedResults: SearchResult[] = [];\n let matchedFixture: SearchFixture | null = null;\n\n for (const fixture of fixtures) {\n if (matchesPattern(query, fixture.match)) {\n matchedFixture = fixture;\n matchedResults = fixture.results;\n break;\n }\n }\n\n if (matchedFixture) {\n logger.debug(`Search fixture matched for query \"${query.slice(0, 80)}\"`);\n } else {\n logger.debug(`No search fixture matched for query \"${query.slice(0, 80)}\" — returning empty`);\n }\n\n // Apply max_results limit\n if (maxResults !== undefined && maxResults > 0) {\n matchedResults = matchedResults.slice(0, maxResults);\n }\n\n journal.add({\n method: req.method ?? \"POST\",\n path: req.url ?? \"/search\",\n headers: flattenHeaders(req.headers),\n body: null,\n service: \"search\",\n response: { status: 200, fixture: null },\n });\n\n res.writeHead(200, { \"Content-Type\": \"application/json\" });\n res.end(\n JSON.stringify({\n query,\n results: matchedResults,\n images: [],\n response_time: 0,\n answer: null,\n }),\n );\n}\n"],"mappings":";;;AA6BA,eAAsB,aACpB,KACA,KACA,KACA,UACA,SACA,UACA,gBACe;CACf,MAAM,EAAE,WAAW;AACnB,gBAAe,IAAI;CAEnB,IAAI;AACJ,KAAI;AACF,SAAO,KAAK,MAAM,IAAI;SAChB;AACN,UAAQ,IAAI;GACV,QAAQ,IAAI,UAAU;GACtB,MAAM,IAAI,OAAO;GACjB,SAASA,+BAAe,IAAI,QAAQ;GACpC,MAAM;GACN,SAAS;GACT,UAAU;IAAE,QAAQ;IAAK,SAAS;IAAM;GACzC,CAAC;AACF,MAAI,UAAU,KAAK,EAAE,gBAAgB,oBAAoB,CAAC;AAC1D,MAAI,IACF,KAAK,UAAU,EACb,OAAO;GACL,SAAS;GACT,MAAM;GACN,MAAM;GACP,EACF,CAAC,CACH;AACD;;CAGF,MAAM,QAAQ,KAAK,SAAS;CAC5B,MAAM,aAAa,KAAK;CAGxB,IAAI,iBAAiC,EAAE;CACvC,IAAI,iBAAuC;AAE3C,MAAK,MAAM,WAAW,SACpB,KAAIC,+BAAe,OAAO,QAAQ,MAAM,EAAE;AACxC,mBAAiB;AACjB,mBAAiB,QAAQ;AACzB;;AAIJ,KAAI,eACF,QAAO,MAAM,qCAAqC,MAAM,MAAM,GAAG,GAAG,CAAC,GAAG;KAExE,QAAO,MAAM,wCAAwC,MAAM,MAAM,GAAG,GAAG,CAAC,qBAAqB;AAI/F,KAAI,eAAe,UAAa,aAAa,EAC3C,kBAAiB,eAAe,MAAM,GAAG,WAAW;AAGtD,SAAQ,IAAI;EACV,QAAQ,IAAI,UAAU;EACtB,MAAM,IAAI,OAAO;EACjB,SAASD,+BAAe,IAAI,QAAQ;EACpC,MAAM;EACN,SAAS;EACT,UAAU;GAAE,QAAQ;GAAK,SAAS;GAAM;EACzC,CAAC;AAEF,KAAI,UAAU,KAAK,EAAE,gBAAgB,oBAAoB,CAAC;AAC1D,KAAI,IACF,KAAK,UAAU;EACb;EACA,SAAS;EACT,QAAQ,EAAE;EACV,eAAe;EACf,QAAQ;EACT,CAAC,CACH"}
1
+ {"version":3,"file":"search.cjs","names":["flattenHeaders","matchesPattern"],"sources":["../src/search.ts"],"sourcesContent":["/**\n * Web Search API support for LLMock.\n *\n * Handles POST /search requests (Tavily-compatible). Matches fixtures by\n * comparing the request `query` field against registered patterns. First\n * match wins; no match returns empty results.\n */\n\nimport type * as http from \"node:http\";\nimport { flattenHeaders, matchesPattern } from \"./helpers.js\";\nimport type { Journal } from \"./journal.js\";\nimport type { Logger } from \"./logger.js\";\n\n// ─── Search types ─────────────────────────────────────────────────────────\n\nexport interface SearchResult {\n title: string;\n url: string;\n content: string;\n score?: number;\n}\n\nexport interface SearchFixture {\n match: string | RegExp;\n results: SearchResult[];\n}\n\n// ─── Request handler ──────────────────────────────────────────────────────\n\nexport async function handleSearch(\n req: http.IncomingMessage,\n res: http.ServerResponse,\n raw: string,\n fixtures: SearchFixture[],\n journal: Journal,\n defaults: { logger: Logger },\n setCorsHeaders: (res: http.ServerResponse) => void,\n): Promise<void> {\n const { logger } = defaults;\n setCorsHeaders(res);\n\n let body: { query?: string; max_results?: number };\n try {\n body = JSON.parse(raw) as { query?: string; max_results?: number };\n } catch (parseErr) {\n const detail = parseErr instanceof Error ? parseErr.message : \"unknown\";\n journal.add({\n method: req.method ?? \"POST\",\n path: req.url ?? \"/search\",\n headers: flattenHeaders(req.headers),\n body: null,\n service: \"search\",\n response: { status: 400, fixture: null },\n });\n res.writeHead(400, { \"Content-Type\": \"application/json\" });\n res.end(\n JSON.stringify({\n error: {\n message: `Malformed JSON: ${detail}`,\n type: \"invalid_request_error\",\n code: \"invalid_json\",\n },\n }),\n );\n return;\n }\n\n const query = body.query ?? \"\";\n const maxResults = body.max_results;\n\n // Find first matching fixture\n let matchedResults: SearchResult[] = [];\n let matchedFixture: SearchFixture | null = null;\n\n for (const fixture of fixtures) {\n if (matchesPattern(query, fixture.match)) {\n matchedFixture = fixture;\n matchedResults = fixture.results;\n break;\n }\n }\n\n if (matchedFixture) {\n logger.debug(`Search fixture matched for query \"${query.slice(0, 80)}\"`);\n } else {\n logger.debug(`No search fixture matched for query \"${query.slice(0, 80)}\" — returning empty`);\n }\n\n // Apply max_results limit\n if (maxResults !== undefined && maxResults > 0) {\n matchedResults = matchedResults.slice(0, maxResults);\n }\n\n journal.add({\n method: req.method ?? \"POST\",\n path: req.url ?? \"/search\",\n headers: flattenHeaders(req.headers),\n body: null,\n service: \"search\",\n response: { status: 200, fixture: null },\n });\n\n res.writeHead(200, { \"Content-Type\": \"application/json\" });\n res.end(\n JSON.stringify({\n query,\n results: matchedResults,\n images: [],\n response_time: 0,\n answer: null,\n }),\n );\n}\n"],"mappings":";;;AA6BA,eAAsB,aACpB,KACA,KACA,KACA,UACA,SACA,UACA,gBACe;CACf,MAAM,EAAE,WAAW;AACnB,gBAAe,IAAI;CAEnB,IAAI;AACJ,KAAI;AACF,SAAO,KAAK,MAAM,IAAI;UACf,UAAU;EACjB,MAAM,SAAS,oBAAoB,QAAQ,SAAS,UAAU;AAC9D,UAAQ,IAAI;GACV,QAAQ,IAAI,UAAU;GACtB,MAAM,IAAI,OAAO;GACjB,SAASA,+BAAe,IAAI,QAAQ;GACpC,MAAM;GACN,SAAS;GACT,UAAU;IAAE,QAAQ;IAAK,SAAS;IAAM;GACzC,CAAC;AACF,MAAI,UAAU,KAAK,EAAE,gBAAgB,oBAAoB,CAAC;AAC1D,MAAI,IACF,KAAK,UAAU,EACb,OAAO;GACL,SAAS,mBAAmB;GAC5B,MAAM;GACN,MAAM;GACP,EACF,CAAC,CACH;AACD;;CAGF,MAAM,QAAQ,KAAK,SAAS;CAC5B,MAAM,aAAa,KAAK;CAGxB,IAAI,iBAAiC,EAAE;CACvC,IAAI,iBAAuC;AAE3C,MAAK,MAAM,WAAW,SACpB,KAAIC,+BAAe,OAAO,QAAQ,MAAM,EAAE;AACxC,mBAAiB;AACjB,mBAAiB,QAAQ;AACzB;;AAIJ,KAAI,eACF,QAAO,MAAM,qCAAqC,MAAM,MAAM,GAAG,GAAG,CAAC,GAAG;KAExE,QAAO,MAAM,wCAAwC,MAAM,MAAM,GAAG,GAAG,CAAC,qBAAqB;AAI/F,KAAI,eAAe,UAAa,aAAa,EAC3C,kBAAiB,eAAe,MAAM,GAAG,WAAW;AAGtD,SAAQ,IAAI;EACV,QAAQ,IAAI,UAAU;EACtB,MAAM,IAAI,OAAO;EACjB,SAASD,+BAAe,IAAI,QAAQ;EACpC,MAAM;EACN,SAAS;EACT,UAAU;GAAE,QAAQ;GAAK,SAAS;GAAM;EACzC,CAAC;AAEF,KAAI,UAAU,KAAK,EAAE,gBAAgB,oBAAoB,CAAC;AAC1D,KAAI,IACF,KAAK,UAAU;EACb;EACA,SAAS;EACT,QAAQ,EAAE;EACV,eAAe;EACf,QAAQ;EACT,CAAC,CACH"}
package/dist/search.js CHANGED
@@ -7,7 +7,8 @@ async function handleSearch(req, res, raw, fixtures, journal, defaults, setCorsH
7
7
  let body;
8
8
  try {
9
9
  body = JSON.parse(raw);
10
- } catch {
10
+ } catch (parseErr) {
11
+ const detail = parseErr instanceof Error ? parseErr.message : "unknown";
11
12
  journal.add({
12
13
  method: req.method ?? "POST",
13
14
  path: req.url ?? "/search",
@@ -21,7 +22,7 @@ async function handleSearch(req, res, raw, fixtures, journal, defaults, setCorsH
21
22
  });
22
23
  res.writeHead(400, { "Content-Type": "application/json" });
23
24
  res.end(JSON.stringify({ error: {
24
- message: "Malformed JSON",
25
+ message: `Malformed JSON: ${detail}`,
25
26
  type: "invalid_request_error",
26
27
  code: "invalid_json"
27
28
  } }));
@@ -1 +1 @@
1
- {"version":3,"file":"search.js","names":[],"sources":["../src/search.ts"],"sourcesContent":["/**\n * Web Search API support for LLMock.\n *\n * Handles POST /search requests (Tavily-compatible). Matches fixtures by\n * comparing the request `query` field against registered patterns. First\n * match wins; no match returns empty results.\n */\n\nimport type * as http from \"node:http\";\nimport { flattenHeaders, matchesPattern } from \"./helpers.js\";\nimport type { Journal } from \"./journal.js\";\nimport type { Logger } from \"./logger.js\";\n\n// ─── Search types ─────────────────────────────────────────────────────────\n\nexport interface SearchResult {\n title: string;\n url: string;\n content: string;\n score?: number;\n}\n\nexport interface SearchFixture {\n match: string | RegExp;\n results: SearchResult[];\n}\n\n// ─── Request handler ──────────────────────────────────────────────────────\n\nexport async function handleSearch(\n req: http.IncomingMessage,\n res: http.ServerResponse,\n raw: string,\n fixtures: SearchFixture[],\n journal: Journal,\n defaults: { logger: Logger },\n setCorsHeaders: (res: http.ServerResponse) => void,\n): Promise<void> {\n const { logger } = defaults;\n setCorsHeaders(res);\n\n let body: { query?: string; max_results?: number };\n try {\n body = JSON.parse(raw) as { query?: string; max_results?: number };\n } catch {\n journal.add({\n method: req.method ?? \"POST\",\n path: req.url ?? \"/search\",\n headers: flattenHeaders(req.headers),\n body: null,\n service: \"search\",\n response: { status: 400, fixture: null },\n });\n res.writeHead(400, { \"Content-Type\": \"application/json\" });\n res.end(\n JSON.stringify({\n error: {\n message: \"Malformed JSON\",\n type: \"invalid_request_error\",\n code: \"invalid_json\",\n },\n }),\n );\n return;\n }\n\n const query = body.query ?? \"\";\n const maxResults = body.max_results;\n\n // Find first matching fixture\n let matchedResults: SearchResult[] = [];\n let matchedFixture: SearchFixture | null = null;\n\n for (const fixture of fixtures) {\n if (matchesPattern(query, fixture.match)) {\n matchedFixture = fixture;\n matchedResults = fixture.results;\n break;\n }\n }\n\n if (matchedFixture) {\n logger.debug(`Search fixture matched for query \"${query.slice(0, 80)}\"`);\n } else {\n logger.debug(`No search fixture matched for query \"${query.slice(0, 80)}\" — returning empty`);\n }\n\n // Apply max_results limit\n if (maxResults !== undefined && maxResults > 0) {\n matchedResults = matchedResults.slice(0, maxResults);\n }\n\n journal.add({\n method: req.method ?? \"POST\",\n path: req.url ?? \"/search\",\n headers: flattenHeaders(req.headers),\n body: null,\n service: \"search\",\n response: { status: 200, fixture: null },\n });\n\n res.writeHead(200, { \"Content-Type\": \"application/json\" });\n res.end(\n JSON.stringify({\n query,\n results: matchedResults,\n images: [],\n response_time: 0,\n answer: null,\n }),\n );\n}\n"],"mappings":";;;AA6BA,eAAsB,aACpB,KACA,KACA,KACA,UACA,SACA,UACA,gBACe;CACf,MAAM,EAAE,WAAW;AACnB,gBAAe,IAAI;CAEnB,IAAI;AACJ,KAAI;AACF,SAAO,KAAK,MAAM,IAAI;SAChB;AACN,UAAQ,IAAI;GACV,QAAQ,IAAI,UAAU;GACtB,MAAM,IAAI,OAAO;GACjB,SAAS,eAAe,IAAI,QAAQ;GACpC,MAAM;GACN,SAAS;GACT,UAAU;IAAE,QAAQ;IAAK,SAAS;IAAM;GACzC,CAAC;AACF,MAAI,UAAU,KAAK,EAAE,gBAAgB,oBAAoB,CAAC;AAC1D,MAAI,IACF,KAAK,UAAU,EACb,OAAO;GACL,SAAS;GACT,MAAM;GACN,MAAM;GACP,EACF,CAAC,CACH;AACD;;CAGF,MAAM,QAAQ,KAAK,SAAS;CAC5B,MAAM,aAAa,KAAK;CAGxB,IAAI,iBAAiC,EAAE;CACvC,IAAI,iBAAuC;AAE3C,MAAK,MAAM,WAAW,SACpB,KAAI,eAAe,OAAO,QAAQ,MAAM,EAAE;AACxC,mBAAiB;AACjB,mBAAiB,QAAQ;AACzB;;AAIJ,KAAI,eACF,QAAO,MAAM,qCAAqC,MAAM,MAAM,GAAG,GAAG,CAAC,GAAG;KAExE,QAAO,MAAM,wCAAwC,MAAM,MAAM,GAAG,GAAG,CAAC,qBAAqB;AAI/F,KAAI,eAAe,UAAa,aAAa,EAC3C,kBAAiB,eAAe,MAAM,GAAG,WAAW;AAGtD,SAAQ,IAAI;EACV,QAAQ,IAAI,UAAU;EACtB,MAAM,IAAI,OAAO;EACjB,SAAS,eAAe,IAAI,QAAQ;EACpC,MAAM;EACN,SAAS;EACT,UAAU;GAAE,QAAQ;GAAK,SAAS;GAAM;EACzC,CAAC;AAEF,KAAI,UAAU,KAAK,EAAE,gBAAgB,oBAAoB,CAAC;AAC1D,KAAI,IACF,KAAK,UAAU;EACb;EACA,SAAS;EACT,QAAQ,EAAE;EACV,eAAe;EACf,QAAQ;EACT,CAAC,CACH"}
1
+ {"version":3,"file":"search.js","names":[],"sources":["../src/search.ts"],"sourcesContent":["/**\n * Web Search API support for LLMock.\n *\n * Handles POST /search requests (Tavily-compatible). Matches fixtures by\n * comparing the request `query` field against registered patterns. First\n * match wins; no match returns empty results.\n */\n\nimport type * as http from \"node:http\";\nimport { flattenHeaders, matchesPattern } from \"./helpers.js\";\nimport type { Journal } from \"./journal.js\";\nimport type { Logger } from \"./logger.js\";\n\n// ─── Search types ─────────────────────────────────────────────────────────\n\nexport interface SearchResult {\n title: string;\n url: string;\n content: string;\n score?: number;\n}\n\nexport interface SearchFixture {\n match: string | RegExp;\n results: SearchResult[];\n}\n\n// ─── Request handler ──────────────────────────────────────────────────────\n\nexport async function handleSearch(\n req: http.IncomingMessage,\n res: http.ServerResponse,\n raw: string,\n fixtures: SearchFixture[],\n journal: Journal,\n defaults: { logger: Logger },\n setCorsHeaders: (res: http.ServerResponse) => void,\n): Promise<void> {\n const { logger } = defaults;\n setCorsHeaders(res);\n\n let body: { query?: string; max_results?: number };\n try {\n body = JSON.parse(raw) as { query?: string; max_results?: number };\n } catch (parseErr) {\n const detail = parseErr instanceof Error ? parseErr.message : \"unknown\";\n journal.add({\n method: req.method ?? \"POST\",\n path: req.url ?? \"/search\",\n headers: flattenHeaders(req.headers),\n body: null,\n service: \"search\",\n response: { status: 400, fixture: null },\n });\n res.writeHead(400, { \"Content-Type\": \"application/json\" });\n res.end(\n JSON.stringify({\n error: {\n message: `Malformed JSON: ${detail}`,\n type: \"invalid_request_error\",\n code: \"invalid_json\",\n },\n }),\n );\n return;\n }\n\n const query = body.query ?? \"\";\n const maxResults = body.max_results;\n\n // Find first matching fixture\n let matchedResults: SearchResult[] = [];\n let matchedFixture: SearchFixture | null = null;\n\n for (const fixture of fixtures) {\n if (matchesPattern(query, fixture.match)) {\n matchedFixture = fixture;\n matchedResults = fixture.results;\n break;\n }\n }\n\n if (matchedFixture) {\n logger.debug(`Search fixture matched for query \"${query.slice(0, 80)}\"`);\n } else {\n logger.debug(`No search fixture matched for query \"${query.slice(0, 80)}\" — returning empty`);\n }\n\n // Apply max_results limit\n if (maxResults !== undefined && maxResults > 0) {\n matchedResults = matchedResults.slice(0, maxResults);\n }\n\n journal.add({\n method: req.method ?? \"POST\",\n path: req.url ?? \"/search\",\n headers: flattenHeaders(req.headers),\n body: null,\n service: \"search\",\n response: { status: 200, fixture: null },\n });\n\n res.writeHead(200, { \"Content-Type\": \"application/json\" });\n res.end(\n JSON.stringify({\n query,\n results: matchedResults,\n images: [],\n response_time: 0,\n answer: null,\n }),\n );\n}\n"],"mappings":";;;AA6BA,eAAsB,aACpB,KACA,KACA,KACA,UACA,SACA,UACA,gBACe;CACf,MAAM,EAAE,WAAW;AACnB,gBAAe,IAAI;CAEnB,IAAI;AACJ,KAAI;AACF,SAAO,KAAK,MAAM,IAAI;UACf,UAAU;EACjB,MAAM,SAAS,oBAAoB,QAAQ,SAAS,UAAU;AAC9D,UAAQ,IAAI;GACV,QAAQ,IAAI,UAAU;GACtB,MAAM,IAAI,OAAO;GACjB,SAAS,eAAe,IAAI,QAAQ;GACpC,MAAM;GACN,SAAS;GACT,UAAU;IAAE,QAAQ;IAAK,SAAS;IAAM;GACzC,CAAC;AACF,MAAI,UAAU,KAAK,EAAE,gBAAgB,oBAAoB,CAAC;AAC1D,MAAI,IACF,KAAK,UAAU,EACb,OAAO;GACL,SAAS,mBAAmB;GAC5B,MAAM;GACN,MAAM;GACP,EACF,CAAC,CACH;AACD;;CAGF,MAAM,QAAQ,KAAK,SAAS;CAC5B,MAAM,aAAa,KAAK;CAGxB,IAAI,iBAAiC,EAAE;CACvC,IAAI,iBAAuC;AAE3C,MAAK,MAAM,WAAW,SACpB,KAAI,eAAe,OAAO,QAAQ,MAAM,EAAE;AACxC,mBAAiB;AACjB,mBAAiB,QAAQ;AACzB;;AAIJ,KAAI,eACF,QAAO,MAAM,qCAAqC,MAAM,MAAM,GAAG,GAAG,CAAC,GAAG;KAExE,QAAO,MAAM,wCAAwC,MAAM,MAAM,GAAG,GAAG,CAAC,qBAAqB;AAI/F,KAAI,eAAe,UAAa,aAAa,EAC3C,kBAAiB,eAAe,MAAM,GAAG,WAAW;AAGtD,SAAQ,IAAI;EACV,QAAQ,IAAI,UAAU;EACtB,MAAM,IAAI,OAAO;EACjB,SAAS,eAAe,IAAI,QAAQ;EACpC,MAAM;EACN,SAAS;EACT,UAAU;GAAE,QAAQ;GAAK,SAAS;GAAM;EACzC,CAAC;AAEF,KAAI,UAAU,KAAK,EAAE,gBAAgB,oBAAoB,CAAC;AAC1D,KAAI,IACF,KAAK,UAAU;EACb;EACA,SAAS;EACT,QAAQ,EAAE;EACV,eAAe;EACf,QAAQ;EACT,CAAC,CACH"}
package/dist/server.cjs CHANGED
@@ -254,12 +254,17 @@ async function handleControlAPI(req, res, pathname, fixtures, journal, videoStat
254
254
  }
255
255
  };
256
256
  fixtures.unshift(errorFixture);
257
+ let consumed = false;
257
258
  const original = errorFixture.match.predicate;
258
259
  errorFixture.match.predicate = (req) => {
260
+ if (consumed) return false;
259
261
  const result = original(req);
260
262
  if (result) {
261
- const idx = fixtures.indexOf(errorFixture);
262
- if (idx !== -1) fixtures.splice(idx, 1);
263
+ consumed = true;
264
+ queueMicrotask(() => {
265
+ const idx = fixtures.indexOf(errorFixture);
266
+ if (idx !== -1) fixtures.splice(idx, 1);
267
+ });
263
268
  }
264
269
  return result;
265
270
  };
@@ -297,7 +302,8 @@ async function handleCompletions(req, res, fixtures, journal, defaults, modelFal
297
302
  try {
298
303
  body = JSON.parse(raw);
299
304
  if (modelFallback && !body.model) body.model = modelFallback;
300
- } catch {
305
+ } catch (parseErr) {
306
+ const detail = parseErr instanceof Error ? parseErr.message : "unknown parse error";
301
307
  journal.add({
302
308
  method: req.method ?? "POST",
303
309
  path: req.url ?? COMPLETIONS_PATH,
@@ -309,7 +315,7 @@ async function handleCompletions(req, res, fixtures, journal, defaults, modelFal
309
315
  }
310
316
  });
311
317
  require_sse_writer.writeErrorResponse(res, 400, JSON.stringify({ error: {
312
- message: "Malformed JSON",
318
+ message: `Malformed JSON: ${detail}`,
313
319
  type: "invalid_request_error",
314
320
  param: null,
315
321
  code: "invalid_json"
@@ -372,7 +378,7 @@ async function handleCompletions(req, res, fixtures, journal, defaults, modelFal
372
378
  return true;
373
379
  },
374
380
  onHookBypassed: (reason) => {
375
- defaults.logger.warn(`[chaos] malformed bypassed on proxy: upstream returned SSE (${reason})`);
381
+ defaults.logger.warn(`[chaos] malformed bypassed on proxy: upstream returned streaming response (${reason})`);
376
382
  defaults.registry?.incrementCounter("aimock_chaos_bypassed_total", {
377
383
  action: "malformed",
378
384
  source: "proxy",
@@ -397,9 +403,10 @@ async function handleCompletions(req, res, fixtures, journal, defaults, modelFal
397
403
  return;
398
404
  }
399
405
  }
400
- const strictStatus = defaults.strict ? 503 : 404;
401
- const strictMessage = defaults.strict ? "Strict mode: no fixture matched" : "No fixture matched";
402
- if (defaults.strict) defaults.logger.error(`STRICT: No fixture matched for ${req.method ?? "POST"} ${req.url ?? COMPLETIONS_PATH}`);
406
+ const effectiveStrict = require_helpers.resolveStrictMode(defaults.strict, req.headers);
407
+ const strictStatus = effectiveStrict ? 503 : 404;
408
+ const strictMessage = effectiveStrict ? "Strict mode: no fixture matched" : "No fixture matched";
409
+ if (effectiveStrict) defaults.logger.error(`STRICT: No fixture matched for ${req.method ?? "POST"} ${req.url ?? COMPLETIONS_PATH}`);
403
410
  journal.add({
404
411
  method: req.method ?? "POST",
405
412
  path: req.url ?? COMPLETIONS_PATH,
@@ -407,7 +414,8 @@ async function handleCompletions(req, res, fixtures, journal, defaults, modelFal
407
414
  body,
408
415
  response: {
409
416
  status: strictStatus,
410
- fixture: null
417
+ fixture: null,
418
+ ...require_helpers.strictOverrideField(defaults.strict, req.headers)
411
419
  }
412
420
  });
413
421
  require_sse_writer.writeErrorResponse(res, strictStatus, JSON.stringify({ error: {
@@ -636,7 +644,7 @@ async function createServer(fixtures, options, mounts, serviceFixtures) {
636
644
  message: msg,
637
645
  type: "server_error"
638
646
  } }));
639
- }
647
+ } else if (!res.writableEnded) res.end();
640
648
  });
641
649
  });
642
650
  async function handleHttpRequest(req, res) {
@@ -777,13 +785,11 @@ async function createServer(fixtures, options, mounts, serviceFixtures) {
777
785
  message: msg,
778
786
  type: "server_error"
779
787
  } }));
780
- else if (!res.writableEnded) {
781
- try {
782
- res.write(`event: error\ndata: ${JSON.stringify({ error: { message: msg } })}\n\n`);
783
- } catch (writeErr) {
784
- logger.debug("Failed to write error recovery response:", writeErr);
785
- }
788
+ else if (!res.writableEnded) try {
789
+ res.write(`event: error\ndata: ${JSON.stringify({ error: { message: msg } })}\n\n`);
786
790
  res.end();
791
+ } catch (writeErr) {
792
+ logger.debug("Failed to write error recovery response:", writeErr);
787
793
  }
788
794
  }
789
795
  return;
@@ -797,13 +803,11 @@ async function createServer(fixtures, options, mounts, serviceFixtures) {
797
803
  message: msg,
798
804
  type: "server_error"
799
805
  } }));
800
- else if (!res.writableEnded) {
801
- try {
802
- res.write(`event: error\ndata: ${JSON.stringify({ error: { message: msg } })}\n\n`);
803
- } catch (writeErr) {
804
- logger.debug("Failed to write error recovery response:", writeErr);
805
- }
806
+ else if (!res.writableEnded) try {
807
+ res.write(`event: error\ndata: ${JSON.stringify({ error: { message: msg } })}\n\n`);
806
808
  res.end();
809
+ } catch (writeErr) {
810
+ logger.debug("Failed to write error recovery response:", writeErr);
807
811
  }
808
812
  }
809
813
  return;
@@ -817,13 +821,11 @@ async function createServer(fixtures, options, mounts, serviceFixtures) {
817
821
  message: msg,
818
822
  type: "server_error"
819
823
  } }));
820
- else if (!res.writableEnded) {
821
- try {
822
- res.write(`event: error\ndata: ${JSON.stringify({ error: { message: msg } })}\n\n`);
823
- } catch (writeErr) {
824
- logger.debug("Failed to write error recovery response:", writeErr);
825
- }
824
+ else if (!res.writableEnded) try {
825
+ res.write(`event: error\ndata: ${JSON.stringify({ error: { message: msg } })}\n\n`);
826
826
  res.end();
827
+ } catch (writeErr) {
828
+ logger.debug("Failed to write error recovery response:", writeErr);
827
829
  }
828
830
  }
829
831
  return;
@@ -839,7 +841,9 @@ async function createServer(fixtures, options, mounts, serviceFixtures) {
839
841
  parsed.model = deploymentId;
840
842
  raw = JSON.stringify(parsed);
841
843
  }
842
- } catch {}
844
+ } catch (err) {
845
+ if (!(err instanceof SyntaxError)) defaults.logger.error(`Unexpected error in Azure model injection: ${err instanceof Error ? err.message : String(err)}`);
846
+ }
843
847
  await require_embeddings.handleEmbeddings(req, res, raw, fixtures, journal, defaults, setCorsHeaders, embeddingsProvider);
844
848
  } catch (err) {
845
849
  const msg = err instanceof Error ? err.message : "Internal error";
@@ -933,13 +937,11 @@ async function createServer(fixtures, options, mounts, serviceFixtures) {
933
937
  message: msg,
934
938
  type: "server_error"
935
939
  } }));
936
- else if (!res.writableEnded) {
937
- try {
938
- res.write(`data: ${JSON.stringify({ error: { message: msg } })}\n\n`);
939
- } catch (writeErr) {
940
- logger.debug("Failed to write error recovery response:", writeErr);
941
- }
940
+ else if (!res.writableEnded) try {
941
+ res.write(`data: ${JSON.stringify({ error: { message: msg } })}\n\n`);
942
942
  res.end();
943
+ } catch (writeErr) {
944
+ logger.debug("Failed to write error recovery response:", writeErr);
943
945
  }
944
946
  }
945
947
  return;
@@ -956,13 +958,11 @@ async function createServer(fixtures, options, mounts, serviceFixtures) {
956
958
  message: msg,
957
959
  type: "server_error"
958
960
  } }));
959
- else if (!res.writableEnded) {
960
- try {
961
- res.write(`data: ${JSON.stringify({ error: { message: msg } })}\n\n`);
962
- } catch (writeErr) {
963
- logger.debug("Failed to write error recovery response:", writeErr);
964
- }
961
+ else if (!res.writableEnded) try {
962
+ res.write(`data: ${JSON.stringify({ error: { message: msg } })}\n\n`);
965
963
  res.end();
964
+ } catch (writeErr) {
965
+ logger.debug("Failed to write error recovery response:", writeErr);
966
966
  }
967
967
  }
968
968
  return;
@@ -979,13 +979,11 @@ async function createServer(fixtures, options, mounts, serviceFixtures) {
979
979
  message: msg,
980
980
  type: "server_error"
981
981
  } }));
982
- else if (!res.writableEnded) {
983
- try {
984
- res.write(`data: ${JSON.stringify({ error: { message: msg } })}\n\n`);
985
- } catch (writeErr) {
986
- logger.debug("Failed to write error recovery response:", writeErr);
987
- }
982
+ else if (!res.writableEnded) try {
983
+ res.write(`data: ${JSON.stringify({ error: { message: msg } })}\n\n`);
988
984
  res.end();
985
+ } catch (writeErr) {
986
+ logger.debug("Failed to write error recovery response:", writeErr);
989
987
  }
990
988
  }
991
989
  return;
@@ -1135,9 +1133,23 @@ async function createServer(fixtures, options, mounts, serviceFixtures) {
1135
1133
  setCorsHeaders(res);
1136
1134
  try {
1137
1135
  const raw = await readBody(req);
1138
- const chaosAction = require_chaos.evaluateChaos(null, defaults.chaos, req.headers, defaults.logger);
1136
+ let elSoundFixture = null;
1137
+ try {
1138
+ const parsed = JSON.parse(raw);
1139
+ const syntheticReq = {
1140
+ model: parsed.model_id ?? "eleven_text_to_sound_v2",
1141
+ messages: [{
1142
+ role: "user",
1143
+ content: parsed.text ?? ""
1144
+ }],
1145
+ _endpointType: "audio-gen"
1146
+ };
1147
+ const testId = require_helpers.getTestId(req);
1148
+ elSoundFixture = require_router.matchFixture(fixtures, syntheticReq, journal.getFixtureMatchCountsForTest(testId), defaults.requestTransform);
1149
+ } catch {}
1150
+ const chaosAction = require_chaos.evaluateChaos(elSoundFixture, defaults.chaos, req.headers, defaults.logger);
1139
1151
  if (chaosAction) {
1140
- require_chaos.applyChaosAction(chaosAction, res, null, journal, {
1152
+ require_chaos.applyChaosAction(chaosAction, res, elSoundFixture, journal, {
1141
1153
  method: req.method ?? "POST",
1142
1154
  path: pathname,
1143
1155
  headers: require_helpers.flattenHeaders(req.headers),
@@ -1165,9 +1177,24 @@ async function createServer(fixtures, options, mounts, serviceFixtures) {
1165
1177
  const musicSubType = musicMatch[1] ?? "music";
1166
1178
  try {
1167
1179
  const raw = await readBody(req);
1168
- const chaosAction = require_chaos.evaluateChaos(null, defaults.chaos, req.headers, defaults.logger);
1180
+ let elMusicFixture = null;
1181
+ try {
1182
+ const parsed = JSON.parse(raw);
1183
+ const prompt = (typeof parsed.prompt === "string" ? parsed.prompt : null) ?? (parsed.composition_plan != null ? typeof parsed.composition_plan === "string" ? parsed.composition_plan : JSON.stringify(parsed.composition_plan) : "");
1184
+ const syntheticReq = {
1185
+ model: parsed.model_id ?? "music_v1",
1186
+ messages: [{
1187
+ role: "user",
1188
+ content: prompt
1189
+ }],
1190
+ _endpointType: "audio-gen"
1191
+ };
1192
+ const testId = require_helpers.getTestId(req);
1193
+ elMusicFixture = require_router.matchFixture(fixtures, syntheticReq, journal.getFixtureMatchCountsForTest(testId), defaults.requestTransform);
1194
+ } catch {}
1195
+ const chaosAction = require_chaos.evaluateChaos(elMusicFixture, defaults.chaos, req.headers, defaults.logger);
1169
1196
  if (chaosAction) {
1170
- require_chaos.applyChaosAction(chaosAction, res, null, journal, {
1197
+ require_chaos.applyChaosAction(chaosAction, res, elMusicFixture, journal, {
1171
1198
  method: req.method ?? "POST",
1172
1199
  path: pathname,
1173
1200
  headers: require_helpers.flattenHeaders(req.headers),
@@ -1189,10 +1216,12 @@ async function createServer(fixtures, options, mounts, serviceFixtures) {
1189
1216
  }
1190
1217
  return;
1191
1218
  }
1219
+ let falBody;
1192
1220
  if (FAL_PREFIX_RE.test(pathname) && req.headers["x-fal-target-host"]) {
1193
1221
  setCorsHeaders(res);
1194
1222
  try {
1195
- const raw = req.method === "POST" || req.method === "PUT" ? await readBody(req) : "";
1223
+ falBody = req.method === "POST" || req.method === "PUT" ? await readBody(req) : "";
1224
+ const raw = falBody;
1196
1225
  const chaosAction = require_chaos.evaluateChaos(null, defaults.chaos, req.headers, defaults.logger);
1197
1226
  if (chaosAction) {
1198
1227
  require_chaos.applyChaosAction(chaosAction, res, null, journal, {
@@ -1217,13 +1246,29 @@ async function createServer(fixtures, options, mounts, serviceFixtures) {
1217
1246
  return;
1218
1247
  }
1219
1248
  }
1220
- if (pathname.match(FAL_QUEUE_SUBMIT_RE) && req.method === "POST") {
1249
+ const falQueueSubmitMatch = pathname.match(FAL_QUEUE_SUBMIT_RE);
1250
+ if (falQueueSubmitMatch && req.method === "POST") {
1221
1251
  setCorsHeaders(res);
1222
1252
  try {
1223
- const raw = await readBody(req);
1224
- const chaosAction = require_chaos.evaluateChaos(null, defaults.chaos, req.headers, defaults.logger);
1253
+ const raw = falBody ?? await readBody(req);
1254
+ let falSubmitFixture = null;
1255
+ try {
1256
+ const parsed = raw.trim() ? JSON.parse(raw) : {};
1257
+ const prompt = (typeof parsed.prompt === "string" ? parsed.prompt : null) ?? (typeof parsed.text === "string" ? parsed.text : null) ?? "";
1258
+ const syntheticReq = {
1259
+ model: falQueueSubmitMatch[1],
1260
+ messages: [{
1261
+ role: "user",
1262
+ content: prompt
1263
+ }],
1264
+ _endpointType: "fal-audio"
1265
+ };
1266
+ const testId = require_helpers.getTestId(req);
1267
+ falSubmitFixture = require_router.matchFixture(fixtures, syntheticReq, journal.getFixtureMatchCountsForTest(testId), defaults.requestTransform);
1268
+ } catch {}
1269
+ const chaosAction = require_chaos.evaluateChaos(falSubmitFixture, defaults.chaos, req.headers, defaults.logger);
1225
1270
  if (chaosAction) {
1226
- require_chaos.applyChaosAction(chaosAction, res, null, journal, {
1271
+ require_chaos.applyChaosAction(chaosAction, res, falSubmitFixture, journal, {
1227
1272
  method: req.method ?? "POST",
1228
1273
  path: pathname,
1229
1274
  headers: require_helpers.flattenHeaders(req.headers),
@@ -1248,7 +1293,7 @@ async function createServer(fixtures, options, mounts, serviceFixtures) {
1248
1293
  if (pathname.match(FAL_QUEUE_REQUESTS_RE) && (req.method === "GET" || req.method === "POST" || req.method === "PUT")) {
1249
1294
  setCorsHeaders(res);
1250
1295
  try {
1251
- const raw = req.method === "POST" ? await readBody(req) : "{}";
1296
+ const raw = req.method === "POST" || req.method === "PUT" ? falBody ?? await readBody(req) : "{}";
1252
1297
  const chaosAction = require_chaos.evaluateChaos(null, defaults.chaos, req.headers, defaults.logger);
1253
1298
  if (chaosAction) {
1254
1299
  require_chaos.applyChaosAction(chaosAction, res, null, journal, {
@@ -1273,13 +1318,29 @@ async function createServer(fixtures, options, mounts, serviceFixtures) {
1273
1318
  }
1274
1319
  return;
1275
1320
  }
1276
- if (pathname.match(FAL_RUN_RE) && req.method === "POST") {
1321
+ const falRunMatch = pathname.match(FAL_RUN_RE);
1322
+ if (falRunMatch && req.method === "POST") {
1277
1323
  setCorsHeaders(res);
1278
1324
  try {
1279
- const raw = await readBody(req);
1280
- const chaosAction = require_chaos.evaluateChaos(null, defaults.chaos, req.headers, defaults.logger);
1325
+ const raw = falBody ?? await readBody(req);
1326
+ let falRunFixture = null;
1327
+ try {
1328
+ const parsed = raw.trim() ? JSON.parse(raw) : {};
1329
+ const prompt = (typeof parsed.prompt === "string" ? parsed.prompt : null) ?? (typeof parsed.text === "string" ? parsed.text : null) ?? "";
1330
+ const syntheticReq = {
1331
+ model: falRunMatch[1],
1332
+ messages: [{
1333
+ role: "user",
1334
+ content: prompt
1335
+ }],
1336
+ _endpointType: "fal-audio"
1337
+ };
1338
+ const testId = require_helpers.getTestId(req);
1339
+ falRunFixture = require_router.matchFixture(fixtures, syntheticReq, journal.getFixtureMatchCountsForTest(testId), defaults.requestTransform);
1340
+ } catch {}
1341
+ const chaosAction = require_chaos.evaluateChaos(falRunFixture, defaults.chaos, req.headers, defaults.logger);
1281
1342
  if (chaosAction) {
1282
- require_chaos.applyChaosAction(chaosAction, res, null, journal, {
1343
+ require_chaos.applyChaosAction(chaosAction, res, falRunFixture, journal, {
1283
1344
  method: req.method ?? "POST",
1284
1345
  path: pathname,
1285
1346
  headers: require_helpers.flattenHeaders(req.headers),
@@ -1318,16 +1379,14 @@ async function createServer(fixtures, options, mounts, serviceFixtures) {
1318
1379
  message: msg,
1319
1380
  type: "server_error"
1320
1381
  } }));
1321
- else if (!res.writableEnded) {
1322
- try {
1323
- res.write(`data: ${JSON.stringify({ error: {
1324
- message: msg,
1325
- type: "server_error"
1326
- } })}\n\n`);
1327
- } catch (writeErr) {
1328
- logger.debug("Failed to write error recovery response:", writeErr);
1329
- }
1382
+ else if (!res.writableEnded) try {
1383
+ res.write(`data: ${JSON.stringify({ error: {
1384
+ message: msg,
1385
+ type: "server_error"
1386
+ } })}\n\n`);
1330
1387
  res.end();
1388
+ } catch (writeErr) {
1389
+ logger.debug("Failed to write error recovery response:", writeErr);
1331
1390
  }
1332
1391
  }
1333
1392
  }
@@ -1376,19 +1435,22 @@ async function createServer(fixtures, options, mounts, serviceFixtures) {
1376
1435
  if (pathname === RESPONSES_PATH) require_ws_responses.handleWebSocketResponses(ws, fixtures, journal, {
1377
1436
  ...defaults,
1378
1437
  model: "gpt-4",
1379
- testId: wsTestId
1438
+ testId: wsTestId,
1439
+ upgradeHeaders: req.headers
1380
1440
  });
1381
1441
  else if (pathname === REALTIME_PATH) {
1382
1442
  const model = parsedUrl.searchParams.get("model") ?? "gpt-4o-realtime";
1383
1443
  require_ws_realtime.handleWebSocketRealtime(ws, fixtures, journal, {
1384
1444
  ...defaults,
1385
1445
  model,
1386
- testId: wsTestId
1446
+ testId: wsTestId,
1447
+ upgradeHeaders: req.headers
1387
1448
  });
1388
1449
  } else if (pathname === GEMINI_LIVE_PATH) require_ws_gemini_live.handleWebSocketGeminiLive(ws, fixtures, journal, {
1389
1450
  ...defaults,
1390
1451
  model: "gemini-2.0-flash",
1391
- testId: wsTestId
1452
+ testId: wsTestId,
1453
+ upgradeHeaders: req.headers
1392
1454
  });
1393
1455
  }
1394
1456
  const originalClose = server.close.bind(server);