ai 6.0.32 → 6.0.34

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 (353) hide show
  1. package/CHANGELOG.md +16 -0
  2. package/dist/index.js +12 -2
  3. package/dist/index.js.map +1 -1
  4. package/dist/index.mjs +12 -2
  5. package/dist/index.mjs.map +1 -1
  6. package/dist/internal/index.js +1 -1
  7. package/dist/internal/index.mjs +1 -1
  8. package/docs/02-foundations/03-prompts.mdx +2 -2
  9. package/docs/03-ai-sdk-core/15-tools-and-tool-calling.mdx +1 -1
  10. package/docs/07-reference/01-ai-sdk-core/28-output.mdx +1 -1
  11. package/package.json +6 -4
  12. package/src/agent/agent.ts +116 -0
  13. package/src/agent/create-agent-ui-stream-response.test.ts +258 -0
  14. package/src/agent/create-agent-ui-stream-response.ts +50 -0
  15. package/src/agent/create-agent-ui-stream.ts +73 -0
  16. package/src/agent/index.ts +33 -0
  17. package/src/agent/infer-agent-tools.ts +7 -0
  18. package/src/agent/infer-agent-ui-message.test-d.ts +54 -0
  19. package/src/agent/infer-agent-ui-message.ts +11 -0
  20. package/src/agent/pipe-agent-ui-stream-to-response.ts +52 -0
  21. package/src/agent/tool-loop-agent-on-finish-callback.ts +31 -0
  22. package/src/agent/tool-loop-agent-on-step-finish-callback.ts +11 -0
  23. package/src/agent/tool-loop-agent-settings.ts +182 -0
  24. package/src/agent/tool-loop-agent.test-d.ts +114 -0
  25. package/src/agent/tool-loop-agent.test.ts +442 -0
  26. package/src/agent/tool-loop-agent.ts +114 -0
  27. package/src/embed/__snapshots__/embed-many.test.ts.snap +191 -0
  28. package/src/embed/__snapshots__/embed.test.ts.snap +81 -0
  29. package/src/embed/embed-many-result.ts +53 -0
  30. package/src/embed/embed-many.test.ts +653 -0
  31. package/src/embed/embed-many.ts +378 -0
  32. package/src/embed/embed-result.ts +50 -0
  33. package/src/embed/embed.test.ts +298 -0
  34. package/src/embed/embed.ts +211 -0
  35. package/src/embed/index.ts +4 -0
  36. package/src/error/index.ts +34 -0
  37. package/src/error/invalid-argument-error.ts +34 -0
  38. package/src/error/invalid-stream-part-error.ts +28 -0
  39. package/src/error/invalid-tool-approval-error.ts +26 -0
  40. package/src/error/invalid-tool-input-error.ts +33 -0
  41. package/src/error/no-image-generated-error.ts +39 -0
  42. package/src/error/no-object-generated-error.ts +70 -0
  43. package/src/error/no-output-generated-error.ts +26 -0
  44. package/src/error/no-speech-generated-error.ts +18 -0
  45. package/src/error/no-such-tool-error.ts +35 -0
  46. package/src/error/no-transcript-generated-error.ts +20 -0
  47. package/src/error/tool-call-not-found-for-approval-error.ts +32 -0
  48. package/src/error/tool-call-repair-error.ts +30 -0
  49. package/src/error/unsupported-model-version-error.ts +23 -0
  50. package/src/error/verify-no-object-generated-error.ts +27 -0
  51. package/src/generate-image/generate-image-result.ts +42 -0
  52. package/src/generate-image/generate-image.test.ts +1420 -0
  53. package/src/generate-image/generate-image.ts +360 -0
  54. package/src/generate-image/index.ts +18 -0
  55. package/src/generate-object/__snapshots__/generate-object.test.ts.snap +133 -0
  56. package/src/generate-object/__snapshots__/stream-object.test.ts.snap +297 -0
  57. package/src/generate-object/generate-object-result.ts +67 -0
  58. package/src/generate-object/generate-object.test-d.ts +49 -0
  59. package/src/generate-object/generate-object.test.ts +1191 -0
  60. package/src/generate-object/generate-object.ts +518 -0
  61. package/src/generate-object/index.ts +9 -0
  62. package/src/generate-object/inject-json-instruction.test.ts +181 -0
  63. package/src/generate-object/inject-json-instruction.ts +30 -0
  64. package/src/generate-object/output-strategy.ts +415 -0
  65. package/src/generate-object/parse-and-validate-object-result.ts +111 -0
  66. package/src/generate-object/repair-text.ts +12 -0
  67. package/src/generate-object/stream-object-result.ts +120 -0
  68. package/src/generate-object/stream-object.test-d.ts +74 -0
  69. package/src/generate-object/stream-object.test.ts +1950 -0
  70. package/src/generate-object/stream-object.ts +986 -0
  71. package/src/generate-object/validate-object-generation-input.ts +144 -0
  72. package/src/generate-speech/generate-speech-result.ts +30 -0
  73. package/src/generate-speech/generate-speech.test.ts +300 -0
  74. package/src/generate-speech/generate-speech.ts +190 -0
  75. package/src/generate-speech/generated-audio-file.ts +65 -0
  76. package/src/generate-speech/index.ts +3 -0
  77. package/src/generate-text/__snapshots__/generate-text.test.ts.snap +1872 -0
  78. package/src/generate-text/__snapshots__/stream-text.test.ts.snap +1255 -0
  79. package/src/generate-text/collect-tool-approvals.test.ts +553 -0
  80. package/src/generate-text/collect-tool-approvals.ts +116 -0
  81. package/src/generate-text/content-part.ts +25 -0
  82. package/src/generate-text/execute-tool-call.ts +129 -0
  83. package/src/generate-text/extract-reasoning-content.ts +17 -0
  84. package/src/generate-text/extract-text-content.ts +15 -0
  85. package/src/generate-text/generate-text-result.ts +168 -0
  86. package/src/generate-text/generate-text.test-d.ts +68 -0
  87. package/src/generate-text/generate-text.test.ts +7011 -0
  88. package/src/generate-text/generate-text.ts +1223 -0
  89. package/src/generate-text/generated-file.ts +70 -0
  90. package/src/generate-text/index.ts +57 -0
  91. package/src/generate-text/is-approval-needed.ts +29 -0
  92. package/src/generate-text/output-utils.ts +23 -0
  93. package/src/generate-text/output.test.ts +698 -0
  94. package/src/generate-text/output.ts +590 -0
  95. package/src/generate-text/parse-tool-call.test.ts +570 -0
  96. package/src/generate-text/parse-tool-call.ts +188 -0
  97. package/src/generate-text/prepare-step.ts +103 -0
  98. package/src/generate-text/prune-messages.test.ts +720 -0
  99. package/src/generate-text/prune-messages.ts +167 -0
  100. package/src/generate-text/reasoning-output.ts +20 -0
  101. package/src/generate-text/reasoning.ts +8 -0
  102. package/src/generate-text/response-message.ts +10 -0
  103. package/src/generate-text/run-tools-transformation.test.ts +1143 -0
  104. package/src/generate-text/run-tools-transformation.ts +420 -0
  105. package/src/generate-text/smooth-stream.test.ts +2101 -0
  106. package/src/generate-text/smooth-stream.ts +162 -0
  107. package/src/generate-text/step-result.ts +238 -0
  108. package/src/generate-text/stop-condition.ts +29 -0
  109. package/src/generate-text/stream-text-result.ts +463 -0
  110. package/src/generate-text/stream-text.test-d.ts +200 -0
  111. package/src/generate-text/stream-text.test.ts +19979 -0
  112. package/src/generate-text/stream-text.ts +2505 -0
  113. package/src/generate-text/to-response-messages.test.ts +922 -0
  114. package/src/generate-text/to-response-messages.ts +163 -0
  115. package/src/generate-text/tool-approval-request-output.ts +21 -0
  116. package/src/generate-text/tool-call-repair-function.ts +27 -0
  117. package/src/generate-text/tool-call.ts +47 -0
  118. package/src/generate-text/tool-error.ts +34 -0
  119. package/src/generate-text/tool-output-denied.ts +21 -0
  120. package/src/generate-text/tool-output.ts +7 -0
  121. package/src/generate-text/tool-result.ts +36 -0
  122. package/src/generate-text/tool-set.ts +14 -0
  123. package/src/global.ts +24 -0
  124. package/src/index.ts +50 -0
  125. package/src/logger/index.ts +6 -0
  126. package/src/logger/log-warnings.test.ts +351 -0
  127. package/src/logger/log-warnings.ts +119 -0
  128. package/src/middleware/__snapshots__/simulate-streaming-middleware.test.ts.snap +64 -0
  129. package/src/middleware/add-tool-input-examples-middleware.test.ts +476 -0
  130. package/src/middleware/add-tool-input-examples-middleware.ts +90 -0
  131. package/src/middleware/default-embedding-settings-middleware.test.ts +126 -0
  132. package/src/middleware/default-embedding-settings-middleware.ts +22 -0
  133. package/src/middleware/default-settings-middleware.test.ts +388 -0
  134. package/src/middleware/default-settings-middleware.ts +33 -0
  135. package/src/middleware/extract-json-middleware.test.ts +827 -0
  136. package/src/middleware/extract-json-middleware.ts +197 -0
  137. package/src/middleware/extract-reasoning-middleware.test.ts +1028 -0
  138. package/src/middleware/extract-reasoning-middleware.ts +238 -0
  139. package/src/middleware/index.ts +10 -0
  140. package/src/middleware/simulate-streaming-middleware.test.ts +911 -0
  141. package/src/middleware/simulate-streaming-middleware.ts +79 -0
  142. package/src/middleware/wrap-embedding-model.test.ts +358 -0
  143. package/src/middleware/wrap-embedding-model.ts +86 -0
  144. package/src/middleware/wrap-image-model.test.ts +423 -0
  145. package/src/middleware/wrap-image-model.ts +85 -0
  146. package/src/middleware/wrap-language-model.test.ts +518 -0
  147. package/src/middleware/wrap-language-model.ts +104 -0
  148. package/src/middleware/wrap-provider.test.ts +120 -0
  149. package/src/middleware/wrap-provider.ts +51 -0
  150. package/src/model/as-embedding-model-v3.test.ts +319 -0
  151. package/src/model/as-embedding-model-v3.ts +24 -0
  152. package/src/model/as-image-model-v3.test.ts +409 -0
  153. package/src/model/as-image-model-v3.ts +24 -0
  154. package/src/model/as-language-model-v3.test.ts +508 -0
  155. package/src/model/as-language-model-v3.ts +103 -0
  156. package/src/model/as-provider-v3.ts +36 -0
  157. package/src/model/as-speech-model-v3.test.ts +356 -0
  158. package/src/model/as-speech-model-v3.ts +24 -0
  159. package/src/model/as-transcription-model-v3.test.ts +529 -0
  160. package/src/model/as-transcription-model-v3.ts +24 -0
  161. package/src/model/resolve-model.test.ts +244 -0
  162. package/src/model/resolve-model.ts +126 -0
  163. package/src/prompt/call-settings.ts +148 -0
  164. package/src/prompt/content-part.ts +209 -0
  165. package/src/prompt/convert-to-language-model-prompt.test.ts +2018 -0
  166. package/src/prompt/convert-to-language-model-prompt.ts +442 -0
  167. package/src/prompt/create-tool-model-output.test.ts +508 -0
  168. package/src/prompt/create-tool-model-output.ts +34 -0
  169. package/src/prompt/data-content.test.ts +15 -0
  170. package/src/prompt/data-content.ts +134 -0
  171. package/src/prompt/index.ts +27 -0
  172. package/src/prompt/invalid-data-content-error.ts +29 -0
  173. package/src/prompt/invalid-message-role-error.ts +27 -0
  174. package/src/prompt/message-conversion-error.ts +28 -0
  175. package/src/prompt/message.ts +68 -0
  176. package/src/prompt/prepare-call-settings.test.ts +159 -0
  177. package/src/prompt/prepare-call-settings.ts +108 -0
  178. package/src/prompt/prepare-tools-and-tool-choice.test.ts +461 -0
  179. package/src/prompt/prepare-tools-and-tool-choice.ts +86 -0
  180. package/src/prompt/prompt.ts +43 -0
  181. package/src/prompt/split-data-url.ts +17 -0
  182. package/src/prompt/standardize-prompt.test.ts +82 -0
  183. package/src/prompt/standardize-prompt.ts +99 -0
  184. package/src/prompt/wrap-gateway-error.ts +29 -0
  185. package/src/registry/custom-provider.test.ts +211 -0
  186. package/src/registry/custom-provider.ts +155 -0
  187. package/src/registry/index.ts +7 -0
  188. package/src/registry/no-such-provider-error.ts +41 -0
  189. package/src/registry/provider-registry.test.ts +691 -0
  190. package/src/registry/provider-registry.ts +328 -0
  191. package/src/rerank/index.ts +2 -0
  192. package/src/rerank/rerank-result.ts +70 -0
  193. package/src/rerank/rerank.test.ts +516 -0
  194. package/src/rerank/rerank.ts +237 -0
  195. package/src/telemetry/assemble-operation-name.ts +21 -0
  196. package/src/telemetry/get-base-telemetry-attributes.ts +53 -0
  197. package/src/telemetry/get-tracer.ts +20 -0
  198. package/src/telemetry/noop-tracer.ts +69 -0
  199. package/src/telemetry/record-span.ts +63 -0
  200. package/src/telemetry/select-telemetry-attributes.ts +78 -0
  201. package/src/telemetry/select-temetry-attributes.test.ts +114 -0
  202. package/src/telemetry/stringify-for-telemetry.test.ts +114 -0
  203. package/src/telemetry/stringify-for-telemetry.ts +33 -0
  204. package/src/telemetry/telemetry-settings.ts +44 -0
  205. package/src/test/mock-embedding-model-v2.ts +35 -0
  206. package/src/test/mock-embedding-model-v3.ts +48 -0
  207. package/src/test/mock-image-model-v2.ts +28 -0
  208. package/src/test/mock-image-model-v3.ts +28 -0
  209. package/src/test/mock-language-model-v2.ts +72 -0
  210. package/src/test/mock-language-model-v3.ts +77 -0
  211. package/src/test/mock-provider-v2.ts +68 -0
  212. package/src/test/mock-provider-v3.ts +80 -0
  213. package/src/test/mock-reranking-model-v3.ts +25 -0
  214. package/src/test/mock-server-response.ts +69 -0
  215. package/src/test/mock-speech-model-v2.ts +24 -0
  216. package/src/test/mock-speech-model-v3.ts +24 -0
  217. package/src/test/mock-tracer.ts +156 -0
  218. package/src/test/mock-transcription-model-v2.ts +24 -0
  219. package/src/test/mock-transcription-model-v3.ts +24 -0
  220. package/src/test/mock-values.ts +4 -0
  221. package/src/test/not-implemented.ts +3 -0
  222. package/src/text-stream/create-text-stream-response.test.ts +38 -0
  223. package/src/text-stream/create-text-stream-response.ts +18 -0
  224. package/src/text-stream/index.ts +2 -0
  225. package/src/text-stream/pipe-text-stream-to-response.test.ts +38 -0
  226. package/src/text-stream/pipe-text-stream-to-response.ts +26 -0
  227. package/src/transcribe/index.ts +2 -0
  228. package/src/transcribe/transcribe-result.ts +60 -0
  229. package/src/transcribe/transcribe.test.ts +313 -0
  230. package/src/transcribe/transcribe.ts +173 -0
  231. package/src/types/embedding-model-middleware.ts +3 -0
  232. package/src/types/embedding-model.ts +18 -0
  233. package/src/types/image-model-middleware.ts +3 -0
  234. package/src/types/image-model-response-metadata.ts +16 -0
  235. package/src/types/image-model.ts +19 -0
  236. package/src/types/index.ts +29 -0
  237. package/src/types/json-value.ts +15 -0
  238. package/src/types/language-model-middleware.ts +3 -0
  239. package/src/types/language-model-request-metadata.ts +6 -0
  240. package/src/types/language-model-response-metadata.ts +21 -0
  241. package/src/types/language-model.ts +104 -0
  242. package/src/types/provider-metadata.ts +16 -0
  243. package/src/types/provider.ts +55 -0
  244. package/src/types/reranking-model.ts +6 -0
  245. package/src/types/speech-model-response-metadata.ts +21 -0
  246. package/src/types/speech-model.ts +6 -0
  247. package/src/types/transcription-model-response-metadata.ts +16 -0
  248. package/src/types/transcription-model.ts +9 -0
  249. package/src/types/usage.ts +200 -0
  250. package/src/types/warning.ts +7 -0
  251. package/src/ui/__snapshots__/append-response-messages.test.ts.snap +416 -0
  252. package/src/ui/__snapshots__/convert-to-model-messages.test.ts.snap +419 -0
  253. package/src/ui/__snapshots__/process-chat-text-response.test.ts.snap +142 -0
  254. package/src/ui/call-completion-api.ts +157 -0
  255. package/src/ui/chat-transport.ts +83 -0
  256. package/src/ui/chat.test-d.ts +233 -0
  257. package/src/ui/chat.test.ts +2695 -0
  258. package/src/ui/chat.ts +716 -0
  259. package/src/ui/convert-file-list-to-file-ui-parts.ts +36 -0
  260. package/src/ui/convert-to-model-messages.test.ts +2775 -0
  261. package/src/ui/convert-to-model-messages.ts +373 -0
  262. package/src/ui/default-chat-transport.ts +36 -0
  263. package/src/ui/direct-chat-transport.test.ts +446 -0
  264. package/src/ui/direct-chat-transport.ts +118 -0
  265. package/src/ui/http-chat-transport.test.ts +185 -0
  266. package/src/ui/http-chat-transport.ts +292 -0
  267. package/src/ui/index.ts +71 -0
  268. package/src/ui/last-assistant-message-is-complete-with-approval-responses.ts +44 -0
  269. package/src/ui/last-assistant-message-is-complete-with-tool-calls.test.ts +371 -0
  270. package/src/ui/last-assistant-message-is-complete-with-tool-calls.ts +39 -0
  271. package/src/ui/process-text-stream.test.ts +38 -0
  272. package/src/ui/process-text-stream.ts +16 -0
  273. package/src/ui/process-ui-message-stream.test.ts +8052 -0
  274. package/src/ui/process-ui-message-stream.ts +713 -0
  275. package/src/ui/text-stream-chat-transport.ts +23 -0
  276. package/src/ui/transform-text-to-ui-message-stream.test.ts +124 -0
  277. package/src/ui/transform-text-to-ui-message-stream.ts +27 -0
  278. package/src/ui/ui-messages.test.ts +48 -0
  279. package/src/ui/ui-messages.ts +534 -0
  280. package/src/ui/use-completion.ts +84 -0
  281. package/src/ui/validate-ui-messages.test.ts +1428 -0
  282. package/src/ui/validate-ui-messages.ts +476 -0
  283. package/src/ui-message-stream/create-ui-message-stream-response.test.ts +266 -0
  284. package/src/ui-message-stream/create-ui-message-stream-response.ts +32 -0
  285. package/src/ui-message-stream/create-ui-message-stream.test.ts +639 -0
  286. package/src/ui-message-stream/create-ui-message-stream.ts +124 -0
  287. package/src/ui-message-stream/get-response-ui-message-id.test.ts +55 -0
  288. package/src/ui-message-stream/get-response-ui-message-id.ts +24 -0
  289. package/src/ui-message-stream/handle-ui-message-stream-finish.test.ts +429 -0
  290. package/src/ui-message-stream/handle-ui-message-stream-finish.ts +135 -0
  291. package/src/ui-message-stream/index.ts +13 -0
  292. package/src/ui-message-stream/json-to-sse-transform-stream.ts +12 -0
  293. package/src/ui-message-stream/pipe-ui-message-stream-to-response.test.ts +90 -0
  294. package/src/ui-message-stream/pipe-ui-message-stream-to-response.ts +40 -0
  295. package/src/ui-message-stream/read-ui-message-stream.test.ts +122 -0
  296. package/src/ui-message-stream/read-ui-message-stream.ts +87 -0
  297. package/src/ui-message-stream/ui-message-chunks.test-d.ts +18 -0
  298. package/src/ui-message-stream/ui-message-chunks.ts +344 -0
  299. package/src/ui-message-stream/ui-message-stream-headers.ts +7 -0
  300. package/src/ui-message-stream/ui-message-stream-on-finish-callback.ts +32 -0
  301. package/src/ui-message-stream/ui-message-stream-response-init.ts +5 -0
  302. package/src/ui-message-stream/ui-message-stream-writer.ts +24 -0
  303. package/src/util/as-array.ts +3 -0
  304. package/src/util/async-iterable-stream.test.ts +241 -0
  305. package/src/util/async-iterable-stream.ts +94 -0
  306. package/src/util/consume-stream.ts +29 -0
  307. package/src/util/cosine-similarity.test.ts +57 -0
  308. package/src/util/cosine-similarity.ts +47 -0
  309. package/src/util/create-resolvable-promise.ts +30 -0
  310. package/src/util/create-stitchable-stream.test.ts +239 -0
  311. package/src/util/create-stitchable-stream.ts +112 -0
  312. package/src/util/data-url.ts +17 -0
  313. package/src/util/deep-partial.ts +84 -0
  314. package/src/util/detect-media-type.test.ts +670 -0
  315. package/src/util/detect-media-type.ts +184 -0
  316. package/src/util/download/download-function.ts +45 -0
  317. package/src/util/download/download.test.ts +69 -0
  318. package/src/util/download/download.ts +46 -0
  319. package/src/util/error-handler.ts +1 -0
  320. package/src/util/fix-json.test.ts +279 -0
  321. package/src/util/fix-json.ts +401 -0
  322. package/src/util/get-potential-start-index.test.ts +34 -0
  323. package/src/util/get-potential-start-index.ts +30 -0
  324. package/src/util/index.ts +11 -0
  325. package/src/util/is-deep-equal-data.test.ts +119 -0
  326. package/src/util/is-deep-equal-data.ts +48 -0
  327. package/src/util/is-non-empty-object.ts +5 -0
  328. package/src/util/job.ts +1 -0
  329. package/src/util/log-v2-compatibility-warning.ts +21 -0
  330. package/src/util/merge-abort-signals.test.ts +155 -0
  331. package/src/util/merge-abort-signals.ts +43 -0
  332. package/src/util/merge-objects.test.ts +118 -0
  333. package/src/util/merge-objects.ts +79 -0
  334. package/src/util/now.ts +4 -0
  335. package/src/util/parse-partial-json.test.ts +80 -0
  336. package/src/util/parse-partial-json.ts +30 -0
  337. package/src/util/prepare-headers.test.ts +51 -0
  338. package/src/util/prepare-headers.ts +14 -0
  339. package/src/util/prepare-retries.test.ts +10 -0
  340. package/src/util/prepare-retries.ts +47 -0
  341. package/src/util/retry-error.ts +41 -0
  342. package/src/util/retry-with-exponential-backoff.test.ts +446 -0
  343. package/src/util/retry-with-exponential-backoff.ts +154 -0
  344. package/src/util/serial-job-executor.test.ts +162 -0
  345. package/src/util/serial-job-executor.ts +36 -0
  346. package/src/util/simulate-readable-stream.test.ts +98 -0
  347. package/src/util/simulate-readable-stream.ts +39 -0
  348. package/src/util/split-array.test.ts +60 -0
  349. package/src/util/split-array.ts +20 -0
  350. package/src/util/value-of.ts +65 -0
  351. package/src/util/write-to-server-response.test.ts +266 -0
  352. package/src/util/write-to-server-response.ts +49 -0
  353. package/src/version.ts +5 -0
@@ -0,0 +1,670 @@
1
+ import { describe, expect, it } from 'vitest';
2
+ import {
3
+ audioMediaTypeSignatures,
4
+ detectMediaType,
5
+ imageMediaTypeSignatures,
6
+ } from './detect-media-type';
7
+ import { convertUint8ArrayToBase64 } from '@ai-sdk/provider-utils';
8
+
9
+ describe('detectMediaType', () => {
10
+ describe('GIF', () => {
11
+ it('should detect GIF from bytes', () => {
12
+ const gifBytes = new Uint8Array([0x47, 0x49, 0x46, 0xff, 0xff]);
13
+ expect(
14
+ detectMediaType({
15
+ data: gifBytes,
16
+ signatures: imageMediaTypeSignatures,
17
+ }),
18
+ ).toBe('image/gif');
19
+ });
20
+
21
+ it('should detect GIF from base64', () => {
22
+ const gifBase64 = 'R0lGabc123'; // Base64 string starting with GIF signature
23
+ expect(
24
+ detectMediaType({
25
+ data: gifBase64,
26
+ signatures: imageMediaTypeSignatures,
27
+ }),
28
+ ).toBe('image/gif');
29
+ });
30
+ });
31
+
32
+ describe('PNG', () => {
33
+ it('should detect PNG from bytes', () => {
34
+ const pngBytes = new Uint8Array([0x89, 0x50, 0x4e, 0x47, 0xff, 0xff]);
35
+ expect(
36
+ detectMediaType({
37
+ data: pngBytes,
38
+ signatures: imageMediaTypeSignatures,
39
+ }),
40
+ ).toBe('image/png');
41
+ });
42
+
43
+ it('should detect PNG from base64', () => {
44
+ const pngBase64 = 'iVBORwabc123'; // Base64 string starting with PNG signature
45
+ expect(
46
+ detectMediaType({
47
+ data: pngBase64,
48
+ signatures: imageMediaTypeSignatures,
49
+ }),
50
+ ).toBe('image/png');
51
+ });
52
+ });
53
+
54
+ describe('JPEG', () => {
55
+ it('should detect JPEG from bytes', () => {
56
+ const jpegBytes = new Uint8Array([0xff, 0xd8, 0xff, 0xff]);
57
+ expect(
58
+ detectMediaType({
59
+ data: jpegBytes,
60
+ signatures: imageMediaTypeSignatures,
61
+ }),
62
+ ).toBe('image/jpeg');
63
+ });
64
+
65
+ it('should detect JPEG from base64', () => {
66
+ const jpegBase64 = '/9j/abc123'; // Base64 string starting with JPEG signature
67
+ expect(
68
+ detectMediaType({
69
+ data: jpegBase64,
70
+ signatures: imageMediaTypeSignatures,
71
+ }),
72
+ ).toBe('image/jpeg');
73
+ });
74
+ });
75
+
76
+ describe('WebP', () => {
77
+ it('should detect WebP from bytes (positive webp image uint8)', () => {
78
+ // WebP format: RIFF + 4 bytes file size + WEBP
79
+ const webpBytes = new Uint8Array([
80
+ 0x52,
81
+ 0x49,
82
+ 0x46,
83
+ 0x46, // "RIFF"
84
+ 0x24,
85
+ 0x00,
86
+ 0x00,
87
+ 0x00, // file size (example: 36 bytes)
88
+ 0x57,
89
+ 0x45,
90
+ 0x42,
91
+ 0x50, // "WEBP"
92
+ 0x56,
93
+ 0x50,
94
+ 0x38,
95
+ 0x20, // VP8 chunk (additional WebP data)
96
+ ]);
97
+ expect(
98
+ detectMediaType({
99
+ data: webpBytes,
100
+ signatures: imageMediaTypeSignatures,
101
+ }),
102
+ ).toBe('image/webp');
103
+ });
104
+
105
+ it('should detect WebP from base64 (positive webp image base64)', () => {
106
+ // WebP: RIFF + file size + WEBP encoded to base64
107
+ const webpBytes = new Uint8Array([
108
+ 0x52,
109
+ 0x49,
110
+ 0x46,
111
+ 0x46, // "RIFF"
112
+ 0x24,
113
+ 0x00,
114
+ 0x00,
115
+ 0x00, // file size
116
+ 0x57,
117
+ 0x45,
118
+ 0x42,
119
+ 0x50, // "WEBP"
120
+ 0x56,
121
+ 0x50,
122
+ 0x38,
123
+ 0x20, // VP8 chunk
124
+ ]);
125
+ const webpBase64 = convertUint8ArrayToBase64(webpBytes);
126
+ expect(
127
+ detectMediaType({
128
+ data: webpBase64,
129
+ signatures: imageMediaTypeSignatures,
130
+ }),
131
+ ).toBe('image/webp');
132
+ });
133
+
134
+ it('should NOT detect RIFF audio as WebP from bytes (negative riff audio uint8)', () => {
135
+ // WAV format: RIFF + file size + WAVE (not WEBP)
136
+ const wavBytes = new Uint8Array([
137
+ 0x52,
138
+ 0x49,
139
+ 0x46,
140
+ 0x46, // "RIFF"
141
+ 0x24,
142
+ 0x00,
143
+ 0x00,
144
+ 0x00, // file size
145
+ 0x57,
146
+ 0x41,
147
+ 0x56,
148
+ 0x45, // "WAVE" (not "WEBP")
149
+ 0x66,
150
+ 0x6d,
151
+ 0x74,
152
+ 0x20, // fmt chunk
153
+ ]);
154
+ expect(
155
+ detectMediaType({
156
+ data: wavBytes,
157
+ signatures: imageMediaTypeSignatures,
158
+ }),
159
+ ).toBeUndefined(); // Should not detect as WebP
160
+ });
161
+
162
+ it('should NOT detect RIFF audio as WebP from base64 (negative riff audio base64)', () => {
163
+ // WAV format encoded to base64
164
+ const wavBytes = new Uint8Array([
165
+ 0x52,
166
+ 0x49,
167
+ 0x46,
168
+ 0x46, // "RIFF"
169
+ 0x24,
170
+ 0x00,
171
+ 0x00,
172
+ 0x00, // file size
173
+ 0x57,
174
+ 0x41,
175
+ 0x56,
176
+ 0x45, // "WAVE" (not "WEBP")
177
+ 0x66,
178
+ 0x6d,
179
+ 0x74,
180
+ 0x20, // fmt chunk
181
+ ]);
182
+ const wavBase64 = convertUint8ArrayToBase64(wavBytes);
183
+ expect(
184
+ detectMediaType({
185
+ data: wavBase64,
186
+ signatures: imageMediaTypeSignatures,
187
+ }),
188
+ ).toBeUndefined(); // Should not detect as WebP
189
+ });
190
+ });
191
+
192
+ describe('BMP', () => {
193
+ it('should detect BMP from bytes', () => {
194
+ const bmpBytes = new Uint8Array([0x42, 0x4d, 0xff, 0xff]);
195
+ expect(
196
+ detectMediaType({
197
+ data: bmpBytes,
198
+ signatures: imageMediaTypeSignatures,
199
+ }),
200
+ ).toBe('image/bmp');
201
+ });
202
+
203
+ it('should detect BMP from base64', () => {
204
+ const bmpBytes = new Uint8Array([0x42, 0x4d, 0xff, 0xff]);
205
+ expect(
206
+ detectMediaType({
207
+ data: convertUint8ArrayToBase64(bmpBytes),
208
+ signatures: imageMediaTypeSignatures,
209
+ }),
210
+ ).toBe('image/bmp');
211
+ });
212
+ });
213
+
214
+ describe('TIFF', () => {
215
+ it('should detect TIFF (little endian) from bytes', () => {
216
+ const tiffLEBytes = new Uint8Array([0x49, 0x49, 0x2a, 0x00, 0xff]);
217
+ expect(
218
+ detectMediaType({
219
+ data: tiffLEBytes,
220
+ signatures: imageMediaTypeSignatures,
221
+ }),
222
+ ).toBe('image/tiff');
223
+ });
224
+
225
+ it('should detect TIFF (little endian) from base64', () => {
226
+ const tiffLEBase64 = 'SUkqAAabc123'; // Base64 string starting with TIFF LE signature
227
+ expect(
228
+ detectMediaType({
229
+ data: tiffLEBase64,
230
+ signatures: imageMediaTypeSignatures,
231
+ }),
232
+ ).toBe('image/tiff');
233
+ });
234
+
235
+ it('should detect TIFF (big endian) from bytes', () => {
236
+ const tiffBEBytes = new Uint8Array([0x4d, 0x4d, 0x00, 0x2a, 0xff]);
237
+ expect(
238
+ detectMediaType({
239
+ data: tiffBEBytes,
240
+ signatures: imageMediaTypeSignatures,
241
+ }),
242
+ ).toBe('image/tiff');
243
+ });
244
+
245
+ it('should detect TIFF (big endian) from base64', () => {
246
+ const tiffBEBase64 = 'TU0AKgabc123'; // Base64 string starting with TIFF BE signature
247
+ expect(
248
+ detectMediaType({
249
+ data: tiffBEBase64,
250
+ signatures: imageMediaTypeSignatures,
251
+ }),
252
+ ).toBe('image/tiff');
253
+ });
254
+ });
255
+
256
+ describe('AVIF', () => {
257
+ it('should detect AVIF from bytes', () => {
258
+ const avifBytes = new Uint8Array([
259
+ 0x00, 0x00, 0x00, 0x20, 0x66, 0x74, 0x79, 0x70, 0x61, 0x76, 0x69, 0x66,
260
+ 0xff,
261
+ ]);
262
+ expect(
263
+ detectMediaType({
264
+ data: avifBytes,
265
+ signatures: imageMediaTypeSignatures,
266
+ }),
267
+ ).toBe('image/avif');
268
+ });
269
+
270
+ it('should detect AVIF from base64', () => {
271
+ const avifBase64 = 'AAAAIGZ0eXBhdmlmabc123'; // Base64 string starting with AVIF signature
272
+ expect(
273
+ detectMediaType({
274
+ data: avifBase64,
275
+ signatures: imageMediaTypeSignatures,
276
+ }),
277
+ ).toBe('image/avif');
278
+ });
279
+ });
280
+
281
+ describe('HEIC', () => {
282
+ it('should detect HEIC from bytes', () => {
283
+ const heicBytes = new Uint8Array([
284
+ 0x00, 0x00, 0x00, 0x20, 0x66, 0x74, 0x79, 0x70, 0x68, 0x65, 0x69, 0x63,
285
+ 0xff,
286
+ ]);
287
+ expect(
288
+ detectMediaType({
289
+ data: heicBytes,
290
+ signatures: imageMediaTypeSignatures,
291
+ }),
292
+ ).toBe('image/heic');
293
+ });
294
+
295
+ it('should detect HEIC from base64', () => {
296
+ const heicBase64 = 'AAAAIGZ0eXBoZWljabc123'; // Base64 string starting with HEIC signature
297
+ expect(
298
+ detectMediaType({
299
+ data: heicBase64,
300
+ signatures: imageMediaTypeSignatures,
301
+ }),
302
+ ).toBe('image/heic');
303
+ });
304
+ });
305
+
306
+ describe('MP3', () => {
307
+ it('should detect MP3 from bytes', () => {
308
+ const mp3Bytes = new Uint8Array([0xff, 0xfb]);
309
+ expect(
310
+ detectMediaType({
311
+ data: mp3Bytes,
312
+ signatures: audioMediaTypeSignatures,
313
+ }),
314
+ ).toBe('audio/mpeg');
315
+ });
316
+
317
+ it('should detect MP3 from base64', () => {
318
+ const mp3Base64 = '//s='; // Base64 string starting with MP3 signature
319
+ expect(
320
+ detectMediaType({
321
+ data: mp3Base64,
322
+ signatures: audioMediaTypeSignatures,
323
+ }),
324
+ ).toBe('audio/mpeg');
325
+ });
326
+
327
+ it('should detect MP3 with ID3v2 tags from bytes', () => {
328
+ const mp3WithID3Bytes = new Uint8Array([
329
+ 0x49,
330
+ 0x44,
331
+ 0x33, // 'ID3'
332
+ 0x03,
333
+ 0x00, // version
334
+ 0x00, // flags
335
+ 0x00,
336
+ 0x00,
337
+ 0x00,
338
+ 0x0a, // size (10 bytes)
339
+ // 10 bytes of ID3 data
340
+ 0x00,
341
+ 0x00,
342
+ 0x00,
343
+ 0x00,
344
+ 0x00,
345
+ 0x00,
346
+ 0x00,
347
+ 0x00,
348
+ 0x00,
349
+ 0x00,
350
+ // MP3 frame header
351
+ 0xff,
352
+ 0xfb,
353
+ 0x00,
354
+ 0x00,
355
+ ]);
356
+ expect(
357
+ detectMediaType({
358
+ data: mp3WithID3Bytes,
359
+ signatures: audioMediaTypeSignatures,
360
+ }),
361
+ ).toBe('audio/mpeg');
362
+ });
363
+ it('should detect MP3 with ID3v2 tags from base64', () => {
364
+ const mp3WithID3Bytes = new Uint8Array([
365
+ 0x49,
366
+ 0x44,
367
+ 0x33, // 'ID3'
368
+ 0x03,
369
+ 0x00, // version
370
+ 0x00, // flags
371
+ 0x00,
372
+ 0x00,
373
+ 0x00,
374
+ 0x0a, // size (10 bytes)
375
+ // 10 bytes of ID3 data
376
+ 0x00,
377
+ 0x00,
378
+ 0x00,
379
+ 0x00,
380
+ 0x00,
381
+ 0x00,
382
+ 0x00,
383
+ 0x00,
384
+ 0x00,
385
+ 0x00,
386
+ // MP3 frame header
387
+ 0xff,
388
+ 0xfb,
389
+ 0x00,
390
+ 0x00,
391
+ ]);
392
+ const mp3WithID3Base64 = convertUint8ArrayToBase64(mp3WithID3Bytes);
393
+ expect(
394
+ detectMediaType({
395
+ data: mp3WithID3Base64,
396
+ signatures: audioMediaTypeSignatures,
397
+ }),
398
+ ).toBe('audio/mpeg');
399
+ });
400
+ });
401
+
402
+ describe('WAV', () => {
403
+ // WebP format: RIFF + 4 bytes file size + WEBP
404
+ const webpBytes = new Uint8Array([
405
+ 0x52,
406
+ 0x49,
407
+ 0x46,
408
+ 0x46, // "RIFF"
409
+ 0x24,
410
+ 0x00,
411
+ 0x00,
412
+ 0x00, // file size (example: 36 bytes)
413
+ 0x57,
414
+ 0x45,
415
+ 0x42,
416
+ 0x50, // "WEBP"
417
+ 0x56,
418
+ 0x50,
419
+ 0x38,
420
+ 0x20, // VP8 chunk (additional WebP data)
421
+ ]);
422
+
423
+ const wavBytes = new Uint8Array([
424
+ 0x52,
425
+ 0x49,
426
+ 0x46,
427
+ 0x46, // "RIFF"
428
+ 0x24,
429
+ 0x00,
430
+ 0x00,
431
+ 0x00, // file size (example: 36 bytes)
432
+ 0x57,
433
+ 0x41,
434
+ 0x56,
435
+ 0x45, // "WAVE" (not "WEBP")
436
+ 0x66,
437
+ 0x6d,
438
+ 0x74,
439
+ 0x20, // fmt chunk
440
+ ]);
441
+
442
+ it('should detect WAV from bytes', () => {
443
+ expect(
444
+ detectMediaType({
445
+ data: wavBytes,
446
+ signatures: audioMediaTypeSignatures,
447
+ }),
448
+ ).toBe('audio/wav');
449
+ });
450
+
451
+ it('should detect WAV from base64', () => {
452
+ expect(
453
+ detectMediaType({
454
+ data: convertUint8ArrayToBase64(wavBytes),
455
+ signatures: audioMediaTypeSignatures,
456
+ }),
457
+ ).toBe('audio/wav');
458
+ });
459
+
460
+ it('should NOT detect WebP as WAV from bytes (negative webp image uint8)', () => {
461
+ expect(
462
+ detectMediaType({
463
+ data: webpBytes,
464
+ signatures: audioMediaTypeSignatures,
465
+ }),
466
+ ).toBeUndefined(); // Should not detect as WAV
467
+ });
468
+
469
+ it('should NOT detect WebP as WAV from base64 (negative webp image base64)', () => {
470
+ expect(
471
+ detectMediaType({
472
+ data: convertUint8ArrayToBase64(webpBytes),
473
+ signatures: audioMediaTypeSignatures,
474
+ }),
475
+ ).toBeUndefined(); // Should not detect as WAV
476
+ });
477
+ });
478
+
479
+ describe('OGG', () => {
480
+ it('should detect OGG from bytes', () => {
481
+ const oggBytes = new Uint8Array([0x4f, 0x67, 0x67, 0x53]);
482
+ expect(
483
+ detectMediaType({
484
+ data: oggBytes,
485
+ signatures: audioMediaTypeSignatures,
486
+ }),
487
+ ).toBe('audio/ogg');
488
+ });
489
+
490
+ it('should detect OGG from base64', () => {
491
+ const oggBase64 = 'T2dnUw'; // Base64 string starting with OGG signature
492
+ expect(
493
+ detectMediaType({
494
+ data: oggBase64,
495
+ signatures: audioMediaTypeSignatures,
496
+ }),
497
+ ).toBe('audio/ogg');
498
+ });
499
+ });
500
+
501
+ describe('FLAC', () => {
502
+ it('should detect FLAC from bytes', () => {
503
+ const flacBytes = new Uint8Array([0x66, 0x4c, 0x61, 0x43]);
504
+ expect(
505
+ detectMediaType({
506
+ data: flacBytes,
507
+ signatures: audioMediaTypeSignatures,
508
+ }),
509
+ ).toBe('audio/flac');
510
+ });
511
+
512
+ it('should detect FLAC from base64', () => {
513
+ const flacBase64 = 'ZkxhQw'; // Base64 string starting with FLAC signature
514
+ expect(
515
+ detectMediaType({
516
+ data: flacBase64,
517
+ signatures: audioMediaTypeSignatures,
518
+ }),
519
+ ).toBe('audio/flac');
520
+ });
521
+ });
522
+
523
+ describe('AAC', () => {
524
+ it('should detect AAC from bytes', () => {
525
+ const aacBytes = new Uint8Array([0x40, 0x15, 0x00, 0x00]);
526
+ expect(
527
+ detectMediaType({
528
+ data: aacBytes,
529
+ signatures: audioMediaTypeSignatures,
530
+ }),
531
+ ).toBe('audio/aac');
532
+ });
533
+
534
+ it('should detect AAC from base64', () => {
535
+ const aacBytes = new Uint8Array([0x40, 0x15, 0x00, 0x00]);
536
+ expect(
537
+ detectMediaType({
538
+ data: convertUint8ArrayToBase64(aacBytes),
539
+ signatures: audioMediaTypeSignatures,
540
+ }),
541
+ ).toBe('audio/aac');
542
+ });
543
+ });
544
+
545
+ describe('MP4', () => {
546
+ it('should detect MP4 from bytes', () => {
547
+ const mp4Bytes = new Uint8Array([0x66, 0x74, 0x79, 0x70]);
548
+ expect(
549
+ detectMediaType({
550
+ data: mp4Bytes,
551
+ signatures: audioMediaTypeSignatures,
552
+ }),
553
+ ).toBe('audio/mp4');
554
+ });
555
+
556
+ it('should detect MP4 from base64', () => {
557
+ const mp4Base64 = 'ZnR5cA'; // Base64 string starting with MP4 signature
558
+ expect(
559
+ detectMediaType({
560
+ data: mp4Base64,
561
+ signatures: audioMediaTypeSignatures,
562
+ }),
563
+ ).toBe('audio/mp4');
564
+ });
565
+ });
566
+
567
+ describe('WEBM', () => {
568
+ it('should detect WEBM from bytes', () => {
569
+ const webmBytes = new Uint8Array([0x1a, 0x45, 0xdf, 0xa3]);
570
+ expect(
571
+ detectMediaType({
572
+ data: webmBytes,
573
+ signatures: audioMediaTypeSignatures,
574
+ }),
575
+ ).toBe('audio/webm');
576
+ });
577
+
578
+ it('should detect WEBM from base64', () => {
579
+ const webmBase64 = 'GkXfow=='; // Base64 string starting with WEBM signature
580
+ expect(
581
+ detectMediaType({
582
+ data: webmBase64,
583
+ signatures: audioMediaTypeSignatures,
584
+ }),
585
+ ).toBe('audio/webm');
586
+ });
587
+ });
588
+
589
+ describe('error cases', () => {
590
+ it('should return undefined for unknown image formats', () => {
591
+ const unknownBytes = new Uint8Array([0x00, 0x01, 0x02, 0x03]);
592
+ expect(
593
+ detectMediaType({
594
+ data: unknownBytes,
595
+ signatures: imageMediaTypeSignatures,
596
+ }),
597
+ ).toBeUndefined();
598
+ });
599
+
600
+ it('should return undefined for unknown audio formats', () => {
601
+ const unknownBytes = new Uint8Array([0x00, 0x01, 0x02, 0x03]);
602
+ expect(
603
+ detectMediaType({
604
+ data: unknownBytes,
605
+ signatures: audioMediaTypeSignatures,
606
+ }),
607
+ ).toBeUndefined();
608
+ });
609
+
610
+ it('should return undefined for empty arrays for image', () => {
611
+ const emptyBytes = new Uint8Array([]);
612
+ expect(
613
+ detectMediaType({
614
+ data: emptyBytes,
615
+ signatures: imageMediaTypeSignatures,
616
+ }),
617
+ ).toBeUndefined();
618
+ });
619
+
620
+ it('should return undefined for empty arrays for audio', () => {
621
+ const emptyBytes = new Uint8Array([]);
622
+ expect(
623
+ detectMediaType({
624
+ data: emptyBytes,
625
+ signatures: audioMediaTypeSignatures,
626
+ }),
627
+ ).toBeUndefined();
628
+ });
629
+
630
+ it('should return undefined for arrays shorter than signature length for image', () => {
631
+ const shortBytes = new Uint8Array([0x89, 0x50]); // Incomplete PNG signature
632
+ expect(
633
+ detectMediaType({
634
+ data: shortBytes,
635
+ signatures: imageMediaTypeSignatures,
636
+ }),
637
+ ).toBeUndefined();
638
+ });
639
+
640
+ it('should return undefined for arrays shorter than signature length for audio', () => {
641
+ const shortBytes = new Uint8Array([0x4f, 0x67]); // Incomplete OGG signature
642
+ expect(
643
+ detectMediaType({
644
+ data: shortBytes,
645
+ signatures: audioMediaTypeSignatures,
646
+ }),
647
+ ).toBeUndefined();
648
+ });
649
+
650
+ it('should return undefined for invalid base64 strings for image', () => {
651
+ const invalidBase64 = 'invalid123';
652
+ expect(
653
+ detectMediaType({
654
+ data: invalidBase64,
655
+ signatures: imageMediaTypeSignatures,
656
+ }),
657
+ ).toBeUndefined();
658
+ });
659
+
660
+ it('should return undefined for invalid base64 strings for audio', () => {
661
+ const invalidBase64 = 'invalid123';
662
+ expect(
663
+ detectMediaType({
664
+ data: invalidBase64,
665
+ signatures: audioMediaTypeSignatures,
666
+ }),
667
+ ).toBeUndefined();
668
+ });
669
+ });
670
+ });