diff --git a/.prettierrc b/.prettierrc new file mode 100644 index 0000000..544138b --- /dev/null +++ b/.prettierrc @@ -0,0 +1,3 @@ +{ + "singleQuote": true +} diff --git a/package.json b/package.json index f3dbbc3..17f06c8 100644 --- a/package.json +++ b/package.json @@ -22,8 +22,8 @@ "scripts": { "build": "unbuild", "dev": "vitest dev", - "lint": "eslint --cache --ext .ts,.js,.mjs,.cjs .", - "lint:fix": "eslint --cache --ext .ts,.js,.mjs,.cjs . --fix", + "lint": "eslint --cache --ext .ts,.js,.mjs,.cjs . && prettier -c src test", + "lint:fix": "eslint --cache --ext .ts,.js,.mjs,.cjs . --fix && prettier -c src test -w", "prepack": "pnpm run build", "release": "pnpm test && changelogen --release && npm publish && git push --follow-tags", "test": "pnpm lint && vitest run --coverage" @@ -33,7 +33,8 @@ "@vitest/coverage-c8": "^0.29.1", "changelogen": "^0.4.1", "eslint": "^8.34.0", - "eslint-config-misaon": "^0.0.3", + "eslint-config-misaon": "^0.0.4", + "prettier": "^2.8.4", "typescript": "^4.9.5", "unbuild": "^1.1.2", "vitest": "^0.29.1" diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index d80cfcd..e4f1d82 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -6,7 +6,8 @@ specifiers: changelogen: ^0.4.1 crypto-es: ^1.2.7 eslint: ^8.34.0 - eslint-config-misaon: ^0.0.3 + eslint-config-misaon: ^0.0.4 + prettier: ^2.8.4 typescript: ^4.9.5 ufo: ^1.1.1 unbuild: ^1.1.2 @@ -21,7 +22,8 @@ devDependencies: '@vitest/coverage-c8': 0.29.2_vitest@0.29.2 changelogen: 0.4.1 eslint: 8.35.0 - eslint-config-misaon: 0.0.3_ycpbpc6yetojsgtrx3mwntkhsu + eslint-config-misaon: 0.0.4_ycpbpc6yetojsgtrx3mwntkhsu + prettier: 2.8.4 typescript: 4.9.5 unbuild: 1.1.2 vitest: 0.29.2 @@ -1701,8 +1703,8 @@ packages: engines: {node: '>=10'} dev: true - /eslint-config-misaon/0.0.3_ycpbpc6yetojsgtrx3mwntkhsu: - resolution: {integrity: sha512-JNtDwzMAhsXstO8uCtdFZE01LthwoApiU/piqKg/4+cgp1yRUiv0cnW+9JrSzdpeCMTwvhwwV+iKsitH8Koq2w==} + /eslint-config-misaon/0.0.4_ycpbpc6yetojsgtrx3mwntkhsu: + resolution: {integrity: sha512-wbk6hhvWOOA6de2fe507+TVD6tzfFaVxU51849qPzPVVPJJ5BtrUo1utw/syGmjYmgHC8divi6fYKRp1ZnI72g==} peerDependencies: eslint: '*' typescript: '*' @@ -3196,6 +3198,12 @@ packages: engines: {node: '>= 0.8.0'} dev: true + /prettier/2.8.4: + resolution: {integrity: sha512-vIS4Rlc2FNh0BySk3Wkd6xmwxB0FpOndW5fisM5H8hsZSxU2VWVB5CWIkIjWvrHjIhxk2g3bfMKM87zNTrZddw==} + engines: {node: '>=10.13.0'} + hasBin: true + dev: true + /pretty-bytes/6.1.0: resolution: {integrity: sha512-Rk753HI8f4uivXi4ZCIYdhmG1V+WKzvRMg/X+M42a6t7D07RcmopXJMDNk6N++7Bl75URRGsb40ruvg7Hcp2wQ==} engines: {node: ^14.13.1 || >=16.0.0} diff --git a/src/index.ts b/src/index.ts index 15d403b..ff32de7 100644 --- a/src/index.ts +++ b/src/index.ts @@ -12,184 +12,184 @@ import { Base64 } from 'crypto-es/lib/enc-base64.js'; // @docs https://docs.imgproxy.net/generating_the_url?id=processing-options export type Modifiers = { - 'min-height'?: string; - 'min-width'?: string; - adjust?: string; - auto_rotate?: string; - autoquality?: string; - background?: string; - background_alpha?: string; - blur?: string; - blur_detections?: string; - brightness?: string; - cachebuster?: string; - contrast?: string; - crop?: string; - disable_animation?: string; - dpr?: string; - draw_detections?: string; - enforce_thumbnail?: string; - enlarge?: string; - expires?: string; - extend?: string; - fallback_image_url?: string; - filename?: string; - format?: string; - format_quality?: string; - gradient?: string; - gravity?: string; - height?: string; - jpeg_options?: string; - keep_copyright?: string; - max_bytes?: string; - padding?: string; - page?: string; - pixelate?: string; - png_options?: string; - preset?: string; - quality?: string; - raw?: string; - resize?: string; - resizing_algorithm?: string; - resizing_type?: string; - return_attachment?: string; - rotate?: string; - saturation?: string; - sharpen?: string; - size?: string; - skip_processing?: string; - strip_color_profile?: string; - strip_metadata?: string; - style?: string; - trim?: string; - unsharpening?: string; - video_thumbnail_second?: string; - watermark?: string; - watermark_shadow?: string; - watermark_size?: string; - watermark_text?: string; - watermark_url?: string; - width?: string; - zoom?: string; + 'min-height'?: string; + 'min-width'?: string; + adjust?: string; + auto_rotate?: string; + autoquality?: string; + background?: string; + background_alpha?: string; + blur?: string; + blur_detections?: string; + brightness?: string; + cachebuster?: string; + contrast?: string; + crop?: string; + disable_animation?: string; + dpr?: string; + draw_detections?: string; + enforce_thumbnail?: string; + enlarge?: string; + expires?: string; + extend?: string; + fallback_image_url?: string; + filename?: string; + format?: string; + format_quality?: string; + gradient?: string; + gravity?: string; + height?: string; + jpeg_options?: string; + keep_copyright?: string; + max_bytes?: string; + padding?: string; + page?: string; + pixelate?: string; + png_options?: string; + preset?: string; + quality?: string; + raw?: string; + resize?: string; + resizing_algorithm?: string; + resizing_type?: string; + return_attachment?: string; + rotate?: string; + saturation?: string; + sharpen?: string; + size?: string; + skip_processing?: string; + strip_color_profile?: string; + strip_metadata?: string; + style?: string; + trim?: string; + unsharpening?: string; + video_thumbnail_second?: string; + watermark?: string; + watermark_shadow?: string; + watermark_size?: string; + watermark_text?: string; + watermark_url?: string; + width?: string; + zoom?: string; }; const modifiersKeyMap: Modifiers = { - 'min-height': 'mh', - 'min-width': 'mw', - adjust: 'a', - auto_rotate: 'ar', - autoquality: 'aq', - background: 'bg', - background_alpha: 'bga', - blur: 'bl', - blur_detections: 'bd', - brightness: 'br', - cachebuster: 'cb', - contrast: 'co', - crop: 'c', - disable_animation: 'da', - dpr: 'dpr', - draw_detections: 'dd', - enforce_thumbnail: 'eth', - enlarge: 'el', - expires: 'exp', - extend: 'ex', - fallback_image_url: 'fiu', - filename: 'fn', - format: 'f', - format_quality: 'fq', - gradient: 'gr', - gravity: 'g', - height: 'h', - jpeg_options: 'jpgo', - keep_copyright: 'kcr', - max_bytes: 'mb', - padding: 'pd', - page: 'pg', - pixelate: 'pix', - png_options: 'pngo', - preset: 'pr', - quality: 'q', - raw: 'raw', - resize: 'rs', - resizing_algorithm: 'ra', - resizing_type: 'rt', - return_attachment: 'att', - rotate: 'rot', - saturation: 'sa', - sharpen: 'sh', - size: 's', - skip_processing: 'skp', - strip_color_profile: 'scp', - strip_metadata: 'sm', - style: 'st', - trim: 't', - unsharpening: 'ush', - video_thumbnail_second: 'vts', - watermark: 'wm', - watermark_shadow: 'wmsh', - watermark_size: 'wms', - watermark_text: 'wmt', - watermark_url: 'wmu', - width: 'w', - zoom: 'z', + 'min-height': 'mh', + 'min-width': 'mw', + adjust: 'a', + auto_rotate: 'ar', + autoquality: 'aq', + background: 'bg', + background_alpha: 'bga', + blur: 'bl', + blur_detections: 'bd', + brightness: 'br', + cachebuster: 'cb', + contrast: 'co', + crop: 'c', + disable_animation: 'da', + dpr: 'dpr', + draw_detections: 'dd', + enforce_thumbnail: 'eth', + enlarge: 'el', + expires: 'exp', + extend: 'ex', + fallback_image_url: 'fiu', + filename: 'fn', + format: 'f', + format_quality: 'fq', + gradient: 'gr', + gravity: 'g', + height: 'h', + jpeg_options: 'jpgo', + keep_copyright: 'kcr', + max_bytes: 'mb', + padding: 'pd', + page: 'pg', + pixelate: 'pix', + png_options: 'pngo', + preset: 'pr', + quality: 'q', + raw: 'raw', + resize: 'rs', + resizing_algorithm: 'ra', + resizing_type: 'rt', + return_attachment: 'att', + rotate: 'rot', + saturation: 'sa', + sharpen: 'sh', + size: 's', + skip_processing: 'skp', + strip_color_profile: 'scp', + strip_metadata: 'sm', + style: 'st', + trim: 't', + unsharpening: 'ush', + video_thumbnail_second: 'vts', + watermark: 'wm', + watermark_shadow: 'wmsh', + watermark_size: 'wms', + watermark_text: 'wmt', + watermark_url: 'wmu', + width: 'w', + zoom: 'z', }; const isBrowser = typeof window !== 'undefined'; export const toBase64 = (value: string) => - isBrowser - ? window.btoa(value) - : Buffer.from(value, 'binary').toString('base64'); + isBrowser + ? window.btoa(value) + : Buffer.from(value, 'binary').toString('base64'); export const trimBase64 = (value: string) => - value.replaceAll('=', '').replaceAll('+', '-').replaceAll('/', '_'); + value.replaceAll('=', '').replaceAll('+', '-').replaceAll('/', '_'); export const generateHmac = (secret: string, salt: string) => { - // eslint-disable-next-line @typescript-eslint/no-unsafe-call,@typescript-eslint/no-unsafe-assignment - const hmac = HMAC.create(SHA256Algo, Hex.parse(secret)); - // eslint-disable-next-line @typescript-eslint/no-unsafe-call - hmac.update(Hex.parse(salt)); + // eslint-disable-next-line @typescript-eslint/no-unsafe-call,@typescript-eslint/no-unsafe-assignment + const hmac = HMAC.create(SHA256Algo, Hex.parse(secret)); + // eslint-disable-next-line @typescript-eslint/no-unsafe-call + hmac.update(Hex.parse(salt)); - // eslint-disable-next-line @typescript-eslint/no-unsafe-return - return hmac; + // eslint-disable-next-line @typescript-eslint/no-unsafe-return + return hmac; }; // eslint-disable-next-line @typescript-eslint/no-explicit-any export const generateSignature = (hmac: any, path: string): string => { - // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment,@typescript-eslint/no-unsafe-call - const hash = Base64.stringify(hmac.finalize(path)); + // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment,@typescript-eslint/no-unsafe-call + const hash = Base64.stringify(hmac.finalize(path)); - // eslint-disable-next-line @typescript-eslint/no-unsafe-argument - return trimBase64(hash); + // eslint-disable-next-line @typescript-eslint/no-unsafe-argument + return trimBase64(hash); }; export const modifiersGenerator = (modifiers: Modifiers) => - // eslint-disable-next-line @typescript-eslint/require-array-sort-compare - Object.entries(Object.fromEntries(Object.entries(modifiers).sort())) - .map( - modifier => - `${(modifiersKeyMap as Record)[modifier[0]]}:${ - modifier[1] - }`, - ) - .join('/'); + // eslint-disable-next-line @typescript-eslint/require-array-sort-compare + Object.entries(Object.fromEntries(Object.entries(modifiers).sort())) + .map( + (modifier) => + `${(modifiersKeyMap as Record)[modifier[0]]}:${ + modifier[1] + }` + ) + .join('/'); export function getImageUrl( - imageSource: string, - options: { - secret: string; - salt: string; - baseURL?: string; - modifiers: Modifiers; - }, + imageSource: string, + options: { + secret: string; + salt: string; + baseURL?: string; + modifiers: Modifiers; + } ) { - const encodedUrl = trimBase64(toBase64(imageSource)); - const operations = modifiersGenerator(options.modifiers); - const encodedUrlWithModifiers = joinURL('/', operations, encodedUrl); - // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment - const hmac = generateHmac(options.secret, options.salt); - const signature = generateSignature(hmac, encodedUrlWithModifiers); + const encodedUrl = trimBase64(toBase64(imageSource)); + const operations = modifiersGenerator(options.modifiers); + const encodedUrlWithModifiers = joinURL('/', operations, encodedUrl); + // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment + const hmac = generateHmac(options.secret, options.salt); + const signature = generateSignature(hmac, encodedUrlWithModifiers); - return joinURL(options.baseURL ?? '/', signature, encodedUrlWithModifiers); + return joinURL(options.baseURL ?? '/', signature, encodedUrlWithModifiers); } diff --git a/test/index.test.ts b/test/index.test.ts index 240677d..0e78a1b 100644 --- a/test/index.test.ts +++ b/test/index.test.ts @@ -1,11 +1,11 @@ import { expect, describe, test } from 'vitest'; import { - toBase64, - trimBase64, - modifiersGenerator, - generateSignature, - generateHmac, - getImageUrl, + toBase64, + trimBase64, + modifiersGenerator, + generateSignature, + generateHmac, + getImageUrl, } from '../src'; const imgproxySecret = '6F787973686F70'; @@ -13,49 +13,49 @@ const imgproxySalt = '10D16F7A61'; const imageUrl = 'https://img.dog-learn.com/dog-breeds/maltese/maltese-sz5.jpg'; describe('@misaon/imgproxy', () => { - test('toBase64', () => { - expect(toBase64(imageUrl)).toBe( - 'aHR0cHM6Ly9pbWcuZG9nLWxlYXJuLmNvbS9kb2ctYnJlZWRzL21hbHRlc2UvbWFsdGVzZS1zejUuanBn', - ); - }); + test('toBase64', () => { + expect(toBase64(imageUrl)).toBe( + 'aHR0cHM6Ly9pbWcuZG9nLWxlYXJuLmNvbS9kb2ctYnJlZWRzL21hbHRlc2UvbWFsdGVzZS1zejUuanBn' + ); + }); - test('trimBase64', () => { - expect( - trimBase64( - 'a=HR0cHM6Ly9pbWcuZG9nLWxlYXJuLmNvbS9k+b2ctYnJlZWRzL21hbHRlc2U/bWFsdGVzZS1zejUuanBn', - ), - ).toBe( - 'aHR0cHM6Ly9pbWcuZG9nLWxlYXJuLmNvbS9k-b2ctYnJlZWRzL21hbHRlc2U_bWFsdGVzZS1zejUuanBn', - ); - }); + test('trimBase64', () => { + expect( + trimBase64( + 'a=HR0cHM6Ly9pbWcuZG9nLWxlYXJuLmNvbS9k+b2ctYnJlZWRzL21hbHRlc2U/bWFsdGVzZS1zejUuanBn' + ) + ).toBe( + 'aHR0cHM6Ly9pbWcuZG9nLWxlYXJuLmNvbS9k-b2ctYnJlZWRzL21hbHRlc2U_bWFsdGVzZS1zejUuanBn' + ); + }); - test('modifiersGenerator', () => { - expect(modifiersGenerator({ width: '768', height: '0' })).toBe('h:0/w:768'); - }); + test('modifiersGenerator', () => { + expect(modifiersGenerator({ width: '768', height: '0' })).toBe('h:0/w:768'); + }); - test('generateSignature', () => { - expect( - generateSignature( - generateHmac(imgproxySecret, imgproxySalt), - '/h:250/w:300/aHR0cHM6Ly9pbWcuZG9nLWxlYXJuLmNvbS9kb2ctYnJlZWRzL21hbHRlc2UvbWFsdGVzZS1zejUuanBn', - ), - ).toBe('3mqw14PjyZJLm2__0RNPEDZcX8NLtLZkURz1MfoCTm4'); - }); + test('generateSignature', () => { + expect( + generateSignature( + generateHmac(imgproxySecret, imgproxySalt), + '/h:250/w:300/aHR0cHM6Ly9pbWcuZG9nLWxlYXJuLmNvbS9kb2ctYnJlZWRzL21hbHRlc2UvbWFsdGVzZS1zejUuanBn' + ) + ).toBe('3mqw14PjyZJLm2__0RNPEDZcX8NLtLZkURz1MfoCTm4'); + }); - test('getImageUrl', () => { - expect( - getImageUrl(imageUrl, { - secret: imgproxySecret, - salt: imgproxySalt, - // eslint-disable-next-line @typescript-eslint/naming-convention - baseURL: '/imgproxy', - modifiers: { - width: '300', - height: '250', - }, - }), - ).toBe( - '/imgproxy/3mqw14PjyZJLm2__0RNPEDZcX8NLtLZkURz1MfoCTm4/h:250/w:300/aHR0cHM6Ly9pbWcuZG9nLWxlYXJuLmNvbS9kb2ctYnJlZWRzL21hbHRlc2UvbWFsdGVzZS1zejUuanBn', - ); - }); + test('getImageUrl', () => { + expect( + getImageUrl(imageUrl, { + secret: imgproxySecret, + salt: imgproxySalt, + // eslint-disable-next-line @typescript-eslint/naming-convention + baseURL: '/imgproxy', + modifiers: { + width: '300', + height: '250', + }, + }) + ).toBe( + '/imgproxy/3mqw14PjyZJLm2__0RNPEDZcX8NLtLZkURz1MfoCTm4/h:250/w:300/aHR0cHM6Ly9pbWcuZG9nLWxlYXJuLmNvbS9kb2ctYnJlZWRzL21hbHRlc2UvbWFsdGVzZS1zejUuanBn' + ); + }); });