UNPKG

@bitpatty/imgproxy-url-builder

Version:

A TypeScript helper library for building imgproxy URLs

1,422 lines (1,350 loc) 78.4 kB
'use strict'; Object.defineProperty(exports, '__esModule', { value: true }); /** * Masks the lowest 6 bits of the specified number * * @param num The number * @returns The lowest 6 bits */ const low6 = (num) => num & 0b0011_1111; /** * UTF-8 encodes the specified message * * @param msg The message * @returns The encoded message as a Uint8Array of bytes */ const utf8encode = (msg) => { const encoder = new TextEncoder(); return encoder.encode(msg); }; /** * Parses a hex character and returns its numeric value * * @param char The hex char * @returns The numeric value */ const parseHexChar = (char) => { if (['0', '1', '2', '3', '4', '5', '6', '7', '8', '9'].includes(char)) return +char; switch (char.toUpperCase()) { case 'A': return 0xa; case 'B': return 0xb; case 'C': return 0xc; case 'D': return 0xd; case 'E': return 0xe; case 'F': return 0xf; } throw new Error(`Invalid hex char: ${char}`); }; /** * Parses a string containing hex characters * into an array of bytes * * @param str The string * @returns The Uint8Array of parsed bytes */ const parseHexString = (str) => { const res = new Uint8Array(Math.ceil(str.length / 2)); for (let i = 0; i < str.length; i += 2) { res[i / 2] = (parseHexChar(str[i]) << 4) | parseHexChar(str[i + 1] || '0'); } return res; }; /** * Encodes the lowest 6 bits of the specified * number to its base64url representation * * @param b The number * @returns The base64url encoded character */ const base64urlChar = (b) => { const r = low6(b); // Uppercase letters if (r <= 25) return String.fromCharCode(65 + r); // Lowercase letters if (r <= 51) return String.fromCharCode(97 + r - 26); // Numbers if (r <= 61) return String.fromCharCode(48 + r - 52); // Base64 URL replacements for '+' and '/' return r === 62 ? '-' : '_'; }; /** * Encodes the specified array of bytes to * its base64url representation * * @param bytes The Uint8Array of bytes * @returns The base64url string */ const base64urlEncode = (bytes) => { let res = ''; for (let i = 0; i < bytes.length; i += 3) { // First byte res += base64urlChar(bytes[i] >>> 2); // Second byte res += base64urlChar(((bytes[i] & 0b11) << 4) | (bytes[i + 1] >>> 4)); // Handle cases where fewer than 3 bytes are available if (i + 1 < bytes.length) { res += base64urlChar(((bytes[i + 1] & 0b1111) << 2) | (bytes[i + 2] >>> 6)); if (i + 2 < bytes.length) { res += base64urlChar(bytes[i + 2] & 0b0011_1111); } } } return res; }; /** * The block size used for hashing functions */ const BLOCK_SIZE = 512; /** * Converts a word array (Uint32Array) into a byte array (Uint8Array) * * @param wordArray The Uint32Array * @returns The Uint8Array */ const wordArrayToByteArray = (wordArray) => { const byteArray = new Uint8Array(wordArray.length * 4); for (let i = 0; i < wordArray.length; i++) { byteArray[i * 4] = (wordArray[i] >>> 24) & 0xff; byteArray[i * 4 + 1] = (wordArray[i] >>> 16) & 0xff; byteArray[i * 4 + 2] = (wordArray[i] >>> 8) & 0xff; byteArray[i * 4 + 3] = wordArray[i] & 0xff; } return byteArray; }; /** * Converts a byte array (Uint8Array) into a word array (Uint32Array) * * @param byteArray The Uint8Array * @returns The Uint32Array */ const byteArrayToWordArray = (byteArray) => { const wordArray = new Uint32Array(byteArray.length / 4); for (let i = 0; i < wordArray.length; i++) { wordArray[i] = (byteArray[i * 4] << 24) | (byteArray[i * 4 + 1] << 16) | (byteArray[i * 4 + 2] << 8) | byteArray[i * 4 + 3]; } return wordArray; }; // Constants const HASH_VALUES = new Uint32Array([ 0x6a09e667, 0xbb67ae85, 0x3c6ef372, 0xa54ff53a, 0x510e527f, 0x9b05688c, 0x1f83d9ab, 0x5be0cd19, ]); const ROUND_CONSTANTS = new Uint32Array([ 0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5, 0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174, 0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc, 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da, 0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, 0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967, 0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13, 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85, 0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3, 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070, 0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3, 0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2, ]); /** * Rotates the word right by the specified number of bits * * @param word The word * @param cnt The number of rotations * @returns The rotated word */ const rotr = (word, cnt) => { return (word >>> cnt) | (word << (32 - cnt)); }; /** * Prepares the message for chunking * * @param bytes The message bytes * @returns The padded message (32-bit word array) */ const prepareMessage = (bytes) => { const msgLen = bytes.length * 8; const totalLen = Math.ceil((msgLen + 65) / BLOCK_SIZE) * (BLOCK_SIZE / 8); const paddedMessage = new Uint8Array(totalLen); // Copy original bytes paddedMessage.set(bytes); // Append '1' bit paddedMessage[bytes.length] = 0b1000_0000; // Append the length of the message in bits (as 64-bit big-endian) const lenBytes = new DataView(new ArrayBuffer(8)); lenBytes.setUint32(4, msgLen); // Only the lower 32 bits (since we're not hashing files > 4GB) paddedMessage.set(new Uint8Array(lenBytes.buffer), totalLen - 8); // Convert the byte array to 32-bit word array return byteArrayToWordArray(paddedMessage); }; /** * Processes a single chunk (512-bit block) of the message * * @param chunk The chunk (32-bit word array) * @param hv The current hash value * @returns The updated hash value */ const processChunk = (chunk, hv) => { const w = new Uint32Array(64); w.set(chunk); // Extend the message schedule array for (let i = 16; i < 64; i++) { const s0 = rotr(w[i - 15], 7) ^ rotr(w[i - 15], 18) ^ (w[i - 15] >>> 3); const s1 = rotr(w[i - 2], 17) ^ rotr(w[i - 2], 19) ^ (w[i - 2] >>> 10); w[i] = (w[i - 16] + s0 + w[i - 7] + s1) >>> 0; } // Initialize working variables let [a, b, c, d, e, f, g, h] = hv; // Compression function main loop for (let i = 0; i < 64; i++) { const s0 = rotr(a, 2) ^ rotr(a, 13) ^ rotr(a, 22); const s1 = rotr(e, 6) ^ rotr(e, 11) ^ rotr(e, 25); const ch = (e & f) ^ (~e & g); const maj = (a & b) ^ (a & c) ^ (b & c); const temp1 = (h + s1 + ch + ROUND_CONSTANTS[i] + w[i]) >>> 0; const temp2 = (s0 + maj) >>> 0; h = g; g = f; f = e; e = (d + temp1) >>> 0; d = c; c = b; b = a; a = (temp1 + temp2) >>> 0; } // Add the compressed chunk to the current hash value hv[0] = (hv[0] + a) >>> 0; hv[1] = (hv[1] + b) >>> 0; hv[2] = (hv[2] + c) >>> 0; hv[3] = (hv[3] + d) >>> 0; hv[4] = (hv[4] + e) >>> 0; hv[5] = (hv[5] + f) >>> 0; hv[6] = (hv[6] + g) >>> 0; hv[7] = (hv[7] + h) >>> 0; return hv; }; /** * Calculates the SHA256 of the specified byte array * * @param bytes The Uint8Array of the byte array * @returns The SHA256 as a Uint32Array */ const sha256 = (bytes) => { const message = prepareMessage(bytes); let hash = new Uint32Array(HASH_VALUES); // Copy of initial hash values // Process each 512-bit chunk for (let i = 0; i < message.length; i += 16) { const chunk = message.subarray(i, i + 16); hash = processChunk(chunk, hash); } return hash; }; /** * Pads the specified byte-array to the block size. * * @param arr The Uint8Array * @param val The value to pad the array with * @returns The padded Uint8Array */ const blockPad = (arr, val = 0) => { const paddingSize = BLOCK_SIZE / 8 - arr.length; const paddedArray = new Uint8Array(arr.length + paddingSize); paddedArray.set(arr); if (val !== 0) { paddedArray.fill(val, arr.length); } return paddedArray; }; /** * Gets the block-padded key. * If the key is longer than the block size, it is hashed using SHA-256. * If the key is shorter, it is padded to the block size. * * @param keyBytes The key as a Uint8Array * @returns The block-padded key */ const getBlockKey = (keyBytes) => { // If key is exactly BLOCK_SIZE (512 bits), return as is. if (keyBytes.length === BLOCK_SIZE / 8) return keyBytes; // If key is longer than BLOCK_SIZE, hash it and use that as the key. if (keyBytes.length > BLOCK_SIZE / 8) return blockPad(wordArrayToByteArray(sha256(keyBytes))); // Otherwise, pad the key to the block size. return blockPad(keyBytes); }; /** * Creates the HMAC of the specified message. * Combines the key and message using inner and outer padding and returns the final HMAC as a Uint32Array. * * @param key The key (Uint8Array) * @param message The message (Uint8Array) * @returns The HMAC as a Uint32Array */ const hmac = (key, message) => { const bK = getBlockKey(key); // Get block-padded key // Initialize inner and outer padding const oPad = new Uint8Array(bK.length); const iPad = new Uint8Array(bK.length); // XOR the key with 0x36 for iPad and 0x5c for oPad for (let i = 0; i < bK.length; i++) { oPad[i] = bK[i] ^ 0x5c; iPad[i] = bK[i] ^ 0x36; } // Perform inner hash: H( K ⊕ ipad || message ) const innerHash = sha256(new Uint8Array([...iPad, ...message])); // Perform outer hash: H( K ⊕ opad || innerHash ) return sha256(new Uint8Array([...oPad, ...wordArrayToByteArray(innerHash)])); }; /** * Stringifies the imgproxy modifier for use within the * imgproxy URL. * * @param opCode The operation key * @param values The values * @returns The stringified modifier */ const stringifyOptions = (opCode, values) => { return [ opCode, ...values.map((v) => (v == null ? '' : encodeURIComponent(v))), ] .join(':') .replace(/:+$/, ''); }; /** * Encodes the filepath to base64url. * * @param filePath The file path * @returns The base64url encoded file path */ const encodeFilePath = (filePath) => { return base64urlEncode(utf8encode(filePath)); }; /** * Generates the URL signature for the specified imgproxy param string. * * See https://github.com/imgproxy/imgproxy/blob/947d65cf29fe26e5e4d38ca8a17e44c7402e437c/docs/configuration.md#url-signature * * @param paramString The param string * @param key The hex-encoded key * @param salt The hex-encoded salt * @param length The number of bytes to use for the signature before encoding to Base64 * @returns The base64url encoded signature */ const generateSignature = (paramString, key, salt, length) => { const path = paramString.startsWith('/') ? paramString : `/${paramString}`; // Parse key and salt from hex and ensure they're Uint8Array const keyBytes = parseHexString(key); const saltBytes = parseHexString(salt); // Create HMAC using the key and the combination of salt and path const h = hmac(keyBytes, new Uint8Array([...saltBytes, ...utf8encode(path)])); // Convert the HMAC output (Uint32Array) into a byte array (Uint8Array) const truncated = wordArrayToByteArray(h).slice(0, length); // Return the base64url-encoded signature return base64urlEncode(truncated); }; /** * Defines the brightness, contrast, and saturation. * * See https://github.com/imgproxy/imgproxy/blob/6f292443eafb2e39f9252175b61faa6b38105a7c/docs/generating_the_url.md#adjust-idadjust for the imgproxy documentation * * @param options The adjustment options * @returns The adjustment param string */ const adjust = (options) => stringifyOptions('a', [ options.brightness, options.contrast, options.saturation, ]); /** * Automatically rotates the image based on the EXIF orientation parameter. * * See https://github.com/imgproxy/imgproxy/blob/6f292443eafb2e39f9252175b61faa6b38105a7c/docs/generating_the_url.md#auto-rotate for the imgproxy documentation * * @returns The auto-rotate param string */ const autoRotate = () => stringifyOptions('ar', [true]); /** * Adds alpha channel to background. * * See https://github.com/imgproxy/imgproxy/blob/6f292443eafb2e39f9252175b61faa6b38105a7c/docs/generating_the_url.md#background-alpha-idbackground-alpha for the imgproxy documentation * * @param percentage A positive floating point number between 0 and 1 * @returns The background alpha param string */ const backgroundAlpha = (percentage) => stringifyOptions('bga', [percentage]); /** * Fills the image background with the specified color. * * See https://github.com/imgproxy/imgproxy/blob/6f292443eafb2e39f9252175b61faa6b38105a7c/docs/generating_the_url.md#background for the imgproxy documentation * * @param options The background color (hex encoded string or RGB object) * @returns The background param string */ const background = (options) => stringifyOptions('bg', [ ...(typeof options === 'string' ? [options] : [options.r, options.g, options.b]), ]); /** * Detects objects of the provided classes and blurs them. * * See https://github.com/imgproxy/imgproxy/blob/6f292443eafb2e39f9252175b61faa6b38105a7c/docs/generating_the_url.md#blur-detections-idblur-detections for the imgproxy documentation * * @param options The detection options * @returns The blur detection param string */ const blurDetections = (options) => stringifyOptions('bd', [options.sigma, ...(options.classNames ?? [])]); /** * Applies a gaussian blur filter to the image. * * See https://github.com/imgproxy/imgproxy/blob/6f292443eafb2e39f9252175b61faa6b38105a7c/docs/generating_the_url.md#blur for the imgproxy documentation * * @param sigma The size of the blur mask * @returns The blur param string */ const blur = (sigma) => stringifyOptions('bl', [sigma]); /** * Adjusts the brightness of an image. * * See https://github.com/imgproxy/imgproxy/blob/6f292443eafb2e39f9252175b61faa6b38105a7c/docs/generating_the_url.md#brightness-idbrightness for the imgproxy documentation * * @param value An integer number ranging from -255 to 255. * @returns The brightness param string */ const brightness = (value) => stringifyOptions('br', [value]); /** * Adds a cache buster to the imgproxy params. * * See https://github.com/imgproxy/imgproxy/blob/6f292443eafb2e39f9252175b61faa6b38105a7c/docs/generating_the_url.md#cache-buster for the imgproxy documentation * * @param buster The cache buster * @returns The cache buster param string */ const cacheBuster = (buster) => stringifyOptions('cb', [buster]); /** * Adjust contrast of the resulting image. * * See https://github.com/imgproxy/imgproxy/blob/6f292443eafb2e39f9252175b61faa6b38105a7c/docs/generating_the_url.md#contrast-idcontrast for the imgproxy documentation * * @param percentage A positive floating point number, where 1 * keeps the contrast unchanged. * @returns The contrast param string */ const contrast = (percentage) => stringifyOptions('co', [percentage]); /** * Crops the image. * * See https://github.com/imgproxy/imgproxy/blob/6f292443eafb2e39f9252175b61faa6b38105a7c/docs/generating_the_url.md#crop for the imgproxy documentation * * @param options The cropping options * @returns The cropping param string */ const crop = (options) => stringifyOptions('c', [ options?.width ?? 0, options?.height ?? 0, options?.gravity?.type, options?.gravity?.offset?.x, options?.gravity?.offset?.y, ]); /** * Use a single frame of animated images. * * See https://github.com/imgproxy/imgproxy/blob/cfa4b596d1f31656f9116cc16f2a4ff7d15c2837/docs/generating_the_url.md#disable-animation-iddisable-animation for the imgproxy documentation * * @returns The disable animation param string */ const disableAnimation = () => stringifyOptions('da', [true]); /** * When set, imgproxy will replace the image's DPI metadata with the provided value. * * See https://github.com/imgproxy/imgproxy/blob/8629c5eca1e422908363f471513bfc887d778a85/docs/generating_the_url.md#dpi-iddpi for the imgproxy documentation * * @param value The DPI value * @returns The DPI param string */ const dpi = (value) => stringifyOptions('dpi', [value]); /** * Multiplies the dimensions according to the specified factor. * * See https://github.com/imgproxy/imgproxy/blob/6f292443eafb2e39f9252175b61faa6b38105a7c/docs/generating_the_url.md#dpr for the imgproxy documentation * * @param value The DPR factor * @returns The DPR param string */ const dpr = (value) => stringifyOptions('dpr', [value]); /** * Detects objects of the provided classes and draws their * bounding boxes. * * See https://github.com/imgproxy/imgproxy/blob/6f292443eafb2e39f9252175b61faa6b38105a7c/docs/generating_the_url.md#draw-detections-iddraw-detections for the imgproxy documentation * * @param options The detection options * @returns The draw detection param string */ const drawDetections = (options) => stringifyOptions('dd', [true, ...(options.classNames ?? [])]); /** * Converts the image to duotone with specified intensity and colors. * * See https://github.com/imgproxy/imgproxy-docs/blob/7d15484aea6a1fae5f1dfd1806b5551a4774658d/docs/usage/processing.mdx?plain=1#L429 for the imgproxy documentation * * @param options The duotone options * @returns The duotone param string */ const duotone = (options) => stringifyOptions('dt', [options.intensity, options.color1, options.color2]); /** * If the source image has an embedded thumbnail, imgproxy will use the * embedded thumbnail instead of the main image. * * See https://github.com/imgproxy/imgproxy/blob/6f292443eafb2e39f9252175b61faa6b38105a7c/docs/generating_the_url.md#enforce-thumbnail for the imgproxy documentation * * @returns The enforce thumbnail param string */ const enforceThumbnail = () => stringifyOptions('eth', [true]); /** * Enlarges the image if it is smaller than the given size. * * See https://github.com/imgproxy/imgproxy/blob/6f292443eafb2e39f9252175b61faa6b38105a7c/docs/generating_the_url.md#enlarge for the imgproxy documentation * * @returns The enlarge param string */ const enlarge = () => stringifyOptions('el', [true]); /** * Returns a 404 if the expiration date is reached. * * See https://github.com/imgproxy/imgproxy/blob/6f292443eafb2e39f9252175b61faa6b38105a7c/docs/generating_the_url.md#expires for the imgproxy documentation * * @param options The unix timestamp * @returns The expiration param string */ const expires = (options) => stringifyOptions('exp', [ typeof options === 'number' ? options : Math.floor(options.getTime() / 1000), ]); /** * Extends the image to the requested aspect ratio. * * See https://github.com/imgproxy/imgproxy/blob/1a9768a2c682e88820064aa3d9a05ea234ff3cc4/docs/generating_the_url.md#extend-aspect-ratio for the imgproxy documentation * * @returns The extend aspect ratio param string */ const extendAspectRatio = (options) => stringifyOptions('exar', [ true, options?.gravity?.type, options?.gravity?.offset?.x, options?.gravity?.offset?.y, ]); /** * Extends the image if it is smaller than the given size. * * See https://github.com/imgproxy/imgproxy/blob/6f292443eafb2e39f9252175b61faa6b38105a7c/docs/generating_the_url.md#extend for the imgproxy documentation * * @param options The extend options * @returns The extend param string */ const extend = (options) => stringifyOptions('ex', [ true, options?.gravity.type, options?.gravity.offset?.x, options?.gravity.offset?.y, ]); /** * Sets a custom fallback image by specifying its URL. * * See https://github.com/imgproxy/imgproxy/blob/6f292443eafb2e39f9252175b61faa6b38105a7c/docs/generating_the_url.md#fallback-image-url-idfallback-image-url for the imgproxy documentation * * @param url The fallback image URL * @returns The fallback image URL param string */ const fallbackImageUrl = (url) => stringifyOptions('fiu', [base64urlEncode(utf8encode(url))]); /** * Sets the filename for the Content-Disposition header. * * See https://github.com/imgproxy/imgproxy/blob/41b9ebe9277ef3e664e0a842fbc0e912b2640969/docs/generating_the_url.md#filename for the imgproxy documentation * * @param name The filename * @param base64Encoded Whether the file name is base64 encoded * @returns The filename param string */ const fileName = (name, base64Encoded = false) => stringifyOptions('fn', [name, base64Encoded ? true : undefined]); /** * Sets the desired quality for each format. * * See https://github.com/imgproxy/imgproxy/blob/6f292443eafb2e39f9252175b61faa6b38105a7c/docs/generating_the_url.md#format-quality for the imgproxy documentation * * @param options The format quality options * @returns The format quality param string */ const formatQuality = (options) => stringifyOptions('fq', Object.entries(options).flatMap((e) => e)); /** * Specifies the resulting image format. * * See https://github.com/imgproxy/imgproxy/blob/6f292443eafb2e39f9252175b61faa6b38105a7c/docs/generating_the_url.md#format for the imgproxy documentation * * @param imageFormat The target format * @returns The format param string */ const format = (imageFormat) => stringifyOptions('f', [imageFormat]); // Removed in version 3.0.0 // See: https://github.com/imgproxy/imgproxy/blob/6f292443eafb2e39f9252175b61faa6b38105a7c/CHANGELOG.md#removed /** * Allows redefining GIF saving options. * * @deprecated Automatically applied since version 3.0.0 * @param options The gif options * @returns The gif option param string */ const gifOptions = (options) => stringifyOptions('gifo', [ options.optimizeFrames, options.optimizeTransparency, ]); var GradientDirection; (function (GradientDirection) { GradientDirection["UP"] = "up"; GradientDirection["DOWN"] = "down"; GradientDirection["LEFT"] = "left"; GradientDirection["RIGHT"] = "RIGHT"; })(GradientDirection || (GradientDirection = {})); var GradientDirection$1 = GradientDirection; /** * Places a gradient on the processed image. * * See https://github.com/imgproxy/imgproxy/blob/cfa4b596d1f31656f9116cc16f2a4ff7d15c2837/docs/generating_the_url.md#gradient-idgradient for the imgproxy documentation * * @param options The gradient options * @returns The gradient param string */ const gradient = (options) => stringifyOptions('gr', [ options.opacity, options.color ?? '000', options.direction ?? GradientDirection$1.DOWN, options.start ?? '0.0', options.stop ?? '1.0', ]); /** * Sets the gravity. * * See https://github.com/imgproxy/imgproxy/blob/6f292443eafb2e39f9252175b61faa6b38105a7c/docs/generating_the_url.md#gravity for the imgproxy documentation * * @param options The gravity options * @returns The gravity param string */ const gravity = (options) => stringifyOptions('g', [options.type, options.offset?.x, options.offset?.y]); /** * Allows redefining JPEG saving options. * * See https://github.com/imgproxy/imgproxy/blob/6f292443eafb2e39f9252175b61faa6b38105a7c/docs/generating_the_url.md#jpeg-options-idjpeg-options for the imgproxy documentation * * @param options The jpeg options * @returns The jpeg option param string */ const jpegOptions = (options) => stringifyOptions('jpgo', [ options.progressive, options.noSubsample, options.trellisQuant, options.overshootDeringing, options.optimizeScans, options.quantizationTable, ]); /** * Preserve the copyright info while stripping metadata. * * See https://github.com/imgproxy/imgproxy/blob/6f292443eafb2e39f9252175b61faa6b38105a7c/docs/generating_the_url.md#keep-copyright for the imgproxy documentation * * @returns The keep copyright param string */ const keepCopyright = () => stringifyOptions('kcr', [true]); /** * Limits the file size to the specified number of bytes. * * Note: only applicable to jpg, webp, heic and tiff. * * See https://github.com/imgproxy/imgproxy/blob/6f292443eafb2e39f9252175b61faa6b38105a7c/docs/generating_the_url.md#max-bytes for the imgproxy documentation * * @param maxBytes The number of bytes * @returns The max bytes param string */ const maxBytes = (bytes) => stringifyOptions('mb', [bytes]); /** * Defines the minimum height of the resulting image. * * See https://github.com/imgproxy/imgproxy/blob/6f292443eafb2e39f9252175b61faa6b38105a7c/docs/generating_the_url.md#min-height for the imgproxy documentation * * @param height The minimum height * @returns The min height param string */ const minHeight = (height) => stringifyOptions('mh', [height]); /** * Defines the minimum width of the resulting image. * * See https://github.com/imgproxy/imgproxy/blob/6f292443eafb2e39f9252175b61faa6b38105a7c/docs/generating_the_url.md#min-width for the imgproxy documentation * * @param width The minimum width * @returns The min width param string */ const minWidth = (width) => stringifyOptions('mw', [width]); /** * Converts the image to monochrome. * * See https://github.com/imgproxy/imgproxy-docs/blob/7d15484aea6a1fae5f1dfd1806b5551a4774658d/docs/usage/processing.mdx?plain=1#L415 for the imgproxy documentation * * @param options The monochrome options * @returns The monochrome param string */ const monochrome = (options) => stringifyOptions('mc', [ options.intensity, ...(options.color ? [options.color] : []), ]); /** * Applies the specified padding to the image. * * See https://github.com/imgproxy/imgproxy/blob/6f292443eafb2e39f9252175b61faa6b38105a7c/docs/generating_the_url.md#padding for the imgproxy documentation * * @param options The padding options * @returns The padding param string */ const pad = (options) => stringifyOptions('pd', [ options.top, options.right, options.bottom, options.left, ]); /** * When source image supports pagination (PDF, TIFF) or animation (GIF, WebP), this option allows * specifying the page to use. * * Pages numeration starts from zero. * * See https://github.com/imgproxy/imgproxy/blob/6f292443eafb2e39f9252175b61faa6b38105a7c/docs/generating_the_url.md#page-idpage for the imgproxy documentation * * @param pg The page to use * @returns The page param string */ const page = (pg) => stringifyOptions('pg', [pg]); /** * Apply the pixelate filter to the resulting image. * * See https://github.com/imgproxy/imgproxy/blob/6f292443eafb2e39f9252175b61faa6b38105a7c/docs/generating_the_url.md#pixelate for the imgproxy documentation * * @param pixelSize The size of a pixel * @returns The pixelate param string */ const pixelate = (pixelSize) => stringifyOptions('pix', [pixelSize]); /** * Allows redefining PNG saving options. * * See https://github.com/imgproxy/imgproxy/blob/6f292443eafb2e39f9252175b61faa6b38105a7c/docs/generating_the_url.md#png-options-idpng-options for the imgproxy documentation * * @param options The png options * @returns The png option param string */ const pngOptions = (options) => stringifyOptions('pngo', [ options.interlaced, options.quantize, options.quantization_colors, ]); /** * Sets one or many presets to be used by the imgproxy. * * See https://github.com/imgproxy/imgproxy/blob/6f292443eafb2e39f9252175b61faa6b38105a7c/docs/generating_the_url.md#preset for the imgproxy documentation * * @param presets The preset(s) * @returns The preset param string */ const preset = (presets) => stringifyOptions('pr', Array.isArray(presets) ? presets : [presets]); /** * Redefines the quality of the resulting image. * * See https://github.com/imgproxy/imgproxy/blob/6f292443eafb2e39f9252175b61faa6b38105a7c/docs/generating_the_url.md#quality for the imgproxy documentation * * @param percentage The percentage as floating point number from 0 to 1 * @returns The quality param string */ const quality = (percentage) => stringifyOptions('q', [percentage]); /** * Returns a raw unprocessed and unchecked source image * * See https://github.com/imgproxy/imgproxy/blob/f95f57bb4df35c69ae2257958006ef54b1c1d8c7/docs/generating_the_url.md#raw for the imgproxy documentation * * @returns The raw param string */ const raw = () => stringifyOptions('raw', [true]); /** * Resizes the image. * * See https://github.com/imgproxy/imgproxy/blob/6f292443eafb2e39f9252175b61faa6b38105a7c/docs/generating_the_url.md#resize for the imgproxy documentation * * @param options The resizing options * @returns The resizing param string */ const resize = (options) => stringifyOptions('rs', [options.type, options.width, options.height]); /** * Defines the algorithm that imgproxy will use for resizing. * * See https://github.com/imgproxy/imgproxy/blob/6f292443eafb2e39f9252175b61faa6b38105a7c/docs/generating_the_url.md#resizing-algorithm-idresizing-algorithm for the imgproxy documentation * * @param algorithm The resizing algorithm * @returns The resizing algorithm param string */ const resizingAlgorithm = (algorithm) => stringifyOptions('ra', [algorithm]); /** * Returns attachment in the Content-Disposition header. * * See https://github.com/imgproxy/imgproxy/blob/6f292443eafb2e39f9252175b61faa6b38105a7c/docs/generating_the_url.md#return-attachment for the imgproxy documentation * * @returns The attachment param string */ const returnAttachment = () => stringifyOptions('att', [true]); /** * Rotates the image by the specified angle. * * See https://github.com/imgproxy/imgproxy/blob/6f292443eafb2e39f9252175b61faa6b38105a7c/docs/generating_the_url.md#rotate for the imgproxy documentation * * @param angle The angle * @returns The rotate param string */ const rotate = (angle) => stringifyOptions('rot', [angle]); /** * Adjust saturation of the resulting image. * * See https://github.com/imgproxy/imgproxy/blob/6f292443eafb2e39f9252175b61faa6b38105a7c/docs/generating_the_url.md#saturation-idsaturation for the imgproxy documentation * * @param percentage A positive floating point number, where 1 * keeps the saturation unchanged * @returns The saturation param string */ const saturation = (percentage) => stringifyOptions('sa', [percentage]); /** * Applies a sharpen filter to the image. * * See https://github.com/imgproxy/imgproxy/blob/6f292443eafb2e39f9252175b61faa6b38105a7c/docs/generating_the_url.md#sharpen for the imgproxy documentation * * @param sigma The size of the sharpen mask * @returns The sharpen param string */ const sharpen = (sigma) => stringifyOptions('sh', [sigma]); /** * Skip the processing of the listed formats. * * Note: Processing can only be skipped when the requested format is * the same as the source format. * * Note: Video thumbnail processing can't be skipped * * See https://github.com/imgproxy/imgproxy/blob/6f292443eafb2e39f9252175b61faa6b38105a7c/docs/generating_the_url.md#skip-processing for the imgproxy documentation * * @param extensions The list of file extensions * @returns The skip processing param string */ const skipProcessing = (extensions) => stringifyOptions('skp', extensions); /** * Strips the color profile from the image. * * See https://github.com/imgproxy/imgproxy/blob/6f292443eafb2e39f9252175b61faa6b38105a7c/docs/generating_the_url.md#strip-color-profile for the imgproxy documentation * * @returns The strip color profile param string */ const stripColorProfile = () => stringifyOptions('scp', [true]); /** * Strips the metadata from the image. * * See https://github.com/imgproxy/imgproxy/blob/6f292443eafb2e39f9252175b61faa6b38105a7c/docs/generating_the_url.md#strip-metadata for the imgproxy documentation * * @returns The strip metadata param string */ const stripMetadata = () => stringifyOptions('sm', [true]); /** * Prepend a `<style>` node with the provided CSS styles to the * `<svg>` node of a source SVG image. * * See https://github.com/imgproxy/imgproxy/blob/6f292443eafb2e39f9252175b61faa6b38105a7c/docs/generating_the_url.md#style-idstyle for the imgproxy documentation * * @param styles The styles to apply * @returns The style param string */ const style = (styles) => { const styleString = typeof styles === 'string' ? styles : Object.entries(styles) .flatMap((v) => `${v[0]}:${v[1]}`) .join(';'); return stringifyOptions('st', [base64urlEncode(utf8encode(styleString))]); }; /** * Trims the image background. * * See https://github.com/imgproxy/imgproxy/blob/6f292443eafb2e39f9252175b61faa6b38105a7c/docs/generating_the_url.md#trim for the imgproxy documentation * * @param options The trimming options * @returns The trim param string */ const trim = (options) => stringifyOptions('t', [ options.threshold, options.color, options.equal?.horizontal, options.equal?.vertical, ]); /** * Allows redefining unsharpening options. * * See https://github.com/imgproxy/imgproxy/blob/6f292443eafb2e39f9252175b61faa6b38105a7c/docs/generating_the_url.md#unsharpening-idunsharpening for the imgproxy documentation * * @param options The unsharpening options * @returns The unsharpening param string */ const unsharpen = (options) => stringifyOptions('ush', [options.mode, options.weight, options.dividor]); /** * Redefines the second used for the thumbnail. * * See https://github.com/imgproxy/imgproxy/blob/6f292443eafb2e39f9252175b61faa6b38105a7c/docs/generating_the_url.md#video-thumbnail-second-idvideo-thumbnail-second for the imgproxy documentation * * @param second The timestamp of the frame in seconds * that will be used for a thumbnail. * @returns The video thumbnail second param string */ const videoThumbnailSecond = (second) => stringifyOptions('vts', [second]); /** * Generates a tiled sprite using the source video frames * * See https://github.com/imgproxy/imgproxy-docs/blob/676c6d4b1f5d9fee79abfecf130fc7dda3f9124e/versioned_docs/version-3.24.x/usage/processing.mdx#video-thumbnail-tile-pro-video-thumbnail-tile for the imgproxy documentation * * @param second The video thumbnail tile options * @returns The video thumbnail tile param string */ const videoThumbnailTile = (options) => stringifyOptions('vtt', [ options.step, options.columns, options.rows, options.tileWidth, options.tileHeight, options.extendTile ?? false, options.trim ?? false, options.fill ?? false, options.focusX ?? '0.5', options.focusY ?? '0.5', ]); /** * Adds a shadow to the watermark. * * See https://github.com/imgproxy/imgproxy/blob/f95f57bb4df35c69ae2257958006ef54b1c1d8c7/docs/generating_the_url.md#watermark-shadow-idwatermark-shadow for the imgproxy documentation * * @param sigma The size of the shadow mask * @returns The watermark shadow param string */ const watermarkShadow = (sigma) => stringifyOptions('wmsh', [sigma]); /** * Defines the desired width and height of the watermark. imgproxy always * uses `fit` resizing type when resizing watermarks and enlarges them * when needed. * * Note: this processing option only takes effect when the `scale` argument * of the `watermark` option is set to zero. * * See https://github.com/imgproxy/imgproxy/blob/6f292443eafb2e39f9252175b61faa6b38105a7c/docs/generating_the_url.md#watermark-size-idwatermark-size for the imgproxy documentation * * @param options The size of the blur mask * @returns The blur param string */ const watermarkSize = (options) => stringifyOptions('wms', [options.width ?? 0, options.height ?? 0]); /** * Generate an image from the provided text and use it as a watermark. * * See https://github.com/imgproxy/imgproxy/blob/6f292443eafb2e39f9252175b61faa6b38105a7c/docs/generating_the_url.md#watermark-text-idwatermark-text for the imgproxy documentation * * @param text The watermark text * @returns The watermark text param string */ const watermarkText = (text) => stringifyOptions('wmt', [base64urlEncode(utf8encode(text))]); /** * Use the image from the specified URL as a watermark. * * See https://github.com/imgproxy/imgproxy/blob/6f292443eafb2e39f9252175b61faa6b38105a7c/docs/generating_the_url.md#watermark-url-idwatermark-url for the imgproxy documentation * * @param url The watermark URL * @returns The watermark URL param string */ const watermarkUrl = (url) => stringifyOptions('wmu', [base64urlEncode(utf8encode(url))]); /** * Places a watermark on the processed image. * * See https://github.com/imgproxy/imgproxy/blob/6f292443eafb2e39f9252175b61faa6b38105a7c/docs/generating_the_url.md#watermark for the imgproxy documentation * * @param options The watermark options * @returns The watermark param string */ const watermark = (options) => stringifyOptions('wm', [ options.opacity, options.position, options.offset?.x, options.offset?.y, options.scale, ]); /** * Multiply the image dimensions according to the specified factors. * * See https://github.com/imgproxy/imgproxy/blob/6f292443eafb2e39f9252175b61faa6b38105a7c/docs/generating_the_url.md#zoom for the imgproxy documentation * * The values must be greater than 0. * * @param options The zoom options * @returns The zoom param string */ const zoom = (options) => stringifyOptions('z', [ typeof options === 'number' ? options : options.join(' '), ]); var HashsumType; (function (HashsumType) { HashsumType["NONE"] = "none"; HashsumType["MD5"] = "md5"; HashsumType["SHA1"] = "sha1"; HashsumType["SHA256"] = "sha256"; HashsumType["SHA512"] = "sha512"; })(HashsumType || (HashsumType = {})); var HashsumType$1 = HashsumType; /** * When `hashsum_type` is not `none`, imgproxy will calculate the hashsum of the source image * and compare it with the provided hashsum. * * If they don't match, imgproxy will respond with 422. * * See https://github.com/imgproxy/imgproxy-docs/blob/f9d7908d253ec2b31425b988a48f8c28cb271c58/docs/usage/processing.mdx#L916 for the imgproxy documentation * * @returns The auto-rotate param string */ const hashsum = (options) => stringifyOptions('hashsum', [ options.type ?? HashsumType$1.NONE, options.hashsum, ]); /** * Specifies whether the latest keyframe before the video thumbnail second * should be used for thumbnail generation * * See https://github.com/imgproxy/imgproxy-docs/blob/676c6d4b1f5d9fee79abfecf130fc7dda3f9124e/versioned_docs/version-3.24.x/usage/processing.mdx#video-thumbnail-keyframes-pro-video-thumbnail-keyframes for the imgproxy documentation * * @param keyframes The keyframes to be used. * @returns The video thumbnail keyframes param string */ const videoThumbnailKeyframes = (keyframes) => stringifyOptions('vtk', [keyframes]); class ParamBuilder { /** * The currently applied imgproxy modifiers */ modifiers; constructor(initialModifiers = new Map()) { this.modifiers = initialModifiers; } /** * Creates a new param builder instance with a copy of the * current modifiers * * @returns A copy of this param builder */ clone() { return new ParamBuilder(new Map(this.modifiers)); } /** * Removes the specified modifier from the currently applied * modifiers * * @param modifier The modifier */ unset(modifier) { this.modifiers.delete(modifier); return this; } /** * Builds the imgproxy URL * * If a path is supplied, the full URL path will be returned, * else only the stringified modifiers will be returned. * * If a base URL is supplied, the full imgproxy URL will be returned. * * @param options The build options * @returns The imgproxy URL */ build(options) { const { baseUrl, path, plain, signature } = options ?? {}; const mods = Array.from(this.modifiers.values()); if (!path) return mods.join('/'); if (path && plain) mods.push('plain', path); else mods.push(encodeFilePath(path)); // Get the file extension if requested const extension = options?.addExtension ? this.getExtension(plain ? '@' : '.') : ''; const res = mods.join('/') + extension; // If no signature is calculated add a - as placeholder // See https://github.com/imgproxy/imgproxy/blob/b243a08254b9ca7da2c628429cd870c111ece5c9/docs/signing_the_url.md const finalPath = signature ? `${generateSignature(res, signature.key, signature.salt, signature.size ?? 32)}/${res}` : `-/${res}`; return baseUrl ? `${baseUrl}/${finalPath}` : `/${finalPath}`; } /** * Get the extension for the file. Checks the specified format and the target image. * * See https://github.com/imgproxy/imgproxy/blob/5ac79477dfa76ed3c014a1472e31e26a2d2257a0/docs/generating_the_url.md#source-url for the imgproxy documentation * * @param path The path to the target image, e.g. `https://example.com/foo.png` * @returns An extension if found, or an empty string */ getExtension(separator) { const formatString = this.modifiers.get('format'); if (!formatString) return ''; return `${separator}${formatString.split(':').pop()}`; } /** * Defines the brightness, contrast, and saturation. * * See https://github.com/imgproxy/imgproxy/blob/6f292443eafb2e39f9252175b61faa6b38105a7c/docs/generating_the_url.md#adjust-idadjust for the imgproxy documentation * * @example * ```typescript * pb().adjust({ * brightness: 100, // optional * contrast: 0.8, // optional * saturation: 0.9 // optional * }); * ``` */ adjust(...options) { this.modifiers.set('adjust', adjust(...options)); return this; } /** * Automatically rotates the image based on the EXIF orientation parameter. * * See https://github.com/imgproxy/imgproxy/blob/6f292443eafb2e39f9252175b61faa6b38105a7c/docs/generating_the_url.md#auto-rotate for the imgproxy documentation * * @example * ```typescript * pb().autoRotate(); * ``` */ autoRotate() { this.modifiers.set('autoRotate', autoRotate()); return this; } /** * Fills the image background with the specified color. * * See https://github.com/imgproxy/imgproxy/blob/6f292443eafb2e39f9252175b61faa6b38105a7c/docs/generating_the_url.md#background for the imgproxy documentation * * @example * ```typescript * pb().background('ff0000'); * * pb().background({ * r: 255, * g: 0, * b: 0 * }); * ``` */ background(...options) { this.modifiers.set('background', background(...options)); return this; } /** * Adds alpha channel to background. * * See https://github.com/imgproxy/imgproxy/blob/6f292443eafb2e39f9252175b61faa6b38105a7c/docs/generating_the_url.md#background-alpha-idbackground-alpha for the imgproxy documentation * * @example * ```typescript * pb().backgroundAlpha(0.4); * ``` */ backgroundAlpha(...options) { this.modifiers.set('backgroundAlpha', backgroundAlpha(...options)); return this; } /** * Applies a gaussian blur filter to the image. * * See https://github.com/imgproxy/imgproxy/blob/6f292443eafb2e39f9252175b61faa6b38105a7c/docs/generating_the_url.md#blur for the imgproxy documentation * * @example * ```typescript * pb().blur(10); * ``` */ blur(...options) { this.modifiers.set('blur', blur(...options)); return this; } /** * Detects objects of the provided classes and blurs them. * * See https://github.com/imgproxy/imgproxy/blob/6f292443eafb2e39f9252175b61faa6b38105a7c/docs/generating_the_url.md#blur-detections-idblur-detections for the imgproxy documentation * * @example * ```typescript * pb().blurDetections({ * sigma: 10, * classNames: ['face'] * }); * ``` */ blurDetections(...options) { this.modifiers.set('blurDetections', blurDetections(...options)); return this; } /** * Adjusts the brightness of an image. * * See https://github.com/imgproxy/imgproxy/blob/6f292443eafb2e39f9252175b61faa6b38105a7c/docs/generating_the_url.md#brightness-idbrightness for the imgproxy documentation * * @example * ```typescript * pb().brightness(-100); * ``` */ brightness(...options) { this.modifiers.set('brightness', brightness(...options)); return this; } /** * Adds a cache buster to the imgproxy params. * * See https://github.com/imgproxy/imgproxy/blob/6f292443eafb2e39f9252175b61faa6b38105a7c/docs/generating_the_url.md#cache-buster for the imgproxy documentation * * @example * ```typescript * pb().cacheBuster("abcdef123"); * ``` */ cacheBuster(...options) { this.modifiers.set('cacheBuster', cacheBuster(...options)); return this; } /** * Adjust contrast of the resulting image. * * See https://github.com/imgproxy/imgproxy/blob/6f292443eafb2e39f9252175b61faa6b38105a7c/docs/generating_the_url.md#contrast-idcontrast for the imgproxy documentation * * @example * ```typescript * pb().contrast(0.3); * ``` */ contrast(...options) { this.modifiers.set('contrast', contrast(...options)); return this; } /** * Crops the image. * * See https://github.com/imgproxy/imgproxy/blob/6f292443eafb2e39f9252175b61faa6b38105a7c/docs/generating_the_url.md#crop for the imgproxy documentation * * @example * ```typescript * pb().crop({ * width: 100, // optional * height: 50, // optional * gravity: { // optional * type: GravityType.CENTER, // required * offset: { // optional * x: 20, // required * y: 20 // required * } * } * }) * ``` */ crop(...options) { this.modifiers.set('crop', crop(...options)); return this; } /** * Use a single frame of animated images. * * See https://github.com/imgproxy/imgproxy/blob/cfa4b596d1f31656f9116cc16f2a4ff7d15c2837/docs/generating_the_url.md#disable-animation-iddisable-animation for the imgproxy documentation * * @example * ```typescript * pb().disableAnimation(); * ``` */ disableAnimation(...options) { this.modifiers.set('disableAnimation', disableAnimation(...options)); return this; } /** * When set, imgproxy will replace the image's DPI metadata with the provided value. * * See https://github.com/imgproxy/imgproxy/blob/8629c5eca1e422908363f471513bfc887d778a85/docs/generating_the_url.md#dpi-iddpi for the imgproxy documentation * * @example * ```typescript * pb().dpi(300); * ``` */ dpi(...options) { this.modifiers.set('dpi', dpi(...options)); return this; } /** * Multiplies the dimensions according to the specified factor. * * See https://github.com/imgproxy/imgproxy/blob/6f292443eafb2e39f9252175b61faa6b38105a7c/docs/generating_the_url.md#dpr for the imgproxy documentation * * @example * ```typescript * pb().dpr(18); * ``` */ dpr(...options) { this.modifiers.set('dpr', dpr(...options)); return this; } /** * Detects objects of the provided classes and draws their * bounding boxes. * * See https://github.com/imgproxy/imgproxy/blob/6f292443eafb2e39f9252175b61faa6b38105a7c/docs/generating_the_url.md#draw-detections-iddraw-detections for the imgproxy documentation * * @example * ```typescript * pb().drawDetections({ * classNames: ["face"] * }); * ``` */ drawDetections(...options) { this.modifiers.set('drawDetections', drawDetections(...options)); return this; } /** * Converts the image to duotone with specified intensity and colors. * * See https://github.com/imgproxy/imgproxy-docs/blob/7d15484aea6a1fae5f1dfd1806b5551a4774658d/docs/usage/processing.mdx?plain=1#L429 for the imgproxy documentation * * @example * ```typescript * pb().duotone({ * intensity: 1.0, // required * color1: 'ff0000', // required * color2: '00ff00' // required * }); * ``` */ duotone(...options) { this.modifiers.set('duotone', duotone(...options));