@copilotkit/aimock 1.14.9 → 1.15.1

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 (119) hide show
  1. package/CHANGELOG.md +29 -0
  2. package/dist/bedrock-converse.cjs +129 -21
  3. package/dist/bedrock-converse.cjs.map +1 -1
  4. package/dist/bedrock-converse.d.cts.map +1 -1
  5. package/dist/bedrock-converse.d.ts.map +1 -1
  6. package/dist/bedrock-converse.js +131 -23
  7. package/dist/bedrock-converse.js.map +1 -1
  8. package/dist/bedrock.cjs +258 -44
  9. package/dist/bedrock.cjs.map +1 -1
  10. package/dist/bedrock.d.cts.map +1 -1
  11. package/dist/bedrock.d.ts.map +1 -1
  12. package/dist/bedrock.js +259 -46
  13. package/dist/bedrock.js.map +1 -1
  14. package/dist/cohere.cjs +287 -31
  15. package/dist/cohere.cjs.map +1 -1
  16. package/dist/cohere.d.cts +9 -0
  17. package/dist/cohere.d.cts.map +1 -1
  18. package/dist/cohere.d.ts +9 -0
  19. package/dist/cohere.d.ts.map +1 -1
  20. package/dist/cohere.js +288 -32
  21. package/dist/cohere.js.map +1 -1
  22. package/dist/config-loader.d.ts.map +1 -1
  23. package/dist/embeddings.cjs +21 -3
  24. package/dist/embeddings.cjs.map +1 -1
  25. package/dist/embeddings.d.cts +2 -2
  26. package/dist/embeddings.d.cts.map +1 -1
  27. package/dist/embeddings.d.ts +2 -2
  28. package/dist/embeddings.d.ts.map +1 -1
  29. package/dist/embeddings.js +21 -3
  30. package/dist/embeddings.js.map +1 -1
  31. package/dist/gemini.cjs +46 -43
  32. package/dist/gemini.cjs.map +1 -1
  33. package/dist/gemini.d.cts.map +1 -1
  34. package/dist/gemini.d.ts.map +1 -1
  35. package/dist/gemini.js +46 -43
  36. package/dist/gemini.js.map +1 -1
  37. package/dist/helpers.cjs +9 -0
  38. package/dist/helpers.cjs.map +1 -1
  39. package/dist/helpers.d.cts.map +1 -1
  40. package/dist/helpers.d.ts.map +1 -1
  41. package/dist/helpers.js +9 -0
  42. package/dist/helpers.js.map +1 -1
  43. package/dist/images.cjs +19 -1
  44. package/dist/images.cjs.map +1 -1
  45. package/dist/images.js +19 -1
  46. package/dist/images.js.map +1 -1
  47. package/dist/index.cjs +2 -0
  48. package/dist/index.d.cts +2 -2
  49. package/dist/index.d.ts +2 -2
  50. package/dist/index.js +3 -3
  51. package/dist/jest.cjs +10 -3
  52. package/dist/jest.cjs.map +1 -1
  53. package/dist/jest.js +10 -3
  54. package/dist/jest.js.map +1 -1
  55. package/dist/journal.cjs +1 -1
  56. package/dist/journal.cjs.map +1 -1
  57. package/dist/journal.d.cts.map +1 -1
  58. package/dist/journal.d.ts.map +1 -1
  59. package/dist/journal.js +1 -1
  60. package/dist/journal.js.map +1 -1
  61. package/dist/messages.cjs +3 -2
  62. package/dist/messages.cjs.map +1 -1
  63. package/dist/messages.js +3 -2
  64. package/dist/messages.js.map +1 -1
  65. package/dist/ollama.cjs +125 -4
  66. package/dist/ollama.cjs.map +1 -1
  67. package/dist/ollama.d.cts.map +1 -1
  68. package/dist/ollama.d.ts.map +1 -1
  69. package/dist/ollama.js +126 -5
  70. package/dist/ollama.js.map +1 -1
  71. package/dist/recorder.cjs +224 -53
  72. package/dist/recorder.cjs.map +1 -1
  73. package/dist/recorder.js +224 -53
  74. package/dist/recorder.js.map +1 -1
  75. package/dist/responses.cjs +10 -1
  76. package/dist/responses.cjs.map +1 -1
  77. package/dist/responses.d.cts +2 -1
  78. package/dist/responses.d.cts.map +1 -1
  79. package/dist/responses.d.ts +2 -1
  80. package/dist/responses.d.ts.map +1 -1
  81. package/dist/responses.js +10 -2
  82. package/dist/responses.js.map +1 -1
  83. package/dist/router.cjs +13 -6
  84. package/dist/router.cjs.map +1 -1
  85. package/dist/router.js +13 -6
  86. package/dist/router.js.map +1 -1
  87. package/dist/server.cjs +144 -62
  88. package/dist/server.cjs.map +1 -1
  89. package/dist/server.d.cts.map +1 -1
  90. package/dist/server.d.ts.map +1 -1
  91. package/dist/server.js +145 -63
  92. package/dist/server.js.map +1 -1
  93. package/dist/speech.cjs +19 -1
  94. package/dist/speech.cjs.map +1 -1
  95. package/dist/speech.js +19 -1
  96. package/dist/speech.js.map +1 -1
  97. package/dist/transcription.cjs +8 -4
  98. package/dist/transcription.cjs.map +1 -1
  99. package/dist/transcription.d.cts.map +1 -1
  100. package/dist/transcription.d.ts.map +1 -1
  101. package/dist/transcription.js +8 -4
  102. package/dist/transcription.js.map +1 -1
  103. package/dist/types.d.cts +1 -0
  104. package/dist/types.d.cts.map +1 -1
  105. package/dist/types.d.ts +1 -0
  106. package/dist/types.d.ts.map +1 -1
  107. package/dist/video.cjs +63 -1
  108. package/dist/video.cjs.map +1 -1
  109. package/dist/video.d.cts +15 -2
  110. package/dist/video.d.cts.map +1 -1
  111. package/dist/video.d.ts +15 -2
  112. package/dist/video.d.ts.map +1 -1
  113. package/dist/video.js +63 -2
  114. package/dist/video.js.map +1 -1
  115. package/dist/vitest.cjs +10 -3
  116. package/dist/vitest.cjs.map +1 -1
  117. package/dist/vitest.js +10 -3
  118. package/dist/vitest.js.map +1 -1
  119. package/package.json +1 -1
@@ -1 +1 @@
1
- {"version":3,"file":"transcription.cjs","names":["getTestId","matchFixture","applyChaos","flattenHeaders","proxyAndRecord","isErrorResponse","isTranscriptionResponse"],"sources":["../src/transcription.ts"],"sourcesContent":["import type * as http from \"node:http\";\nimport type { ChatCompletionRequest, Fixture, HandlerDefaults } from \"./types.js\";\nimport { isTranscriptionResponse, isErrorResponse, flattenHeaders, getTestId } from \"./helpers.js\";\nimport { matchFixture } from \"./router.js\";\nimport { writeErrorResponse } from \"./sse-writer.js\";\nimport type { Journal } from \"./journal.js\";\nimport { applyChaos } from \"./chaos.js\";\nimport { proxyAndRecord } from \"./recorder.js\";\n\n/**\n * Extract a named field value from a multipart/form-data body.\n * Lightweight parser scans for Content-Disposition headers\n * to find simple string field values.\n */\nfunction extractFormField(raw: string, fieldName: string): string | undefined {\n const pattern = new RegExp(\n `Content-Disposition:\\\\s*form-data;[^\\\\r\\\\n]*name=\"${fieldName}\"[^\\\\r\\\\n]*\\\\r\\\\n\\\\r\\\\n([^\\\\r\\\\n]*)`,\n \"i\",\n );\n const match = raw.match(pattern);\n return match?.[1];\n}\n\nexport async function handleTranscription(\n req: http.IncomingMessage,\n res: http.ServerResponse,\n raw: string,\n fixtures: Fixture[],\n journal: Journal,\n defaults: HandlerDefaults,\n setCorsHeaders: (res: http.ServerResponse) => void,\n): Promise<void> {\n setCorsHeaders(res);\n const path = req.url ?? \"/v1/audio/transcriptions\";\n const method = req.method ?? \"POST\";\n\n const model = extractFormField(raw, \"model\") ?? \"whisper-1\";\n const responseFormat = extractFormField(raw, \"response_format\") ?? \"json\";\n\n const syntheticReq: ChatCompletionRequest = {\n model,\n messages: [],\n _endpointType: \"transcription\",\n };\n\n const testId = getTestId(req);\n const fixture = matchFixture(\n fixtures,\n syntheticReq,\n journal.getFixtureMatchCountsForTest(testId),\n defaults.requestTransform,\n );\n\n if (fixture) {\n journal.incrementFixtureMatchCount(fixture, fixtures, testId);\n }\n\n if (\n applyChaos(\n res,\n fixture,\n defaults.chaos,\n req.headers,\n journal,\n { method, path, headers: flattenHeaders(req.headers), body: syntheticReq },\n defaults.registry,\n defaults.logger,\n )\n )\n return;\n\n if (!fixture) {\n if (defaults.record) {\n const proxied = await proxyAndRecord(\n req,\n res,\n syntheticReq,\n \"openai\",\n req.url ?? \"/v1/audio/transcriptions\",\n fixtures,\n defaults,\n raw,\n );\n if (proxied) {\n journal.add({\n method,\n path,\n headers: flattenHeaders(req.headers),\n body: syntheticReq,\n response: { status: res.statusCode ?? 200, fixture: null },\n });\n return;\n }\n }\n\n const strictStatus = defaults.strict ? 503 : 404;\n const strictMessage = defaults.strict\n ? \"Strict mode: no fixture matched\"\n : \"No fixture matched\";\n journal.add({\n method,\n path,\n headers: flattenHeaders(req.headers),\n body: syntheticReq,\n response: { status: strictStatus, fixture: null },\n });\n writeErrorResponse(\n res,\n strictStatus,\n JSON.stringify({\n error: {\n message: strictMessage,\n type: \"invalid_request_error\",\n code: \"no_fixture_match\",\n },\n }),\n );\n return;\n }\n\n const response = fixture.response;\n\n if (isErrorResponse(response)) {\n const status = response.status ?? 500;\n journal.add({\n method,\n path,\n headers: flattenHeaders(req.headers),\n body: syntheticReq,\n response: { status, fixture },\n });\n writeErrorResponse(res, status, JSON.stringify(response));\n return;\n }\n\n if (!isTranscriptionResponse(response)) {\n journal.add({\n method,\n path,\n headers: flattenHeaders(req.headers),\n body: syntheticReq,\n response: { status: 500, fixture },\n });\n writeErrorResponse(\n res,\n 500,\n JSON.stringify({\n error: {\n message: \"Fixture response is not a transcription type\",\n type: \"server_error\",\n },\n }),\n );\n return;\n }\n\n journal.add({\n method,\n path,\n headers: flattenHeaders(req.headers),\n body: syntheticReq,\n response: { status: 200, fixture },\n });\n\n const t = response.transcription;\n const useVerbose = responseFormat === \"verbose_json\" || t.words != null || t.segments != null;\n\n if (useVerbose) {\n res.writeHead(200, { \"Content-Type\": \"application/json\" });\n res.end(\n JSON.stringify({\n task: \"transcribe\",\n language: t.language ?? \"english\",\n duration: t.duration ?? 0,\n text: t.text,\n words: t.words ?? [],\n segments: t.segments ?? [],\n }),\n );\n } else {\n res.writeHead(200, { \"Content-Type\": \"application/json\" });\n res.end(JSON.stringify({ text: t.text }));\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"transcription.cjs","names":["getTestId","matchFixture","applyChaos","flattenHeaders","proxyAndRecord","isErrorResponse","isTranscriptionResponse"],"sources":["../src/transcription.ts"],"sourcesContent":["import type * as http from \"node:http\";\nimport type { ChatCompletionRequest, Fixture, HandlerDefaults } from \"./types.js\";\nimport { isTranscriptionResponse, isErrorResponse, flattenHeaders, getTestId } from \"./helpers.js\";\nimport { matchFixture } from \"./router.js\";\nimport { writeErrorResponse } from \"./sse-writer.js\";\nimport type { Journal } from \"./journal.js\";\nimport { applyChaos } from \"./chaos.js\";\nimport { proxyAndRecord } from \"./recorder.js\";\n\n/**\n * Extract a text field from multipart form data using regex.\n * NOTE: This runs against the full body including binary audio data.\n * It works because text metadata fields (model, response_format, etc.)\n * appear before the binary audio part in standard multipart encoding.\n * A proper multipart parser would be more robust but is overkill for\n * the small set of fields we extract.\n */\nfunction extractFormField(raw: string, fieldName: string): string | undefined {\n const pattern = new RegExp(\n `Content-Disposition:\\\\s*form-data;[^\\\\r\\\\n]*name=\"${fieldName}\"[^\\\\r\\\\n]*\\\\r\\\\n\\\\r\\\\n([^\\\\r\\\\n]*)`,\n \"i\",\n );\n const match = raw.match(pattern);\n return match?.[1];\n}\n\nexport async function handleTranscription(\n req: http.IncomingMessage,\n res: http.ServerResponse,\n raw: string,\n fixtures: Fixture[],\n journal: Journal,\n defaults: HandlerDefaults,\n setCorsHeaders: (res: http.ServerResponse) => void,\n): Promise<void> {\n setCorsHeaders(res);\n const path = req.url ?? \"/v1/audio/transcriptions\";\n const method = req.method ?? \"POST\";\n\n const model = extractFormField(raw, \"model\") ?? \"whisper-1\";\n const responseFormat = extractFormField(raw, \"response_format\") ?? \"json\";\n\n const syntheticReq: ChatCompletionRequest = {\n model,\n messages: [],\n _endpointType: \"transcription\",\n };\n\n const testId = getTestId(req);\n const fixture = matchFixture(\n fixtures,\n syntheticReq,\n journal.getFixtureMatchCountsForTest(testId),\n defaults.requestTransform,\n );\n\n if (fixture) {\n journal.incrementFixtureMatchCount(fixture, fixtures, testId);\n }\n\n if (\n applyChaos(\n res,\n fixture,\n defaults.chaos,\n req.headers,\n journal,\n { method, path, headers: flattenHeaders(req.headers), body: syntheticReq },\n defaults.registry,\n defaults.logger,\n )\n )\n return;\n\n if (!fixture) {\n if (defaults.record) {\n const proxied = await proxyAndRecord(\n req,\n res,\n syntheticReq,\n \"openai\",\n req.url ?? \"/v1/audio/transcriptions\",\n fixtures,\n defaults,\n raw,\n );\n if (proxied) {\n journal.add({\n method,\n path,\n headers: flattenHeaders(req.headers),\n body: syntheticReq,\n response: { status: res.statusCode ?? 200, fixture: null, source: \"proxy\" },\n });\n return;\n }\n }\n\n const strictStatus = defaults.strict ? 503 : 404;\n const strictMessage = defaults.strict\n ? \"Strict mode: no fixture matched\"\n : \"No fixture matched\";\n journal.add({\n method,\n path,\n headers: flattenHeaders(req.headers),\n body: syntheticReq,\n response: { status: strictStatus, fixture: null },\n });\n writeErrorResponse(\n res,\n strictStatus,\n JSON.stringify({\n error: {\n message: strictMessage,\n type: \"invalid_request_error\",\n code: \"no_fixture_match\",\n },\n }),\n );\n return;\n }\n\n const response = fixture.response;\n\n if (isErrorResponse(response)) {\n const status = response.status ?? 500;\n journal.add({\n method,\n path,\n headers: flattenHeaders(req.headers),\n body: syntheticReq,\n response: { status, fixture },\n });\n writeErrorResponse(res, status, JSON.stringify(response));\n return;\n }\n\n if (!isTranscriptionResponse(response)) {\n journal.add({\n method,\n path,\n headers: flattenHeaders(req.headers),\n body: syntheticReq,\n response: { status: 500, fixture },\n });\n writeErrorResponse(\n res,\n 500,\n JSON.stringify({\n error: {\n message: \"Fixture response is not a transcription type\",\n type: \"server_error\",\n },\n }),\n );\n return;\n }\n\n journal.add({\n method,\n path,\n headers: flattenHeaders(req.headers),\n body: syntheticReq,\n response: { status: 200, fixture },\n });\n\n const t = response.transcription;\n const useVerbose = responseFormat === \"verbose_json\" || t.words != null || t.segments != null;\n\n if (useVerbose) {\n res.writeHead(200, { \"Content-Type\": \"application/json\" });\n res.end(\n JSON.stringify({\n task: \"transcribe\",\n language: t.language ?? \"english\",\n duration: t.duration ?? 0,\n text: t.text,\n words: t.words ?? [],\n segments: t.segments ?? [],\n }),\n );\n } else {\n res.writeHead(200, { \"Content-Type\": \"application/json\" });\n res.end(JSON.stringify({ text: t.text }));\n }\n}\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"transcription.d.cts","names":[],"sources":["../src/transcription.ts"],"sourcesContent":[],"mappings":";;;;;iBAuBsB,mBAAA,MACf,IAAA,CAAK,sBACL,IAAA,CAAK,uCAEA,oBACD,mBACC,uCACY,IAAA,CAAK,0BAC1B"}
1
+ {"version":3,"file":"transcription.d.cts","names":[],"sources":["../src/transcription.ts"],"sourcesContent":[],"mappings":";;;;;iBA0BsB,mBAAA,MACf,IAAA,CAAK,sBACL,IAAA,CAAK,uCAEA,oBACD,mBACC,uCACY,IAAA,CAAK,0BAC1B"}
@@ -1 +1 @@
1
- {"version":3,"file":"transcription.d.ts","names":[],"sources":["../src/transcription.ts"],"sourcesContent":[],"mappings":";;;;;iBAuBsB,mBAAA,MACf,IAAA,CAAK,sBACL,IAAA,CAAK,uCAEA,oBACD,mBACC,uCACY,IAAA,CAAK,0BAC1B"}
1
+ {"version":3,"file":"transcription.d.ts","names":[],"sources":["../src/transcription.ts"],"sourcesContent":[],"mappings":";;;;;iBA0BsB,mBAAA,MACf,IAAA,CAAK,sBACL,IAAA,CAAK,uCAEA,oBACD,mBACC,uCACY,IAAA,CAAK,0BAC1B"}
@@ -6,9 +6,12 @@ import { proxyAndRecord } from "./recorder.js";
6
6
 
7
7
  //#region src/transcription.ts
8
8
  /**
9
- * Extract a named field value from a multipart/form-data body.
10
- * Lightweight parser scans for Content-Disposition headers
11
- * to find simple string field values.
9
+ * Extract a text field from multipart form data using regex.
10
+ * NOTE: This runs against the full body including binary audio data.
11
+ * It works because text metadata fields (model, response_format, etc.)
12
+ * appear before the binary audio part in standard multipart encoding.
13
+ * A proper multipart parser would be more robust but is overkill for
14
+ * the small set of fields we extract.
12
15
  */
13
16
  function extractFormField(raw, fieldName) {
14
17
  const pattern = new RegExp(`Content-Disposition:\\s*form-data;[^\\r\\n]*name="${fieldName}"[^\\r\\n]*\\r\\n\\r\\n([^\\r\\n]*)`, "i");
@@ -44,7 +47,8 @@ async function handleTranscription(req, res, raw, fixtures, journal, defaults, s
44
47
  body: syntheticReq,
45
48
  response: {
46
49
  status: res.statusCode ?? 200,
47
- fixture: null
50
+ fixture: null,
51
+ source: "proxy"
48
52
  }
49
53
  });
50
54
  return;
@@ -1 +1 @@
1
- {"version":3,"file":"transcription.js","names":[],"sources":["../src/transcription.ts"],"sourcesContent":["import type * as http from \"node:http\";\nimport type { ChatCompletionRequest, Fixture, HandlerDefaults } from \"./types.js\";\nimport { isTranscriptionResponse, isErrorResponse, flattenHeaders, getTestId } from \"./helpers.js\";\nimport { matchFixture } from \"./router.js\";\nimport { writeErrorResponse } from \"./sse-writer.js\";\nimport type { Journal } from \"./journal.js\";\nimport { applyChaos } from \"./chaos.js\";\nimport { proxyAndRecord } from \"./recorder.js\";\n\n/**\n * Extract a named field value from a multipart/form-data body.\n * Lightweight parser scans for Content-Disposition headers\n * to find simple string field values.\n */\nfunction extractFormField(raw: string, fieldName: string): string | undefined {\n const pattern = new RegExp(\n `Content-Disposition:\\\\s*form-data;[^\\\\r\\\\n]*name=\"${fieldName}\"[^\\\\r\\\\n]*\\\\r\\\\n\\\\r\\\\n([^\\\\r\\\\n]*)`,\n \"i\",\n );\n const match = raw.match(pattern);\n return match?.[1];\n}\n\nexport async function handleTranscription(\n req: http.IncomingMessage,\n res: http.ServerResponse,\n raw: string,\n fixtures: Fixture[],\n journal: Journal,\n defaults: HandlerDefaults,\n setCorsHeaders: (res: http.ServerResponse) => void,\n): Promise<void> {\n setCorsHeaders(res);\n const path = req.url ?? \"/v1/audio/transcriptions\";\n const method = req.method ?? \"POST\";\n\n const model = extractFormField(raw, \"model\") ?? \"whisper-1\";\n const responseFormat = extractFormField(raw, \"response_format\") ?? \"json\";\n\n const syntheticReq: ChatCompletionRequest = {\n model,\n messages: [],\n _endpointType: \"transcription\",\n };\n\n const testId = getTestId(req);\n const fixture = matchFixture(\n fixtures,\n syntheticReq,\n journal.getFixtureMatchCountsForTest(testId),\n defaults.requestTransform,\n );\n\n if (fixture) {\n journal.incrementFixtureMatchCount(fixture, fixtures, testId);\n }\n\n if (\n applyChaos(\n res,\n fixture,\n defaults.chaos,\n req.headers,\n journal,\n { method, path, headers: flattenHeaders(req.headers), body: syntheticReq },\n defaults.registry,\n defaults.logger,\n )\n )\n return;\n\n if (!fixture) {\n if (defaults.record) {\n const proxied = await proxyAndRecord(\n req,\n res,\n syntheticReq,\n \"openai\",\n req.url ?? \"/v1/audio/transcriptions\",\n fixtures,\n defaults,\n raw,\n );\n if (proxied) {\n journal.add({\n method,\n path,\n headers: flattenHeaders(req.headers),\n body: syntheticReq,\n response: { status: res.statusCode ?? 200, fixture: null },\n });\n return;\n }\n }\n\n const strictStatus = defaults.strict ? 503 : 404;\n const strictMessage = defaults.strict\n ? \"Strict mode: no fixture matched\"\n : \"No fixture matched\";\n journal.add({\n method,\n path,\n headers: flattenHeaders(req.headers),\n body: syntheticReq,\n response: { status: strictStatus, fixture: null },\n });\n writeErrorResponse(\n res,\n strictStatus,\n JSON.stringify({\n error: {\n message: strictMessage,\n type: \"invalid_request_error\",\n code: \"no_fixture_match\",\n },\n }),\n );\n return;\n }\n\n const response = fixture.response;\n\n if (isErrorResponse(response)) {\n const status = response.status ?? 500;\n journal.add({\n method,\n path,\n headers: flattenHeaders(req.headers),\n body: syntheticReq,\n response: { status, fixture },\n });\n writeErrorResponse(res, status, JSON.stringify(response));\n return;\n }\n\n if (!isTranscriptionResponse(response)) {\n journal.add({\n method,\n path,\n headers: flattenHeaders(req.headers),\n body: syntheticReq,\n response: { status: 500, fixture },\n });\n writeErrorResponse(\n res,\n 500,\n JSON.stringify({\n error: {\n message: \"Fixture response is not a transcription type\",\n type: \"server_error\",\n },\n }),\n );\n return;\n }\n\n journal.add({\n method,\n path,\n headers: flattenHeaders(req.headers),\n body: syntheticReq,\n response: { status: 200, fixture },\n });\n\n const t = response.transcription;\n const useVerbose = responseFormat === \"verbose_json\" || t.words != null || t.segments != null;\n\n if (useVerbose) {\n res.writeHead(200, { \"Content-Type\": \"application/json\" });\n res.end(\n JSON.stringify({\n task: \"transcribe\",\n language: t.language ?? \"english\",\n duration: t.duration ?? 0,\n text: t.text,\n words: t.words ?? [],\n segments: t.segments ?? [],\n }),\n );\n } else {\n res.writeHead(200, { \"Content-Type\": \"application/json\" });\n res.end(JSON.stringify({ text: t.text }));\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"transcription.js","names":[],"sources":["../src/transcription.ts"],"sourcesContent":["import type * as http from \"node:http\";\nimport type { ChatCompletionRequest, Fixture, HandlerDefaults } from \"./types.js\";\nimport { isTranscriptionResponse, isErrorResponse, flattenHeaders, getTestId } from \"./helpers.js\";\nimport { matchFixture } from \"./router.js\";\nimport { writeErrorResponse } from \"./sse-writer.js\";\nimport type { Journal } from \"./journal.js\";\nimport { applyChaos } from \"./chaos.js\";\nimport { proxyAndRecord } from \"./recorder.js\";\n\n/**\n * Extract a text field from multipart form data using regex.\n * NOTE: This runs against the full body including binary audio data.\n * It works because text metadata fields (model, response_format, etc.)\n * appear before the binary audio part in standard multipart encoding.\n * A proper multipart parser would be more robust but is overkill for\n * the small set of fields we extract.\n */\nfunction extractFormField(raw: string, fieldName: string): string | undefined {\n const pattern = new RegExp(\n `Content-Disposition:\\\\s*form-data;[^\\\\r\\\\n]*name=\"${fieldName}\"[^\\\\r\\\\n]*\\\\r\\\\n\\\\r\\\\n([^\\\\r\\\\n]*)`,\n \"i\",\n );\n const match = raw.match(pattern);\n return match?.[1];\n}\n\nexport async function handleTranscription(\n req: http.IncomingMessage,\n res: http.ServerResponse,\n raw: string,\n fixtures: Fixture[],\n journal: Journal,\n defaults: HandlerDefaults,\n setCorsHeaders: (res: http.ServerResponse) => void,\n): Promise<void> {\n setCorsHeaders(res);\n const path = req.url ?? \"/v1/audio/transcriptions\";\n const method = req.method ?? \"POST\";\n\n const model = extractFormField(raw, \"model\") ?? \"whisper-1\";\n const responseFormat = extractFormField(raw, \"response_format\") ?? \"json\";\n\n const syntheticReq: ChatCompletionRequest = {\n model,\n messages: [],\n _endpointType: \"transcription\",\n };\n\n const testId = getTestId(req);\n const fixture = matchFixture(\n fixtures,\n syntheticReq,\n journal.getFixtureMatchCountsForTest(testId),\n defaults.requestTransform,\n );\n\n if (fixture) {\n journal.incrementFixtureMatchCount(fixture, fixtures, testId);\n }\n\n if (\n applyChaos(\n res,\n fixture,\n defaults.chaos,\n req.headers,\n journal,\n { method, path, headers: flattenHeaders(req.headers), body: syntheticReq },\n defaults.registry,\n defaults.logger,\n )\n )\n return;\n\n if (!fixture) {\n if (defaults.record) {\n const proxied = await proxyAndRecord(\n req,\n res,\n syntheticReq,\n \"openai\",\n req.url ?? \"/v1/audio/transcriptions\",\n fixtures,\n defaults,\n raw,\n );\n if (proxied) {\n journal.add({\n method,\n path,\n headers: flattenHeaders(req.headers),\n body: syntheticReq,\n response: { status: res.statusCode ?? 200, fixture: null, source: \"proxy\" },\n });\n return;\n }\n }\n\n const strictStatus = defaults.strict ? 503 : 404;\n const strictMessage = defaults.strict\n ? \"Strict mode: no fixture matched\"\n : \"No fixture matched\";\n journal.add({\n method,\n path,\n headers: flattenHeaders(req.headers),\n body: syntheticReq,\n response: { status: strictStatus, fixture: null },\n });\n writeErrorResponse(\n res,\n strictStatus,\n JSON.stringify({\n error: {\n message: strictMessage,\n type: \"invalid_request_error\",\n code: \"no_fixture_match\",\n },\n }),\n );\n return;\n }\n\n const response = fixture.response;\n\n if (isErrorResponse(response)) {\n const status = response.status ?? 500;\n journal.add({\n method,\n path,\n headers: flattenHeaders(req.headers),\n body: syntheticReq,\n response: { status, fixture },\n });\n writeErrorResponse(res, status, JSON.stringify(response));\n return;\n }\n\n if (!isTranscriptionResponse(response)) {\n journal.add({\n method,\n path,\n headers: flattenHeaders(req.headers),\n body: syntheticReq,\n response: { status: 500, fixture },\n });\n writeErrorResponse(\n res,\n 500,\n JSON.stringify({\n error: {\n message: \"Fixture response is not a transcription type\",\n type: \"server_error\",\n },\n }),\n );\n return;\n }\n\n journal.add({\n method,\n path,\n headers: flattenHeaders(req.headers),\n body: syntheticReq,\n response: { status: 200, fixture },\n });\n\n const t = response.transcription;\n const useVerbose = responseFormat === \"verbose_json\" || t.words != null || t.segments != null;\n\n if (useVerbose) {\n res.writeHead(200, { \"Content-Type\": \"application/json\" });\n res.end(\n JSON.stringify({\n task: \"transcribe\",\n language: t.language ?? \"english\",\n duration: t.duration ?? 0,\n text: t.text,\n words: t.words ?? [],\n segments: t.segments ?? [],\n }),\n );\n } else {\n res.writeHead(200, { \"Content-Type\": \"application/json\" });\n res.end(JSON.stringify({ text: t.text }));\n }\n}\n"],"mappings":"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"}
package/dist/types.d.cts CHANGED
@@ -255,6 +255,7 @@ interface JournalEntry {
255
255
  response: {
256
256
  status: number;
257
257
  fixture: Fixture | null;
258
+ source?: "fixture" | "proxy";
258
259
  interrupted?: boolean;
259
260
  interruptReason?: string;
260
261
  chaosAction?: ChaosAction;
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.cts","names":[],"sources":["../src/types.ts"],"sourcesContent":[],"mappings":"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"}
1
+ {"version":3,"file":"types.d.cts","names":[],"sources":["../src/types.ts"],"sourcesContent":[],"mappings":"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"}
package/dist/types.d.ts CHANGED
@@ -255,6 +255,7 @@ interface JournalEntry {
255
255
  response: {
256
256
  status: number;
257
257
  fixture: Fixture | null;
258
+ source?: "fixture" | "proxy";
258
259
  interrupted?: boolean;
259
260
  interruptReason?: string;
260
261
  chaosAction?: ChaosAction;
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","names":[],"sources":["../src/types.ts"],"sourcesContent":[],"mappings":"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"}
1
+ {"version":3,"file":"types.d.ts","names":[],"sources":["../src/types.ts"],"sourcesContent":[],"mappings":"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"}
package/dist/video.cjs CHANGED
@@ -5,6 +5,49 @@ const require_chaos = require('./chaos.cjs');
5
5
  const require_recorder = require('./recorder.cjs');
6
6
 
7
7
  //#region src/video.ts
8
+ const VIDEO_STATE_MAX_ENTRIES = 1e4;
9
+ const VIDEO_STATE_TTL_MS = 36e5;
10
+ /**
11
+ * A Map wrapper for video state that enforces a maximum size and per-entry TTL.
12
+ * Entries older than VIDEO_STATE_TTL_MS are lazily evicted on `get`.
13
+ * When the map exceeds VIDEO_STATE_MAX_ENTRIES on `set`, the oldest entries
14
+ * are removed to stay within bounds.
15
+ */
16
+ var VideoStateMap = class {
17
+ entries = /* @__PURE__ */ new Map();
18
+ get(key) {
19
+ const entry = this.entries.get(key);
20
+ if (!entry) return void 0;
21
+ if (Date.now() - entry.createdAt > VIDEO_STATE_TTL_MS) {
22
+ this.entries.delete(key);
23
+ return;
24
+ }
25
+ return entry.video;
26
+ }
27
+ set(key, video) {
28
+ this.entries.set(key, {
29
+ video,
30
+ createdAt: Date.now()
31
+ });
32
+ if (this.entries.size > VIDEO_STATE_MAX_ENTRIES) {
33
+ const excess = this.entries.size - VIDEO_STATE_MAX_ENTRIES;
34
+ const iter = this.entries.keys();
35
+ for (let i = 0; i < excess; i++) {
36
+ const next = iter.next();
37
+ if (!next.done) this.entries.delete(next.value);
38
+ }
39
+ }
40
+ }
41
+ delete(key) {
42
+ return this.entries.delete(key);
43
+ }
44
+ clear() {
45
+ this.entries.clear();
46
+ }
47
+ get size() {
48
+ return this.entries.size;
49
+ }
50
+ };
8
51
  async function handleVideoCreate(req, res, raw, fixtures, journal, defaults, setCorsHeaders, videoStates) {
9
52
  setCorsHeaders(res);
10
53
  const path = req.url ?? "/v1/videos";
@@ -30,6 +73,23 @@ async function handleVideoCreate(req, res, raw, fixtures, journal, defaults, set
30
73
  } }));
31
74
  return;
32
75
  }
76
+ if (!videoReq.prompt) {
77
+ journal.add({
78
+ method,
79
+ path,
80
+ headers: require_helpers.flattenHeaders(req.headers),
81
+ body: null,
82
+ response: {
83
+ status: 400,
84
+ fixture: null
85
+ }
86
+ });
87
+ require_sse_writer.writeErrorResponse(res, 400, JSON.stringify({ error: {
88
+ message: "Missing required parameter: 'prompt'",
89
+ type: "invalid_request_error"
90
+ } }));
91
+ return;
92
+ }
33
93
  const syntheticReq = {
34
94
  model: videoReq.model ?? "sora-2",
35
95
  messages: [{
@@ -57,7 +117,8 @@ async function handleVideoCreate(req, res, raw, fixtures, journal, defaults, set
57
117
  body: syntheticReq,
58
118
  response: {
59
119
  status: res.statusCode ?? 200,
60
- fixture: null
120
+ fixture: null,
121
+ source: "proxy"
61
122
  }
62
123
  });
63
124
  return;
@@ -191,6 +252,7 @@ function handleVideoStatus(req, res, videoId, journal, setCorsHeaders, videoStat
191
252
  }
192
253
 
193
254
  //#endregion
255
+ exports.VideoStateMap = VideoStateMap;
194
256
  exports.handleVideoCreate = handleVideoCreate;
195
257
  exports.handleVideoStatus = handleVideoStatus;
196
258
  //# sourceMappingURL=video.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"video.cjs","names":["flattenHeaders","getTestId","matchFixture","applyChaos","proxyAndRecord","isErrorResponse","isVideoResponse"],"sources":["../src/video.ts"],"sourcesContent":["import type * as http from \"node:http\";\nimport type { ChatCompletionRequest, Fixture, HandlerDefaults, VideoResponse } from \"./types.js\";\nimport { isVideoResponse, isErrorResponse, flattenHeaders, getTestId } from \"./helpers.js\";\nimport { matchFixture } from \"./router.js\";\nimport { writeErrorResponse } from \"./sse-writer.js\";\nimport type { Journal } from \"./journal.js\";\nimport { applyChaos } from \"./chaos.js\";\nimport { proxyAndRecord } from \"./recorder.js\";\n\ninterface VideoRequest {\n model?: string;\n prompt: string;\n [key: string]: unknown;\n}\n\n/** Stored video state for GET status checks. Key: `${testId}:${videoId}` */\nexport type VideoStateMap = Map<string, VideoResponse[\"video\"]>;\n\nexport async function handleVideoCreate(\n req: http.IncomingMessage,\n res: http.ServerResponse,\n raw: string,\n fixtures: Fixture[],\n journal: Journal,\n defaults: HandlerDefaults,\n setCorsHeaders: (res: http.ServerResponse) => void,\n videoStates: VideoStateMap,\n): Promise<void> {\n setCorsHeaders(res);\n const path = req.url ?? \"/v1/videos\";\n const method = req.method ?? \"POST\";\n\n let videoReq: VideoRequest;\n try {\n videoReq = JSON.parse(raw) as VideoRequest;\n } catch {\n journal.add({\n method,\n path,\n headers: flattenHeaders(req.headers),\n body: null,\n response: { status: 400, fixture: null },\n });\n writeErrorResponse(\n res,\n 400,\n JSON.stringify({\n error: { message: \"Malformed JSON\", type: \"invalid_request_error\", code: \"invalid_json\" },\n }),\n );\n return;\n }\n\n const syntheticReq: ChatCompletionRequest = {\n model: videoReq.model ?? \"sora-2\",\n messages: [{ role: \"user\", content: videoReq.prompt }],\n _endpointType: \"video\",\n };\n\n const testId = getTestId(req);\n const fixture = matchFixture(\n fixtures,\n syntheticReq,\n journal.getFixtureMatchCountsForTest(testId),\n defaults.requestTransform,\n );\n\n if (fixture) {\n journal.incrementFixtureMatchCount(fixture, fixtures, testId);\n }\n\n if (\n applyChaos(\n res,\n fixture,\n defaults.chaos,\n req.headers,\n journal,\n { method, path, headers: flattenHeaders(req.headers), body: syntheticReq },\n defaults.registry,\n defaults.logger,\n )\n )\n return;\n\n if (!fixture) {\n if (defaults.record) {\n const proxied = await proxyAndRecord(\n req,\n res,\n syntheticReq,\n \"openai\",\n req.url ?? \"/v1/videos\",\n fixtures,\n defaults,\n raw,\n );\n if (proxied) {\n journal.add({\n method,\n path,\n headers: flattenHeaders(req.headers),\n body: syntheticReq,\n response: { status: res.statusCode ?? 200, fixture: null },\n });\n return;\n }\n }\n\n const strictStatus = defaults.strict ? 503 : 404;\n const strictMessage = defaults.strict\n ? \"Strict mode: no fixture matched\"\n : \"No fixture matched\";\n journal.add({\n method,\n path,\n headers: flattenHeaders(req.headers),\n body: syntheticReq,\n response: { status: strictStatus, fixture: null },\n });\n writeErrorResponse(\n res,\n strictStatus,\n JSON.stringify({\n error: { message: strictMessage, type: \"invalid_request_error\", code: \"no_fixture_match\" },\n }),\n );\n return;\n }\n\n const response = fixture.response;\n\n if (isErrorResponse(response)) {\n const status = response.status ?? 500;\n journal.add({\n method,\n path,\n headers: flattenHeaders(req.headers),\n body: syntheticReq,\n response: { status, fixture },\n });\n writeErrorResponse(res, status, JSON.stringify(response));\n return;\n }\n\n if (!isVideoResponse(response)) {\n journal.add({\n method,\n path,\n headers: flattenHeaders(req.headers),\n body: syntheticReq,\n response: { status: 500, fixture },\n });\n writeErrorResponse(\n res,\n 500,\n JSON.stringify({\n error: { message: \"Fixture response is not a video type\", type: \"server_error\" },\n }),\n );\n return;\n }\n\n journal.add({\n method,\n path,\n headers: flattenHeaders(req.headers),\n body: syntheticReq,\n response: { status: 200, fixture },\n });\n\n const video = response.video;\n const created_at = Math.floor(Date.now() / 1000);\n\n // Store for GET status checks\n const stateKey = `${testId}:${video.id}`;\n videoStates.set(stateKey, video);\n\n if (video.status === \"completed\") {\n res.writeHead(200, { \"Content-Type\": \"application/json\" });\n res.end(JSON.stringify({ id: video.id, status: video.status, url: video.url, created_at }));\n } else {\n res.writeHead(200, { \"Content-Type\": \"application/json\" });\n res.end(JSON.stringify({ id: video.id, status: video.status, created_at }));\n }\n}\n\nexport function handleVideoStatus(\n req: http.IncomingMessage,\n res: http.ServerResponse,\n videoId: string,\n journal: Journal,\n setCorsHeaders: (res: http.ServerResponse) => void,\n videoStates: VideoStateMap,\n): void {\n setCorsHeaders(res);\n const path = req.url ?? `/v1/videos/${videoId}`;\n const method = req.method ?? \"GET\";\n\n const testId = getTestId(req);\n const stateKey = `${testId}:${videoId}`;\n const video = videoStates.get(stateKey);\n\n if (!video) {\n journal.add({\n method,\n path,\n headers: flattenHeaders(req.headers),\n body: null,\n response: { status: 404, fixture: null },\n });\n writeErrorResponse(\n res,\n 404,\n JSON.stringify({ error: { message: `Video ${videoId} not found`, type: \"not_found\" } }),\n );\n return;\n }\n\n journal.add({\n method,\n path,\n headers: flattenHeaders(req.headers),\n body: null,\n response: { status: 200, fixture: null },\n });\n\n const created_at = Math.floor(Date.now() / 1000);\n const body: Record<string, unknown> = {\n id: video.id,\n status: video.status,\n created_at,\n };\n if (video.url) body.url = video.url;\n\n res.writeHead(200, { \"Content-Type\": \"application/json\" });\n res.end(JSON.stringify(body));\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"video.cjs","names":["flattenHeaders","getTestId","matchFixture","applyChaos","proxyAndRecord","isErrorResponse","isVideoResponse"],"sources":["../src/video.ts"],"sourcesContent":["import type * as http from \"node:http\";\nimport type { ChatCompletionRequest, Fixture, HandlerDefaults, VideoResponse } from \"./types.js\";\nimport { isVideoResponse, isErrorResponse, flattenHeaders, getTestId } from \"./helpers.js\";\nimport { matchFixture } from \"./router.js\";\nimport { writeErrorResponse } from \"./sse-writer.js\";\nimport type { Journal } from \"./journal.js\";\nimport { applyChaos } from \"./chaos.js\";\nimport { proxyAndRecord } from \"./recorder.js\";\n\ninterface VideoRequest {\n model?: string;\n prompt: string;\n [key: string]: unknown;\n}\n\n// ─── VideoStateMap with TTL and size bound ────────────────────────────────\n\nconst VIDEO_STATE_MAX_ENTRIES = 10_000;\nconst VIDEO_STATE_TTL_MS = 3_600_000; // 1 hour\n\ninterface VideoStateEntry {\n video: VideoResponse[\"video\"];\n createdAt: number;\n}\n\n/**\n * A Map wrapper for video state that enforces a maximum size and per-entry TTL.\n * Entries older than VIDEO_STATE_TTL_MS are lazily evicted on `get`.\n * When the map exceeds VIDEO_STATE_MAX_ENTRIES on `set`, the oldest entries\n * are removed to stay within bounds.\n */\nexport class VideoStateMap {\n private readonly entries = new Map<string, VideoStateEntry>();\n\n get(key: string): VideoResponse[\"video\"] | undefined {\n const entry = this.entries.get(key);\n if (!entry) return undefined;\n if (Date.now() - entry.createdAt > VIDEO_STATE_TTL_MS) {\n this.entries.delete(key);\n return undefined;\n }\n return entry.video;\n }\n\n set(key: string, video: VideoResponse[\"video\"]): void {\n this.entries.set(key, { video, createdAt: Date.now() });\n // Evict oldest entries if over capacity\n if (this.entries.size > VIDEO_STATE_MAX_ENTRIES) {\n const excess = this.entries.size - VIDEO_STATE_MAX_ENTRIES;\n const iter = this.entries.keys();\n for (let i = 0; i < excess; i++) {\n const next = iter.next();\n if (!next.done) this.entries.delete(next.value);\n }\n }\n }\n\n delete(key: string): boolean {\n return this.entries.delete(key);\n }\n\n clear(): void {\n this.entries.clear();\n }\n\n get size(): number {\n return this.entries.size;\n }\n}\n\nexport async function handleVideoCreate(\n req: http.IncomingMessage,\n res: http.ServerResponse,\n raw: string,\n fixtures: Fixture[],\n journal: Journal,\n defaults: HandlerDefaults,\n setCorsHeaders: (res: http.ServerResponse) => void,\n videoStates: VideoStateMap,\n): Promise<void> {\n setCorsHeaders(res);\n const path = req.url ?? \"/v1/videos\";\n const method = req.method ?? \"POST\";\n\n let videoReq: VideoRequest;\n try {\n videoReq = JSON.parse(raw) as VideoRequest;\n } catch {\n journal.add({\n method,\n path,\n headers: flattenHeaders(req.headers),\n body: null,\n response: { status: 400, fixture: null },\n });\n writeErrorResponse(\n res,\n 400,\n JSON.stringify({\n error: { message: \"Malformed JSON\", type: \"invalid_request_error\", code: \"invalid_json\" },\n }),\n );\n return;\n }\n\n if (!videoReq.prompt) {\n journal.add({\n method,\n path,\n headers: flattenHeaders(req.headers),\n body: null,\n response: { status: 400, fixture: null },\n });\n writeErrorResponse(\n res,\n 400,\n JSON.stringify({\n error: { message: \"Missing required parameter: 'prompt'\", type: \"invalid_request_error\" },\n }),\n );\n return;\n }\n\n const syntheticReq: ChatCompletionRequest = {\n model: videoReq.model ?? \"sora-2\",\n messages: [{ role: \"user\", content: videoReq.prompt }],\n _endpointType: \"video\",\n };\n\n const testId = getTestId(req);\n const fixture = matchFixture(\n fixtures,\n syntheticReq,\n journal.getFixtureMatchCountsForTest(testId),\n defaults.requestTransform,\n );\n\n if (fixture) {\n journal.incrementFixtureMatchCount(fixture, fixtures, testId);\n }\n\n if (\n applyChaos(\n res,\n fixture,\n defaults.chaos,\n req.headers,\n journal,\n { method, path, headers: flattenHeaders(req.headers), body: syntheticReq },\n defaults.registry,\n defaults.logger,\n )\n )\n return;\n\n if (!fixture) {\n if (defaults.record) {\n const proxied = await proxyAndRecord(\n req,\n res,\n syntheticReq,\n \"openai\",\n req.url ?? \"/v1/videos\",\n fixtures,\n defaults,\n raw,\n );\n if (proxied) {\n journal.add({\n method,\n path,\n headers: flattenHeaders(req.headers),\n body: syntheticReq,\n response: { status: res.statusCode ?? 200, fixture: null, source: \"proxy\" },\n });\n return;\n }\n }\n\n const strictStatus = defaults.strict ? 503 : 404;\n const strictMessage = defaults.strict\n ? \"Strict mode: no fixture matched\"\n : \"No fixture matched\";\n journal.add({\n method,\n path,\n headers: flattenHeaders(req.headers),\n body: syntheticReq,\n response: { status: strictStatus, fixture: null },\n });\n writeErrorResponse(\n res,\n strictStatus,\n JSON.stringify({\n error: { message: strictMessage, type: \"invalid_request_error\", code: \"no_fixture_match\" },\n }),\n );\n return;\n }\n\n const response = fixture.response;\n\n if (isErrorResponse(response)) {\n const status = response.status ?? 500;\n journal.add({\n method,\n path,\n headers: flattenHeaders(req.headers),\n body: syntheticReq,\n response: { status, fixture },\n });\n writeErrorResponse(res, status, JSON.stringify(response));\n return;\n }\n\n if (!isVideoResponse(response)) {\n journal.add({\n method,\n path,\n headers: flattenHeaders(req.headers),\n body: syntheticReq,\n response: { status: 500, fixture },\n });\n writeErrorResponse(\n res,\n 500,\n JSON.stringify({\n error: { message: \"Fixture response is not a video type\", type: \"server_error\" },\n }),\n );\n return;\n }\n\n journal.add({\n method,\n path,\n headers: flattenHeaders(req.headers),\n body: syntheticReq,\n response: { status: 200, fixture },\n });\n\n const video = response.video;\n const created_at = Math.floor(Date.now() / 1000);\n\n // Store for GET status checks\n const stateKey = `${testId}:${video.id}`;\n videoStates.set(stateKey, video);\n\n if (video.status === \"completed\") {\n res.writeHead(200, { \"Content-Type\": \"application/json\" });\n res.end(JSON.stringify({ id: video.id, status: video.status, url: video.url, created_at }));\n } else {\n res.writeHead(200, { \"Content-Type\": \"application/json\" });\n res.end(JSON.stringify({ id: video.id, status: video.status, created_at }));\n }\n}\n\nexport function handleVideoStatus(\n req: http.IncomingMessage,\n res: http.ServerResponse,\n videoId: string,\n journal: Journal,\n setCorsHeaders: (res: http.ServerResponse) => void,\n videoStates: VideoStateMap,\n): void {\n setCorsHeaders(res);\n const path = req.url ?? `/v1/videos/${videoId}`;\n const method = req.method ?? \"GET\";\n\n const testId = getTestId(req);\n const stateKey = `${testId}:${videoId}`;\n const video = videoStates.get(stateKey);\n\n if (!video) {\n journal.add({\n method,\n path,\n headers: flattenHeaders(req.headers),\n body: null,\n response: { status: 404, fixture: null },\n });\n writeErrorResponse(\n res,\n 404,\n JSON.stringify({ error: { message: `Video ${videoId} not found`, type: \"not_found\" } }),\n );\n return;\n }\n\n journal.add({\n method,\n path,\n headers: flattenHeaders(req.headers),\n body: null,\n response: { status: 200, fixture: null },\n });\n\n const created_at = Math.floor(Date.now() / 1000);\n const body: Record<string, unknown> = {\n id: video.id,\n status: video.status,\n created_at,\n };\n if (video.url) body.url = video.url;\n\n res.writeHead(200, { \"Content-Type\": \"application/json\" });\n res.end(JSON.stringify(body));\n}\n"],"mappings":"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"}
package/dist/video.d.cts CHANGED
@@ -3,8 +3,21 @@ import { Fixture, HandlerDefaults, VideoResponse } from "./types.cjs";
3
3
  import * as http from "node:http";
4
4
 
5
5
  //#region src/video.d.ts
6
- /** Stored video state for GET status checks. Key: `${testId}:${videoId}` */
7
- type VideoStateMap = Map<string, VideoResponse["video"]>;
6
+
7
+ /**
8
+ * A Map wrapper for video state that enforces a maximum size and per-entry TTL.
9
+ * Entries older than VIDEO_STATE_TTL_MS are lazily evicted on `get`.
10
+ * When the map exceeds VIDEO_STATE_MAX_ENTRIES on `set`, the oldest entries
11
+ * are removed to stay within bounds.
12
+ */
13
+ declare class VideoStateMap {
14
+ private readonly entries;
15
+ get(key: string): VideoResponse["video"] | undefined;
16
+ set(key: string, video: VideoResponse["video"]): void;
17
+ delete(key: string): boolean;
18
+ clear(): void;
19
+ get size(): number;
20
+ }
8
21
  declare function handleVideoCreate(req: http.IncomingMessage, res: http.ServerResponse, raw: string, fixtures: Fixture[], journal: Journal, defaults: HandlerDefaults, setCorsHeaders: (res: http.ServerResponse) => void, videoStates: VideoStateMap): Promise<void>;
9
22
  declare function handleVideoStatus(req: http.IncomingMessage, res: http.ServerResponse, videoId: string, journal: Journal, setCorsHeaders: (res: http.ServerResponse) => void, videoStates: VideoStateMap): void;
10
23
  //# sourceMappingURL=video.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"video.d.cts","names":[],"sources":["../src/video.ts"],"sourcesContent":[],"mappings":";;;;;;KAgBY,aAAA,GAAgB,YAAY;AAA5B,iBAEU,iBAAA,CAFG,GAAA,EAGlB,IAAA,CAAK,eAHa,EAAA,GAAA,EAIlB,IAAA,CAAK,cAJa,EAAA,GAAA,EAAA,MAAA,EAAA,QAAA,EAMb,OANa,EAAA,EAAA,OAAA,EAOd,OAPc,EAAA,QAAA,EAQb,eARa,EAAA,cAAA,EAAA,CAAA,GAAA,EASD,IAAA,CAAK,cATJ,EAAA,GAAA,IAAA,EAAA,WAAA,EAUV,aAVU,CAAA,EAWtB,OAXsB,CAAA,IAAA,CAAA;AAAA,iBA2KT,iBAAA,CA3KS,GAAA,EA4KlB,IAAA,CAAK,eA5Ka,EAAA,GAAA,EA6KlB,IAAA,CAAK,cA7Ka,EAAA,OAAA,EAAA,MAAA,EAAA,OAAA,EA+Kd,OA/Kc,EAAA,cAAA,EAAA,CAAA,GAAA,EAgLD,IAAA,CAAK,cAhLJ,EAAA,GAAA,IAAA,EAAA,WAAA,EAiLV,aAjLU,CAAA,EAAA,IAAA"}
1
+ {"version":3,"file":"video.d.cts","names":[],"sources":["../src/video.ts"],"sourcesContent":[],"mappings":";;;;;;;;AA+BA;;;;AAauC,cAb1B,aAAA,CAa0B;EA0BjB,iBAAA,OAAiB;EAAA,GAAA,CAAA,GAAA,EAAA,MAAA,CAAA,EApCnB,aAoCmB,CAAA,OAAA,CAAA,GAAA,SAAA;KAChC,CAAA,GAAA,EAAK,MAAA,EAAA,KAAA,EA3Bc,aA2Bd,CAAA,OAAA,CAAA,CAAA,EAAA,IAAA;QACL,CAAA,GAAK,EAAA,MAAA,CAAA,EAAA,OAAA;OAEA,CAAA,CAAA,EAAA,IAAA;MACD,IAAA,CAAA,CAAA,EAAA,MAAA;;AAEa,iBAPF,iBAAA,CAOO,GAAA,EANtB,IAAA,CAAK,eAMiB,EAAA,GAAA,EALtB,IAAA,CAAK,cAKiB,EAAA,GAAA,EAAA,MAAA,EAAA,QAAA,EAHjB,OAGiB,EAAA,EAAA,OAAA,EAFlB,OAEkB,EAAA,QAAA,EADjB,eACiB,EAAA,cAAA,EAAA,CAAA,GAAA,EAAL,IAAA,CAAK,cAAA,EAAA,GAAA,IAAA,EAAA,WAAA,EACd,aADc,CAAA,EAE1B,OAF0B,CAAA,IAAA,CAAA;AACd,iBAmLC,iBAAA,CAnLD,GAAA,EAoLR,IAAA,CAAK,eApLG,EAAA,GAAA,EAqLR,IAAA,CAAK,cArLG,EAAA,OAAA,EAAA,MAAA,EAAA,OAAA,EAuLJ,OAvLI,EAAA,cAAA,EAAA,CAAA,GAAA,EAwLS,IAAA,CAAK,cAxLd,EAAA,GAAA,IAAA,EAAA,WAAA,EAyLA,aAzLA,CAAA,EAAA,IAAA"}
package/dist/video.d.ts CHANGED
@@ -3,8 +3,21 @@ import { Fixture, HandlerDefaults, VideoResponse } from "./types.js";
3
3
  import * as http from "node:http";
4
4
 
5
5
  //#region src/video.d.ts
6
- /** Stored video state for GET status checks. Key: `${testId}:${videoId}` */
7
- type VideoStateMap = Map<string, VideoResponse["video"]>;
6
+
7
+ /**
8
+ * A Map wrapper for video state that enforces a maximum size and per-entry TTL.
9
+ * Entries older than VIDEO_STATE_TTL_MS are lazily evicted on `get`.
10
+ * When the map exceeds VIDEO_STATE_MAX_ENTRIES on `set`, the oldest entries
11
+ * are removed to stay within bounds.
12
+ */
13
+ declare class VideoStateMap {
14
+ private readonly entries;
15
+ get(key: string): VideoResponse["video"] | undefined;
16
+ set(key: string, video: VideoResponse["video"]): void;
17
+ delete(key: string): boolean;
18
+ clear(): void;
19
+ get size(): number;
20
+ }
8
21
  declare function handleVideoCreate(req: http.IncomingMessage, res: http.ServerResponse, raw: string, fixtures: Fixture[], journal: Journal, defaults: HandlerDefaults, setCorsHeaders: (res: http.ServerResponse) => void, videoStates: VideoStateMap): Promise<void>;
9
22
  declare function handleVideoStatus(req: http.IncomingMessage, res: http.ServerResponse, videoId: string, journal: Journal, setCorsHeaders: (res: http.ServerResponse) => void, videoStates: VideoStateMap): void;
10
23
  //# sourceMappingURL=video.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"video.d.ts","names":[],"sources":["../src/video.ts"],"sourcesContent":[],"mappings":";;;;;;KAgBY,aAAA,GAAgB,YAAY;AAA5B,iBAEU,iBAAA,CAFG,GAAA,EAGlB,IAAA,CAAK,eAHa,EAAA,GAAA,EAIlB,IAAA,CAAK,cAJa,EAAA,GAAA,EAAA,MAAA,EAAA,QAAA,EAMb,OANa,EAAA,EAAA,OAAA,EAOd,OAPc,EAAA,QAAA,EAQb,eARa,EAAA,cAAA,EAAA,CAAA,GAAA,EASD,IAAA,CAAK,cATJ,EAAA,GAAA,IAAA,EAAA,WAAA,EAUV,aAVU,CAAA,EAWtB,OAXsB,CAAA,IAAA,CAAA;AAAA,iBA2KT,iBAAA,CA3KS,GAAA,EA4KlB,IAAA,CAAK,eA5Ka,EAAA,GAAA,EA6KlB,IAAA,CAAK,cA7Ka,EAAA,OAAA,EAAA,MAAA,EAAA,OAAA,EA+Kd,OA/Kc,EAAA,cAAA,EAAA,CAAA,GAAA,EAgLD,IAAA,CAAK,cAhLJ,EAAA,GAAA,IAAA,EAAA,WAAA,EAiLV,aAjLU,CAAA,EAAA,IAAA"}
1
+ {"version":3,"file":"video.d.ts","names":[],"sources":["../src/video.ts"],"sourcesContent":[],"mappings":";;;;;;;;AA+BA;;;;AAauC,cAb1B,aAAA,CAa0B;EA0BjB,iBAAA,OAAiB;EAAA,GAAA,CAAA,GAAA,EAAA,MAAA,CAAA,EApCnB,aAoCmB,CAAA,OAAA,CAAA,GAAA,SAAA;KAChC,CAAA,GAAA,EAAK,MAAA,EAAA,KAAA,EA3Bc,aA2Bd,CAAA,OAAA,CAAA,CAAA,EAAA,IAAA;QACL,CAAA,GAAK,EAAA,MAAA,CAAA,EAAA,OAAA;OAEA,CAAA,CAAA,EAAA,IAAA;MACD,IAAA,CAAA,CAAA,EAAA,MAAA;;AAEa,iBAPF,iBAAA,CAOO,GAAA,EANtB,IAAA,CAAK,eAMiB,EAAA,GAAA,EALtB,IAAA,CAAK,cAKiB,EAAA,GAAA,EAAA,MAAA,EAAA,QAAA,EAHjB,OAGiB,EAAA,EAAA,OAAA,EAFlB,OAEkB,EAAA,QAAA,EADjB,eACiB,EAAA,cAAA,EAAA,CAAA,GAAA,EAAL,IAAA,CAAK,cAAA,EAAA,GAAA,IAAA,EAAA,WAAA,EACd,aADc,CAAA,EAE1B,OAF0B,CAAA,IAAA,CAAA;AACd,iBAmLC,iBAAA,CAnLD,GAAA,EAoLR,IAAA,CAAK,eApLG,EAAA,GAAA,EAqLR,IAAA,CAAK,cArLG,EAAA,OAAA,EAAA,MAAA,EAAA,OAAA,EAuLJ,OAvLI,EAAA,cAAA,EAAA,CAAA,GAAA,EAwLS,IAAA,CAAK,cAxLd,EAAA,GAAA,IAAA,EAAA,WAAA,EAyLA,aAzLA,CAAA,EAAA,IAAA"}
package/dist/video.js CHANGED
@@ -5,6 +5,49 @@ import { applyChaos } from "./chaos.js";
5
5
  import { proxyAndRecord } from "./recorder.js";
6
6
 
7
7
  //#region src/video.ts
8
+ const VIDEO_STATE_MAX_ENTRIES = 1e4;
9
+ const VIDEO_STATE_TTL_MS = 36e5;
10
+ /**
11
+ * A Map wrapper for video state that enforces a maximum size and per-entry TTL.
12
+ * Entries older than VIDEO_STATE_TTL_MS are lazily evicted on `get`.
13
+ * When the map exceeds VIDEO_STATE_MAX_ENTRIES on `set`, the oldest entries
14
+ * are removed to stay within bounds.
15
+ */
16
+ var VideoStateMap = class {
17
+ entries = /* @__PURE__ */ new Map();
18
+ get(key) {
19
+ const entry = this.entries.get(key);
20
+ if (!entry) return void 0;
21
+ if (Date.now() - entry.createdAt > VIDEO_STATE_TTL_MS) {
22
+ this.entries.delete(key);
23
+ return;
24
+ }
25
+ return entry.video;
26
+ }
27
+ set(key, video) {
28
+ this.entries.set(key, {
29
+ video,
30
+ createdAt: Date.now()
31
+ });
32
+ if (this.entries.size > VIDEO_STATE_MAX_ENTRIES) {
33
+ const excess = this.entries.size - VIDEO_STATE_MAX_ENTRIES;
34
+ const iter = this.entries.keys();
35
+ for (let i = 0; i < excess; i++) {
36
+ const next = iter.next();
37
+ if (!next.done) this.entries.delete(next.value);
38
+ }
39
+ }
40
+ }
41
+ delete(key) {
42
+ return this.entries.delete(key);
43
+ }
44
+ clear() {
45
+ this.entries.clear();
46
+ }
47
+ get size() {
48
+ return this.entries.size;
49
+ }
50
+ };
8
51
  async function handleVideoCreate(req, res, raw, fixtures, journal, defaults, setCorsHeaders, videoStates) {
9
52
  setCorsHeaders(res);
10
53
  const path = req.url ?? "/v1/videos";
@@ -30,6 +73,23 @@ async function handleVideoCreate(req, res, raw, fixtures, journal, defaults, set
30
73
  } }));
31
74
  return;
32
75
  }
76
+ if (!videoReq.prompt) {
77
+ journal.add({
78
+ method,
79
+ path,
80
+ headers: flattenHeaders(req.headers),
81
+ body: null,
82
+ response: {
83
+ status: 400,
84
+ fixture: null
85
+ }
86
+ });
87
+ writeErrorResponse(res, 400, JSON.stringify({ error: {
88
+ message: "Missing required parameter: 'prompt'",
89
+ type: "invalid_request_error"
90
+ } }));
91
+ return;
92
+ }
33
93
  const syntheticReq = {
34
94
  model: videoReq.model ?? "sora-2",
35
95
  messages: [{
@@ -57,7 +117,8 @@ async function handleVideoCreate(req, res, raw, fixtures, journal, defaults, set
57
117
  body: syntheticReq,
58
118
  response: {
59
119
  status: res.statusCode ?? 200,
60
- fixture: null
120
+ fixture: null,
121
+ source: "proxy"
61
122
  }
62
123
  });
63
124
  return;
@@ -191,5 +252,5 @@ function handleVideoStatus(req, res, videoId, journal, setCorsHeaders, videoStat
191
252
  }
192
253
 
193
254
  //#endregion
194
- export { handleVideoCreate, handleVideoStatus };
255
+ export { VideoStateMap, handleVideoCreate, handleVideoStatus };
195
256
  //# sourceMappingURL=video.js.map
package/dist/video.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"video.js","names":[],"sources":["../src/video.ts"],"sourcesContent":["import type * as http from \"node:http\";\nimport type { ChatCompletionRequest, Fixture, HandlerDefaults, VideoResponse } from \"./types.js\";\nimport { isVideoResponse, isErrorResponse, flattenHeaders, getTestId } from \"./helpers.js\";\nimport { matchFixture } from \"./router.js\";\nimport { writeErrorResponse } from \"./sse-writer.js\";\nimport type { Journal } from \"./journal.js\";\nimport { applyChaos } from \"./chaos.js\";\nimport { proxyAndRecord } from \"./recorder.js\";\n\ninterface VideoRequest {\n model?: string;\n prompt: string;\n [key: string]: unknown;\n}\n\n/** Stored video state for GET status checks. Key: `${testId}:${videoId}` */\nexport type VideoStateMap = Map<string, VideoResponse[\"video\"]>;\n\nexport async function handleVideoCreate(\n req: http.IncomingMessage,\n res: http.ServerResponse,\n raw: string,\n fixtures: Fixture[],\n journal: Journal,\n defaults: HandlerDefaults,\n setCorsHeaders: (res: http.ServerResponse) => void,\n videoStates: VideoStateMap,\n): Promise<void> {\n setCorsHeaders(res);\n const path = req.url ?? \"/v1/videos\";\n const method = req.method ?? \"POST\";\n\n let videoReq: VideoRequest;\n try {\n videoReq = JSON.parse(raw) as VideoRequest;\n } catch {\n journal.add({\n method,\n path,\n headers: flattenHeaders(req.headers),\n body: null,\n response: { status: 400, fixture: null },\n });\n writeErrorResponse(\n res,\n 400,\n JSON.stringify({\n error: { message: \"Malformed JSON\", type: \"invalid_request_error\", code: \"invalid_json\" },\n }),\n );\n return;\n }\n\n const syntheticReq: ChatCompletionRequest = {\n model: videoReq.model ?? \"sora-2\",\n messages: [{ role: \"user\", content: videoReq.prompt }],\n _endpointType: \"video\",\n };\n\n const testId = getTestId(req);\n const fixture = matchFixture(\n fixtures,\n syntheticReq,\n journal.getFixtureMatchCountsForTest(testId),\n defaults.requestTransform,\n );\n\n if (fixture) {\n journal.incrementFixtureMatchCount(fixture, fixtures, testId);\n }\n\n if (\n applyChaos(\n res,\n fixture,\n defaults.chaos,\n req.headers,\n journal,\n { method, path, headers: flattenHeaders(req.headers), body: syntheticReq },\n defaults.registry,\n defaults.logger,\n )\n )\n return;\n\n if (!fixture) {\n if (defaults.record) {\n const proxied = await proxyAndRecord(\n req,\n res,\n syntheticReq,\n \"openai\",\n req.url ?? \"/v1/videos\",\n fixtures,\n defaults,\n raw,\n );\n if (proxied) {\n journal.add({\n method,\n path,\n headers: flattenHeaders(req.headers),\n body: syntheticReq,\n response: { status: res.statusCode ?? 200, fixture: null },\n });\n return;\n }\n }\n\n const strictStatus = defaults.strict ? 503 : 404;\n const strictMessage = defaults.strict\n ? \"Strict mode: no fixture matched\"\n : \"No fixture matched\";\n journal.add({\n method,\n path,\n headers: flattenHeaders(req.headers),\n body: syntheticReq,\n response: { status: strictStatus, fixture: null },\n });\n writeErrorResponse(\n res,\n strictStatus,\n JSON.stringify({\n error: { message: strictMessage, type: \"invalid_request_error\", code: \"no_fixture_match\" },\n }),\n );\n return;\n }\n\n const response = fixture.response;\n\n if (isErrorResponse(response)) {\n const status = response.status ?? 500;\n journal.add({\n method,\n path,\n headers: flattenHeaders(req.headers),\n body: syntheticReq,\n response: { status, fixture },\n });\n writeErrorResponse(res, status, JSON.stringify(response));\n return;\n }\n\n if (!isVideoResponse(response)) {\n journal.add({\n method,\n path,\n headers: flattenHeaders(req.headers),\n body: syntheticReq,\n response: { status: 500, fixture },\n });\n writeErrorResponse(\n res,\n 500,\n JSON.stringify({\n error: { message: \"Fixture response is not a video type\", type: \"server_error\" },\n }),\n );\n return;\n }\n\n journal.add({\n method,\n path,\n headers: flattenHeaders(req.headers),\n body: syntheticReq,\n response: { status: 200, fixture },\n });\n\n const video = response.video;\n const created_at = Math.floor(Date.now() / 1000);\n\n // Store for GET status checks\n const stateKey = `${testId}:${video.id}`;\n videoStates.set(stateKey, video);\n\n if (video.status === \"completed\") {\n res.writeHead(200, { \"Content-Type\": \"application/json\" });\n res.end(JSON.stringify({ id: video.id, status: video.status, url: video.url, created_at }));\n } else {\n res.writeHead(200, { \"Content-Type\": \"application/json\" });\n res.end(JSON.stringify({ id: video.id, status: video.status, created_at }));\n }\n}\n\nexport function handleVideoStatus(\n req: http.IncomingMessage,\n res: http.ServerResponse,\n videoId: string,\n journal: Journal,\n setCorsHeaders: (res: http.ServerResponse) => void,\n videoStates: VideoStateMap,\n): void {\n setCorsHeaders(res);\n const path = req.url ?? `/v1/videos/${videoId}`;\n const method = req.method ?? \"GET\";\n\n const testId = getTestId(req);\n const stateKey = `${testId}:${videoId}`;\n const video = videoStates.get(stateKey);\n\n if (!video) {\n journal.add({\n method,\n path,\n headers: flattenHeaders(req.headers),\n body: null,\n response: { status: 404, fixture: null },\n });\n writeErrorResponse(\n res,\n 404,\n JSON.stringify({ error: { message: `Video ${videoId} not found`, type: \"not_found\" } }),\n );\n return;\n }\n\n journal.add({\n method,\n path,\n headers: flattenHeaders(req.headers),\n body: null,\n response: { status: 200, fixture: null },\n });\n\n const created_at = Math.floor(Date.now() / 1000);\n const body: Record<string, unknown> = {\n id: video.id,\n status: video.status,\n created_at,\n };\n if (video.url) body.url = video.url;\n\n res.writeHead(200, { \"Content-Type\": \"application/json\" });\n res.end(JSON.stringify(body));\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"video.js","names":[],"sources":["../src/video.ts"],"sourcesContent":["import type * as http from \"node:http\";\nimport type { ChatCompletionRequest, Fixture, HandlerDefaults, VideoResponse } from \"./types.js\";\nimport { isVideoResponse, isErrorResponse, flattenHeaders, getTestId } from \"./helpers.js\";\nimport { matchFixture } from \"./router.js\";\nimport { writeErrorResponse } from \"./sse-writer.js\";\nimport type { Journal } from \"./journal.js\";\nimport { applyChaos } from \"./chaos.js\";\nimport { proxyAndRecord } from \"./recorder.js\";\n\ninterface VideoRequest {\n model?: string;\n prompt: string;\n [key: string]: unknown;\n}\n\n// ─── VideoStateMap with TTL and size bound ────────────────────────────────\n\nconst VIDEO_STATE_MAX_ENTRIES = 10_000;\nconst VIDEO_STATE_TTL_MS = 3_600_000; // 1 hour\n\ninterface VideoStateEntry {\n video: VideoResponse[\"video\"];\n createdAt: number;\n}\n\n/**\n * A Map wrapper for video state that enforces a maximum size and per-entry TTL.\n * Entries older than VIDEO_STATE_TTL_MS are lazily evicted on `get`.\n * When the map exceeds VIDEO_STATE_MAX_ENTRIES on `set`, the oldest entries\n * are removed to stay within bounds.\n */\nexport class VideoStateMap {\n private readonly entries = new Map<string, VideoStateEntry>();\n\n get(key: string): VideoResponse[\"video\"] | undefined {\n const entry = this.entries.get(key);\n if (!entry) return undefined;\n if (Date.now() - entry.createdAt > VIDEO_STATE_TTL_MS) {\n this.entries.delete(key);\n return undefined;\n }\n return entry.video;\n }\n\n set(key: string, video: VideoResponse[\"video\"]): void {\n this.entries.set(key, { video, createdAt: Date.now() });\n // Evict oldest entries if over capacity\n if (this.entries.size > VIDEO_STATE_MAX_ENTRIES) {\n const excess = this.entries.size - VIDEO_STATE_MAX_ENTRIES;\n const iter = this.entries.keys();\n for (let i = 0; i < excess; i++) {\n const next = iter.next();\n if (!next.done) this.entries.delete(next.value);\n }\n }\n }\n\n delete(key: string): boolean {\n return this.entries.delete(key);\n }\n\n clear(): void {\n this.entries.clear();\n }\n\n get size(): number {\n return this.entries.size;\n }\n}\n\nexport async function handleVideoCreate(\n req: http.IncomingMessage,\n res: http.ServerResponse,\n raw: string,\n fixtures: Fixture[],\n journal: Journal,\n defaults: HandlerDefaults,\n setCorsHeaders: (res: http.ServerResponse) => void,\n videoStates: VideoStateMap,\n): Promise<void> {\n setCorsHeaders(res);\n const path = req.url ?? \"/v1/videos\";\n const method = req.method ?? \"POST\";\n\n let videoReq: VideoRequest;\n try {\n videoReq = JSON.parse(raw) as VideoRequest;\n } catch {\n journal.add({\n method,\n path,\n headers: flattenHeaders(req.headers),\n body: null,\n response: { status: 400, fixture: null },\n });\n writeErrorResponse(\n res,\n 400,\n JSON.stringify({\n error: { message: \"Malformed JSON\", type: \"invalid_request_error\", code: \"invalid_json\" },\n }),\n );\n return;\n }\n\n if (!videoReq.prompt) {\n journal.add({\n method,\n path,\n headers: flattenHeaders(req.headers),\n body: null,\n response: { status: 400, fixture: null },\n });\n writeErrorResponse(\n res,\n 400,\n JSON.stringify({\n error: { message: \"Missing required parameter: 'prompt'\", type: \"invalid_request_error\" },\n }),\n );\n return;\n }\n\n const syntheticReq: ChatCompletionRequest = {\n model: videoReq.model ?? \"sora-2\",\n messages: [{ role: \"user\", content: videoReq.prompt }],\n _endpointType: \"video\",\n };\n\n const testId = getTestId(req);\n const fixture = matchFixture(\n fixtures,\n syntheticReq,\n journal.getFixtureMatchCountsForTest(testId),\n defaults.requestTransform,\n );\n\n if (fixture) {\n journal.incrementFixtureMatchCount(fixture, fixtures, testId);\n }\n\n if (\n applyChaos(\n res,\n fixture,\n defaults.chaos,\n req.headers,\n journal,\n { method, path, headers: flattenHeaders(req.headers), body: syntheticReq },\n defaults.registry,\n defaults.logger,\n )\n )\n return;\n\n if (!fixture) {\n if (defaults.record) {\n const proxied = await proxyAndRecord(\n req,\n res,\n syntheticReq,\n \"openai\",\n req.url ?? \"/v1/videos\",\n fixtures,\n defaults,\n raw,\n );\n if (proxied) {\n journal.add({\n method,\n path,\n headers: flattenHeaders(req.headers),\n body: syntheticReq,\n response: { status: res.statusCode ?? 200, fixture: null, source: \"proxy\" },\n });\n return;\n }\n }\n\n const strictStatus = defaults.strict ? 503 : 404;\n const strictMessage = defaults.strict\n ? \"Strict mode: no fixture matched\"\n : \"No fixture matched\";\n journal.add({\n method,\n path,\n headers: flattenHeaders(req.headers),\n body: syntheticReq,\n response: { status: strictStatus, fixture: null },\n });\n writeErrorResponse(\n res,\n strictStatus,\n JSON.stringify({\n error: { message: strictMessage, type: \"invalid_request_error\", code: \"no_fixture_match\" },\n }),\n );\n return;\n }\n\n const response = fixture.response;\n\n if (isErrorResponse(response)) {\n const status = response.status ?? 500;\n journal.add({\n method,\n path,\n headers: flattenHeaders(req.headers),\n body: syntheticReq,\n response: { status, fixture },\n });\n writeErrorResponse(res, status, JSON.stringify(response));\n return;\n }\n\n if (!isVideoResponse(response)) {\n journal.add({\n method,\n path,\n headers: flattenHeaders(req.headers),\n body: syntheticReq,\n response: { status: 500, fixture },\n });\n writeErrorResponse(\n res,\n 500,\n JSON.stringify({\n error: { message: \"Fixture response is not a video type\", type: \"server_error\" },\n }),\n );\n return;\n }\n\n journal.add({\n method,\n path,\n headers: flattenHeaders(req.headers),\n body: syntheticReq,\n response: { status: 200, fixture },\n });\n\n const video = response.video;\n const created_at = Math.floor(Date.now() / 1000);\n\n // Store for GET status checks\n const stateKey = `${testId}:${video.id}`;\n videoStates.set(stateKey, video);\n\n if (video.status === \"completed\") {\n res.writeHead(200, { \"Content-Type\": \"application/json\" });\n res.end(JSON.stringify({ id: video.id, status: video.status, url: video.url, created_at }));\n } else {\n res.writeHead(200, { \"Content-Type\": \"application/json\" });\n res.end(JSON.stringify({ id: video.id, status: video.status, created_at }));\n }\n}\n\nexport function handleVideoStatus(\n req: http.IncomingMessage,\n res: http.ServerResponse,\n videoId: string,\n journal: Journal,\n setCorsHeaders: (res: http.ServerResponse) => void,\n videoStates: VideoStateMap,\n): void {\n setCorsHeaders(res);\n const path = req.url ?? `/v1/videos/${videoId}`;\n const method = req.method ?? \"GET\";\n\n const testId = getTestId(req);\n const stateKey = `${testId}:${videoId}`;\n const video = videoStates.get(stateKey);\n\n if (!video) {\n journal.add({\n method,\n path,\n headers: flattenHeaders(req.headers),\n body: null,\n response: { status: 404, fixture: null },\n });\n writeErrorResponse(\n res,\n 404,\n JSON.stringify({ error: { message: `Video ${videoId} not found`, type: \"not_found\" } }),\n );\n return;\n }\n\n journal.add({\n method,\n path,\n headers: flattenHeaders(req.headers),\n body: null,\n response: { status: 200, fixture: null },\n });\n\n const created_at = Math.floor(Date.now() / 1000);\n const body: Record<string, unknown> = {\n id: video.id,\n status: video.status,\n created_at,\n };\n if (video.url) body.url = video.url;\n\n res.writeHead(200, { \"Content-Type\": \"application/json\" });\n res.end(JSON.stringify(body));\n}\n"],"mappings":"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"}
package/dist/vitest.cjs CHANGED
@@ -30,6 +30,8 @@ let vitest = require("vitest");
30
30
  */
31
31
  function useAimock(options = {}) {
32
32
  let handle = null;
33
+ let origOpenaiUrl;
34
+ let origAnthropicUrl;
33
35
  (0, vitest.beforeAll)(async () => {
34
36
  const { fixtures: fixturePath, patchEnv, ...serverOpts } = options;
35
37
  const llm = new require_llmock.LLMock(serverOpts);
@@ -39,6 +41,8 @@ function useAimock(options = {}) {
39
41
  }
40
42
  const url = await llm.start();
41
43
  if (patchEnv !== false) {
44
+ origOpenaiUrl = process.env.OPENAI_BASE_URL;
45
+ origAnthropicUrl = process.env.ANTHROPIC_BASE_URL;
42
46
  process.env.OPENAI_BASE_URL = `${url}/v1`;
43
47
  process.env.ANTHROPIC_BASE_URL = `${url}/v1`;
44
48
  }
@@ -53,8 +57,10 @@ function useAimock(options = {}) {
53
57
  (0, vitest.afterAll)(async () => {
54
58
  if (handle) {
55
59
  if (options.patchEnv !== false) {
56
- delete process.env.OPENAI_BASE_URL;
57
- delete process.env.ANTHROPIC_BASE_URL;
60
+ if (origOpenaiUrl !== void 0) process.env.OPENAI_BASE_URL = origOpenaiUrl;
61
+ else delete process.env.OPENAI_BASE_URL;
62
+ if (origAnthropicUrl !== void 0) process.env.ANTHROPIC_BASE_URL = origAnthropicUrl;
63
+ else delete process.env.ANTHROPIC_BASE_URL;
58
64
  }
59
65
  await handle.llm.stop();
60
66
  handle = null;
@@ -69,7 +75,8 @@ function loadFixtures(fixturePath) {
69
75
  try {
70
76
  if ((0, node_fs.statSync)(fixturePath).isDirectory()) return require_fixture_loader.loadFixturesFromDir(fixturePath);
71
77
  return require_fixture_loader.loadFixtureFile(fixturePath);
72
- } catch {
78
+ } catch (err) {
79
+ console.warn(`[aimock] Failed to load fixtures from ${fixturePath}: ${err instanceof Error ? err.message : String(err)}`);
73
80
  return [];
74
81
  }
75
82
  }
@@ -1 +1 @@
1
- {"version":3,"file":"vitest.cjs","names":["LLMock","loadFixturesFromDir","loadFixtureFile"],"sources":["../src/vitest.ts"],"sourcesContent":["/**\n * Vitest integration for aimock.\n *\n * Usage:\n * import { useAimock } from \"@copilotkit/aimock/vitest\";\n *\n * const mock = useAimock({ fixtures: \"./fixtures\" });\n *\n * it(\"responds\", async () => {\n * const res = await fetch(`${mock().url}/v1/chat/completions`, { ... });\n * });\n */\n\nimport { beforeAll, afterAll, beforeEach } from \"vitest\";\nimport { LLMock } from \"./llmock.js\";\nimport { loadFixtureFile, loadFixturesFromDir } from \"./fixture-loader.js\";\nimport type { Fixture, MockServerOptions } from \"./types.js\";\nimport { statSync } from \"node:fs\";\nimport { resolve } from \"node:path\";\n\nexport interface UseAimockOptions extends MockServerOptions {\n /** Path to fixture file or directory. Loaded automatically on start. */\n fixtures?: string;\n /** If true, sets process.env.OPENAI_BASE_URL to the mock URL + /v1. */\n patchEnv?: boolean;\n}\n\nexport interface AimockHandle {\n /** The LLMock instance. */\n readonly llm: LLMock;\n /** The server URL (e.g., http://127.0.0.1:4010). */\n readonly url: string;\n}\n\n/**\n * Start an aimock server for the duration of the test suite.\n *\n * - `beforeAll`: starts the server and optionally loads fixtures\n * - `beforeEach`: resets fixture match counts (not fixtures themselves)\n * - `afterAll`: stops the server\n *\n * Returns a getter function — call it inside tests to access the handle.\n */\nexport function useAimock(options: UseAimockOptions = {}): () => AimockHandle {\n let handle: AimockHandle | null = null;\n\n beforeAll(async () => {\n const { fixtures: fixturePath, patchEnv, ...serverOpts } = options;\n const llm = new LLMock(serverOpts);\n\n if (fixturePath) {\n const resolved = resolve(fixturePath);\n const loadedFixtures = loadFixtures(resolved);\n for (const f of loadedFixtures) {\n llm.addFixture(f);\n }\n }\n\n const url = await llm.start();\n\n if (patchEnv !== false) {\n process.env.OPENAI_BASE_URL = `${url}/v1`;\n process.env.ANTHROPIC_BASE_URL = `${url}/v1`;\n }\n\n handle = { llm, url };\n });\n\n beforeEach(() => {\n if (handle) {\n handle.llm.resetMatchCounts();\n }\n });\n\n afterAll(async () => {\n if (handle) {\n if (options.patchEnv !== false) {\n delete process.env.OPENAI_BASE_URL;\n delete process.env.ANTHROPIC_BASE_URL;\n }\n await handle.llm.stop();\n handle = null;\n }\n });\n\n return () => {\n if (!handle) {\n throw new Error(\"useAimock(): server not started — are you calling this inside a test?\");\n }\n return handle;\n };\n}\n\nfunction loadFixtures(fixturePath: string): Fixture[] {\n try {\n const stat = statSync(fixturePath);\n if (stat.isDirectory()) {\n return loadFixturesFromDir(fixturePath);\n }\n return loadFixtureFile(fixturePath);\n } catch {\n return [];\n }\n}\n\nexport { LLMock } from \"./llmock.js\";\nexport type { MockServerOptions, Fixture } from \"./types.js\";\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA2CA,SAAgB,UAAU,UAA4B,EAAE,EAAsB;CAC5E,IAAI,SAA8B;AAElC,uBAAU,YAAY;EACpB,MAAM,EAAE,UAAU,aAAa,UAAU,GAAG,eAAe;EAC3D,MAAM,MAAM,IAAIA,sBAAO,WAAW;AAElC,MAAI,aAAa;GAEf,MAAM,iBAAiB,oCADE,YAAY,CACQ;AAC7C,QAAK,MAAM,KAAK,eACd,KAAI,WAAW,EAAE;;EAIrB,MAAM,MAAM,MAAM,IAAI,OAAO;AAE7B,MAAI,aAAa,OAAO;AACtB,WAAQ,IAAI,kBAAkB,GAAG,IAAI;AACrC,WAAQ,IAAI,qBAAqB,GAAG,IAAI;;AAG1C,WAAS;GAAE;GAAK;GAAK;GACrB;AAEF,8BAAiB;AACf,MAAI,OACF,QAAO,IAAI,kBAAkB;GAE/B;AAEF,sBAAS,YAAY;AACnB,MAAI,QAAQ;AACV,OAAI,QAAQ,aAAa,OAAO;AAC9B,WAAO,QAAQ,IAAI;AACnB,WAAO,QAAQ,IAAI;;AAErB,SAAM,OAAO,IAAI,MAAM;AACvB,YAAS;;GAEX;AAEF,cAAa;AACX,MAAI,CAAC,OACH,OAAM,IAAI,MAAM,wEAAwE;AAE1F,SAAO;;;AAIX,SAAS,aAAa,aAAgC;AACpD,KAAI;AAEF,4BADsB,YAAY,CACzB,aAAa,CACpB,QAAOC,2CAAoB,YAAY;AAEzC,SAAOC,uCAAgB,YAAY;SAC7B;AACN,SAAO,EAAE"}
1
+ {"version":3,"file":"vitest.cjs","names":["LLMock","loadFixturesFromDir","loadFixtureFile"],"sources":["../src/vitest.ts"],"sourcesContent":["/**\n * Vitest integration for aimock.\n *\n * Usage:\n * import { useAimock } from \"@copilotkit/aimock/vitest\";\n *\n * const mock = useAimock({ fixtures: \"./fixtures\" });\n *\n * it(\"responds\", async () => {\n * const res = await fetch(`${mock().url}/v1/chat/completions`, { ... });\n * });\n */\n\nimport { beforeAll, afterAll, beforeEach } from \"vitest\";\nimport { LLMock } from \"./llmock.js\";\nimport { loadFixtureFile, loadFixturesFromDir } from \"./fixture-loader.js\";\nimport type { Fixture, MockServerOptions } from \"./types.js\";\nimport { statSync } from \"node:fs\";\nimport { resolve } from \"node:path\";\n\nexport interface UseAimockOptions extends MockServerOptions {\n /** Path to fixture file or directory. Loaded automatically on start. */\n fixtures?: string;\n /** If true, sets process.env.OPENAI_BASE_URL to the mock URL + /v1. */\n patchEnv?: boolean;\n}\n\nexport interface AimockHandle {\n /** The LLMock instance. */\n readonly llm: LLMock;\n /** The server URL (e.g., http://127.0.0.1:4010). */\n readonly url: string;\n}\n\n/**\n * Start an aimock server for the duration of the test suite.\n *\n * - `beforeAll`: starts the server and optionally loads fixtures\n * - `beforeEach`: resets fixture match counts (not fixtures themselves)\n * - `afterAll`: stops the server\n *\n * Returns a getter function — call it inside tests to access the handle.\n */\nexport function useAimock(options: UseAimockOptions = {}): () => AimockHandle {\n let handle: AimockHandle | null = null;\n let origOpenaiUrl: string | undefined;\n let origAnthropicUrl: string | undefined;\n\n beforeAll(async () => {\n const { fixtures: fixturePath, patchEnv, ...serverOpts } = options;\n const llm = new LLMock(serverOpts);\n\n if (fixturePath) {\n const resolved = resolve(fixturePath);\n const loadedFixtures = loadFixtures(resolved);\n for (const f of loadedFixtures) {\n llm.addFixture(f);\n }\n }\n\n const url = await llm.start();\n\n if (patchEnv !== false) {\n origOpenaiUrl = process.env.OPENAI_BASE_URL;\n origAnthropicUrl = process.env.ANTHROPIC_BASE_URL;\n process.env.OPENAI_BASE_URL = `${url}/v1`;\n process.env.ANTHROPIC_BASE_URL = `${url}/v1`;\n }\n\n handle = { llm, url };\n });\n\n beforeEach(() => {\n if (handle) {\n handle.llm.resetMatchCounts();\n }\n });\n\n afterAll(async () => {\n if (handle) {\n if (options.patchEnv !== false) {\n if (origOpenaiUrl !== undefined) process.env.OPENAI_BASE_URL = origOpenaiUrl;\n else delete process.env.OPENAI_BASE_URL;\n if (origAnthropicUrl !== undefined) process.env.ANTHROPIC_BASE_URL = origAnthropicUrl;\n else delete process.env.ANTHROPIC_BASE_URL;\n }\n await handle.llm.stop();\n handle = null;\n }\n });\n\n return () => {\n if (!handle) {\n throw new Error(\"useAimock(): server not started — are you calling this inside a test?\");\n }\n return handle;\n };\n}\n\nfunction loadFixtures(fixturePath: string): Fixture[] {\n try {\n const stat = statSync(fixturePath);\n if (stat.isDirectory()) {\n return loadFixturesFromDir(fixturePath);\n }\n return loadFixtureFile(fixturePath);\n } catch (err) {\n console.warn(\n `[aimock] Failed to load fixtures from ${fixturePath}: ${err instanceof Error ? err.message : String(err)}`,\n );\n return [];\n }\n}\n\nexport { LLMock } from \"./llmock.js\";\nexport type { MockServerOptions, Fixture } from \"./types.js\";\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA2CA,SAAgB,UAAU,UAA4B,EAAE,EAAsB;CAC5E,IAAI,SAA8B;CAClC,IAAI;CACJ,IAAI;AAEJ,uBAAU,YAAY;EACpB,MAAM,EAAE,UAAU,aAAa,UAAU,GAAG,eAAe;EAC3D,MAAM,MAAM,IAAIA,sBAAO,WAAW;AAElC,MAAI,aAAa;GAEf,MAAM,iBAAiB,oCADE,YAAY,CACQ;AAC7C,QAAK,MAAM,KAAK,eACd,KAAI,WAAW,EAAE;;EAIrB,MAAM,MAAM,MAAM,IAAI,OAAO;AAE7B,MAAI,aAAa,OAAO;AACtB,mBAAgB,QAAQ,IAAI;AAC5B,sBAAmB,QAAQ,IAAI;AAC/B,WAAQ,IAAI,kBAAkB,GAAG,IAAI;AACrC,WAAQ,IAAI,qBAAqB,GAAG,IAAI;;AAG1C,WAAS;GAAE;GAAK;GAAK;GACrB;AAEF,8BAAiB;AACf,MAAI,OACF,QAAO,IAAI,kBAAkB;GAE/B;AAEF,sBAAS,YAAY;AACnB,MAAI,QAAQ;AACV,OAAI,QAAQ,aAAa,OAAO;AAC9B,QAAI,kBAAkB,OAAW,SAAQ,IAAI,kBAAkB;QAC1D,QAAO,QAAQ,IAAI;AACxB,QAAI,qBAAqB,OAAW,SAAQ,IAAI,qBAAqB;QAChE,QAAO,QAAQ,IAAI;;AAE1B,SAAM,OAAO,IAAI,MAAM;AACvB,YAAS;;GAEX;AAEF,cAAa;AACX,MAAI,CAAC,OACH,OAAM,IAAI,MAAM,wEAAwE;AAE1F,SAAO;;;AAIX,SAAS,aAAa,aAAgC;AACpD,KAAI;AAEF,4BADsB,YAAY,CACzB,aAAa,CACpB,QAAOC,2CAAoB,YAAY;AAEzC,SAAOC,uCAAgB,YAAY;UAC5B,KAAK;AACZ,UAAQ,KACN,yCAAyC,YAAY,IAAI,eAAe,QAAQ,IAAI,UAAU,OAAO,IAAI,GAC1G;AACD,SAAO,EAAE"}