first commit
This commit is contained in:
@@ -0,0 +1,81 @@
|
||||
/**
|
||||
* Minimal extension ↔ MIME mapping + `accept` matching. Zero-dep: just a table
|
||||
* big enough for the common upload types (images, docs, media, archives).
|
||||
*/
|
||||
|
||||
const BY_EXT: Record<string, string> = {
|
||||
// images
|
||||
png: "image/png",
|
||||
jpg: "image/jpeg",
|
||||
jpeg: "image/jpeg",
|
||||
gif: "image/gif",
|
||||
webp: "image/webp",
|
||||
avif: "image/avif",
|
||||
svg: "image/svg+xml",
|
||||
ico: "image/x-icon",
|
||||
bmp: "image/bmp",
|
||||
// documents
|
||||
pdf: "application/pdf",
|
||||
txt: "text/plain",
|
||||
html: "text/html",
|
||||
htm: "text/html",
|
||||
csv: "text/csv",
|
||||
json: "application/json",
|
||||
xml: "application/xml",
|
||||
doc: "application/msword",
|
||||
docx: "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
|
||||
xls: "application/vnd.ms-excel",
|
||||
xlsx: "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
|
||||
ppt: "application/vnd.ms-powerpoint",
|
||||
pptx: "application/vnd.openxmlformats-officedocument.presentationml.presentation",
|
||||
// media
|
||||
mp3: "audio/mpeg",
|
||||
wav: "audio/wav",
|
||||
ogg: "audio/ogg",
|
||||
mp4: "video/mp4",
|
||||
webm: "video/webm",
|
||||
mov: "video/quicktime",
|
||||
// archives / misc
|
||||
zip: "application/zip",
|
||||
gz: "application/gzip",
|
||||
tar: "application/x-tar",
|
||||
};
|
||||
|
||||
/** Lowercased extension WITHOUT the dot (e.g. "png"), or "" if none. */
|
||||
export function extOf(name: string): string {
|
||||
const clean = name.split(/[?#]/)[0] ?? "";
|
||||
const dot = clean.lastIndexOf(".");
|
||||
return dot >= 0 ? clean.slice(dot + 1).toLowerCase() : "";
|
||||
}
|
||||
|
||||
/** MIME type for a filename/key by its extension, or a safe default. */
|
||||
export function contentTypeOf(name: string, fallback = "application/octet-stream"): string {
|
||||
return BY_EXT[extOf(name)] ?? fallback;
|
||||
}
|
||||
|
||||
/** The conventional extension for a MIME type, or "" (used to name S3 keys). */
|
||||
export function extForType(type: string): string {
|
||||
const t = type.split(";")[0]!.trim().toLowerCase();
|
||||
for (const [ext, mime] of Object.entries(BY_EXT)) if (mime === t) return ext;
|
||||
return "";
|
||||
}
|
||||
|
||||
/**
|
||||
* Does `file` (its MIME `type` + `name`) satisfy an `accept` list? Each accept
|
||||
* entry is a MIME type (`"image/png"`), a wildcard MIME (`"image/*"`), or a
|
||||
* dotted extension (`".pdf"`). An empty/omitted list accepts everything.
|
||||
*/
|
||||
export function accepts(
|
||||
accept: string[] | undefined,
|
||||
file: { type: string; name: string },
|
||||
): boolean {
|
||||
if (!accept || accept.length === 0) return true;
|
||||
const type = (file.type || contentTypeOf(file.name)).toLowerCase();
|
||||
const ext = "." + extOf(file.name);
|
||||
return accept.some((raw) => {
|
||||
const rule = raw.trim().toLowerCase();
|
||||
if (rule.startsWith(".")) return rule === ext;
|
||||
if (rule.endsWith("/*")) return type.startsWith(rule.slice(0, -1)); // "image/" prefix
|
||||
return rule === type;
|
||||
});
|
||||
}
|
||||
Reference in New Issue
Block a user