From b1a88f79ff2926366e2fa45f810f31cb80f9391e Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 7 Jul 2025 12:31:14 +0000 Subject: [PATCH 1/4] Initial plan From c6f32dac15e71b61ddf550a58b65237a4d2466e3 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 7 Jul 2025 12:42:59 +0000 Subject: [PATCH 2/4] Add comprehensive MimeType utility functions Co-authored-by: neotrow <92933708+neotrow@users.noreply.github.com> --- src/index.ts | 1 + src/lib/mimeType.spec.ts | 122 +++++++++++++++++++++++++++++++++++++++ src/lib/mimeType.ts | 85 +++++++++++++++++++++++++++ 3 files changed, 208 insertions(+) create mode 100644 src/lib/mimeType.spec.ts create mode 100644 src/lib/mimeType.ts diff --git a/src/index.ts b/src/index.ts index 7d4b9b4..b2fc510 100644 --- a/src/index.ts +++ b/src/index.ts @@ -4,6 +4,7 @@ export * from "./lib/date"; export * from "./lib/enum"; export * from "./lib/guid"; export * from "./lib/localStorage"; +export * from "./lib/mimeType"; export * from "./lib/number"; export * from "./lib/object"; export * from "./lib/string"; diff --git a/src/lib/mimeType.spec.ts b/src/lib/mimeType.spec.ts new file mode 100644 index 0000000..d23bb33 --- /dev/null +++ b/src/lib/mimeType.spec.ts @@ -0,0 +1,122 @@ +import { isImageMimeType, isPdfMimeType, isVideoMimeType, isAudioMimeType, isTextMimeType, isApplicationMimeType } from "./mimeType"; + +describe("mimeType tests", () => { + // Common invalid inputs + const invalidInputs: [string | null | undefined | number | Date, boolean][] = [ + [null as unknown as string, false], + [undefined as unknown as string, false], + [0 as unknown as string, false], + [new Date() as unknown as string, false], + ["", false], + [" ", false], + ["not-a-mime-type", false], + ]; + + test.each([ + ...invalidInputs, + // Valid image MIME types + ["image/jpeg", true], + ["image/png", true], + ["image/gif", true], + ["image/webp", true], + ["image/svg+xml", true], + ["IMAGE/JPEG", true], // Case insensitive + [" image/gif ", true], // Whitespace trimmed + // Invalid cases + ["application/pdf", false], + ["text/plain", false], + ["video/mp4", false], + ["image", false], + ["image/", false], + ["/image", false], + ])("isImageMimeType", (mimeType, expected) => { + expect(isImageMimeType(mimeType as string)).toBe(expected); + }); + + test.each([ + ...invalidInputs, + // Valid PDF MIME type + ["application/pdf", true], + ["APPLICATION/PDF", true], // Case insensitive + [" application/pdf ", true], // Whitespace trimmed + // Invalid cases + ["image/jpeg", false], + ["application/json", false], + ["pdf", false], + ["application/", false], + ])("isPdfMimeType", (mimeType, expected) => { + expect(isPdfMimeType(mimeType as string)).toBe(expected); + }); + + test.each([ + ...invalidInputs, + // Valid video MIME types + ["video/mp4", true], + ["video/mpeg", true], + ["video/webm", true], + ["VIDEO/MP4", true], // Case insensitive + [" video/webm ", true], // Whitespace trimmed + // Invalid cases + ["image/jpeg", false], + ["audio/mp3", false], + ["video", false], + ["video/", false], + ["/video", false], + ])("isVideoMimeType", (mimeType, expected) => { + expect(isVideoMimeType(mimeType as string)).toBe(expected); + }); + + test.each([ + ...invalidInputs, + // Valid audio MIME types + ["audio/mp3", true], + ["audio/mpeg", true], + ["audio/wav", true], + ["AUDIO/MP3", true], // Case insensitive + [" audio/ogg ", true], // Whitespace trimmed + // Invalid cases + ["video/mp4", false], + ["image/jpeg", false], + ["audio", false], + ["audio/", false], + ["/audio", false], + ])("isAudioMimeType", (mimeType, expected) => { + expect(isAudioMimeType(mimeType as string)).toBe(expected); + }); + + test.each([ + ...invalidInputs, + // Valid text MIME types + ["text/plain", true], + ["text/html", true], + ["text/css", true], + ["TEXT/PLAIN", true], // Case insensitive + [" text/css ", true], // Whitespace trimmed + // Invalid cases + ["application/pdf", false], + ["image/jpeg", false], + ["text", false], + ["text/", false], + ["/text", false], + ])("isTextMimeType", (mimeType, expected) => { + expect(isTextMimeType(mimeType as string)).toBe(expected); + }); + + test.each([ + ...invalidInputs, + // Valid application MIME types + ["application/pdf", true], + ["application/json", true], + ["application/xml", true], + ["APPLICATION/PDF", true], // Case insensitive + [" application/xml ", true], // Whitespace trimmed + // Invalid cases + ["text/plain", false], + ["image/jpeg", false], + ["application", false], + ["application/", false], + ["/application", false], + ])("isApplicationMimeType", (mimeType, expected) => { + expect(isApplicationMimeType(mimeType as string)).toBe(expected); + }); +}); diff --git a/src/lib/mimeType.ts b/src/lib/mimeType.ts new file mode 100644 index 0000000..fcd3385 --- /dev/null +++ b/src/lib/mimeType.ts @@ -0,0 +1,85 @@ +import { isNullOrEmpty } from "./string"; + +/** + * Check if the MIME type represents an image + * @param mimeType The MIME type to check + * @returns true if the MIME type is for an image + */ +export function isImageMimeType(mimeType?: string): boolean { + if (isNullOrEmpty(mimeType)) { + return false; + } + + const normalizedMimeType = mimeType!.toLowerCase().trim(); + return normalizedMimeType.startsWith("image/") && normalizedMimeType.length > 6; +} + +/** + * Check if the MIME type represents a PDF document + * @param mimeType The MIME type to check + * @returns true if the MIME type is for a PDF document + */ +export function isPdfMimeType(mimeType?: string): boolean { + if (isNullOrEmpty(mimeType)) { + return false; + } + + const normalizedMimeType = mimeType!.toLowerCase().trim(); + return normalizedMimeType === "application/pdf"; +} + +/** + * Check if the MIME type represents a video + * @param mimeType The MIME type to check + * @returns true if the MIME type is for a video + */ +export function isVideoMimeType(mimeType?: string): boolean { + if (isNullOrEmpty(mimeType)) { + return false; + } + + const normalizedMimeType = mimeType!.toLowerCase().trim(); + return normalizedMimeType.startsWith("video/") && normalizedMimeType.length > 6; +} + +/** + * Check if the MIME type represents an audio file + * @param mimeType The MIME type to check + * @returns true if the MIME type is for an audio file + */ +export function isAudioMimeType(mimeType?: string): boolean { + if (isNullOrEmpty(mimeType)) { + return false; + } + + const normalizedMimeType = mimeType!.toLowerCase().trim(); + return normalizedMimeType.startsWith("audio/") && normalizedMimeType.length > 6; +} + +/** + * Check if the MIME type represents a text file + * @param mimeType The MIME type to check + * @returns true if the MIME type is for a text file + */ +export function isTextMimeType(mimeType?: string): boolean { + if (isNullOrEmpty(mimeType)) { + return false; + } + + const normalizedMimeType = mimeType!.toLowerCase().trim(); + return normalizedMimeType.startsWith("text/") && normalizedMimeType.length > 5; +} + +/** + * Check if the MIME type represents an application file + * @param mimeType The MIME type to check + * @returns true if the MIME type is for an application file + */ +export function isApplicationMimeType(mimeType?: string): boolean { + if (isNullOrEmpty(mimeType)) { + return false; + } + + const normalizedMimeType = mimeType!.toLowerCase().trim(); + return normalizedMimeType.startsWith("application/") && normalizedMimeType.length > 12; +} From d39ff47a8982a89da9ffe5cb354335504281a92c Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 7 Jul 2025 12:59:56 +0000 Subject: [PATCH 3/4] Add MIME type utility functions to CHANGELOG.md Co-authored-by: neotrow <92933708+neotrow@users.noreply.github.com> --- CHANGELOG.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1e39a1f..82d6c5c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +### Added + +- `isImageMimeType`, `isPdfMimeType`, `isVideoMimeType`, `isAudioMimeType`, `isTextMimeType`, and `isApplicationMimeType` MIME type utility functions + ## [1.4.0] - 2025-06-24 ### Added From f4f85fee4183ff3684562b13dc4d0dfa68c4423b Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 7 Jul 2025 13:10:38 +0000 Subject: [PATCH 4/4] Refactor MIME type functions to use shared helper function Co-authored-by: drebrez <1611547+drebrez@users.noreply.github.com> --- src/lib/mimeType.ts | 26 ++++++++++++++++---------- 1 file changed, 16 insertions(+), 10 deletions(-) diff --git a/src/lib/mimeType.ts b/src/lib/mimeType.ts index fcd3385..3766380 100644 --- a/src/lib/mimeType.ts +++ b/src/lib/mimeType.ts @@ -1,5 +1,16 @@ import { isNullOrEmpty } from "./string"; +/** + * Private helper function to check if a MIME type matches a main type pattern + * @param mimeType The MIME type to check + * @param mainType The main MIME type (e.g., "image", "video") + * @returns true if the MIME type matches the pattern + */ +function isMainMimeType(mimeType: string, mainType: string): boolean { + const normalizedMimeType = mimeType.toLowerCase().trim(); + return normalizedMimeType.startsWith(`${mainType}/`) && normalizedMimeType.length > mainType.length + 1; +} + /** * Check if the MIME type represents an image * @param mimeType The MIME type to check @@ -10,8 +21,7 @@ export function isImageMimeType(mimeType?: string): boolean { return false; } - const normalizedMimeType = mimeType!.toLowerCase().trim(); - return normalizedMimeType.startsWith("image/") && normalizedMimeType.length > 6; + return isMainMimeType(mimeType!, "image"); } /** @@ -38,8 +48,7 @@ export function isVideoMimeType(mimeType?: string): boolean { return false; } - const normalizedMimeType = mimeType!.toLowerCase().trim(); - return normalizedMimeType.startsWith("video/") && normalizedMimeType.length > 6; + return isMainMimeType(mimeType!, "video"); } /** @@ -52,8 +61,7 @@ export function isAudioMimeType(mimeType?: string): boolean { return false; } - const normalizedMimeType = mimeType!.toLowerCase().trim(); - return normalizedMimeType.startsWith("audio/") && normalizedMimeType.length > 6; + return isMainMimeType(mimeType!, "audio"); } /** @@ -66,8 +74,7 @@ export function isTextMimeType(mimeType?: string): boolean { return false; } - const normalizedMimeType = mimeType!.toLowerCase().trim(); - return normalizedMimeType.startsWith("text/") && normalizedMimeType.length > 5; + return isMainMimeType(mimeType!, "text"); } /** @@ -80,6 +87,5 @@ export function isApplicationMimeType(mimeType?: string): boolean { return false; } - const normalizedMimeType = mimeType!.toLowerCase().trim(); - return normalizedMimeType.startsWith("application/") && normalizedMimeType.length > 12; + return isMainMimeType(mimeType!, "application"); }