feat: add MIME sniffing and output MIME lookup

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
2026-07-30 09:00:45 +02:00
co-authored by Claude Sonnet 5
parent d0faa162dc
commit f7c7e547d2
3 changed files with 56 additions and 0 deletions
+29
View File
@@ -0,0 +1,29 @@
import { fileTypeFromFile } from 'file-type';
const OUTPUT_MIME_TYPES = {
jpg: 'image/jpeg',
jpeg: 'image/jpeg',
png: 'image/png',
webp: 'image/webp',
gif: 'image/gif',
tiff: 'image/tiff',
avif: 'image/avif',
bmp: 'image/bmp',
pdf: 'application/pdf',
html: 'text/html',
txt: 'text/plain',
docx: 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
};
export async function detectInputMime(filePath) {
const result = await fileTypeFromFile(filePath);
return result ?? null;
}
export function outputMimeType(targetFormat) {
const mime = OUTPUT_MIME_TYPES[targetFormat];
if (!mime) {
throw new Error(`No known MIME type for target format "${targetFormat}"`);
}
return mime;
}
BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 68 B

+27
View File
@@ -0,0 +1,27 @@
import { describe, it, expect } from 'vitest';
import path from 'node:path';
import { detectInputMime, outputMimeType } from '../src/mime.js';
describe('detectInputMime', () => {
it('detects PNG from magic bytes regardless of file extension', async () => {
const fixturePath = path.join(import.meta.dirname, 'fixtures', 'sample.png');
const result = await detectInputMime(fixturePath);
expect(result).toEqual({ ext: 'png', mime: 'image/png' });
});
});
describe('outputMimeType', () => {
it('returns the MIME type for a known target format', () => {
expect(outputMimeType('pdf')).toBe('application/pdf');
expect(outputMimeType('png')).toBe('image/png');
expect(outputMimeType('docx')).toBe(
'application/vnd.openxmlformats-officedocument.wordprocessingml.document'
);
});
it('throws for an unknown target format', () => {
expect(() => outputMimeType('made-up-format')).toThrowError(/made-up-format/);
});
});