Files
upiecep-website/.vercel/output/_functions/chunks/index_BMX6-2tC.mjs
T
2026-04-29 02:27:15 +08:00

9918 lines
319 KiB
JavaScript

import { x as getDefaultExportFromCjs } from './entrypoint_CZ7e8met.mjs';
import require$$0$2 from 'node:util';
import require$$1$1 from 'node:stream';
import require$$4 from 'child_process';
import require$$0 from 'fs';
import require$$0$1 from 'node:child_process';
import require$$1 from 'node:crypto';
import nodePath from 'node:path';
import require$$0$3 from 'node:events';
import require$$7 from 'node:os';
function _mergeNamespaces(n, m) {
for (var i = 0; i < m.length; i++) {
const e = m[i];
if (typeof e !== 'string' && !Array.isArray(e)) { for (const k in e) {
if (k !== 'default' && !(k in n)) {
const d = Object.getOwnPropertyDescriptor(e, k);
if (d) {
Object.defineProperty(n, k, d.get ? d : {
enumerable: true,
get: () => e[k]
});
}
}
} }
}
return Object.freeze(Object.defineProperty(n, Symbol.toStringTag, { value: 'Module' }));
}
/*!
Copyright 2013 Lovell Fuller and others.
SPDX-License-Identifier: Apache-2.0
*/
var is;
var hasRequiredIs;
function requireIs () {
if (hasRequiredIs) return is;
hasRequiredIs = 1;
/**
* Is this value defined and not null?
* @private
*/
const defined = (val) => typeof val !== 'undefined' && val !== null;
/**
* Is this value an object?
* @private
*/
const object = (val) => typeof val === 'object';
/**
* Is this value a plain object?
* @private
*/
const plainObject = (val) => Object.prototype.toString.call(val) === '[object Object]';
/**
* Is this value a function?
* @private
*/
const fn = (val) => typeof val === 'function';
/**
* Is this value a boolean?
* @private
*/
const bool = (val) => typeof val === 'boolean';
/**
* Is this value a Buffer object?
* @private
*/
const buffer = (val) => val instanceof Buffer;
/**
* Is this value a typed array object?. E.g. Uint8Array or Uint8ClampedArray?
* @private
*/
const typedArray = (val) => {
if (defined(val)) {
switch (val.constructor) {
case Uint8Array:
case Uint8ClampedArray:
case Int8Array:
case Uint16Array:
case Int16Array:
case Uint32Array:
case Int32Array:
case Float32Array:
case Float64Array:
return true;
}
}
return false;
};
/**
* Is this value an ArrayBuffer object?
* @private
*/
const arrayBuffer = (val) => val instanceof ArrayBuffer;
/**
* Is this value a non-empty string?
* @private
*/
const string = (val) => typeof val === 'string' && val.length > 0;
/**
* Is this value a real number?
* @private
*/
const number = (val) => typeof val === 'number' && !Number.isNaN(val);
/**
* Is this value an integer?
* @private
*/
const integer = (val) => Number.isInteger(val);
/**
* Is this value within an inclusive given range?
* @private
*/
const inRange = (val, min, max) => val >= min && val <= max;
/**
* Is this value within the elements of an array?
* @private
*/
const inArray = (val, list) => list.includes(val);
/**
* Create an Error with a message relating to an invalid parameter.
*
* @param {string} name - parameter name.
* @param {string} expected - description of the type/value/range expected.
* @param {*} actual - the value received.
* @returns {Error} Containing the formatted message.
* @private
*/
const invalidParameterError = (name, expected, actual) => new Error(
`Expected ${expected} for ${name} but received ${actual} of type ${typeof actual}`
);
/**
* Ensures an Error from C++ contains a JS stack.
*
* @param {Error} native - Error with message from C++.
* @param {Error} context - Error with stack from JS.
* @returns {Error} Error with message and stack.
* @private
*/
const nativeError = (native, context) => {
context.message = native.message;
return context;
};
is = {
defined,
object,
plainObject,
fn,
bool,
buffer,
typedArray,
arrayBuffer,
string,
number,
integer,
inRange,
inArray,
invalidParameterError,
nativeError
};
return is;
}
function commonjsRequire(path) {
throw new Error('Could not dynamically require "' + path + '". Please configure the dynamicRequireTargets or/and ignoreDynamicRequires option of @rollup/plugin-commonjs appropriately for this require call to work.');
}
var sharp = {exports: {}};
var process_1;
var hasRequiredProcess;
function requireProcess () {
if (hasRequiredProcess) return process_1;
hasRequiredProcess = 1;
const isLinux = () => process.platform === 'linux';
let report = null;
const getReport = () => {
if (!report) {
/* istanbul ignore next */
if (isLinux() && process.report) {
const orig = process.report.excludeNetwork;
process.report.excludeNetwork = true;
report = process.report.getReport();
process.report.excludeNetwork = orig;
} else {
report = {};
}
}
return report;
};
process_1 = { isLinux, getReport };
return process_1;
}
var filesystem;
var hasRequiredFilesystem;
function requireFilesystem () {
if (hasRequiredFilesystem) return filesystem;
hasRequiredFilesystem = 1;
const fs = require$$0;
const LDD_PATH = '/usr/bin/ldd';
const SELF_PATH = '/proc/self/exe';
const MAX_LENGTH = 2048;
/**
* Read the content of a file synchronous
*
* @param {string} path
* @returns {Buffer}
*/
const readFileSync = (path) => {
const fd = fs.openSync(path, 'r');
const buffer = Buffer.alloc(MAX_LENGTH);
const bytesRead = fs.readSync(fd, buffer, 0, MAX_LENGTH, 0);
fs.close(fd, () => {});
return buffer.subarray(0, bytesRead);
};
/**
* Read the content of a file
*
* @param {string} path
* @returns {Promise<Buffer>}
*/
const readFile = (path) => new Promise((resolve, reject) => {
fs.open(path, 'r', (err, fd) => {
if (err) {
reject(err);
} else {
const buffer = Buffer.alloc(MAX_LENGTH);
fs.read(fd, buffer, 0, MAX_LENGTH, 0, (_, bytesRead) => {
resolve(buffer.subarray(0, bytesRead));
fs.close(fd, () => {});
});
}
});
});
filesystem = {
LDD_PATH,
SELF_PATH,
readFileSync,
readFile
};
return filesystem;
}
var elf;
var hasRequiredElf;
function requireElf () {
if (hasRequiredElf) return elf;
hasRequiredElf = 1;
const interpreterPath = (elf) => {
if (elf.length < 64) {
return null;
}
if (elf.readUInt32BE(0) !== 0x7F454C46) {
// Unexpected magic bytes
return null;
}
if (elf.readUInt8(4) !== 2) {
// Not a 64-bit ELF
return null;
}
if (elf.readUInt8(5) !== 1) {
// Not little-endian
return null;
}
const offset = elf.readUInt32LE(32);
const size = elf.readUInt16LE(54);
const count = elf.readUInt16LE(56);
for (let i = 0; i < count; i++) {
const headerOffset = offset + (i * size);
const type = elf.readUInt32LE(headerOffset);
if (type === 3) {
const fileOffset = elf.readUInt32LE(headerOffset + 8);
const fileSize = elf.readUInt32LE(headerOffset + 32);
return elf.subarray(fileOffset, fileOffset + fileSize).toString().replace(/\0.*$/g, '');
}
}
return null;
};
elf = {
interpreterPath
};
return elf;
}
var detectLibc;
var hasRequiredDetectLibc;
function requireDetectLibc () {
if (hasRequiredDetectLibc) return detectLibc;
hasRequiredDetectLibc = 1;
const childProcess = require$$4;
const { isLinux, getReport } = requireProcess();
const { LDD_PATH, SELF_PATH, readFile, readFileSync } = requireFilesystem();
const { interpreterPath } = requireElf();
let cachedFamilyInterpreter;
let cachedFamilyFilesystem;
let cachedVersionFilesystem;
const command = 'getconf GNU_LIBC_VERSION 2>&1 || true; ldd --version 2>&1 || true';
let commandOut = '';
const safeCommand = () => {
if (!commandOut) {
return new Promise((resolve) => {
childProcess.exec(command, (err, out) => {
commandOut = err ? ' ' : out;
resolve(commandOut);
});
});
}
return commandOut;
};
const safeCommandSync = () => {
if (!commandOut) {
try {
commandOut = childProcess.execSync(command, { encoding: 'utf8' });
} catch (_err) {
commandOut = ' ';
}
}
return commandOut;
};
/**
* A String constant containing the value `glibc`.
* @type {string}
* @public
*/
const GLIBC = 'glibc';
/**
* A Regexp constant to get the GLIBC Version.
* @type {string}
*/
const RE_GLIBC_VERSION = /LIBC[a-z0-9 \-).]*?(\d+\.\d+)/i;
/**
* A String constant containing the value `musl`.
* @type {string}
* @public
*/
const MUSL = 'musl';
const isFileMusl = (f) => f.includes('libc.musl-') || f.includes('ld-musl-');
const familyFromReport = () => {
const report = getReport();
if (report.header && report.header.glibcVersionRuntime) {
return GLIBC;
}
if (Array.isArray(report.sharedObjects)) {
if (report.sharedObjects.some(isFileMusl)) {
return MUSL;
}
}
return null;
};
const familyFromCommand = (out) => {
const [getconf, ldd1] = out.split(/[\r\n]+/);
if (getconf && getconf.includes(GLIBC)) {
return GLIBC;
}
if (ldd1 && ldd1.includes(MUSL)) {
return MUSL;
}
return null;
};
const familyFromInterpreterPath = (path) => {
if (path) {
if (path.includes('/ld-musl-')) {
return MUSL;
} else if (path.includes('/ld-linux-')) {
return GLIBC;
}
}
return null;
};
const getFamilyFromLddContent = (content) => {
content = content.toString();
if (content.includes('musl')) {
return MUSL;
}
if (content.includes('GNU C Library')) {
return GLIBC;
}
return null;
};
const familyFromFilesystem = async () => {
if (cachedFamilyFilesystem !== undefined) {
return cachedFamilyFilesystem;
}
cachedFamilyFilesystem = null;
try {
const lddContent = await readFile(LDD_PATH);
cachedFamilyFilesystem = getFamilyFromLddContent(lddContent);
} catch (e) {}
return cachedFamilyFilesystem;
};
const familyFromFilesystemSync = () => {
if (cachedFamilyFilesystem !== undefined) {
return cachedFamilyFilesystem;
}
cachedFamilyFilesystem = null;
try {
const lddContent = readFileSync(LDD_PATH);
cachedFamilyFilesystem = getFamilyFromLddContent(lddContent);
} catch (e) {}
return cachedFamilyFilesystem;
};
const familyFromInterpreter = async () => {
if (cachedFamilyInterpreter !== undefined) {
return cachedFamilyInterpreter;
}
cachedFamilyInterpreter = null;
try {
const selfContent = await readFile(SELF_PATH);
const path = interpreterPath(selfContent);
cachedFamilyInterpreter = familyFromInterpreterPath(path);
} catch (e) {}
return cachedFamilyInterpreter;
};
const familyFromInterpreterSync = () => {
if (cachedFamilyInterpreter !== undefined) {
return cachedFamilyInterpreter;
}
cachedFamilyInterpreter = null;
try {
const selfContent = readFileSync(SELF_PATH);
const path = interpreterPath(selfContent);
cachedFamilyInterpreter = familyFromInterpreterPath(path);
} catch (e) {}
return cachedFamilyInterpreter;
};
/**
* Resolves with the libc family when it can be determined, `null` otherwise.
* @returns {Promise<?string>}
*/
const family = async () => {
let family = null;
if (isLinux()) {
family = await familyFromInterpreter();
if (!family) {
family = await familyFromFilesystem();
if (!family) {
family = familyFromReport();
}
if (!family) {
const out = await safeCommand();
family = familyFromCommand(out);
}
}
}
return family;
};
/**
* Returns the libc family when it can be determined, `null` otherwise.
* @returns {?string}
*/
const familySync = () => {
let family = null;
if (isLinux()) {
family = familyFromInterpreterSync();
if (!family) {
family = familyFromFilesystemSync();
if (!family) {
family = familyFromReport();
}
if (!family) {
const out = safeCommandSync();
family = familyFromCommand(out);
}
}
}
return family;
};
/**
* Resolves `true` only when the platform is Linux and the libc family is not `glibc`.
* @returns {Promise<boolean>}
*/
const isNonGlibcLinux = async () => isLinux() && await family() !== GLIBC;
/**
* Returns `true` only when the platform is Linux and the libc family is not `glibc`.
* @returns {boolean}
*/
const isNonGlibcLinuxSync = () => isLinux() && familySync() !== GLIBC;
const versionFromFilesystem = async () => {
if (cachedVersionFilesystem !== undefined) {
return cachedVersionFilesystem;
}
cachedVersionFilesystem = null;
try {
const lddContent = await readFile(LDD_PATH);
const versionMatch = lddContent.match(RE_GLIBC_VERSION);
if (versionMatch) {
cachedVersionFilesystem = versionMatch[1];
}
} catch (e) {}
return cachedVersionFilesystem;
};
const versionFromFilesystemSync = () => {
if (cachedVersionFilesystem !== undefined) {
return cachedVersionFilesystem;
}
cachedVersionFilesystem = null;
try {
const lddContent = readFileSync(LDD_PATH);
const versionMatch = lddContent.match(RE_GLIBC_VERSION);
if (versionMatch) {
cachedVersionFilesystem = versionMatch[1];
}
} catch (e) {}
return cachedVersionFilesystem;
};
const versionFromReport = () => {
const report = getReport();
if (report.header && report.header.glibcVersionRuntime) {
return report.header.glibcVersionRuntime;
}
return null;
};
const versionSuffix = (s) => s.trim().split(/\s+/)[1];
const versionFromCommand = (out) => {
const [getconf, ldd1, ldd2] = out.split(/[\r\n]+/);
if (getconf && getconf.includes(GLIBC)) {
return versionSuffix(getconf);
}
if (ldd1 && ldd2 && ldd1.includes(MUSL)) {
return versionSuffix(ldd2);
}
return null;
};
/**
* Resolves with the libc version when it can be determined, `null` otherwise.
* @returns {Promise<?string>}
*/
const version = async () => {
let version = null;
if (isLinux()) {
version = await versionFromFilesystem();
if (!version) {
version = versionFromReport();
}
if (!version) {
const out = await safeCommand();
version = versionFromCommand(out);
}
}
return version;
};
/**
* Returns the libc version when it can be determined, `null` otherwise.
* @returns {?string}
*/
const versionSync = () => {
let version = null;
if (isLinux()) {
version = versionFromFilesystemSync();
if (!version) {
version = versionFromReport();
}
if (!version) {
const out = safeCommandSync();
version = versionFromCommand(out);
}
}
return version;
};
detectLibc = {
GLIBC,
MUSL,
family,
familySync,
isNonGlibcLinux,
isNonGlibcLinuxSync,
version,
versionSync
};
return detectLibc;
}
var debug_1;
var hasRequiredDebug;
function requireDebug () {
if (hasRequiredDebug) return debug_1;
hasRequiredDebug = 1;
const debug = (
typeof process === 'object' &&
process.env &&
process.env.NODE_DEBUG &&
/\bsemver\b/i.test(process.env.NODE_DEBUG)
) ? (...args) => console.error('SEMVER', ...args)
: () => {};
debug_1 = debug;
return debug_1;
}
var constants;
var hasRequiredConstants;
function requireConstants () {
if (hasRequiredConstants) return constants;
hasRequiredConstants = 1;
// Note: this is the semver.org version of the spec that it implements
// Not necessarily the package version of this code.
const SEMVER_SPEC_VERSION = '2.0.0';
const MAX_LENGTH = 256;
const MAX_SAFE_INTEGER = Number.MAX_SAFE_INTEGER ||
/* istanbul ignore next */ 9007199254740991;
// Max safe segment length for coercion.
const MAX_SAFE_COMPONENT_LENGTH = 16;
// Max safe length for a build identifier. The max length minus 6 characters for
// the shortest version with a build 0.0.0+BUILD.
const MAX_SAFE_BUILD_LENGTH = MAX_LENGTH - 6;
const RELEASE_TYPES = [
'major',
'premajor',
'minor',
'preminor',
'patch',
'prepatch',
'prerelease',
];
constants = {
MAX_LENGTH,
MAX_SAFE_COMPONENT_LENGTH,
MAX_SAFE_BUILD_LENGTH,
MAX_SAFE_INTEGER,
RELEASE_TYPES,
SEMVER_SPEC_VERSION,
FLAG_INCLUDE_PRERELEASE: 0b001,
FLAG_LOOSE: 0b010,
};
return constants;
}
var re = {exports: {}};
var hasRequiredRe;
function requireRe () {
if (hasRequiredRe) return re.exports;
hasRequiredRe = 1;
(function (module, exports$1) {
const {
MAX_SAFE_COMPONENT_LENGTH,
MAX_SAFE_BUILD_LENGTH,
MAX_LENGTH,
} = requireConstants();
const debug = requireDebug();
exports$1 = module.exports = {};
// The actual regexps go on exports.re
const re = exports$1.re = [];
const safeRe = exports$1.safeRe = [];
const src = exports$1.src = [];
const safeSrc = exports$1.safeSrc = [];
const t = exports$1.t = {};
let R = 0;
const LETTERDASHNUMBER = '[a-zA-Z0-9-]';
// Replace some greedy regex tokens to prevent regex dos issues. These regex are
// used internally via the safeRe object since all inputs in this library get
// normalized first to trim and collapse all extra whitespace. The original
// regexes are exported for userland consumption and lower level usage. A
// future breaking change could export the safer regex only with a note that
// all input should have extra whitespace removed.
const safeRegexReplacements = [
['\\s', 1],
['\\d', MAX_LENGTH],
[LETTERDASHNUMBER, MAX_SAFE_BUILD_LENGTH],
];
const makeSafeRegex = (value) => {
for (const [token, max] of safeRegexReplacements) {
value = value
.split(`${token}*`).join(`${token}{0,${max}}`)
.split(`${token}+`).join(`${token}{1,${max}}`);
}
return value
};
const createToken = (name, value, isGlobal) => {
const safe = makeSafeRegex(value);
const index = R++;
debug(name, index, value);
t[name] = index;
src[index] = value;
safeSrc[index] = safe;
re[index] = new RegExp(value, isGlobal ? 'g' : undefined);
safeRe[index] = new RegExp(safe, isGlobal ? 'g' : undefined);
};
// The following Regular Expressions can be used for tokenizing,
// validating, and parsing SemVer version strings.
// ## Numeric Identifier
// A single `0`, or a non-zero digit followed by zero or more digits.
createToken('NUMERICIDENTIFIER', '0|[1-9]\\d*');
createToken('NUMERICIDENTIFIERLOOSE', '\\d+');
// ## Non-numeric Identifier
// Zero or more digits, followed by a letter or hyphen, and then zero or
// more letters, digits, or hyphens.
createToken('NONNUMERICIDENTIFIER', `\\d*[a-zA-Z-]${LETTERDASHNUMBER}*`);
// ## Main Version
// Three dot-separated numeric identifiers.
createToken('MAINVERSION', `(${src[t.NUMERICIDENTIFIER]})\\.` +
`(${src[t.NUMERICIDENTIFIER]})\\.` +
`(${src[t.NUMERICIDENTIFIER]})`);
createToken('MAINVERSIONLOOSE', `(${src[t.NUMERICIDENTIFIERLOOSE]})\\.` +
`(${src[t.NUMERICIDENTIFIERLOOSE]})\\.` +
`(${src[t.NUMERICIDENTIFIERLOOSE]})`);
// ## Pre-release Version Identifier
// A numeric identifier, or a non-numeric identifier.
// Non-numeric identifiers include numeric identifiers but can be longer.
// Therefore non-numeric identifiers must go first.
createToken('PRERELEASEIDENTIFIER', `(?:${src[t.NONNUMERICIDENTIFIER]
}|${src[t.NUMERICIDENTIFIER]})`);
createToken('PRERELEASEIDENTIFIERLOOSE', `(?:${src[t.NONNUMERICIDENTIFIER]
}|${src[t.NUMERICIDENTIFIERLOOSE]})`);
// ## Pre-release Version
// Hyphen, followed by one or more dot-separated pre-release version
// identifiers.
createToken('PRERELEASE', `(?:-(${src[t.PRERELEASEIDENTIFIER]
}(?:\\.${src[t.PRERELEASEIDENTIFIER]})*))`);
createToken('PRERELEASELOOSE', `(?:-?(${src[t.PRERELEASEIDENTIFIERLOOSE]
}(?:\\.${src[t.PRERELEASEIDENTIFIERLOOSE]})*))`);
// ## Build Metadata Identifier
// Any combination of digits, letters, or hyphens.
createToken('BUILDIDENTIFIER', `${LETTERDASHNUMBER}+`);
// ## Build Metadata
// Plus sign, followed by one or more period-separated build metadata
// identifiers.
createToken('BUILD', `(?:\\+(${src[t.BUILDIDENTIFIER]
}(?:\\.${src[t.BUILDIDENTIFIER]})*))`);
// ## Full Version String
// A main version, followed optionally by a pre-release version and
// build metadata.
// Note that the only major, minor, patch, and pre-release sections of
// the version string are capturing groups. The build metadata is not a
// capturing group, because it should not ever be used in version
// comparison.
createToken('FULLPLAIN', `v?${src[t.MAINVERSION]
}${src[t.PRERELEASE]}?${
src[t.BUILD]}?`);
createToken('FULL', `^${src[t.FULLPLAIN]}$`);
// like full, but allows v1.2.3 and =1.2.3, which people do sometimes.
// also, 1.0.0alpha1 (prerelease without the hyphen) which is pretty
// common in the npm registry.
createToken('LOOSEPLAIN', `[v=\\s]*${src[t.MAINVERSIONLOOSE]
}${src[t.PRERELEASELOOSE]}?${
src[t.BUILD]}?`);
createToken('LOOSE', `^${src[t.LOOSEPLAIN]}$`);
createToken('GTLT', '((?:<|>)?=?)');
// Something like "2.*" or "1.2.x".
// Note that "x.x" is a valid xRange identifer, meaning "any version"
// Only the first item is strictly required.
createToken('XRANGEIDENTIFIERLOOSE', `${src[t.NUMERICIDENTIFIERLOOSE]}|x|X|\\*`);
createToken('XRANGEIDENTIFIER', `${src[t.NUMERICIDENTIFIER]}|x|X|\\*`);
createToken('XRANGEPLAIN', `[v=\\s]*(${src[t.XRANGEIDENTIFIER]})` +
`(?:\\.(${src[t.XRANGEIDENTIFIER]})` +
`(?:\\.(${src[t.XRANGEIDENTIFIER]})` +
`(?:${src[t.PRERELEASE]})?${
src[t.BUILD]}?` +
`)?)?`);
createToken('XRANGEPLAINLOOSE', `[v=\\s]*(${src[t.XRANGEIDENTIFIERLOOSE]})` +
`(?:\\.(${src[t.XRANGEIDENTIFIERLOOSE]})` +
`(?:\\.(${src[t.XRANGEIDENTIFIERLOOSE]})` +
`(?:${src[t.PRERELEASELOOSE]})?${
src[t.BUILD]}?` +
`)?)?`);
createToken('XRANGE', `^${src[t.GTLT]}\\s*${src[t.XRANGEPLAIN]}$`);
createToken('XRANGELOOSE', `^${src[t.GTLT]}\\s*${src[t.XRANGEPLAINLOOSE]}$`);
// Coercion.
// Extract anything that could conceivably be a part of a valid semver
createToken('COERCEPLAIN', `${'(^|[^\\d])' +
'(\\d{1,'}${MAX_SAFE_COMPONENT_LENGTH}})` +
`(?:\\.(\\d{1,${MAX_SAFE_COMPONENT_LENGTH}}))?` +
`(?:\\.(\\d{1,${MAX_SAFE_COMPONENT_LENGTH}}))?`);
createToken('COERCE', `${src[t.COERCEPLAIN]}(?:$|[^\\d])`);
createToken('COERCEFULL', src[t.COERCEPLAIN] +
`(?:${src[t.PRERELEASE]})?` +
`(?:${src[t.BUILD]})?` +
`(?:$|[^\\d])`);
createToken('COERCERTL', src[t.COERCE], true);
createToken('COERCERTLFULL', src[t.COERCEFULL], true);
// Tilde ranges.
// Meaning is "reasonably at or greater than"
createToken('LONETILDE', '(?:~>?)');
createToken('TILDETRIM', `(\\s*)${src[t.LONETILDE]}\\s+`, true);
exports$1.tildeTrimReplace = '$1~';
createToken('TILDE', `^${src[t.LONETILDE]}${src[t.XRANGEPLAIN]}$`);
createToken('TILDELOOSE', `^${src[t.LONETILDE]}${src[t.XRANGEPLAINLOOSE]}$`);
// Caret ranges.
// Meaning is "at least and backwards compatible with"
createToken('LONECARET', '(?:\\^)');
createToken('CARETTRIM', `(\\s*)${src[t.LONECARET]}\\s+`, true);
exports$1.caretTrimReplace = '$1^';
createToken('CARET', `^${src[t.LONECARET]}${src[t.XRANGEPLAIN]}$`);
createToken('CARETLOOSE', `^${src[t.LONECARET]}${src[t.XRANGEPLAINLOOSE]}$`);
// A simple gt/lt/eq thing, or just "" to indicate "any version"
createToken('COMPARATORLOOSE', `^${src[t.GTLT]}\\s*(${src[t.LOOSEPLAIN]})$|^$`);
createToken('COMPARATOR', `^${src[t.GTLT]}\\s*(${src[t.FULLPLAIN]})$|^$`);
// An expression to strip any whitespace between the gtlt and the thing
// it modifies, so that `> 1.2.3` ==> `>1.2.3`
createToken('COMPARATORTRIM', `(\\s*)${src[t.GTLT]
}\\s*(${src[t.LOOSEPLAIN]}|${src[t.XRANGEPLAIN]})`, true);
exports$1.comparatorTrimReplace = '$1$2$3';
// Something like `1.2.3 - 1.2.4`
// Note that these all use the loose form, because they'll be
// checked against either the strict or loose comparator form
// later.
createToken('HYPHENRANGE', `^\\s*(${src[t.XRANGEPLAIN]})` +
`\\s+-\\s+` +
`(${src[t.XRANGEPLAIN]})` +
`\\s*$`);
createToken('HYPHENRANGELOOSE', `^\\s*(${src[t.XRANGEPLAINLOOSE]})` +
`\\s+-\\s+` +
`(${src[t.XRANGEPLAINLOOSE]})` +
`\\s*$`);
// Star ranges basically just allow anything at all.
createToken('STAR', '(<|>)?=?\\s*\\*');
// >=0.0.0 is like a star
createToken('GTE0', '^\\s*>=\\s*0\\.0\\.0\\s*$');
createToken('GTE0PRE', '^\\s*>=\\s*0\\.0\\.0-0\\s*$');
} (re, re.exports));
return re.exports;
}
var parseOptions_1;
var hasRequiredParseOptions;
function requireParseOptions () {
if (hasRequiredParseOptions) return parseOptions_1;
hasRequiredParseOptions = 1;
// parse out just the options we care about
const looseOption = Object.freeze({ loose: true });
const emptyOpts = Object.freeze({ });
const parseOptions = options => {
if (!options) {
return emptyOpts
}
if (typeof options !== 'object') {
return looseOption
}
return options
};
parseOptions_1 = parseOptions;
return parseOptions_1;
}
var identifiers;
var hasRequiredIdentifiers;
function requireIdentifiers () {
if (hasRequiredIdentifiers) return identifiers;
hasRequiredIdentifiers = 1;
const numeric = /^[0-9]+$/;
const compareIdentifiers = (a, b) => {
if (typeof a === 'number' && typeof b === 'number') {
return a === b ? 0 : a < b ? -1 : 1
}
const anum = numeric.test(a);
const bnum = numeric.test(b);
if (anum && bnum) {
a = +a;
b = +b;
}
return a === b ? 0
: (anum && !bnum) ? -1
: (bnum && !anum) ? 1
: a < b ? -1
: 1
};
const rcompareIdentifiers = (a, b) => compareIdentifiers(b, a);
identifiers = {
compareIdentifiers,
rcompareIdentifiers,
};
return identifiers;
}
var semver;
var hasRequiredSemver;
function requireSemver () {
if (hasRequiredSemver) return semver;
hasRequiredSemver = 1;
const debug = requireDebug();
const { MAX_LENGTH, MAX_SAFE_INTEGER } = requireConstants();
const { safeRe: re, t } = requireRe();
const parseOptions = requireParseOptions();
const { compareIdentifiers } = requireIdentifiers();
class SemVer {
constructor (version, options) {
options = parseOptions(options);
if (version instanceof SemVer) {
if (version.loose === !!options.loose &&
version.includePrerelease === !!options.includePrerelease) {
return version
} else {
version = version.version;
}
} else if (typeof version !== 'string') {
throw new TypeError(`Invalid version. Must be a string. Got type "${typeof version}".`)
}
if (version.length > MAX_LENGTH) {
throw new TypeError(
`version is longer than ${MAX_LENGTH} characters`
)
}
debug('SemVer', version, options);
this.options = options;
this.loose = !!options.loose;
// this isn't actually relevant for versions, but keep it so that we
// don't run into trouble passing this.options around.
this.includePrerelease = !!options.includePrerelease;
const m = version.trim().match(options.loose ? re[t.LOOSE] : re[t.FULL]);
if (!m) {
throw new TypeError(`Invalid Version: ${version}`)
}
this.raw = version;
// these are actually numbers
this.major = +m[1];
this.minor = +m[2];
this.patch = +m[3];
if (this.major > MAX_SAFE_INTEGER || this.major < 0) {
throw new TypeError('Invalid major version')
}
if (this.minor > MAX_SAFE_INTEGER || this.minor < 0) {
throw new TypeError('Invalid minor version')
}
if (this.patch > MAX_SAFE_INTEGER || this.patch < 0) {
throw new TypeError('Invalid patch version')
}
// numberify any prerelease numeric ids
if (!m[4]) {
this.prerelease = [];
} else {
this.prerelease = m[4].split('.').map((id) => {
if (/^[0-9]+$/.test(id)) {
const num = +id;
if (num >= 0 && num < MAX_SAFE_INTEGER) {
return num
}
}
return id
});
}
this.build = m[5] ? m[5].split('.') : [];
this.format();
}
format () {
this.version = `${this.major}.${this.minor}.${this.patch}`;
if (this.prerelease.length) {
this.version += `-${this.prerelease.join('.')}`;
}
return this.version
}
toString () {
return this.version
}
compare (other) {
debug('SemVer.compare', this.version, this.options, other);
if (!(other instanceof SemVer)) {
if (typeof other === 'string' && other === this.version) {
return 0
}
other = new SemVer(other, this.options);
}
if (other.version === this.version) {
return 0
}
return this.compareMain(other) || this.comparePre(other)
}
compareMain (other) {
if (!(other instanceof SemVer)) {
other = new SemVer(other, this.options);
}
if (this.major < other.major) {
return -1
}
if (this.major > other.major) {
return 1
}
if (this.minor < other.minor) {
return -1
}
if (this.minor > other.minor) {
return 1
}
if (this.patch < other.patch) {
return -1
}
if (this.patch > other.patch) {
return 1
}
return 0
}
comparePre (other) {
if (!(other instanceof SemVer)) {
other = new SemVer(other, this.options);
}
// NOT having a prerelease is > having one
if (this.prerelease.length && !other.prerelease.length) {
return -1
} else if (!this.prerelease.length && other.prerelease.length) {
return 1
} else if (!this.prerelease.length && !other.prerelease.length) {
return 0
}
let i = 0;
do {
const a = this.prerelease[i];
const b = other.prerelease[i];
debug('prerelease compare', i, a, b);
if (a === undefined && b === undefined) {
return 0
} else if (b === undefined) {
return 1
} else if (a === undefined) {
return -1
} else if (a === b) {
continue
} else {
return compareIdentifiers(a, b)
}
} while (++i)
}
compareBuild (other) {
if (!(other instanceof SemVer)) {
other = new SemVer(other, this.options);
}
let i = 0;
do {
const a = this.build[i];
const b = other.build[i];
debug('build compare', i, a, b);
if (a === undefined && b === undefined) {
return 0
} else if (b === undefined) {
return 1
} else if (a === undefined) {
return -1
} else if (a === b) {
continue
} else {
return compareIdentifiers(a, b)
}
} while (++i)
}
// preminor will bump the version up to the next minor release, and immediately
// down to pre-release. premajor and prepatch work the same way.
inc (release, identifier, identifierBase) {
if (release.startsWith('pre')) {
if (!identifier && identifierBase === false) {
throw new Error('invalid increment argument: identifier is empty')
}
// Avoid an invalid semver results
if (identifier) {
const match = `-${identifier}`.match(this.options.loose ? re[t.PRERELEASELOOSE] : re[t.PRERELEASE]);
if (!match || match[1] !== identifier) {
throw new Error(`invalid identifier: ${identifier}`)
}
}
}
switch (release) {
case 'premajor':
this.prerelease.length = 0;
this.patch = 0;
this.minor = 0;
this.major++;
this.inc('pre', identifier, identifierBase);
break
case 'preminor':
this.prerelease.length = 0;
this.patch = 0;
this.minor++;
this.inc('pre', identifier, identifierBase);
break
case 'prepatch':
// If this is already a prerelease, it will bump to the next version
// drop any prereleases that might already exist, since they are not
// relevant at this point.
this.prerelease.length = 0;
this.inc('patch', identifier, identifierBase);
this.inc('pre', identifier, identifierBase);
break
// If the input is a non-prerelease version, this acts the same as
// prepatch.
case 'prerelease':
if (this.prerelease.length === 0) {
this.inc('patch', identifier, identifierBase);
}
this.inc('pre', identifier, identifierBase);
break
case 'release':
if (this.prerelease.length === 0) {
throw new Error(`version ${this.raw} is not a prerelease`)
}
this.prerelease.length = 0;
break
case 'major':
// If this is a pre-major version, bump up to the same major version.
// Otherwise increment major.
// 1.0.0-5 bumps to 1.0.0
// 1.1.0 bumps to 2.0.0
if (
this.minor !== 0 ||
this.patch !== 0 ||
this.prerelease.length === 0
) {
this.major++;
}
this.minor = 0;
this.patch = 0;
this.prerelease = [];
break
case 'minor':
// If this is a pre-minor version, bump up to the same minor version.
// Otherwise increment minor.
// 1.2.0-5 bumps to 1.2.0
// 1.2.1 bumps to 1.3.0
if (this.patch !== 0 || this.prerelease.length === 0) {
this.minor++;
}
this.patch = 0;
this.prerelease = [];
break
case 'patch':
// If this is not a pre-release version, it will increment the patch.
// If it is a pre-release it will bump up to the same patch version.
// 1.2.0-5 patches to 1.2.0
// 1.2.0 patches to 1.2.1
if (this.prerelease.length === 0) {
this.patch++;
}
this.prerelease = [];
break
// This probably shouldn't be used publicly.
// 1.0.0 'pre' would become 1.0.0-0 which is the wrong direction.
case 'pre': {
const base = Number(identifierBase) ? 1 : 0;
if (this.prerelease.length === 0) {
this.prerelease = [base];
} else {
let i = this.prerelease.length;
while (--i >= 0) {
if (typeof this.prerelease[i] === 'number') {
this.prerelease[i]++;
i = -2;
}
}
if (i === -1) {
// didn't increment anything
if (identifier === this.prerelease.join('.') && identifierBase === false) {
throw new Error('invalid increment argument: identifier already exists')
}
this.prerelease.push(base);
}
}
if (identifier) {
// 1.2.0-beta.1 bumps to 1.2.0-beta.2,
// 1.2.0-beta.fooblz or 1.2.0-beta bumps to 1.2.0-beta.0
let prerelease = [identifier, base];
if (identifierBase === false) {
prerelease = [identifier];
}
if (compareIdentifiers(this.prerelease[0], identifier) === 0) {
if (isNaN(this.prerelease[1])) {
this.prerelease = prerelease;
}
} else {
this.prerelease = prerelease;
}
}
break
}
default:
throw new Error(`invalid increment argument: ${release}`)
}
this.raw = this.format();
if (this.build.length) {
this.raw += `+${this.build.join('.')}`;
}
return this
}
}
semver = SemVer;
return semver;
}
var parse_1;
var hasRequiredParse;
function requireParse () {
if (hasRequiredParse) return parse_1;
hasRequiredParse = 1;
const SemVer = requireSemver();
const parse = (version, options, throwErrors = false) => {
if (version instanceof SemVer) {
return version
}
try {
return new SemVer(version, options)
} catch (er) {
if (!throwErrors) {
return null
}
throw er
}
};
parse_1 = parse;
return parse_1;
}
var coerce_1;
var hasRequiredCoerce;
function requireCoerce () {
if (hasRequiredCoerce) return coerce_1;
hasRequiredCoerce = 1;
const SemVer = requireSemver();
const parse = requireParse();
const { safeRe: re, t } = requireRe();
const coerce = (version, options) => {
if (version instanceof SemVer) {
return version
}
if (typeof version === 'number') {
version = String(version);
}
if (typeof version !== 'string') {
return null
}
options = options || {};
let match = null;
if (!options.rtl) {
match = version.match(options.includePrerelease ? re[t.COERCEFULL] : re[t.COERCE]);
} else {
// Find the right-most coercible string that does not share
// a terminus with a more left-ward coercible string.
// Eg, '1.2.3.4' wants to coerce '2.3.4', not '3.4' or '4'
// With includePrerelease option set, '1.2.3.4-rc' wants to coerce '2.3.4-rc', not '2.3.4'
//
// Walk through the string checking with a /g regexp
// Manually set the index so as to pick up overlapping matches.
// Stop when we get a match that ends at the string end, since no
// coercible string can be more right-ward without the same terminus.
const coerceRtlRegex = options.includePrerelease ? re[t.COERCERTLFULL] : re[t.COERCERTL];
let next;
while ((next = coerceRtlRegex.exec(version)) &&
(!match || match.index + match[0].length !== version.length)
) {
if (!match ||
next.index + next[0].length !== match.index + match[0].length) {
match = next;
}
coerceRtlRegex.lastIndex = next.index + next[1].length + next[2].length;
}
// leave it in a clean state
coerceRtlRegex.lastIndex = -1;
}
if (match === null) {
return null
}
const major = match[2];
const minor = match[3] || '0';
const patch = match[4] || '0';
const prerelease = options.includePrerelease && match[5] ? `-${match[5]}` : '';
const build = options.includePrerelease && match[6] ? `+${match[6]}` : '';
return parse(`${major}.${minor}.${patch}${prerelease}${build}`, options)
};
coerce_1 = coerce;
return coerce_1;
}
var compare_1;
var hasRequiredCompare;
function requireCompare () {
if (hasRequiredCompare) return compare_1;
hasRequiredCompare = 1;
const SemVer = requireSemver();
const compare = (a, b, loose) =>
new SemVer(a, loose).compare(new SemVer(b, loose));
compare_1 = compare;
return compare_1;
}
var gte_1;
var hasRequiredGte;
function requireGte () {
if (hasRequiredGte) return gte_1;
hasRequiredGte = 1;
const compare = requireCompare();
const gte = (a, b, loose) => compare(a, b, loose) >= 0;
gte_1 = gte;
return gte_1;
}
var lrucache;
var hasRequiredLrucache;
function requireLrucache () {
if (hasRequiredLrucache) return lrucache;
hasRequiredLrucache = 1;
class LRUCache {
constructor () {
this.max = 1000;
this.map = new Map();
}
get (key) {
const value = this.map.get(key);
if (value === undefined) {
return undefined
} else {
// Remove the key from the map and add it to the end
this.map.delete(key);
this.map.set(key, value);
return value
}
}
delete (key) {
return this.map.delete(key)
}
set (key, value) {
const deleted = this.delete(key);
if (!deleted && value !== undefined) {
// If cache is full, delete the least recently used item
if (this.map.size >= this.max) {
const firstKey = this.map.keys().next().value;
this.delete(firstKey);
}
this.map.set(key, value);
}
return this
}
}
lrucache = LRUCache;
return lrucache;
}
var eq_1;
var hasRequiredEq;
function requireEq () {
if (hasRequiredEq) return eq_1;
hasRequiredEq = 1;
const compare = requireCompare();
const eq = (a, b, loose) => compare(a, b, loose) === 0;
eq_1 = eq;
return eq_1;
}
var neq_1;
var hasRequiredNeq;
function requireNeq () {
if (hasRequiredNeq) return neq_1;
hasRequiredNeq = 1;
const compare = requireCompare();
const neq = (a, b, loose) => compare(a, b, loose) !== 0;
neq_1 = neq;
return neq_1;
}
var gt_1;
var hasRequiredGt;
function requireGt () {
if (hasRequiredGt) return gt_1;
hasRequiredGt = 1;
const compare = requireCompare();
const gt = (a, b, loose) => compare(a, b, loose) > 0;
gt_1 = gt;
return gt_1;
}
var lt_1;
var hasRequiredLt;
function requireLt () {
if (hasRequiredLt) return lt_1;
hasRequiredLt = 1;
const compare = requireCompare();
const lt = (a, b, loose) => compare(a, b, loose) < 0;
lt_1 = lt;
return lt_1;
}
var lte_1;
var hasRequiredLte;
function requireLte () {
if (hasRequiredLte) return lte_1;
hasRequiredLte = 1;
const compare = requireCompare();
const lte = (a, b, loose) => compare(a, b, loose) <= 0;
lte_1 = lte;
return lte_1;
}
var cmp_1;
var hasRequiredCmp;
function requireCmp () {
if (hasRequiredCmp) return cmp_1;
hasRequiredCmp = 1;
const eq = requireEq();
const neq = requireNeq();
const gt = requireGt();
const gte = requireGte();
const lt = requireLt();
const lte = requireLte();
const cmp = (a, op, b, loose) => {
switch (op) {
case '===':
if (typeof a === 'object') {
a = a.version;
}
if (typeof b === 'object') {
b = b.version;
}
return a === b
case '!==':
if (typeof a === 'object') {
a = a.version;
}
if (typeof b === 'object') {
b = b.version;
}
return a !== b
case '':
case '=':
case '==':
return eq(a, b, loose)
case '!=':
return neq(a, b, loose)
case '>':
return gt(a, b, loose)
case '>=':
return gte(a, b, loose)
case '<':
return lt(a, b, loose)
case '<=':
return lte(a, b, loose)
default:
throw new TypeError(`Invalid operator: ${op}`)
}
};
cmp_1 = cmp;
return cmp_1;
}
var comparator;
var hasRequiredComparator;
function requireComparator () {
if (hasRequiredComparator) return comparator;
hasRequiredComparator = 1;
const ANY = Symbol('SemVer ANY');
// hoisted class for cyclic dependency
class Comparator {
static get ANY () {
return ANY
}
constructor (comp, options) {
options = parseOptions(options);
if (comp instanceof Comparator) {
if (comp.loose === !!options.loose) {
return comp
} else {
comp = comp.value;
}
}
comp = comp.trim().split(/\s+/).join(' ');
debug('comparator', comp, options);
this.options = options;
this.loose = !!options.loose;
this.parse(comp);
if (this.semver === ANY) {
this.value = '';
} else {
this.value = this.operator + this.semver.version;
}
debug('comp', this);
}
parse (comp) {
const r = this.options.loose ? re[t.COMPARATORLOOSE] : re[t.COMPARATOR];
const m = comp.match(r);
if (!m) {
throw new TypeError(`Invalid comparator: ${comp}`)
}
this.operator = m[1] !== undefined ? m[1] : '';
if (this.operator === '=') {
this.operator = '';
}
// if it literally is just '>' or '' then allow anything.
if (!m[2]) {
this.semver = ANY;
} else {
this.semver = new SemVer(m[2], this.options.loose);
}
}
toString () {
return this.value
}
test (version) {
debug('Comparator.test', version, this.options.loose);
if (this.semver === ANY || version === ANY) {
return true
}
if (typeof version === 'string') {
try {
version = new SemVer(version, this.options);
} catch (er) {
return false
}
}
return cmp(version, this.operator, this.semver, this.options)
}
intersects (comp, options) {
if (!(comp instanceof Comparator)) {
throw new TypeError('a Comparator is required')
}
if (this.operator === '') {
if (this.value === '') {
return true
}
return new Range(comp.value, options).test(this.value)
} else if (comp.operator === '') {
if (comp.value === '') {
return true
}
return new Range(this.value, options).test(comp.semver)
}
options = parseOptions(options);
// Special cases where nothing can possibly be lower
if (options.includePrerelease &&
(this.value === '<0.0.0-0' || comp.value === '<0.0.0-0')) {
return false
}
if (!options.includePrerelease &&
(this.value.startsWith('<0.0.0') || comp.value.startsWith('<0.0.0'))) {
return false
}
// Same direction increasing (> or >=)
if (this.operator.startsWith('>') && comp.operator.startsWith('>')) {
return true
}
// Same direction decreasing (< or <=)
if (this.operator.startsWith('<') && comp.operator.startsWith('<')) {
return true
}
// same SemVer and both sides are inclusive (<= or >=)
if (
(this.semver.version === comp.semver.version) &&
this.operator.includes('=') && comp.operator.includes('=')) {
return true
}
// opposite directions less than
if (cmp(this.semver, '<', comp.semver, options) &&
this.operator.startsWith('>') && comp.operator.startsWith('<')) {
return true
}
// opposite directions greater than
if (cmp(this.semver, '>', comp.semver, options) &&
this.operator.startsWith('<') && comp.operator.startsWith('>')) {
return true
}
return false
}
}
comparator = Comparator;
const parseOptions = requireParseOptions();
const { safeRe: re, t } = requireRe();
const cmp = requireCmp();
const debug = requireDebug();
const SemVer = requireSemver();
const Range = requireRange();
return comparator;
}
var range;
var hasRequiredRange;
function requireRange () {
if (hasRequiredRange) return range;
hasRequiredRange = 1;
const SPACE_CHARACTERS = /\s+/g;
// hoisted class for cyclic dependency
class Range {
constructor (range, options) {
options = parseOptions(options);
if (range instanceof Range) {
if (
range.loose === !!options.loose &&
range.includePrerelease === !!options.includePrerelease
) {
return range
} else {
return new Range(range.raw, options)
}
}
if (range instanceof Comparator) {
// just put it in the set and return
this.raw = range.value;
this.set = [[range]];
this.formatted = undefined;
return this
}
this.options = options;
this.loose = !!options.loose;
this.includePrerelease = !!options.includePrerelease;
// First reduce all whitespace as much as possible so we do not have to rely
// on potentially slow regexes like \s*. This is then stored and used for
// future error messages as well.
this.raw = range.trim().replace(SPACE_CHARACTERS, ' ');
// First, split on ||
this.set = this.raw
.split('||')
// map the range to a 2d array of comparators
.map(r => this.parseRange(r.trim()))
// throw out any comparator lists that are empty
// this generally means that it was not a valid range, which is allowed
// in loose mode, but will still throw if the WHOLE range is invalid.
.filter(c => c.length);
if (!this.set.length) {
throw new TypeError(`Invalid SemVer Range: ${this.raw}`)
}
// if we have any that are not the null set, throw out null sets.
if (this.set.length > 1) {
// keep the first one, in case they're all null sets
const first = this.set[0];
this.set = this.set.filter(c => !isNullSet(c[0]));
if (this.set.length === 0) {
this.set = [first];
} else if (this.set.length > 1) {
// if we have any that are *, then the range is just *
for (const c of this.set) {
if (c.length === 1 && isAny(c[0])) {
this.set = [c];
break
}
}
}
}
this.formatted = undefined;
}
get range () {
if (this.formatted === undefined) {
this.formatted = '';
for (let i = 0; i < this.set.length; i++) {
if (i > 0) {
this.formatted += '||';
}
const comps = this.set[i];
for (let k = 0; k < comps.length; k++) {
if (k > 0) {
this.formatted += ' ';
}
this.formatted += comps[k].toString().trim();
}
}
}
return this.formatted
}
format () {
return this.range
}
toString () {
return this.range
}
parseRange (range) {
// memoize range parsing for performance.
// this is a very hot path, and fully deterministic.
const memoOpts =
(this.options.includePrerelease && FLAG_INCLUDE_PRERELEASE) |
(this.options.loose && FLAG_LOOSE);
const memoKey = memoOpts + ':' + range;
const cached = cache.get(memoKey);
if (cached) {
return cached
}
const loose = this.options.loose;
// `1.2.3 - 1.2.4` => `>=1.2.3 <=1.2.4`
const hr = loose ? re[t.HYPHENRANGELOOSE] : re[t.HYPHENRANGE];
range = range.replace(hr, hyphenReplace(this.options.includePrerelease));
debug('hyphen replace', range);
// `> 1.2.3 < 1.2.5` => `>1.2.3 <1.2.5`
range = range.replace(re[t.COMPARATORTRIM], comparatorTrimReplace);
debug('comparator trim', range);
// `~ 1.2.3` => `~1.2.3`
range = range.replace(re[t.TILDETRIM], tildeTrimReplace);
debug('tilde trim', range);
// `^ 1.2.3` => `^1.2.3`
range = range.replace(re[t.CARETTRIM], caretTrimReplace);
debug('caret trim', range);
// At this point, the range is completely trimmed and
// ready to be split into comparators.
let rangeList = range
.split(' ')
.map(comp => parseComparator(comp, this.options))
.join(' ')
.split(/\s+/)
// >=0.0.0 is equivalent to *
.map(comp => replaceGTE0(comp, this.options));
if (loose) {
// in loose mode, throw out any that are not valid comparators
rangeList = rangeList.filter(comp => {
debug('loose invalid filter', comp, this.options);
return !!comp.match(re[t.COMPARATORLOOSE])
});
}
debug('range list', rangeList);
// if any comparators are the null set, then replace with JUST null set
// if more than one comparator, remove any * comparators
// also, don't include the same comparator more than once
const rangeMap = new Map();
const comparators = rangeList.map(comp => new Comparator(comp, this.options));
for (const comp of comparators) {
if (isNullSet(comp)) {
return [comp]
}
rangeMap.set(comp.value, comp);
}
if (rangeMap.size > 1 && rangeMap.has('')) {
rangeMap.delete('');
}
const result = [...rangeMap.values()];
cache.set(memoKey, result);
return result
}
intersects (range, options) {
if (!(range instanceof Range)) {
throw new TypeError('a Range is required')
}
return this.set.some((thisComparators) => {
return (
isSatisfiable(thisComparators, options) &&
range.set.some((rangeComparators) => {
return (
isSatisfiable(rangeComparators, options) &&
thisComparators.every((thisComparator) => {
return rangeComparators.every((rangeComparator) => {
return thisComparator.intersects(rangeComparator, options)
})
})
)
})
)
})
}
// if ANY of the sets match ALL of its comparators, then pass
test (version) {
if (!version) {
return false
}
if (typeof version === 'string') {
try {
version = new SemVer(version, this.options);
} catch (er) {
return false
}
}
for (let i = 0; i < this.set.length; i++) {
if (testSet(this.set[i], version, this.options)) {
return true
}
}
return false
}
}
range = Range;
const LRU = requireLrucache();
const cache = new LRU();
const parseOptions = requireParseOptions();
const Comparator = requireComparator();
const debug = requireDebug();
const SemVer = requireSemver();
const {
safeRe: re,
t,
comparatorTrimReplace,
tildeTrimReplace,
caretTrimReplace,
} = requireRe();
const { FLAG_INCLUDE_PRERELEASE, FLAG_LOOSE } = requireConstants();
const isNullSet = c => c.value === '<0.0.0-0';
const isAny = c => c.value === '';
// take a set of comparators and determine whether there
// exists a version which can satisfy it
const isSatisfiable = (comparators, options) => {
let result = true;
const remainingComparators = comparators.slice();
let testComparator = remainingComparators.pop();
while (result && remainingComparators.length) {
result = remainingComparators.every((otherComparator) => {
return testComparator.intersects(otherComparator, options)
});
testComparator = remainingComparators.pop();
}
return result
};
// comprised of xranges, tildes, stars, and gtlt's at this point.
// already replaced the hyphen ranges
// turn into a set of JUST comparators.
const parseComparator = (comp, options) => {
comp = comp.replace(re[t.BUILD], '');
debug('comp', comp, options);
comp = replaceCarets(comp, options);
debug('caret', comp);
comp = replaceTildes(comp, options);
debug('tildes', comp);
comp = replaceXRanges(comp, options);
debug('xrange', comp);
comp = replaceStars(comp, options);
debug('stars', comp);
return comp
};
const isX = id => !id || id.toLowerCase() === 'x' || id === '*';
// ~, ~> --> * (any, kinda silly)
// ~2, ~2.x, ~2.x.x, ~>2, ~>2.x ~>2.x.x --> >=2.0.0 <3.0.0-0
// ~2.0, ~2.0.x, ~>2.0, ~>2.0.x --> >=2.0.0 <2.1.0-0
// ~1.2, ~1.2.x, ~>1.2, ~>1.2.x --> >=1.2.0 <1.3.0-0
// ~1.2.3, ~>1.2.3 --> >=1.2.3 <1.3.0-0
// ~1.2.0, ~>1.2.0 --> >=1.2.0 <1.3.0-0
// ~0.0.1 --> >=0.0.1 <0.1.0-0
const replaceTildes = (comp, options) => {
return comp
.trim()
.split(/\s+/)
.map((c) => replaceTilde(c, options))
.join(' ')
};
const replaceTilde = (comp, options) => {
const r = options.loose ? re[t.TILDELOOSE] : re[t.TILDE];
return comp.replace(r, (_, M, m, p, pr) => {
debug('tilde', comp, _, M, m, p, pr);
let ret;
if (isX(M)) {
ret = '';
} else if (isX(m)) {
ret = `>=${M}.0.0 <${+M + 1}.0.0-0`;
} else if (isX(p)) {
// ~1.2 == >=1.2.0 <1.3.0-0
ret = `>=${M}.${m}.0 <${M}.${+m + 1}.0-0`;
} else if (pr) {
debug('replaceTilde pr', pr);
ret = `>=${M}.${m}.${p}-${pr
} <${M}.${+m + 1}.0-0`;
} else {
// ~1.2.3 == >=1.2.3 <1.3.0-0
ret = `>=${M}.${m}.${p
} <${M}.${+m + 1}.0-0`;
}
debug('tilde return', ret);
return ret
})
};
// ^ --> * (any, kinda silly)
// ^2, ^2.x, ^2.x.x --> >=2.0.0 <3.0.0-0
// ^2.0, ^2.0.x --> >=2.0.0 <3.0.0-0
// ^1.2, ^1.2.x --> >=1.2.0 <2.0.0-0
// ^1.2.3 --> >=1.2.3 <2.0.0-0
// ^1.2.0 --> >=1.2.0 <2.0.0-0
// ^0.0.1 --> >=0.0.1 <0.0.2-0
// ^0.1.0 --> >=0.1.0 <0.2.0-0
const replaceCarets = (comp, options) => {
return comp
.trim()
.split(/\s+/)
.map((c) => replaceCaret(c, options))
.join(' ')
};
const replaceCaret = (comp, options) => {
debug('caret', comp, options);
const r = options.loose ? re[t.CARETLOOSE] : re[t.CARET];
const z = options.includePrerelease ? '-0' : '';
return comp.replace(r, (_, M, m, p, pr) => {
debug('caret', comp, _, M, m, p, pr);
let ret;
if (isX(M)) {
ret = '';
} else if (isX(m)) {
ret = `>=${M}.0.0${z} <${+M + 1}.0.0-0`;
} else if (isX(p)) {
if (M === '0') {
ret = `>=${M}.${m}.0${z} <${M}.${+m + 1}.0-0`;
} else {
ret = `>=${M}.${m}.0${z} <${+M + 1}.0.0-0`;
}
} else if (pr) {
debug('replaceCaret pr', pr);
if (M === '0') {
if (m === '0') {
ret = `>=${M}.${m}.${p}-${pr
} <${M}.${m}.${+p + 1}-0`;
} else {
ret = `>=${M}.${m}.${p}-${pr
} <${M}.${+m + 1}.0-0`;
}
} else {
ret = `>=${M}.${m}.${p}-${pr
} <${+M + 1}.0.0-0`;
}
} else {
debug('no pr');
if (M === '0') {
if (m === '0') {
ret = `>=${M}.${m}.${p
}${z} <${M}.${m}.${+p + 1}-0`;
} else {
ret = `>=${M}.${m}.${p
}${z} <${M}.${+m + 1}.0-0`;
}
} else {
ret = `>=${M}.${m}.${p
} <${+M + 1}.0.0-0`;
}
}
debug('caret return', ret);
return ret
})
};
const replaceXRanges = (comp, options) => {
debug('replaceXRanges', comp, options);
return comp
.split(/\s+/)
.map((c) => replaceXRange(c, options))
.join(' ')
};
const replaceXRange = (comp, options) => {
comp = comp.trim();
const r = options.loose ? re[t.XRANGELOOSE] : re[t.XRANGE];
return comp.replace(r, (ret, gtlt, M, m, p, pr) => {
debug('xRange', comp, ret, gtlt, M, m, p, pr);
const xM = isX(M);
const xm = xM || isX(m);
const xp = xm || isX(p);
const anyX = xp;
if (gtlt === '=' && anyX) {
gtlt = '';
}
// if we're including prereleases in the match, then we need
// to fix this to -0, the lowest possible prerelease value
pr = options.includePrerelease ? '-0' : '';
if (xM) {
if (gtlt === '>' || gtlt === '<') {
// nothing is allowed
ret = '<0.0.0-0';
} else {
// nothing is forbidden
ret = '*';
}
} else if (gtlt && anyX) {
// we know patch is an x, because we have any x at all.
// replace X with 0
if (xm) {
m = 0;
}
p = 0;
if (gtlt === '>') {
// >1 => >=2.0.0
// >1.2 => >=1.3.0
gtlt = '>=';
if (xm) {
M = +M + 1;
m = 0;
p = 0;
} else {
m = +m + 1;
p = 0;
}
} else if (gtlt === '<=') {
// <=0.7.x is actually <0.8.0, since any 0.7.x should
// pass. Similarly, <=7.x is actually <8.0.0, etc.
gtlt = '<';
if (xm) {
M = +M + 1;
} else {
m = +m + 1;
}
}
if (gtlt === '<') {
pr = '-0';
}
ret = `${gtlt + M}.${m}.${p}${pr}`;
} else if (xm) {
ret = `>=${M}.0.0${pr} <${+M + 1}.0.0-0`;
} else if (xp) {
ret = `>=${M}.${m}.0${pr
} <${M}.${+m + 1}.0-0`;
}
debug('xRange return', ret);
return ret
})
};
// Because * is AND-ed with everything else in the comparator,
// and '' means "any version", just remove the *s entirely.
const replaceStars = (comp, options) => {
debug('replaceStars', comp, options);
// Looseness is ignored here. star is always as loose as it gets!
return comp
.trim()
.replace(re[t.STAR], '')
};
const replaceGTE0 = (comp, options) => {
debug('replaceGTE0', comp, options);
return comp
.trim()
.replace(re[options.includePrerelease ? t.GTE0PRE : t.GTE0], '')
};
// This function is passed to string.replace(re[t.HYPHENRANGE])
// M, m, patch, prerelease, build
// 1.2 - 3.4.5 => >=1.2.0 <=3.4.5
// 1.2.3 - 3.4 => >=1.2.0 <3.5.0-0 Any 3.4.x will do
// 1.2 - 3.4 => >=1.2.0 <3.5.0-0
// TODO build?
const hyphenReplace = incPr => ($0,
from, fM, fm, fp, fpr, fb,
to, tM, tm, tp, tpr) => {
if (isX(fM)) {
from = '';
} else if (isX(fm)) {
from = `>=${fM}.0.0${incPr ? '-0' : ''}`;
} else if (isX(fp)) {
from = `>=${fM}.${fm}.0${incPr ? '-0' : ''}`;
} else if (fpr) {
from = `>=${from}`;
} else {
from = `>=${from}${incPr ? '-0' : ''}`;
}
if (isX(tM)) {
to = '';
} else if (isX(tm)) {
to = `<${+tM + 1}.0.0-0`;
} else if (isX(tp)) {
to = `<${tM}.${+tm + 1}.0-0`;
} else if (tpr) {
to = `<=${tM}.${tm}.${tp}-${tpr}`;
} else if (incPr) {
to = `<${tM}.${tm}.${+tp + 1}-0`;
} else {
to = `<=${to}`;
}
return `${from} ${to}`.trim()
};
const testSet = (set, version, options) => {
for (let i = 0; i < set.length; i++) {
if (!set[i].test(version)) {
return false
}
}
if (version.prerelease.length && !options.includePrerelease) {
// Find the set of versions that are allowed to have prereleases
// For example, ^1.2.3-pr.1 desugars to >=1.2.3-pr.1 <2.0.0
// That should allow `1.2.3-pr.2` to pass.
// However, `1.2.4-alpha.notready` should NOT be allowed,
// even though it's within the range set by the comparators.
for (let i = 0; i < set.length; i++) {
debug(set[i].semver);
if (set[i].semver === Comparator.ANY) {
continue
}
if (set[i].semver.prerelease.length > 0) {
const allowed = set[i].semver;
if (allowed.major === version.major &&
allowed.minor === version.minor &&
allowed.patch === version.patch) {
return true
}
}
}
// Version has a -pre, but it's not one of the ones we like.
return false
}
return true
};
return range;
}
var satisfies_1;
var hasRequiredSatisfies;
function requireSatisfies () {
if (hasRequiredSatisfies) return satisfies_1;
hasRequiredSatisfies = 1;
const Range = requireRange();
const satisfies = (version, range, options) => {
try {
range = new Range(range, options);
} catch (er) {
return false
}
return range.test(version)
};
satisfies_1 = satisfies;
return satisfies_1;
}
const version = "0.34.5";
const optionalDependencies = {"@img/sharp-darwin-arm64":"0.34.5","@img/sharp-darwin-x64":"0.34.5","@img/sharp-libvips-darwin-arm64":"1.2.4","@img/sharp-libvips-darwin-x64":"1.2.4","@img/sharp-libvips-linux-arm":"1.2.4","@img/sharp-libvips-linux-arm64":"1.2.4","@img/sharp-libvips-linux-ppc64":"1.2.4","@img/sharp-libvips-linux-riscv64":"1.2.4","@img/sharp-libvips-linux-s390x":"1.2.4","@img/sharp-libvips-linux-x64":"1.2.4","@img/sharp-libvips-linuxmusl-arm64":"1.2.4","@img/sharp-libvips-linuxmusl-x64":"1.2.4","@img/sharp-linux-arm":"0.34.5","@img/sharp-linux-arm64":"0.34.5","@img/sharp-linux-ppc64":"0.34.5","@img/sharp-linux-riscv64":"0.34.5","@img/sharp-linux-s390x":"0.34.5","@img/sharp-linux-x64":"0.34.5","@img/sharp-linuxmusl-arm64":"0.34.5","@img/sharp-linuxmusl-x64":"0.34.5","@img/sharp-wasm32":"0.34.5","@img/sharp-win32-arm64":"0.34.5","@img/sharp-win32-ia32":"0.34.5","@img/sharp-win32-x64":"0.34.5"};
const engines = {"node":"^18.17.0 || ^20.3.0 || >=21.0.0"};
const config = {"libvips":">=8.17.3"};
const require$$6 = {
version,
optionalDependencies,
engines,
config};
/*!
Copyright 2013 Lovell Fuller and others.
SPDX-License-Identifier: Apache-2.0
*/
var libvips;
var hasRequiredLibvips;
function requireLibvips () {
if (hasRequiredLibvips) return libvips;
hasRequiredLibvips = 1;
const { spawnSync } = require$$0$1;
const { createHash } = require$$1;
const semverCoerce = requireCoerce();
const semverGreaterThanOrEqualTo = requireGte();
const semverSatisfies = requireSatisfies();
const detectLibc = requireDetectLibc();
const { config, engines, optionalDependencies } = require$$6;
/* node:coverage ignore next */
const minimumLibvipsVersionLabelled = process.env.npm_package_config_libvips || config.libvips;
const minimumLibvipsVersion = semverCoerce(minimumLibvipsVersionLabelled).version;
const prebuiltPlatforms = [
'darwin-arm64', 'darwin-x64',
'linux-arm', 'linux-arm64', 'linux-ppc64', 'linux-riscv64', 'linux-s390x', 'linux-x64',
'linuxmusl-arm64', 'linuxmusl-x64',
'win32-arm64', 'win32-ia32', 'win32-x64'
];
const spawnSyncOptions = {
encoding: 'utf8',
shell: true
};
const log = (item) => {
if (item instanceof Error) {
console.error(`sharp: Installation error: ${item.message}`);
} else {
console.log(`sharp: ${item}`);
}
};
/* node:coverage ignore next */
const runtimeLibc = () => detectLibc.isNonGlibcLinuxSync() ? detectLibc.familySync() : '';
const runtimePlatformArch = () => `${process.platform}${runtimeLibc()}-${process.arch}`;
const buildPlatformArch = () => {
/* node:coverage ignore next 3 */
if (isEmscripten()) {
return 'wasm32';
}
const { npm_config_arch, npm_config_platform, npm_config_libc } = process.env;
const libc = typeof npm_config_libc === 'string' ? npm_config_libc : runtimeLibc();
return `${npm_config_platform || process.platform}${libc}-${npm_config_arch || process.arch}`;
};
const buildSharpLibvipsIncludeDir = () => {
try {
return commonjsRequire(`@img/sharp-libvips-dev-${buildPlatformArch()}/include`);
} catch {
/* node:coverage ignore next 5 */
try {
return require('@img/sharp-libvips-dev/include');
} catch {}
}
return '';
};
const buildSharpLibvipsCPlusPlusDir = () => {
/* node:coverage ignore next 4 */
try {
return require('@img/sharp-libvips-dev/cplusplus');
} catch {}
return '';
};
const buildSharpLibvipsLibDir = () => {
try {
return commonjsRequire(`@img/sharp-libvips-dev-${buildPlatformArch()}/lib`);
} catch {
/* node:coverage ignore next 5 */
try {
return commonjsRequire(`@img/sharp-libvips-${buildPlatformArch()}/lib`);
} catch {}
}
return '';
};
/* node:coverage disable */
const isUnsupportedNodeRuntime = () => {
if (process.release?.name === 'node' && process.versions) {
if (!semverSatisfies(process.versions.node, engines.node)) {
return { found: process.versions.node, expected: engines.node };
}
}
};
const isEmscripten = () => {
const { CC } = process.env;
return Boolean(CC?.endsWith('/emcc'));
};
const isRosetta = () => {
if (process.platform === 'darwin' && process.arch === 'x64') {
const translated = spawnSync('sysctl sysctl.proc_translated', spawnSyncOptions).stdout;
return (translated || '').trim() === 'sysctl.proc_translated: 1';
}
return false;
};
/* node:coverage enable */
const sha512 = (s) => createHash('sha512').update(s).digest('hex');
const yarnLocator = () => {
try {
const identHash = sha512(`imgsharp-libvips-${buildPlatformArch()}`);
const npmVersion = semverCoerce(optionalDependencies[`@img/sharp-libvips-${buildPlatformArch()}`], {
includePrerelease: true
}).version;
return sha512(`${identHash}npm:${npmVersion}`).slice(0, 10);
} catch {}
return '';
};
/* node:coverage disable */
const spawnRebuild = () =>
spawnSync(`node-gyp rebuild --directory=src ${isEmscripten() ? '--nodedir=emscripten' : ''}`, {
...spawnSyncOptions,
stdio: 'inherit'
}).status;
const globalLibvipsVersion = () => {
if (process.platform !== 'win32') {
const globalLibvipsVersion = spawnSync('pkg-config --modversion vips-cpp', {
...spawnSyncOptions,
env: {
...process.env,
PKG_CONFIG_PATH: pkgConfigPath()
}
}).stdout;
return (globalLibvipsVersion || '').trim();
} else {
return '';
}
};
/* node:coverage enable */
const pkgConfigPath = () => {
if (process.platform !== 'win32') {
/* node:coverage ignore next 4 */
const brewPkgConfigPath = spawnSync(
'which brew >/dev/null 2>&1 && brew environment --plain | grep PKG_CONFIG_LIBDIR | cut -d" " -f2',
spawnSyncOptions
).stdout || '';
return [
brewPkgConfigPath.trim(),
process.env.PKG_CONFIG_PATH,
'/usr/local/lib/pkgconfig',
'/usr/lib/pkgconfig',
'/usr/local/libdata/pkgconfig',
'/usr/libdata/pkgconfig'
].filter(Boolean).join(':');
} else {
return '';
}
};
const skipSearch = (status, reason, logger) => {
if (logger) {
logger(`Detected ${reason}, skipping search for globally-installed libvips`);
}
return status;
};
const useGlobalLibvips = (logger) => {
if (Boolean(process.env.SHARP_IGNORE_GLOBAL_LIBVIPS) === true) {
return skipSearch(false, 'SHARP_IGNORE_GLOBAL_LIBVIPS', logger);
}
if (Boolean(process.env.SHARP_FORCE_GLOBAL_LIBVIPS) === true) {
return skipSearch(true, 'SHARP_FORCE_GLOBAL_LIBVIPS', logger);
}
/* node:coverage ignore next 3 */
if (isRosetta()) {
return skipSearch(false, 'Rosetta', logger);
}
const globalVipsVersion = globalLibvipsVersion();
/* node:coverage ignore next */
return !!globalVipsVersion && semverGreaterThanOrEqualTo(globalVipsVersion, minimumLibvipsVersion);
};
libvips = {
minimumLibvipsVersion,
prebuiltPlatforms,
buildPlatformArch,
buildSharpLibvipsIncludeDir,
buildSharpLibvipsCPlusPlusDir,
buildSharpLibvipsLibDir,
isUnsupportedNodeRuntime,
runtimePlatformArch,
log,
yarnLocator,
spawnRebuild,
globalLibvipsVersion,
pkgConfigPath,
useGlobalLibvips
};
return libvips;
}
/*!
Copyright 2013 Lovell Fuller and others.
SPDX-License-Identifier: Apache-2.0
*/
var hasRequiredSharp;
function requireSharp () {
if (hasRequiredSharp) return sharp.exports;
hasRequiredSharp = 1;
// Inspects the runtime environment and exports the relevant sharp.node binary
const { familySync, versionSync } = requireDetectLibc();
const { runtimePlatformArch, isUnsupportedNodeRuntime, prebuiltPlatforms, minimumLibvipsVersion } = requireLibvips();
const runtimePlatform = runtimePlatformArch();
const paths = [
`../src/build/Release/sharp-${runtimePlatform}.node`,
'../src/build/Release/sharp-wasm32.node',
`@img/sharp-${runtimePlatform}/sharp.node`,
'@img/sharp-wasm32/sharp.node'
];
/* node:coverage disable */
let path, sharp$1;
const errors = [];
for (path of paths) {
try {
sharp$1 = commonjsRequire(path);
break;
} catch (err) {
errors.push(err);
}
}
if (sharp$1 && path.startsWith('@img/sharp-linux-x64') && !sharp$1._isUsingX64V2()) {
const err = new Error('Prebuilt binaries for linux-x64 require v2 microarchitecture');
err.code = 'Unsupported CPU';
errors.push(err);
sharp$1 = null;
}
if (sharp$1) {
sharp.exports = sharp$1;
} else {
const [isLinux, isMacOs, isWindows] = ['linux', 'darwin', 'win32'].map(os => runtimePlatform.startsWith(os));
const help = [`Could not load the "sharp" module using the ${runtimePlatform} runtime`];
errors.forEach(err => {
if (err.code !== 'MODULE_NOT_FOUND') {
help.push(`${err.code}: ${err.message}`);
}
});
const messages = errors.map(err => err.message).join(' ');
help.push('Possible solutions:');
// Common error messages
if (isUnsupportedNodeRuntime()) {
const { found, expected } = isUnsupportedNodeRuntime();
help.push(
'- Please upgrade Node.js:',
` Found ${found}`,
` Requires ${expected}`
);
} else if (prebuiltPlatforms.includes(runtimePlatform)) {
const [os, cpu] = runtimePlatform.split('-');
const libc = os.endsWith('musl') ? ' --libc=musl' : '';
help.push(
'- Ensure optional dependencies can be installed:',
' npm install --include=optional sharp',
'- Ensure your package manager supports multi-platform installation:',
' See https://sharp.pixelplumbing.com/install#cross-platform',
'- Add platform-specific dependencies:',
` npm install --os=${os.replace('musl', '')}${libc} --cpu=${cpu} sharp`
);
} else {
help.push(
`- Manually install libvips >= ${minimumLibvipsVersion}`,
'- Add experimental WebAssembly-based dependencies:',
' npm install --cpu=wasm32 sharp',
' npm install @img/sharp-wasm32'
);
}
if (isLinux && /(symbol not found|CXXABI_)/i.test(messages)) {
try {
const { config } = commonjsRequire(`@img/sharp-libvips-${runtimePlatform}/package`);
const libcFound = `${familySync()} ${versionSync()}`;
const libcRequires = `${config.musl ? 'musl' : 'glibc'} ${config.musl || config.glibc}`;
help.push(
'- Update your OS:',
` Found ${libcFound}`,
` Requires ${libcRequires}`
);
} catch (_errEngines) {}
}
if (isLinux && /\/snap\/core[0-9]{2}/.test(messages)) {
help.push(
'- Remove the Node.js Snap, which does not support native modules',
' snap remove node'
);
}
if (isMacOs && /Incompatible library version/.test(messages)) {
help.push(
'- Update Homebrew:',
' brew update && brew upgrade vips'
);
}
if (errors.some(err => err.code === 'ERR_DLOPEN_DISABLED')) {
help.push('- Run Node.js without using the --no-addons flag');
}
// Link to installation docs
if (isWindows && /The specified procedure could not be found/.test(messages)) {
help.push(
'- Using the canvas package on Windows?',
' See https://sharp.pixelplumbing.com/install#canvas-and-windows',
'- Check for outdated versions of sharp in the dependency tree:',
' npm ls sharp'
);
}
help.push(
'- Consult the installation documentation:',
' See https://sharp.pixelplumbing.com/install'
);
throw new Error(help.join('\n'));
}
return sharp.exports;
}
/*!
Copyright 2013 Lovell Fuller and others.
SPDX-License-Identifier: Apache-2.0
*/
var constructor;
var hasRequiredConstructor;
function requireConstructor () {
if (hasRequiredConstructor) return constructor;
hasRequiredConstructor = 1;
const util = require$$0$2;
const stream = require$$1$1;
const is = requireIs();
requireSharp();
// Use NODE_DEBUG=sharp to enable libvips warnings
const debuglog = util.debuglog('sharp');
const queueListener = (queueLength) => {
Sharp.queue.emit('change', queueLength);
};
/**
* Constructor factory to create an instance of `sharp`, to which further methods are chained.
*
* JPEG, PNG, WebP, GIF, AVIF or TIFF format image data can be streamed out from this object.
* When using Stream based output, derived attributes are available from the `info` event.
*
* Non-critical problems encountered during processing are emitted as `warning` events.
*
* Implements the [stream.Duplex](http://nodejs.org/api/stream.html#stream_class_stream_duplex) class.
*
* When loading more than one page/frame of an animated image,
* these are combined as a vertically-stacked "toilet roll" image
* where the overall height is the `pageHeight` multiplied by the number of `pages`.
*
* @constructs Sharp
*
* @emits Sharp#info
* @emits Sharp#warning
*
* @example
* sharp('input.jpg')
* .resize(300, 200)
* .toFile('output.jpg', function(err) {
* // output.jpg is a 300 pixels wide and 200 pixels high image
* // containing a scaled and cropped version of input.jpg
* });
*
* @example
* // Read image data from remote URL,
* // resize to 300 pixels wide,
* // emit an 'info' event with calculated dimensions
* // and finally write image data to writableStream
* const { body } = fetch('https://...');
* const readableStream = Readable.fromWeb(body);
* const transformer = sharp()
* .resize(300)
* .on('info', ({ height }) => {
* console.log(`Image height is ${height}`);
* });
* readableStream.pipe(transformer).pipe(writableStream);
*
* @example
* // Create a blank 300x200 PNG image of semi-translucent red pixels
* sharp({
* create: {
* width: 300,
* height: 200,
* channels: 4,
* background: { r: 255, g: 0, b: 0, alpha: 0.5 }
* }
* })
* .png()
* .toBuffer()
* .then( ... );
*
* @example
* // Convert an animated GIF to an animated WebP
* await sharp('in.gif', { animated: true }).toFile('out.webp');
*
* @example
* // Read a raw array of pixels and save it to a png
* const input = Uint8Array.from([255, 255, 255, 0, 0, 0]); // or Uint8ClampedArray
* const image = sharp(input, {
* // because the input does not contain its dimensions or how many channels it has
* // we need to specify it in the constructor options
* raw: {
* width: 2,
* height: 1,
* channels: 3
* }
* });
* await image.toFile('my-two-pixels.png');
*
* @example
* // Generate RGB Gaussian noise
* await sharp({
* create: {
* width: 300,
* height: 200,
* channels: 3,
* noise: {
* type: 'gaussian',
* mean: 128,
* sigma: 30
* }
* }
* }).toFile('noise.png');
*
* @example
* // Generate an image from text
* await sharp({
* text: {
* text: 'Hello, world!',
* width: 400, // max width
* height: 300 // max height
* }
* }).toFile('text_bw.png');
*
* @example
* // Generate an rgba image from text using pango markup and font
* await sharp({
* text: {
* text: '<span foreground="red">Red!</span><span background="cyan">blue</span>',
* font: 'sans',
* rgba: true,
* dpi: 300
* }
* }).toFile('text_rgba.png');
*
* @example
* // Join four input images as a 2x2 grid with a 4 pixel gutter
* const data = await sharp(
* [image1, image2, image3, image4],
* { join: { across: 2, shim: 4 } }
* ).toBuffer();
*
* @example
* // Generate a two-frame animated image from emoji
* const images = ['😀', '😛'].map(text => ({
* text: { text, width: 64, height: 64, channels: 4, rgba: true }
* }));
* await sharp(images, { join: { animated: true } }).toFile('out.gif');
*
* @param {(Buffer|ArrayBuffer|Uint8Array|Uint8ClampedArray|Int8Array|Uint16Array|Int16Array|Uint32Array|Int32Array|Float32Array|Float64Array|string|Array)} [input] - if present, can be
* a Buffer / ArrayBuffer / Uint8Array / Uint8ClampedArray containing JPEG, PNG, WebP, AVIF, GIF, SVG or TIFF image data, or
* a TypedArray containing raw pixel image data, or
* a String containing the filesystem path to an JPEG, PNG, WebP, AVIF, GIF, SVG or TIFF image file.
* An array of inputs can be provided, and these will be joined together.
* JPEG, PNG, WebP, AVIF, GIF, SVG, TIFF or raw pixel image data can be streamed into the object when not present.
* @param {Object} [options] - if present, is an Object with optional attributes.
* @param {string} [options.failOn='warning'] - When to abort processing of invalid pixel data, one of (in order of sensitivity, least to most): 'none', 'truncated', 'error', 'warning'. Higher levels imply lower levels. Invalid metadata will always abort.
* @param {number|boolean} [options.limitInputPixels=268402689] - Do not process input images where the number of pixels
* (width x height) exceeds this limit. Assumes image dimensions contained in the input metadata can be trusted.
* An integral Number of pixels, zero or false to remove limit, true to use default limit of 268402689 (0x3FFF x 0x3FFF).
* @param {boolean} [options.unlimited=false] - Set this to `true` to remove safety features that help prevent memory exhaustion (JPEG, PNG, SVG, HEIF).
* @param {boolean} [options.autoOrient=false] - Set this to `true` to rotate/flip the image to match EXIF `Orientation`, if any.
* @param {boolean} [options.sequentialRead=true] - Set this to `false` to use random access rather than sequential read. Some operations will do this automatically.
* @param {number} [options.density=72] - number representing the DPI for vector images in the range 1 to 100000.
* @param {number} [options.ignoreIcc=false] - should the embedded ICC profile, if any, be ignored.
* @param {number} [options.pages=1] - Number of pages to extract for multi-page input (GIF, WebP, TIFF), use -1 for all pages.
* @param {number} [options.page=0] - Page number to start extracting from for multi-page input (GIF, WebP, TIFF), zero based.
* @param {boolean} [options.animated=false] - Set to `true` to read all frames/pages of an animated image (GIF, WebP, TIFF), equivalent of setting `pages` to `-1`.
* @param {Object} [options.raw] - describes raw pixel input image data. See `raw()` for pixel ordering.
* @param {number} [options.raw.width] - integral number of pixels wide.
* @param {number} [options.raw.height] - integral number of pixels high.
* @param {number} [options.raw.channels] - integral number of channels, between 1 and 4.
* @param {boolean} [options.raw.premultiplied] - specifies that the raw input has already been premultiplied, set to `true`
* to avoid sharp premultiplying the image. (optional, default `false`)
* @param {number} [options.raw.pageHeight] - The pixel height of each page/frame for animated images, must be an integral factor of `raw.height`.
* @param {Object} [options.create] - describes a new image to be created.
* @param {number} [options.create.width] - integral number of pixels wide.
* @param {number} [options.create.height] - integral number of pixels high.
* @param {number} [options.create.channels] - integral number of channels, either 3 (RGB) or 4 (RGBA).
* @param {string|Object} [options.create.background] - parsed by the [color](https://www.npmjs.org/package/color) module to extract values for red, green, blue and alpha.
* @param {number} [options.create.pageHeight] - The pixel height of each page/frame for animated images, must be an integral factor of `create.height`.
* @param {Object} [options.create.noise] - describes a noise to be created.
* @param {string} [options.create.noise.type] - type of generated noise, currently only `gaussian` is supported.
* @param {number} [options.create.noise.mean=128] - Mean value of pixels in the generated noise.
* @param {number} [options.create.noise.sigma=30] - Standard deviation of pixel values in the generated noise.
* @param {Object} [options.text] - describes a new text image to be created.
* @param {string} [options.text.text] - text to render as a UTF-8 string. It can contain Pango markup, for example `<i>Le</i>Monde`.
* @param {string} [options.text.font] - font name to render with.
* @param {string} [options.text.fontfile] - absolute filesystem path to a font file that can be used by `font`.
* @param {number} [options.text.width=0] - Integral number of pixels to word-wrap at. Lines of text wider than this will be broken at word boundaries.
* @param {number} [options.text.height=0] - Maximum integral number of pixels high. When defined, `dpi` will be ignored and the text will automatically fit the pixel resolution defined by `width` and `height`. Will be ignored if `width` is not specified or set to 0.
* @param {string} [options.text.align='left'] - Alignment style for multi-line text (`'left'`, `'centre'`, `'center'`, `'right'`).
* @param {boolean} [options.text.justify=false] - set this to true to apply justification to the text.
* @param {number} [options.text.dpi=72] - the resolution (size) at which to render the text. Does not take effect if `height` is specified.
* @param {boolean} [options.text.rgba=false] - set this to true to enable RGBA output. This is useful for colour emoji rendering, or support for pango markup features like `<span foreground="red">Red!</span>`.
* @param {number} [options.text.spacing=0] - text line height in points. Will use the font line height if none is specified.
* @param {string} [options.text.wrap='word'] - word wrapping style when width is provided, one of: 'word', 'char', 'word-char' (prefer word, fallback to char) or 'none'.
* @param {Object} [options.join] - describes how an array of input images should be joined.
* @param {number} [options.join.across=1] - number of images to join horizontally.
* @param {boolean} [options.join.animated=false] - set this to `true` to join the images as an animated image.
* @param {number} [options.join.shim=0] - number of pixels to insert between joined images.
* @param {string|Object} [options.join.background] - parsed by the [color](https://www.npmjs.org/package/color) module to extract values for red, green, blue and alpha.
* @param {string} [options.join.halign='left'] - horizontal alignment style for images joined horizontally (`'left'`, `'centre'`, `'center'`, `'right'`).
* @param {string} [options.join.valign='top'] - vertical alignment style for images joined vertically (`'top'`, `'centre'`, `'center'`, `'bottom'`).
* @param {Object} [options.tiff] - Describes TIFF specific options.
* @param {number} [options.tiff.subifd=-1] - Sub Image File Directory to extract for OME-TIFF, defaults to main image.
* @param {Object} [options.svg] - Describes SVG specific options.
* @param {string} [options.svg.stylesheet] - Custom CSS for SVG input, applied with a User Origin during the CSS cascade.
* @param {boolean} [options.svg.highBitdepth=false] - Set to `true` to render SVG input at 32-bits per channel (128-bit) instead of 8-bits per channel (32-bit) RGBA.
* @param {Object} [options.pdf] - Describes PDF specific options. Requires the use of a globally-installed libvips compiled with support for PDFium, Poppler, ImageMagick or GraphicsMagick.
* @param {string|Object} [options.pdf.background] - Background colour to use when PDF is partially transparent. Parsed by the [color](https://www.npmjs.org/package/color) module to extract values for red, green, blue and alpha.
* @param {Object} [options.openSlide] - Describes OpenSlide specific options. Requires the use of a globally-installed libvips compiled with support for OpenSlide.
* @param {number} [options.openSlide.level=0] - Level to extract from a multi-level input, zero based.
* @param {Object} [options.jp2] - Describes JPEG 2000 specific options. Requires the use of a globally-installed libvips compiled with support for OpenJPEG.
* @param {boolean} [options.jp2.oneshot=false] - Set to `true` to decode tiled JPEG 2000 images in a single operation, improving compatibility.
* @returns {Sharp}
* @throws {Error} Invalid parameters
*/
const Sharp = function (input, options) {
// biome-ignore lint/complexity/noArguments: constructor factory
if (arguments.length === 1 && !is.defined(input)) {
throw new Error('Invalid input');
}
if (!(this instanceof Sharp)) {
return new Sharp(input, options);
}
stream.Duplex.call(this);
this.options = {
// resize options
topOffsetPre: -1,
leftOffsetPre: -1,
widthPre: -1,
heightPre: -1,
topOffsetPost: -1,
leftOffsetPost: -1,
widthPost: -1,
heightPost: -1,
width: -1,
height: -1,
canvas: 'crop',
position: 0,
resizeBackground: [0, 0, 0, 255],
angle: 0,
rotationAngle: 0,
rotationBackground: [0, 0, 0, 255],
rotateBefore: false,
orientBefore: false,
flip: false,
flop: false,
extendTop: 0,
extendBottom: 0,
extendLeft: 0,
extendRight: 0,
extendBackground: [0, 0, 0, 255],
extendWith: 'background',
withoutEnlargement: false,
withoutReduction: false,
affineMatrix: [],
affineBackground: [0, 0, 0, 255],
affineIdx: 0,
affineIdy: 0,
affineOdx: 0,
affineOdy: 0,
affineInterpolator: this.constructor.interpolators.bilinear,
kernel: 'lanczos3',
fastShrinkOnLoad: true,
// operations
tint: [-1, 0, 0, 0],
flatten: false,
flattenBackground: [0, 0, 0],
unflatten: false,
negate: false,
negateAlpha: true,
medianSize: 0,
blurSigma: 0,
precision: 'integer',
minAmpl: 0.2,
sharpenSigma: 0,
sharpenM1: 1,
sharpenM2: 2,
sharpenX1: 2,
sharpenY2: 10,
sharpenY3: 20,
threshold: 0,
thresholdGrayscale: true,
trimBackground: [],
trimThreshold: -1,
trimLineArt: false,
dilateWidth: 0,
erodeWidth: 0,
gamma: 0,
gammaOut: 0,
greyscale: false,
normalise: false,
normaliseLower: 1,
normaliseUpper: 99,
claheWidth: 0,
claheHeight: 0,
claheMaxSlope: 3,
brightness: 1,
saturation: 1,
hue: 0,
lightness: 0,
booleanBufferIn: null,
booleanFileIn: '',
joinChannelIn: [],
extractChannel: -1,
removeAlpha: false,
ensureAlpha: -1,
colourspace: 'srgb',
colourspacePipeline: 'last',
composite: [],
// output
fileOut: '',
formatOut: 'input',
streamOut: false,
keepMetadata: 0,
withMetadataOrientation: -1,
withMetadataDensity: 0,
withIccProfile: '',
withExif: {},
withExifMerge: true,
withXmp: '',
resolveWithObject: false,
loop: -1,
delay: [],
// output format
jpegQuality: 80,
jpegProgressive: false,
jpegChromaSubsampling: '4:2:0',
jpegTrellisQuantisation: false,
jpegOvershootDeringing: false,
jpegOptimiseScans: false,
jpegOptimiseCoding: true,
jpegQuantisationTable: 0,
pngProgressive: false,
pngCompressionLevel: 6,
pngAdaptiveFiltering: false,
pngPalette: false,
pngQuality: 100,
pngEffort: 7,
pngBitdepth: 8,
pngDither: 1,
jp2Quality: 80,
jp2TileHeight: 512,
jp2TileWidth: 512,
jp2Lossless: false,
jp2ChromaSubsampling: '4:4:4',
webpQuality: 80,
webpAlphaQuality: 100,
webpLossless: false,
webpNearLossless: false,
webpSmartSubsample: false,
webpSmartDeblock: false,
webpPreset: 'default',
webpEffort: 4,
webpMinSize: false,
webpMixed: false,
gifBitdepth: 8,
gifEffort: 7,
gifDither: 1,
gifInterFrameMaxError: 0,
gifInterPaletteMaxError: 3,
gifKeepDuplicateFrames: false,
gifReuse: true,
gifProgressive: false,
tiffQuality: 80,
tiffCompression: 'jpeg',
tiffBigtiff: false,
tiffPredictor: 'horizontal',
tiffPyramid: false,
tiffMiniswhite: false,
tiffBitdepth: 8,
tiffTile: false,
tiffTileHeight: 256,
tiffTileWidth: 256,
tiffXres: 1.0,
tiffYres: 1.0,
tiffResolutionUnit: 'inch',
heifQuality: 50,
heifLossless: false,
heifCompression: 'av1',
heifEffort: 4,
heifChromaSubsampling: '4:4:4',
heifBitdepth: 8,
jxlDistance: 1,
jxlDecodingTier: 0,
jxlEffort: 7,
jxlLossless: false,
rawDepth: 'uchar',
tileSize: 256,
tileOverlap: 0,
tileContainer: 'fs',
tileLayout: 'dz',
tileFormat: 'last',
tileDepth: 'last',
tileAngle: 0,
tileSkipBlanks: -1,
tileBackground: [255, 255, 255, 255],
tileCentre: false,
tileId: 'https://example.com/iiif',
tileBasename: '',
timeoutSeconds: 0,
linearA: [],
linearB: [],
pdfBackground: [255, 255, 255, 255],
// Function to notify of libvips warnings
debuglog: warning => {
this.emit('warning', warning);
debuglog(warning);
},
// Function to notify of queue length changes
queueListener
};
this.options.input = this._createInputDescriptor(input, options, { allowStream: true });
return this;
};
Object.setPrototypeOf(Sharp.prototype, stream.Duplex.prototype);
Object.setPrototypeOf(Sharp, stream.Duplex);
/**
* Take a "snapshot" of the Sharp instance, returning a new instance.
* Cloned instances inherit the input of their parent instance.
* This allows multiple output Streams and therefore multiple processing pipelines to share a single input Stream.
*
* @example
* const pipeline = sharp().rotate();
* pipeline.clone().resize(800, 600).pipe(firstWritableStream);
* pipeline.clone().extract({ left: 20, top: 20, width: 100, height: 100 }).pipe(secondWritableStream);
* readableStream.pipe(pipeline);
* // firstWritableStream receives auto-rotated, resized readableStream
* // secondWritableStream receives auto-rotated, extracted region of readableStream
*
* @example
* // Create a pipeline that will download an image, resize it and format it to different files
* // Using Promises to know when the pipeline is complete
* const fs = require("fs");
* const got = require("got");
* const sharpStream = sharp({ failOn: 'none' });
*
* const promises = [];
*
* promises.push(
* sharpStream
* .clone()
* .jpeg({ quality: 100 })
* .toFile("originalFile.jpg")
* );
*
* promises.push(
* sharpStream
* .clone()
* .resize({ width: 500 })
* .jpeg({ quality: 80 })
* .toFile("optimized-500.jpg")
* );
*
* promises.push(
* sharpStream
* .clone()
* .resize({ width: 500 })
* .webp({ quality: 80 })
* .toFile("optimized-500.webp")
* );
*
* // https://github.com/sindresorhus/got/blob/main/documentation/3-streams.md
* got.stream("https://www.example.com/some-file.jpg").pipe(sharpStream);
*
* Promise.all(promises)
* .then(res => { console.log("Done!", res); })
* .catch(err => {
* console.error("Error processing files, let's clean it up", err);
* try {
* fs.unlinkSync("originalFile.jpg");
* fs.unlinkSync("optimized-500.jpg");
* fs.unlinkSync("optimized-500.webp");
* } catch (e) {}
* });
*
* @returns {Sharp}
*/
function clone () {
// Clone existing options
const clone = this.constructor.call();
const { debuglog, queueListener, ...options } = this.options;
clone.options = structuredClone(options);
clone.options.debuglog = debuglog;
clone.options.queueListener = queueListener;
// Pass 'finish' event to clone for Stream-based input
if (this._isStreamInput()) {
this.on('finish', () => {
// Clone inherits input data
this._flattenBufferIn();
clone.options.input.buffer = this.options.input.buffer;
clone.emit('finish');
});
}
return clone;
}
Object.assign(Sharp.prototype, { clone });
/**
* Export constructor.
* @module Sharp
* @private
*/
constructor = Sharp;
return constructor;
}
/*!
Copyright 2013 Lovell Fuller and others.
SPDX-License-Identifier: Apache-2.0
*/
var input;
var hasRequiredInput;
function requireInput () {
if (hasRequiredInput) return input;
hasRequiredInput = 1;
const is = requireIs();
const sharp = requireSharp();
/**
* Justification alignment
* @member
* @private
*/
const align = {
left: 'low',
top: 'low',
low: 'low',
center: 'centre',
centre: 'centre',
right: 'high',
bottom: 'high',
high: 'high'
};
const inputStreamParameters = [
// Limits and error handling
'failOn', 'limitInputPixels', 'unlimited',
// Format-generic
'animated', 'autoOrient', 'density', 'ignoreIcc', 'page', 'pages', 'sequentialRead',
// Format-specific
'jp2', 'openSlide', 'pdf', 'raw', 'svg', 'tiff',
// Deprecated
'failOnError', 'openSlideLevel', 'pdfBackground', 'tiffSubifd'
];
/**
* Extract input options, if any, from an object.
* @private
*/
function _inputOptionsFromObject (obj) {
const params = inputStreamParameters
.filter(p => is.defined(obj[p]))
.map(p => ([p, obj[p]]));
return params.length
? Object.fromEntries(params)
: undefined;
}
/**
* Create Object containing input and input-related options.
* @private
*/
function _createInputDescriptor (input, inputOptions, containerOptions) {
const inputDescriptor = {
autoOrient: false,
failOn: 'warning',
limitInputPixels: 0x3FFF ** 2,
ignoreIcc: false,
unlimited: false,
sequentialRead: true
};
if (is.string(input)) {
// filesystem
inputDescriptor.file = input;
} else if (is.buffer(input)) {
// Buffer
if (input.length === 0) {
throw Error('Input Buffer is empty');
}
inputDescriptor.buffer = input;
} else if (is.arrayBuffer(input)) {
if (input.byteLength === 0) {
throw Error('Input bit Array is empty');
}
inputDescriptor.buffer = Buffer.from(input, 0, input.byteLength);
} else if (is.typedArray(input)) {
if (input.length === 0) {
throw Error('Input Bit Array is empty');
}
inputDescriptor.buffer = Buffer.from(input.buffer, input.byteOffset, input.byteLength);
} else if (is.plainObject(input) && !is.defined(inputOptions)) {
// Plain Object descriptor, e.g. create
inputOptions = input;
if (_inputOptionsFromObject(inputOptions)) {
// Stream with options
inputDescriptor.buffer = [];
}
} else if (!is.defined(input) && !is.defined(inputOptions) && is.object(containerOptions) && containerOptions.allowStream) {
// Stream without options
inputDescriptor.buffer = [];
} else if (Array.isArray(input)) {
if (input.length > 1) {
// Join images together
if (!this.options.joining) {
this.options.joining = true;
this.options.join = input.map(i => this._createInputDescriptor(i));
} else {
throw new Error('Recursive join is unsupported');
}
} else {
throw new Error('Expected at least two images to join');
}
} else {
throw new Error(`Unsupported input '${input}' of type ${typeof input}${
is.defined(inputOptions) ? ` when also providing options of type ${typeof inputOptions}` : ''
}`);
}
if (is.object(inputOptions)) {
// Deprecated: failOnError
if (is.defined(inputOptions.failOnError)) {
if (is.bool(inputOptions.failOnError)) {
inputDescriptor.failOn = inputOptions.failOnError ? 'warning' : 'none';
} else {
throw is.invalidParameterError('failOnError', 'boolean', inputOptions.failOnError);
}
}
// failOn
if (is.defined(inputOptions.failOn)) {
if (is.string(inputOptions.failOn) && is.inArray(inputOptions.failOn, ['none', 'truncated', 'error', 'warning'])) {
inputDescriptor.failOn = inputOptions.failOn;
} else {
throw is.invalidParameterError('failOn', 'one of: none, truncated, error, warning', inputOptions.failOn);
}
}
// autoOrient
if (is.defined(inputOptions.autoOrient)) {
if (is.bool(inputOptions.autoOrient)) {
inputDescriptor.autoOrient = inputOptions.autoOrient;
} else {
throw is.invalidParameterError('autoOrient', 'boolean', inputOptions.autoOrient);
}
}
// Density
if (is.defined(inputOptions.density)) {
if (is.inRange(inputOptions.density, 1, 100000)) {
inputDescriptor.density = inputOptions.density;
} else {
throw is.invalidParameterError('density', 'number between 1 and 100000', inputOptions.density);
}
}
// Ignore embeddded ICC profile
if (is.defined(inputOptions.ignoreIcc)) {
if (is.bool(inputOptions.ignoreIcc)) {
inputDescriptor.ignoreIcc = inputOptions.ignoreIcc;
} else {
throw is.invalidParameterError('ignoreIcc', 'boolean', inputOptions.ignoreIcc);
}
}
// limitInputPixels
if (is.defined(inputOptions.limitInputPixels)) {
if (is.bool(inputOptions.limitInputPixels)) {
inputDescriptor.limitInputPixels = inputOptions.limitInputPixels
? 0x3FFF ** 2
: 0;
} else if (is.integer(inputOptions.limitInputPixels) && is.inRange(inputOptions.limitInputPixels, 0, Number.MAX_SAFE_INTEGER)) {
inputDescriptor.limitInputPixels = inputOptions.limitInputPixels;
} else {
throw is.invalidParameterError('limitInputPixels', 'positive integer', inputOptions.limitInputPixels);
}
}
// unlimited
if (is.defined(inputOptions.unlimited)) {
if (is.bool(inputOptions.unlimited)) {
inputDescriptor.unlimited = inputOptions.unlimited;
} else {
throw is.invalidParameterError('unlimited', 'boolean', inputOptions.unlimited);
}
}
// sequentialRead
if (is.defined(inputOptions.sequentialRead)) {
if (is.bool(inputOptions.sequentialRead)) {
inputDescriptor.sequentialRead = inputOptions.sequentialRead;
} else {
throw is.invalidParameterError('sequentialRead', 'boolean', inputOptions.sequentialRead);
}
}
// Raw pixel input
if (is.defined(inputOptions.raw)) {
if (
is.object(inputOptions.raw) &&
is.integer(inputOptions.raw.width) && inputOptions.raw.width > 0 &&
is.integer(inputOptions.raw.height) && inputOptions.raw.height > 0 &&
is.integer(inputOptions.raw.channels) && is.inRange(inputOptions.raw.channels, 1, 4)
) {
inputDescriptor.rawWidth = inputOptions.raw.width;
inputDescriptor.rawHeight = inputOptions.raw.height;
inputDescriptor.rawChannels = inputOptions.raw.channels;
switch (input.constructor) {
case Uint8Array:
case Uint8ClampedArray:
inputDescriptor.rawDepth = 'uchar';
break;
case Int8Array:
inputDescriptor.rawDepth = 'char';
break;
case Uint16Array:
inputDescriptor.rawDepth = 'ushort';
break;
case Int16Array:
inputDescriptor.rawDepth = 'short';
break;
case Uint32Array:
inputDescriptor.rawDepth = 'uint';
break;
case Int32Array:
inputDescriptor.rawDepth = 'int';
break;
case Float32Array:
inputDescriptor.rawDepth = 'float';
break;
case Float64Array:
inputDescriptor.rawDepth = 'double';
break;
default:
inputDescriptor.rawDepth = 'uchar';
break;
}
} else {
throw new Error('Expected width, height and channels for raw pixel input');
}
inputDescriptor.rawPremultiplied = false;
if (is.defined(inputOptions.raw.premultiplied)) {
if (is.bool(inputOptions.raw.premultiplied)) {
inputDescriptor.rawPremultiplied = inputOptions.raw.premultiplied;
} else {
throw is.invalidParameterError('raw.premultiplied', 'boolean', inputOptions.raw.premultiplied);
}
}
inputDescriptor.rawPageHeight = 0;
if (is.defined(inputOptions.raw.pageHeight)) {
if (is.integer(inputOptions.raw.pageHeight) && inputOptions.raw.pageHeight > 0 && inputOptions.raw.pageHeight <= inputOptions.raw.height) {
if (inputOptions.raw.height % inputOptions.raw.pageHeight !== 0) {
throw new Error(`Expected raw.height ${inputOptions.raw.height} to be a multiple of raw.pageHeight ${inputOptions.raw.pageHeight}`);
}
inputDescriptor.rawPageHeight = inputOptions.raw.pageHeight;
} else {
throw is.invalidParameterError('raw.pageHeight', 'positive integer', inputOptions.raw.pageHeight);
}
}
}
// Multi-page input (GIF, TIFF, PDF)
if (is.defined(inputOptions.animated)) {
if (is.bool(inputOptions.animated)) {
inputDescriptor.pages = inputOptions.animated ? -1 : 1;
} else {
throw is.invalidParameterError('animated', 'boolean', inputOptions.animated);
}
}
if (is.defined(inputOptions.pages)) {
if (is.integer(inputOptions.pages) && is.inRange(inputOptions.pages, -1, 100000)) {
inputDescriptor.pages = inputOptions.pages;
} else {
throw is.invalidParameterError('pages', 'integer between -1 and 100000', inputOptions.pages);
}
}
if (is.defined(inputOptions.page)) {
if (is.integer(inputOptions.page) && is.inRange(inputOptions.page, 0, 100000)) {
inputDescriptor.page = inputOptions.page;
} else {
throw is.invalidParameterError('page', 'integer between 0 and 100000', inputOptions.page);
}
}
// OpenSlide specific options
if (is.object(inputOptions.openSlide) && is.defined(inputOptions.openSlide.level)) {
if (is.integer(inputOptions.openSlide.level) && is.inRange(inputOptions.openSlide.level, 0, 256)) {
inputDescriptor.openSlideLevel = inputOptions.openSlide.level;
} else {
throw is.invalidParameterError('openSlide.level', 'integer between 0 and 256', inputOptions.openSlide.level);
}
} else if (is.defined(inputOptions.level)) {
// Deprecated
if (is.integer(inputOptions.level) && is.inRange(inputOptions.level, 0, 256)) {
inputDescriptor.openSlideLevel = inputOptions.level;
} else {
throw is.invalidParameterError('level', 'integer between 0 and 256', inputOptions.level);
}
}
// TIFF specific options
if (is.object(inputOptions.tiff) && is.defined(inputOptions.tiff.subifd)) {
if (is.integer(inputOptions.tiff.subifd) && is.inRange(inputOptions.tiff.subifd, -1, 100000)) {
inputDescriptor.tiffSubifd = inputOptions.tiff.subifd;
} else {
throw is.invalidParameterError('tiff.subifd', 'integer between -1 and 100000', inputOptions.tiff.subifd);
}
} else if (is.defined(inputOptions.subifd)) {
// Deprecated
if (is.integer(inputOptions.subifd) && is.inRange(inputOptions.subifd, -1, 100000)) {
inputDescriptor.tiffSubifd = inputOptions.subifd;
} else {
throw is.invalidParameterError('subifd', 'integer between -1 and 100000', inputOptions.subifd);
}
}
// SVG specific options
if (is.object(inputOptions.svg)) {
if (is.defined(inputOptions.svg.stylesheet)) {
if (is.string(inputOptions.svg.stylesheet)) {
inputDescriptor.svgStylesheet = inputOptions.svg.stylesheet;
} else {
throw is.invalidParameterError('svg.stylesheet', 'string', inputOptions.svg.stylesheet);
}
}
if (is.defined(inputOptions.svg.highBitdepth)) {
if (is.bool(inputOptions.svg.highBitdepth)) {
inputDescriptor.svgHighBitdepth = inputOptions.svg.highBitdepth;
} else {
throw is.invalidParameterError('svg.highBitdepth', 'boolean', inputOptions.svg.highBitdepth);
}
}
}
// PDF specific options
if (is.object(inputOptions.pdf) && is.defined(inputOptions.pdf.background)) {
inputDescriptor.pdfBackground = this._getBackgroundColourOption(inputOptions.pdf.background);
} else if (is.defined(inputOptions.pdfBackground)) {
// Deprecated
inputDescriptor.pdfBackground = this._getBackgroundColourOption(inputOptions.pdfBackground);
}
// JPEG 2000 specific options
if (is.object(inputOptions.jp2) && is.defined(inputOptions.jp2.oneshot)) {
if (is.bool(inputOptions.jp2.oneshot)) {
inputDescriptor.jp2Oneshot = inputOptions.jp2.oneshot;
} else {
throw is.invalidParameterError('jp2.oneshot', 'boolean', inputOptions.jp2.oneshot);
}
}
// Create new image
if (is.defined(inputOptions.create)) {
if (
is.object(inputOptions.create) &&
is.integer(inputOptions.create.width) && inputOptions.create.width > 0 &&
is.integer(inputOptions.create.height) && inputOptions.create.height > 0 &&
is.integer(inputOptions.create.channels)
) {
inputDescriptor.createWidth = inputOptions.create.width;
inputDescriptor.createHeight = inputOptions.create.height;
inputDescriptor.createChannels = inputOptions.create.channels;
inputDescriptor.createPageHeight = 0;
if (is.defined(inputOptions.create.pageHeight)) {
if (is.integer(inputOptions.create.pageHeight) && inputOptions.create.pageHeight > 0 && inputOptions.create.pageHeight <= inputOptions.create.height) {
if (inputOptions.create.height % inputOptions.create.pageHeight !== 0) {
throw new Error(`Expected create.height ${inputOptions.create.height} to be a multiple of create.pageHeight ${inputOptions.create.pageHeight}`);
}
inputDescriptor.createPageHeight = inputOptions.create.pageHeight;
} else {
throw is.invalidParameterError('create.pageHeight', 'positive integer', inputOptions.create.pageHeight);
}
}
// Noise
if (is.defined(inputOptions.create.noise)) {
if (!is.object(inputOptions.create.noise)) {
throw new Error('Expected noise to be an object');
}
if (inputOptions.create.noise.type !== 'gaussian') {
throw new Error('Only gaussian noise is supported at the moment');
}
inputDescriptor.createNoiseType = inputOptions.create.noise.type;
if (!is.inRange(inputOptions.create.channels, 1, 4)) {
throw is.invalidParameterError('create.channels', 'number between 1 and 4', inputOptions.create.channels);
}
inputDescriptor.createNoiseMean = 128;
if (is.defined(inputOptions.create.noise.mean)) {
if (is.number(inputOptions.create.noise.mean) && is.inRange(inputOptions.create.noise.mean, 0, 10000)) {
inputDescriptor.createNoiseMean = inputOptions.create.noise.mean;
} else {
throw is.invalidParameterError('create.noise.mean', 'number between 0 and 10000', inputOptions.create.noise.mean);
}
}
inputDescriptor.createNoiseSigma = 30;
if (is.defined(inputOptions.create.noise.sigma)) {
if (is.number(inputOptions.create.noise.sigma) && is.inRange(inputOptions.create.noise.sigma, 0, 10000)) {
inputDescriptor.createNoiseSigma = inputOptions.create.noise.sigma;
} else {
throw is.invalidParameterError('create.noise.sigma', 'number between 0 and 10000', inputOptions.create.noise.sigma);
}
}
} else if (is.defined(inputOptions.create.background)) {
if (!is.inRange(inputOptions.create.channels, 3, 4)) {
throw is.invalidParameterError('create.channels', 'number between 3 and 4', inputOptions.create.channels);
}
inputDescriptor.createBackground = this._getBackgroundColourOption(inputOptions.create.background);
} else {
throw new Error('Expected valid noise or background to create a new input image');
}
delete inputDescriptor.buffer;
} else {
throw new Error('Expected valid width, height and channels to create a new input image');
}
}
// Create a new image with text
if (is.defined(inputOptions.text)) {
if (is.object(inputOptions.text) && is.string(inputOptions.text.text)) {
inputDescriptor.textValue = inputOptions.text.text;
if (is.defined(inputOptions.text.height) && is.defined(inputOptions.text.dpi)) {
throw new Error('Expected only one of dpi or height');
}
if (is.defined(inputOptions.text.font)) {
if (is.string(inputOptions.text.font)) {
inputDescriptor.textFont = inputOptions.text.font;
} else {
throw is.invalidParameterError('text.font', 'string', inputOptions.text.font);
}
}
if (is.defined(inputOptions.text.fontfile)) {
if (is.string(inputOptions.text.fontfile)) {
inputDescriptor.textFontfile = inputOptions.text.fontfile;
} else {
throw is.invalidParameterError('text.fontfile', 'string', inputOptions.text.fontfile);
}
}
if (is.defined(inputOptions.text.width)) {
if (is.integer(inputOptions.text.width) && inputOptions.text.width > 0) {
inputDescriptor.textWidth = inputOptions.text.width;
} else {
throw is.invalidParameterError('text.width', 'positive integer', inputOptions.text.width);
}
}
if (is.defined(inputOptions.text.height)) {
if (is.integer(inputOptions.text.height) && inputOptions.text.height > 0) {
inputDescriptor.textHeight = inputOptions.text.height;
} else {
throw is.invalidParameterError('text.height', 'positive integer', inputOptions.text.height);
}
}
if (is.defined(inputOptions.text.align)) {
if (is.string(inputOptions.text.align) && is.string(this.constructor.align[inputOptions.text.align])) {
inputDescriptor.textAlign = this.constructor.align[inputOptions.text.align];
} else {
throw is.invalidParameterError('text.align', 'valid alignment', inputOptions.text.align);
}
}
if (is.defined(inputOptions.text.justify)) {
if (is.bool(inputOptions.text.justify)) {
inputDescriptor.textJustify = inputOptions.text.justify;
} else {
throw is.invalidParameterError('text.justify', 'boolean', inputOptions.text.justify);
}
}
if (is.defined(inputOptions.text.dpi)) {
if (is.integer(inputOptions.text.dpi) && is.inRange(inputOptions.text.dpi, 1, 1000000)) {
inputDescriptor.textDpi = inputOptions.text.dpi;
} else {
throw is.invalidParameterError('text.dpi', 'integer between 1 and 1000000', inputOptions.text.dpi);
}
}
if (is.defined(inputOptions.text.rgba)) {
if (is.bool(inputOptions.text.rgba)) {
inputDescriptor.textRgba = inputOptions.text.rgba;
} else {
throw is.invalidParameterError('text.rgba', 'bool', inputOptions.text.rgba);
}
}
if (is.defined(inputOptions.text.spacing)) {
if (is.integer(inputOptions.text.spacing) && is.inRange(inputOptions.text.spacing, -1e6, 1000000)) {
inputDescriptor.textSpacing = inputOptions.text.spacing;
} else {
throw is.invalidParameterError('text.spacing', 'integer between -1000000 and 1000000', inputOptions.text.spacing);
}
}
if (is.defined(inputOptions.text.wrap)) {
if (is.string(inputOptions.text.wrap) && is.inArray(inputOptions.text.wrap, ['word', 'char', 'word-char', 'none'])) {
inputDescriptor.textWrap = inputOptions.text.wrap;
} else {
throw is.invalidParameterError('text.wrap', 'one of: word, char, word-char, none', inputOptions.text.wrap);
}
}
delete inputDescriptor.buffer;
} else {
throw new Error('Expected a valid string to create an image with text.');
}
}
// Join images together
if (is.defined(inputOptions.join)) {
if (is.defined(this.options.join)) {
if (is.defined(inputOptions.join.animated)) {
if (is.bool(inputOptions.join.animated)) {
inputDescriptor.joinAnimated = inputOptions.join.animated;
} else {
throw is.invalidParameterError('join.animated', 'boolean', inputOptions.join.animated);
}
}
if (is.defined(inputOptions.join.across)) {
if (is.integer(inputOptions.join.across) && is.inRange(inputOptions.join.across, 1, 1000000)) {
inputDescriptor.joinAcross = inputOptions.join.across;
} else {
throw is.invalidParameterError('join.across', 'integer between 1 and 100000', inputOptions.join.across);
}
}
if (is.defined(inputOptions.join.shim)) {
if (is.integer(inputOptions.join.shim) && is.inRange(inputOptions.join.shim, 0, 1000000)) {
inputDescriptor.joinShim = inputOptions.join.shim;
} else {
throw is.invalidParameterError('join.shim', 'integer between 0 and 100000', inputOptions.join.shim);
}
}
if (is.defined(inputOptions.join.background)) {
inputDescriptor.joinBackground = this._getBackgroundColourOption(inputOptions.join.background);
}
if (is.defined(inputOptions.join.halign)) {
if (is.string(inputOptions.join.halign) && is.string(this.constructor.align[inputOptions.join.halign])) {
inputDescriptor.joinHalign = this.constructor.align[inputOptions.join.halign];
} else {
throw is.invalidParameterError('join.halign', 'valid alignment', inputOptions.join.halign);
}
}
if (is.defined(inputOptions.join.valign)) {
if (is.string(inputOptions.join.valign) && is.string(this.constructor.align[inputOptions.join.valign])) {
inputDescriptor.joinValign = this.constructor.align[inputOptions.join.valign];
} else {
throw is.invalidParameterError('join.valign', 'valid alignment', inputOptions.join.valign);
}
}
} else {
throw new Error('Expected input to be an array of images to join');
}
}
} else if (is.defined(inputOptions)) {
throw new Error(`Invalid input options ${inputOptions}`);
}
return inputDescriptor;
}
/**
* Handle incoming Buffer chunk on Writable Stream.
* @private
* @param {Buffer} chunk
* @param {string} encoding - unused
* @param {Function} callback
*/
function _write (chunk, _encoding, callback) {
if (Array.isArray(this.options.input.buffer)) {
if (is.buffer(chunk)) {
if (this.options.input.buffer.length === 0) {
this.on('finish', () => {
this.streamInFinished = true;
});
}
this.options.input.buffer.push(chunk);
callback();
} else {
callback(new Error('Non-Buffer data on Writable Stream'));
}
} else {
callback(new Error('Unexpected data on Writable Stream'));
}
}
/**
* Flattens the array of chunks accumulated in input.buffer.
* @private
*/
function _flattenBufferIn () {
if (this._isStreamInput()) {
this.options.input.buffer = Buffer.concat(this.options.input.buffer);
}
}
/**
* Are we expecting Stream-based input?
* @private
* @returns {boolean}
*/
function _isStreamInput () {
return Array.isArray(this.options.input.buffer);
}
/**
* Fast access to (uncached) image metadata without decoding any compressed pixel data.
*
* This is read from the header of the input image.
* It does not take into consideration any operations to be applied to the output image,
* such as resize or rotate.
*
* Dimensions in the response will respect the `page` and `pages` properties of the
* {@link /api-constructor/ constructor parameters}.
*
* A `Promise` is returned when `callback` is not provided.
*
* - `format`: Name of decoder used to decompress image data e.g. `jpeg`, `png`, `webp`, `gif`, `svg`
* - `size`: Total size of image in bytes, for Stream and Buffer input only
* - `width`: Number of pixels wide (EXIF orientation is not taken into consideration, see example below)
* - `height`: Number of pixels high (EXIF orientation is not taken into consideration, see example below)
* - `space`: Name of colour space interpretation e.g. `srgb`, `rgb`, `cmyk`, `lab`, `b-w` [...](https://www.libvips.org/API/current/enum.Interpretation.html)
* - `channels`: Number of bands e.g. `3` for sRGB, `4` for CMYK
* - `depth`: Name of pixel depth format e.g. `uchar`, `char`, `ushort`, `float` [...](https://www.libvips.org/API/current/enum.BandFormat.html)
* - `density`: Number of pixels per inch (DPI), if present
* - `chromaSubsampling`: String containing JPEG chroma subsampling, `4:2:0` or `4:4:4` for RGB, `4:2:0:4` or `4:4:4:4` for CMYK
* - `isProgressive`: Boolean indicating whether the image is interlaced using a progressive scan
* - `isPalette`: Boolean indicating whether the image is palette-based (GIF, PNG).
* - `bitsPerSample`: Number of bits per sample for each channel (GIF, PNG, HEIF).
* - `pages`: Number of pages/frames contained within the image, with support for TIFF, HEIF, PDF, animated GIF and animated WebP
* - `pageHeight`: Number of pixels high each page in a multi-page image will be.
* - `loop`: Number of times to loop an animated image, zero refers to a continuous loop.
* - `delay`: Delay in ms between each page in an animated image, provided as an array of integers.
* - `pagePrimary`: Number of the primary page in a HEIF image
* - `levels`: Details of each level in a multi-level image provided as an array of objects, requires libvips compiled with support for OpenSlide
* - `subifds`: Number of Sub Image File Directories in an OME-TIFF image
* - `background`: Default background colour, if present, for PNG (bKGD) and GIF images
* - `compression`: The encoder used to compress an HEIF file, `av1` (AVIF) or `hevc` (HEIC)
* - `resolutionUnit`: The unit of resolution (density), either `inch` or `cm`, if present
* - `hasProfile`: Boolean indicating the presence of an embedded ICC profile
* - `hasAlpha`: Boolean indicating the presence of an alpha transparency channel
* - `orientation`: Number value of the EXIF Orientation header, if present
* - `exif`: Buffer containing raw EXIF data, if present
* - `icc`: Buffer containing raw [ICC](https://www.npmjs.com/package/icc) profile data, if present
* - `iptc`: Buffer containing raw IPTC data, if present
* - `xmp`: Buffer containing raw XMP data, if present
* - `xmpAsString`: String containing XMP data, if valid UTF-8.
* - `tifftagPhotoshop`: Buffer containing raw TIFFTAG_PHOTOSHOP data, if present
* - `formatMagick`: String containing format for images loaded via *magick
* - `comments`: Array of keyword/text pairs representing PNG text blocks, if present.
*
* @example
* const metadata = await sharp(input).metadata();
*
* @example
* const image = sharp(inputJpg);
* image
* .metadata()
* .then(function(metadata) {
* return image
* .resize(Math.round(metadata.width / 2))
* .webp()
* .toBuffer();
* })
* .then(function(data) {
* // data contains a WebP image half the width and height of the original JPEG
* });
*
* @example
* // Get dimensions taking EXIF Orientation into account.
* const { autoOrient } = await sharp(input).metadata();
* const { width, height } = autoOrient;
*
* @param {Function} [callback] - called with the arguments `(err, metadata)`
* @returns {Promise<Object>|Sharp}
*/
function metadata (callback) {
const stack = Error();
if (is.fn(callback)) {
if (this._isStreamInput()) {
this.on('finish', () => {
this._flattenBufferIn();
sharp.metadata(this.options, (err, metadata) => {
if (err) {
callback(is.nativeError(err, stack));
} else {
callback(null, metadata);
}
});
});
} else {
sharp.metadata(this.options, (err, metadata) => {
if (err) {
callback(is.nativeError(err, stack));
} else {
callback(null, metadata);
}
});
}
return this;
} else {
if (this._isStreamInput()) {
return new Promise((resolve, reject) => {
const finished = () => {
this._flattenBufferIn();
sharp.metadata(this.options, (err, metadata) => {
if (err) {
reject(is.nativeError(err, stack));
} else {
resolve(metadata);
}
});
};
if (this.writableFinished) {
finished();
} else {
this.once('finish', finished);
}
});
} else {
return new Promise((resolve, reject) => {
sharp.metadata(this.options, (err, metadata) => {
if (err) {
reject(is.nativeError(err, stack));
} else {
resolve(metadata);
}
});
});
}
}
}
/**
* Access to pixel-derived image statistics for every channel in the image.
* A `Promise` is returned when `callback` is not provided.
*
* - `channels`: Array of channel statistics for each channel in the image. Each channel statistic contains
* - `min` (minimum value in the channel)
* - `max` (maximum value in the channel)
* - `sum` (sum of all values in a channel)
* - `squaresSum` (sum of squared values in a channel)
* - `mean` (mean of the values in a channel)
* - `stdev` (standard deviation for the values in a channel)
* - `minX` (x-coordinate of one of the pixel where the minimum lies)
* - `minY` (y-coordinate of one of the pixel where the minimum lies)
* - `maxX` (x-coordinate of one of the pixel where the maximum lies)
* - `maxY` (y-coordinate of one of the pixel where the maximum lies)
* - `isOpaque`: Is the image fully opaque? Will be `true` if the image has no alpha channel or if every pixel is fully opaque.
* - `entropy`: Histogram-based estimation of greyscale entropy, discarding alpha channel if any.
* - `sharpness`: Estimation of greyscale sharpness based on the standard deviation of a Laplacian convolution, discarding alpha channel if any.
* - `dominant`: Object containing most dominant sRGB colour based on a 4096-bin 3D histogram.
*
* **Note**: Statistics are derived from the original input image. Any operations performed on the image must first be
* written to a buffer in order to run `stats` on the result (see third example).
*
* @example
* const image = sharp(inputJpg);
* image
* .stats()
* .then(function(stats) {
* // stats contains the channel-wise statistics array and the isOpaque value
* });
*
* @example
* const { entropy, sharpness, dominant } = await sharp(input).stats();
* const { r, g, b } = dominant;
*
* @example
* const image = sharp(input);
* // store intermediate result
* const part = await image.extract(region).toBuffer();
* // create new instance to obtain statistics of extracted region
* const stats = await sharp(part).stats();
*
* @param {Function} [callback] - called with the arguments `(err, stats)`
* @returns {Promise<Object>}
*/
function stats (callback) {
const stack = Error();
if (is.fn(callback)) {
if (this._isStreamInput()) {
this.on('finish', () => {
this._flattenBufferIn();
sharp.stats(this.options, (err, stats) => {
if (err) {
callback(is.nativeError(err, stack));
} else {
callback(null, stats);
}
});
});
} else {
sharp.stats(this.options, (err, stats) => {
if (err) {
callback(is.nativeError(err, stack));
} else {
callback(null, stats);
}
});
}
return this;
} else {
if (this._isStreamInput()) {
return new Promise((resolve, reject) => {
this.on('finish', function () {
this._flattenBufferIn();
sharp.stats(this.options, (err, stats) => {
if (err) {
reject(is.nativeError(err, stack));
} else {
resolve(stats);
}
});
});
});
} else {
return new Promise((resolve, reject) => {
sharp.stats(this.options, (err, stats) => {
if (err) {
reject(is.nativeError(err, stack));
} else {
resolve(stats);
}
});
});
}
}
}
/**
* Decorate the Sharp prototype with input-related functions.
* @module Sharp
* @private
*/
input = (Sharp) => {
Object.assign(Sharp.prototype, {
// Private
_inputOptionsFromObject,
_createInputDescriptor,
_write,
_flattenBufferIn,
_isStreamInput,
// Public
metadata,
stats
});
// Class attributes
Sharp.align = align;
};
return input;
}
/*!
Copyright 2013 Lovell Fuller and others.
SPDX-License-Identifier: Apache-2.0
*/
var resize_1;
var hasRequiredResize;
function requireResize () {
if (hasRequiredResize) return resize_1;
hasRequiredResize = 1;
const is = requireIs();
/**
* Weighting to apply when using contain/cover fit.
* @member
* @private
*/
const gravity = {
center: 0,
centre: 0,
north: 1,
east: 2,
south: 3,
west: 4,
northeast: 5,
southeast: 6,
southwest: 7,
northwest: 8
};
/**
* Position to apply when using contain/cover fit.
* @member
* @private
*/
const position = {
top: 1,
right: 2,
bottom: 3,
left: 4,
'right top': 5,
'right bottom': 6,
'left bottom': 7,
'left top': 8
};
/**
* How to extend the image.
* @member
* @private
*/
const extendWith = {
background: 'background',
copy: 'copy',
repeat: 'repeat',
mirror: 'mirror'
};
/**
* Strategies for automagic cover behaviour.
* @member
* @private
*/
const strategy = {
entropy: 16,
attention: 17
};
/**
* Reduction kernels.
* @member
* @private
*/
const kernel = {
nearest: 'nearest',
linear: 'linear',
cubic: 'cubic',
mitchell: 'mitchell',
lanczos2: 'lanczos2',
lanczos3: 'lanczos3',
mks2013: 'mks2013',
mks2021: 'mks2021'
};
/**
* Methods by which an image can be resized to fit the provided dimensions.
* @member
* @private
*/
const fit = {
contain: 'contain',
cover: 'cover',
fill: 'fill',
inside: 'inside',
outside: 'outside'
};
/**
* Map external fit property to internal canvas property.
* @member
* @private
*/
const mapFitToCanvas = {
contain: 'embed',
cover: 'crop',
fill: 'ignore_aspect',
inside: 'max',
outside: 'min'
};
/**
* @private
*/
function isRotationExpected (options) {
return (options.angle % 360) !== 0 || options.rotationAngle !== 0;
}
/**
* @private
*/
function isResizeExpected (options) {
return options.width !== -1 || options.height !== -1;
}
/**
* Resize image to `width`, `height` or `width x height`.
*
* When both a `width` and `height` are provided, the possible methods by which the image should **fit** these are:
* - `cover`: (default) Preserving aspect ratio, attempt to ensure the image covers both provided dimensions by cropping/clipping to fit.
* - `contain`: Preserving aspect ratio, contain within both provided dimensions using "letterboxing" where necessary.
* - `fill`: Ignore the aspect ratio of the input and stretch to both provided dimensions.
* - `inside`: Preserving aspect ratio, resize the image to be as large as possible while ensuring its dimensions are less than or equal to both those specified.
* - `outside`: Preserving aspect ratio, resize the image to be as small as possible while ensuring its dimensions are greater than or equal to both those specified.
*
* Some of these values are based on the [object-fit](https://developer.mozilla.org/en-US/docs/Web/CSS/object-fit) CSS property.
*
* <img alt="Examples of various values for the fit property when resizing" width="100%" style="aspect-ratio: 998/243" src="/api-resize-fit.svg">
*
* When using a **fit** of `cover` or `contain`, the default **position** is `centre`. Other options are:
* - `sharp.position`: `top`, `right top`, `right`, `right bottom`, `bottom`, `left bottom`, `left`, `left top`.
* - `sharp.gravity`: `north`, `northeast`, `east`, `southeast`, `south`, `southwest`, `west`, `northwest`, `center` or `centre`.
* - `sharp.strategy`: `cover` only, dynamically crop using either the `entropy` or `attention` strategy.
*
* Some of these values are based on the [object-position](https://developer.mozilla.org/en-US/docs/Web/CSS/object-position) CSS property.
*
* The strategy-based approach initially resizes so one dimension is at its target length
* then repeatedly ranks edge regions, discarding the edge with the lowest score based on the selected strategy.
* - `entropy`: focus on the region with the highest [Shannon entropy](https://en.wikipedia.org/wiki/Entropy_%28information_theory%29).
* - `attention`: focus on the region with the highest luminance frequency, colour saturation and presence of skin tones.
*
* Possible downsizing kernels are:
* - `nearest`: Use [nearest neighbour interpolation](http://en.wikipedia.org/wiki/Nearest-neighbor_interpolation).
* - `linear`: Use a [triangle filter](https://en.wikipedia.org/wiki/Triangular_function).
* - `cubic`: Use a [Catmull-Rom spline](https://en.wikipedia.org/wiki/Centripetal_Catmull%E2%80%93Rom_spline).
* - `mitchell`: Use a [Mitchell-Netravali spline](https://www.cs.utexas.edu/~fussell/courses/cs384g-fall2013/lectures/mitchell/Mitchell.pdf).
* - `lanczos2`: Use a [Lanczos kernel](https://en.wikipedia.org/wiki/Lanczos_resampling#Lanczos_kernel) with `a=2`.
* - `lanczos3`: Use a Lanczos kernel with `a=3` (the default).
* - `mks2013`: Use a [Magic Kernel Sharp](https://johncostella.com/magic/mks.pdf) 2013 kernel, as adopted by Facebook.
* - `mks2021`: Use a Magic Kernel Sharp 2021 kernel, with more accurate (reduced) sharpening than the 2013 version.
*
* When upsampling, these kernels map to `nearest`, `linear` and `cubic` interpolators.
* Downsampling kernels without a matching upsampling interpolator map to `cubic`.
*
* Only one resize can occur per pipeline.
* Previous calls to `resize` in the same pipeline will be ignored.
*
* @example
* sharp(input)
* .resize({ width: 100 })
* .toBuffer()
* .then(data => {
* // 100 pixels wide, auto-scaled height
* });
*
* @example
* sharp(input)
* .resize({ height: 100 })
* .toBuffer()
* .then(data => {
* // 100 pixels high, auto-scaled width
* });
*
* @example
* sharp(input)
* .resize(200, 300, {
* kernel: sharp.kernel.nearest,
* fit: 'contain',
* position: 'right top',
* background: { r: 255, g: 255, b: 255, alpha: 0.5 }
* })
* .toFile('output.png')
* .then(() => {
* // output.png is a 200 pixels wide and 300 pixels high image
* // containing a nearest-neighbour scaled version
* // contained within the north-east corner of a semi-transparent white canvas
* });
*
* @example
* const transformer = sharp()
* .resize({
* width: 200,
* height: 200,
* fit: sharp.fit.cover,
* position: sharp.strategy.entropy
* });
* // Read image data from readableStream
* // Write 200px square auto-cropped image data to writableStream
* readableStream
* .pipe(transformer)
* .pipe(writableStream);
*
* @example
* sharp(input)
* .resize(200, 200, {
* fit: sharp.fit.inside,
* withoutEnlargement: true
* })
* .toFormat('jpeg')
* .toBuffer()
* .then(function(outputBuffer) {
* // outputBuffer contains JPEG image data
* // no wider and no higher than 200 pixels
* // and no larger than the input image
* });
*
* @example
* sharp(input)
* .resize(200, 200, {
* fit: sharp.fit.outside,
* withoutReduction: true
* })
* .toFormat('jpeg')
* .toBuffer()
* .then(function(outputBuffer) {
* // outputBuffer contains JPEG image data
* // of at least 200 pixels wide and 200 pixels high while maintaining aspect ratio
* // and no smaller than the input image
* });
*
* @example
* const scaleByHalf = await sharp(input)
* .metadata()
* .then(({ width }) => sharp(input)
* .resize(Math.round(width * 0.5))
* .toBuffer()
* );
*
* @param {number} [width] - How many pixels wide the resultant image should be. Use `null` or `undefined` to auto-scale the width to match the height.
* @param {number} [height] - How many pixels high the resultant image should be. Use `null` or `undefined` to auto-scale the height to match the width.
* @param {Object} [options]
* @param {number} [options.width] - An alternative means of specifying `width`. If both are present this takes priority.
* @param {number} [options.height] - An alternative means of specifying `height`. If both are present this takes priority.
* @param {String} [options.fit='cover'] - How the image should be resized/cropped to fit the target dimension(s), one of `cover`, `contain`, `fill`, `inside` or `outside`.
* @param {String} [options.position='centre'] - A position, gravity or strategy to use when `fit` is `cover` or `contain`.
* @param {String|Object} [options.background={r: 0, g: 0, b: 0, alpha: 1}] - background colour when `fit` is `contain`, parsed by the [color](https://www.npmjs.org/package/color) module, defaults to black without transparency.
* @param {String} [options.kernel='lanczos3'] - The kernel to use for image reduction and the inferred interpolator to use for upsampling. Use the `fastShrinkOnLoad` option to control kernel vs shrink-on-load.
* @param {Boolean} [options.withoutEnlargement=false] - Do not scale up if the width *or* height are already less than the target dimensions, equivalent to GraphicsMagick's `>` geometry option. This may result in output dimensions smaller than the target dimensions.
* @param {Boolean} [options.withoutReduction=false] - Do not scale down if the width *or* height are already greater than the target dimensions, equivalent to GraphicsMagick's `<` geometry option. This may still result in a crop to reach the target dimensions.
* @param {Boolean} [options.fastShrinkOnLoad=true] - Take greater advantage of the JPEG and WebP shrink-on-load feature, which can lead to a slight moiré pattern or round-down of an auto-scaled dimension.
* @returns {Sharp}
* @throws {Error} Invalid parameters
*/
function resize (widthOrOptions, height, options) {
if (isResizeExpected(this.options)) {
this.options.debuglog('ignoring previous resize options');
}
if (this.options.widthPost !== -1) {
this.options.debuglog('operation order will be: extract, resize, extract');
}
if (is.defined(widthOrOptions)) {
if (is.object(widthOrOptions) && !is.defined(options)) {
options = widthOrOptions;
} else if (is.integer(widthOrOptions) && widthOrOptions > 0) {
this.options.width = widthOrOptions;
} else {
throw is.invalidParameterError('width', 'positive integer', widthOrOptions);
}
} else {
this.options.width = -1;
}
if (is.defined(height)) {
if (is.integer(height) && height > 0) {
this.options.height = height;
} else {
throw is.invalidParameterError('height', 'positive integer', height);
}
} else {
this.options.height = -1;
}
if (is.object(options)) {
// Width
if (is.defined(options.width)) {
if (is.integer(options.width) && options.width > 0) {
this.options.width = options.width;
} else {
throw is.invalidParameterError('width', 'positive integer', options.width);
}
}
// Height
if (is.defined(options.height)) {
if (is.integer(options.height) && options.height > 0) {
this.options.height = options.height;
} else {
throw is.invalidParameterError('height', 'positive integer', options.height);
}
}
// Fit
if (is.defined(options.fit)) {
const canvas = mapFitToCanvas[options.fit];
if (is.string(canvas)) {
this.options.canvas = canvas;
} else {
throw is.invalidParameterError('fit', 'valid fit', options.fit);
}
}
// Position
if (is.defined(options.position)) {
const pos = is.integer(options.position)
? options.position
: strategy[options.position] || position[options.position] || gravity[options.position];
if (is.integer(pos) && (is.inRange(pos, 0, 8) || is.inRange(pos, 16, 17))) {
this.options.position = pos;
} else {
throw is.invalidParameterError('position', 'valid position/gravity/strategy', options.position);
}
}
// Background
this._setBackgroundColourOption('resizeBackground', options.background);
// Kernel
if (is.defined(options.kernel)) {
if (is.string(kernel[options.kernel])) {
this.options.kernel = kernel[options.kernel];
} else {
throw is.invalidParameterError('kernel', 'valid kernel name', options.kernel);
}
}
// Without enlargement
if (is.defined(options.withoutEnlargement)) {
this._setBooleanOption('withoutEnlargement', options.withoutEnlargement);
}
// Without reduction
if (is.defined(options.withoutReduction)) {
this._setBooleanOption('withoutReduction', options.withoutReduction);
}
// Shrink on load
if (is.defined(options.fastShrinkOnLoad)) {
this._setBooleanOption('fastShrinkOnLoad', options.fastShrinkOnLoad);
}
}
if (isRotationExpected(this.options) && isResizeExpected(this.options)) {
this.options.rotateBefore = true;
}
return this;
}
/**
* Extend / pad / extrude one or more edges of the image with either
* the provided background colour or pixels derived from the image.
* This operation will always occur after resizing and extraction, if any.
*
* @example
* // Resize to 140 pixels wide, then add 10 transparent pixels
* // to the top, left and right edges and 20 to the bottom edge
* sharp(input)
* .resize(140)
* .extend({
* top: 10,
* bottom: 20,
* left: 10,
* right: 10,
* background: { r: 0, g: 0, b: 0, alpha: 0 }
* })
* ...
*
* @example
* // Add a row of 10 red pixels to the bottom
* sharp(input)
* .extend({
* bottom: 10,
* background: 'red'
* })
* ...
*
* @example
* // Extrude image by 8 pixels to the right, mirroring existing right hand edge
* sharp(input)
* .extend({
* right: 8,
* background: 'mirror'
* })
* ...
*
* @param {(number|Object)} extend - single pixel count to add to all edges or an Object with per-edge counts
* @param {number} [extend.top=0]
* @param {number} [extend.left=0]
* @param {number} [extend.bottom=0]
* @param {number} [extend.right=0]
* @param {String} [extend.extendWith='background'] - populate new pixels using this method, one of: background, copy, repeat, mirror.
* @param {String|Object} [extend.background={r: 0, g: 0, b: 0, alpha: 1}] - background colour, parsed by the [color](https://www.npmjs.org/package/color) module, defaults to black without transparency.
* @returns {Sharp}
* @throws {Error} Invalid parameters
*/
function extend (extend) {
if (is.integer(extend) && extend > 0) {
this.options.extendTop = extend;
this.options.extendBottom = extend;
this.options.extendLeft = extend;
this.options.extendRight = extend;
} else if (is.object(extend)) {
if (is.defined(extend.top)) {
if (is.integer(extend.top) && extend.top >= 0) {
this.options.extendTop = extend.top;
} else {
throw is.invalidParameterError('top', 'positive integer', extend.top);
}
}
if (is.defined(extend.bottom)) {
if (is.integer(extend.bottom) && extend.bottom >= 0) {
this.options.extendBottom = extend.bottom;
} else {
throw is.invalidParameterError('bottom', 'positive integer', extend.bottom);
}
}
if (is.defined(extend.left)) {
if (is.integer(extend.left) && extend.left >= 0) {
this.options.extendLeft = extend.left;
} else {
throw is.invalidParameterError('left', 'positive integer', extend.left);
}
}
if (is.defined(extend.right)) {
if (is.integer(extend.right) && extend.right >= 0) {
this.options.extendRight = extend.right;
} else {
throw is.invalidParameterError('right', 'positive integer', extend.right);
}
}
this._setBackgroundColourOption('extendBackground', extend.background);
if (is.defined(extend.extendWith)) {
if (is.string(extendWith[extend.extendWith])) {
this.options.extendWith = extendWith[extend.extendWith];
} else {
throw is.invalidParameterError('extendWith', 'one of: background, copy, repeat, mirror', extend.extendWith);
}
}
} else {
throw is.invalidParameterError('extend', 'integer or object', extend);
}
return this;
}
/**
* Extract/crop a region of the image.
*
* - Use `extract` before `resize` for pre-resize extraction.
* - Use `extract` after `resize` for post-resize extraction.
* - Use `extract` twice and `resize` once for extract-then-resize-then-extract in a fixed operation order.
*
* @example
* sharp(input)
* .extract({ left: left, top: top, width: width, height: height })
* .toFile(output, function(err) {
* // Extract a region of the input image, saving in the same format.
* });
* @example
* sharp(input)
* .extract({ left: leftOffsetPre, top: topOffsetPre, width: widthPre, height: heightPre })
* .resize(width, height)
* .extract({ left: leftOffsetPost, top: topOffsetPost, width: widthPost, height: heightPost })
* .toFile(output, function(err) {
* // Extract a region, resize, then extract from the resized image
* });
*
* @param {Object} options - describes the region to extract using integral pixel values
* @param {number} options.left - zero-indexed offset from left edge
* @param {number} options.top - zero-indexed offset from top edge
* @param {number} options.width - width of region to extract
* @param {number} options.height - height of region to extract
* @returns {Sharp}
* @throws {Error} Invalid parameters
*/
function extract (options) {
const suffix = isResizeExpected(this.options) || this.options.widthPre !== -1 ? 'Post' : 'Pre';
if (this.options[`width${suffix}`] !== -1) {
this.options.debuglog('ignoring previous extract options');
}
['left', 'top', 'width', 'height'].forEach(function (name) {
const value = options[name];
if (is.integer(value) && value >= 0) {
this.options[name + (name === 'left' || name === 'top' ? 'Offset' : '') + suffix] = value;
} else {
throw is.invalidParameterError(name, 'integer', value);
}
}, this);
// Ensure existing rotation occurs before pre-resize extraction
if (isRotationExpected(this.options) && !isResizeExpected(this.options)) {
if (this.options.widthPre === -1 || this.options.widthPost === -1) {
this.options.rotateBefore = true;
}
}
if (this.options.input.autoOrient) {
this.options.orientBefore = true;
}
return this;
}
/**
* Trim pixels from all edges that contain values similar to the given background colour, which defaults to that of the top-left pixel.
*
* Images with an alpha channel will use the combined bounding box of alpha and non-alpha channels.
*
* If the result of this operation would trim an image to nothing then no change is made.
*
* The `info` response Object will contain `trimOffsetLeft` and `trimOffsetTop` properties.
*
* @example
* // Trim pixels with a colour similar to that of the top-left pixel.
* await sharp(input)
* .trim()
* .toFile(output);
*
* @example
* // Trim pixels with the exact same colour as that of the top-left pixel.
* await sharp(input)
* .trim({
* threshold: 0
* })
* .toFile(output);
*
* @example
* // Assume input is line art and trim only pixels with a similar colour to red.
* const output = await sharp(input)
* .trim({
* background: "#FF0000",
* lineArt: true
* })
* .toBuffer();
*
* @example
* // Trim all "yellow-ish" pixels, being more lenient with the higher threshold.
* const output = await sharp(input)
* .trim({
* background: "yellow",
* threshold: 42,
* })
* .toBuffer();
*
* @param {Object} [options]
* @param {string|Object} [options.background='top-left pixel'] - Background colour, parsed by the [color](https://www.npmjs.org/package/color) module, defaults to that of the top-left pixel.
* @param {number} [options.threshold=10] - Allowed difference from the above colour, a positive number.
* @param {boolean} [options.lineArt=false] - Does the input more closely resemble line art (e.g. vector) rather than being photographic?
* @returns {Sharp}
* @throws {Error} Invalid parameters
*/
function trim (options) {
this.options.trimThreshold = 10;
if (is.defined(options)) {
if (is.object(options)) {
if (is.defined(options.background)) {
this._setBackgroundColourOption('trimBackground', options.background);
}
if (is.defined(options.threshold)) {
if (is.number(options.threshold) && options.threshold >= 0) {
this.options.trimThreshold = options.threshold;
} else {
throw is.invalidParameterError('threshold', 'positive number', options.threshold);
}
}
if (is.defined(options.lineArt)) {
this._setBooleanOption('trimLineArt', options.lineArt);
}
} else {
throw is.invalidParameterError('trim', 'object', options);
}
}
if (isRotationExpected(this.options)) {
this.options.rotateBefore = true;
}
return this;
}
/**
* Decorate the Sharp prototype with resize-related functions.
* @module Sharp
* @private
*/
resize_1 = (Sharp) => {
Object.assign(Sharp.prototype, {
resize,
extend,
extract,
trim
});
// Class attributes
Sharp.gravity = gravity;
Sharp.strategy = strategy;
Sharp.kernel = kernel;
Sharp.fit = fit;
Sharp.position = position;
};
return resize_1;
}
/*!
Copyright 2013 Lovell Fuller and others.
SPDX-License-Identifier: Apache-2.0
*/
var composite_1;
var hasRequiredComposite;
function requireComposite () {
if (hasRequiredComposite) return composite_1;
hasRequiredComposite = 1;
const is = requireIs();
/**
* Blend modes.
* @member
* @private
*/
const blend = {
clear: 'clear',
source: 'source',
over: 'over',
in: 'in',
out: 'out',
atop: 'atop',
dest: 'dest',
'dest-over': 'dest-over',
'dest-in': 'dest-in',
'dest-out': 'dest-out',
'dest-atop': 'dest-atop',
xor: 'xor',
add: 'add',
saturate: 'saturate',
multiply: 'multiply',
screen: 'screen',
overlay: 'overlay',
darken: 'darken',
lighten: 'lighten',
'colour-dodge': 'colour-dodge',
'color-dodge': 'colour-dodge',
'colour-burn': 'colour-burn',
'color-burn': 'colour-burn',
'hard-light': 'hard-light',
'soft-light': 'soft-light',
difference: 'difference',
exclusion: 'exclusion'
};
/**
* Composite image(s) over the processed (resized, extracted etc.) image.
*
* The images to composite must be the same size or smaller than the processed image.
* If both `top` and `left` options are provided, they take precedence over `gravity`.
*
* Other operations in the same processing pipeline (e.g. resize, rotate, flip,
* flop, extract) will always be applied to the input image before composition.
*
* The `blend` option can be one of `clear`, `source`, `over`, `in`, `out`, `atop`,
* `dest`, `dest-over`, `dest-in`, `dest-out`, `dest-atop`,
* `xor`, `add`, `saturate`, `multiply`, `screen`, `overlay`, `darken`, `lighten`,
* `colour-dodge`, `color-dodge`, `colour-burn`,`color-burn`,
* `hard-light`, `soft-light`, `difference`, `exclusion`.
*
* More information about blend modes can be found at
* https://www.libvips.org/API/current/enum.BlendMode.html
* and https://www.cairographics.org/operators/
*
* @since 0.22.0
*
* @example
* await sharp(background)
* .composite([
* { input: layer1, gravity: 'northwest' },
* { input: layer2, gravity: 'southeast' },
* ])
* .toFile('combined.png');
*
* @example
* const output = await sharp('input.gif', { animated: true })
* .composite([
* { input: 'overlay.png', tile: true, blend: 'saturate' }
* ])
* .toBuffer();
*
* @example
* sharp('input.png')
* .rotate(180)
* .resize(300)
* .flatten( { background: '#ff6600' } )
* .composite([{ input: 'overlay.png', gravity: 'southeast' }])
* .sharpen()
* .withMetadata()
* .webp( { quality: 90 } )
* .toBuffer()
* .then(function(outputBuffer) {
* // outputBuffer contains upside down, 300px wide, alpha channel flattened
* // onto orange background, composited with overlay.png with SE gravity,
* // sharpened, with metadata, 90% quality WebP image data. Phew!
* });
*
* @param {Object[]} images - Ordered list of images to composite
* @param {Buffer|String} [images[].input] - Buffer containing image data, String containing the path to an image file, or Create object (see below)
* @param {Object} [images[].input.create] - describes a blank overlay to be created.
* @param {Number} [images[].input.create.width]
* @param {Number} [images[].input.create.height]
* @param {Number} [images[].input.create.channels] - 3-4
* @param {String|Object} [images[].input.create.background] - parsed by the [color](https://www.npmjs.org/package/color) module to extract values for red, green, blue and alpha.
* @param {Object} [images[].input.text] - describes a new text image to be created.
* @param {string} [images[].input.text.text] - text to render as a UTF-8 string. It can contain Pango markup, for example `<i>Le</i>Monde`.
* @param {string} [images[].input.text.font] - font name to render with.
* @param {string} [images[].input.text.fontfile] - absolute filesystem path to a font file that can be used by `font`.
* @param {number} [images[].input.text.width=0] - integral number of pixels to word-wrap at. Lines of text wider than this will be broken at word boundaries.
* @param {number} [images[].input.text.height=0] - integral number of pixels high. When defined, `dpi` will be ignored and the text will automatically fit the pixel resolution defined by `width` and `height`. Will be ignored if `width` is not specified or set to 0.
* @param {string} [images[].input.text.align='left'] - text alignment (`'left'`, `'centre'`, `'center'`, `'right'`).
* @param {boolean} [images[].input.text.justify=false] - set this to true to apply justification to the text.
* @param {number} [images[].input.text.dpi=72] - the resolution (size) at which to render the text. Does not take effect if `height` is specified.
* @param {boolean} [images[].input.text.rgba=false] - set this to true to enable RGBA output. This is useful for colour emoji rendering, or support for Pango markup features like `<span foreground="red">Red!</span>`.
* @param {number} [images[].input.text.spacing=0] - text line height in points. Will use the font line height if none is specified.
* @param {Boolean} [images[].autoOrient=false] - set to true to use EXIF orientation data, if present, to orient the image.
* @param {String} [images[].blend='over'] - how to blend this image with the image below.
* @param {String} [images[].gravity='centre'] - gravity at which to place the overlay.
* @param {Number} [images[].top] - the pixel offset from the top edge.
* @param {Number} [images[].left] - the pixel offset from the left edge.
* @param {Boolean} [images[].tile=false] - set to true to repeat the overlay image across the entire image with the given `gravity`.
* @param {Boolean} [images[].premultiplied=false] - set to true to avoid premultiplying the image below. Equivalent to the `--premultiplied` vips option.
* @param {Number} [images[].density=72] - number representing the DPI for vector overlay image.
* @param {Object} [images[].raw] - describes overlay when using raw pixel data.
* @param {Number} [images[].raw.width]
* @param {Number} [images[].raw.height]
* @param {Number} [images[].raw.channels]
* @param {boolean} [images[].animated=false] - Set to `true` to read all frames/pages of an animated image.
* @param {string} [images[].failOn='warning'] - @see {@link /api-constructor/ constructor parameters}
* @param {number|boolean} [images[].limitInputPixels=268402689] - @see {@link /api-constructor/ constructor parameters}
* @returns {Sharp}
* @throws {Error} Invalid parameters
*/
function composite (images) {
if (!Array.isArray(images)) {
throw is.invalidParameterError('images to composite', 'array', images);
}
this.options.composite = images.map(image => {
if (!is.object(image)) {
throw is.invalidParameterError('image to composite', 'object', image);
}
const inputOptions = this._inputOptionsFromObject(image);
const composite = {
input: this._createInputDescriptor(image.input, inputOptions, { allowStream: false }),
blend: 'over',
tile: false,
left: 0,
top: 0,
hasOffset: false,
gravity: 0,
premultiplied: false
};
if (is.defined(image.blend)) {
if (is.string(blend[image.blend])) {
composite.blend = blend[image.blend];
} else {
throw is.invalidParameterError('blend', 'valid blend name', image.blend);
}
}
if (is.defined(image.tile)) {
if (is.bool(image.tile)) {
composite.tile = image.tile;
} else {
throw is.invalidParameterError('tile', 'boolean', image.tile);
}
}
if (is.defined(image.left)) {
if (is.integer(image.left)) {
composite.left = image.left;
} else {
throw is.invalidParameterError('left', 'integer', image.left);
}
}
if (is.defined(image.top)) {
if (is.integer(image.top)) {
composite.top = image.top;
} else {
throw is.invalidParameterError('top', 'integer', image.top);
}
}
if (is.defined(image.top) !== is.defined(image.left)) {
throw new Error('Expected both left and top to be set');
} else {
composite.hasOffset = is.integer(image.top) && is.integer(image.left);
}
if (is.defined(image.gravity)) {
if (is.integer(image.gravity) && is.inRange(image.gravity, 0, 8)) {
composite.gravity = image.gravity;
} else if (is.string(image.gravity) && is.integer(this.constructor.gravity[image.gravity])) {
composite.gravity = this.constructor.gravity[image.gravity];
} else {
throw is.invalidParameterError('gravity', 'valid gravity', image.gravity);
}
}
if (is.defined(image.premultiplied)) {
if (is.bool(image.premultiplied)) {
composite.premultiplied = image.premultiplied;
} else {
throw is.invalidParameterError('premultiplied', 'boolean', image.premultiplied);
}
}
return composite;
});
return this;
}
/**
* Decorate the Sharp prototype with composite-related functions.
* @module Sharp
* @private
*/
composite_1 = (Sharp) => {
Sharp.prototype.composite = composite;
Sharp.blend = blend;
};
return composite_1;
}
/*!
Copyright 2013 Lovell Fuller and others.
SPDX-License-Identifier: Apache-2.0
*/
var operation;
var hasRequiredOperation;
function requireOperation () {
if (hasRequiredOperation) return operation;
hasRequiredOperation = 1;
const is = requireIs();
/**
* How accurate an operation should be.
* @member
* @private
*/
const vipsPrecision = {
integer: 'integer',
float: 'float',
approximate: 'approximate'
};
/**
* Rotate the output image.
*
* The provided angle is converted to a valid positive degree rotation.
* For example, `-450` will produce a 270 degree rotation.
*
* When rotating by an angle other than a multiple of 90,
* the background colour can be provided with the `background` option.
*
* For backwards compatibility, if no angle is provided, `.autoOrient()` will be called.
*
* Only one rotation can occur per pipeline (aside from an initial call without
* arguments to orient via EXIF data). Previous calls to `rotate` in the same
* pipeline will be ignored.
*
* Multi-page images can only be rotated by 180 degrees.
*
* Method order is important when rotating, resizing and/or extracting regions,
* for example `.rotate(x).extract(y)` will produce a different result to `.extract(y).rotate(x)`.
*
* @example
* const rotateThenResize = await sharp(input)
* .rotate(90)
* .resize({ width: 16, height: 8, fit: 'fill' })
* .toBuffer();
* const resizeThenRotate = await sharp(input)
* .resize({ width: 16, height: 8, fit: 'fill' })
* .rotate(90)
* .toBuffer();
*
* @param {number} [angle=auto] angle of rotation.
* @param {Object} [options] - if present, is an Object with optional attributes.
* @param {string|Object} [options.background="#000000"] parsed by the [color](https://www.npmjs.org/package/color) module to extract values for red, green, blue and alpha.
* @returns {Sharp}
* @throws {Error} Invalid parameters
*/
function rotate (angle, options) {
if (!is.defined(angle)) {
return this.autoOrient();
}
if (this.options.angle || this.options.rotationAngle) {
this.options.debuglog('ignoring previous rotate options');
this.options.angle = 0;
this.options.rotationAngle = 0;
}
if (is.integer(angle) && !(angle % 90)) {
this.options.angle = angle;
} else if (is.number(angle)) {
this.options.rotationAngle = angle;
if (is.object(options) && options.background) {
this._setBackgroundColourOption('rotationBackground', options.background);
}
} else {
throw is.invalidParameterError('angle', 'numeric', angle);
}
return this;
}
/**
* Auto-orient based on the EXIF `Orientation` tag, then remove the tag.
* Mirroring is supported and may infer the use of a flip operation.
*
* Previous or subsequent use of `rotate(angle)` and either `flip()` or `flop()`
* will logically occur after auto-orientation, regardless of call order.
*
* @example
* const output = await sharp(input).autoOrient().toBuffer();
*
* @example
* const pipeline = sharp()
* .autoOrient()
* .resize(null, 200)
* .toBuffer(function (err, outputBuffer, info) {
* // outputBuffer contains 200px high JPEG image data,
* // auto-oriented using EXIF Orientation tag
* // info.width and info.height contain the dimensions of the resized image
* });
* readableStream.pipe(pipeline);
*
* @returns {Sharp}
*/
function autoOrient () {
this.options.input.autoOrient = true;
return this;
}
/**
* Mirror the image vertically (up-down) about the x-axis.
* This always occurs before rotation, if any.
*
* This operation does not work correctly with multi-page images.
*
* @example
* const output = await sharp(input).flip().toBuffer();
*
* @param {Boolean} [flip=true]
* @returns {Sharp}
*/
function flip (flip) {
this.options.flip = is.bool(flip) ? flip : true;
return this;
}
/**
* Mirror the image horizontally (left-right) about the y-axis.
* This always occurs before rotation, if any.
*
* @example
* const output = await sharp(input).flop().toBuffer();
*
* @param {Boolean} [flop=true]
* @returns {Sharp}
*/
function flop (flop) {
this.options.flop = is.bool(flop) ? flop : true;
return this;
}
/**
* Perform an affine transform on an image. This operation will always occur after resizing, extraction and rotation, if any.
*
* You must provide an array of length 4 or a 2x2 affine transformation matrix.
* By default, new pixels are filled with a black background. You can provide a background colour with the `background` option.
* A particular interpolator may also be specified. Set the `interpolator` option to an attribute of the `sharp.interpolators` Object e.g. `sharp.interpolators.nohalo`.
*
* In the case of a 2x2 matrix, the transform is:
* - X = `matrix[0, 0]` \* (x + `idx`) + `matrix[0, 1]` \* (y + `idy`) + `odx`
* - Y = `matrix[1, 0]` \* (x + `idx`) + `matrix[1, 1]` \* (y + `idy`) + `ody`
*
* where:
* - x and y are the coordinates in input image.
* - X and Y are the coordinates in output image.
* - (0,0) is the upper left corner.
*
* @since 0.27.0
*
* @example
* const pipeline = sharp()
* .affine([[1, 0.3], [0.1, 0.7]], {
* background: 'white',
* interpolator: sharp.interpolators.nohalo
* })
* .toBuffer((err, outputBuffer, info) => {
* // outputBuffer contains the transformed image
* // info.width and info.height contain the new dimensions
* });
*
* inputStream
* .pipe(pipeline);
*
* @param {Array<Array<number>>|Array<number>} matrix - affine transformation matrix
* @param {Object} [options] - if present, is an Object with optional attributes.
* @param {String|Object} [options.background="#000000"] - parsed by the [color](https://www.npmjs.org/package/color) module to extract values for red, green, blue and alpha.
* @param {Number} [options.idx=0] - input horizontal offset
* @param {Number} [options.idy=0] - input vertical offset
* @param {Number} [options.odx=0] - output horizontal offset
* @param {Number} [options.ody=0] - output vertical offset
* @param {String} [options.interpolator=sharp.interpolators.bicubic] - interpolator
* @returns {Sharp}
* @throws {Error} Invalid parameters
*/
function affine (matrix, options) {
const flatMatrix = [].concat(...matrix);
if (flatMatrix.length === 4 && flatMatrix.every(is.number)) {
this.options.affineMatrix = flatMatrix;
} else {
throw is.invalidParameterError('matrix', '1x4 or 2x2 array', matrix);
}
if (is.defined(options)) {
if (is.object(options)) {
this._setBackgroundColourOption('affineBackground', options.background);
if (is.defined(options.idx)) {
if (is.number(options.idx)) {
this.options.affineIdx = options.idx;
} else {
throw is.invalidParameterError('options.idx', 'number', options.idx);
}
}
if (is.defined(options.idy)) {
if (is.number(options.idy)) {
this.options.affineIdy = options.idy;
} else {
throw is.invalidParameterError('options.idy', 'number', options.idy);
}
}
if (is.defined(options.odx)) {
if (is.number(options.odx)) {
this.options.affineOdx = options.odx;
} else {
throw is.invalidParameterError('options.odx', 'number', options.odx);
}
}
if (is.defined(options.ody)) {
if (is.number(options.ody)) {
this.options.affineOdy = options.ody;
} else {
throw is.invalidParameterError('options.ody', 'number', options.ody);
}
}
if (is.defined(options.interpolator)) {
if (is.inArray(options.interpolator, Object.values(this.constructor.interpolators))) {
this.options.affineInterpolator = options.interpolator;
} else {
throw is.invalidParameterError('options.interpolator', 'valid interpolator name', options.interpolator);
}
}
} else {
throw is.invalidParameterError('options', 'object', options);
}
}
return this;
}
/**
* Sharpen the image.
*
* When used without parameters, performs a fast, mild sharpen of the output image.
*
* When a `sigma` is provided, performs a slower, more accurate sharpen of the L channel in the LAB colour space.
* Fine-grained control over the level of sharpening in "flat" (m1) and "jagged" (m2) areas is available.
*
* See {@link https://www.libvips.org/API/current/method.Image.sharpen.html libvips sharpen} operation.
*
* @example
* const data = await sharp(input).sharpen().toBuffer();
*
* @example
* const data = await sharp(input).sharpen({ sigma: 2 }).toBuffer();
*
* @example
* const data = await sharp(input)
* .sharpen({
* sigma: 2,
* m1: 0,
* m2: 3,
* x1: 3,
* y2: 15,
* y3: 15,
* })
* .toBuffer();
*
* @param {Object|number} [options] - if present, is an Object with attributes
* @param {number} [options.sigma] - the sigma of the Gaussian mask, where `sigma = 1 + radius / 2`, between 0.000001 and 10
* @param {number} [options.m1=1.0] - the level of sharpening to apply to "flat" areas, between 0 and 1000000
* @param {number} [options.m2=2.0] - the level of sharpening to apply to "jagged" areas, between 0 and 1000000
* @param {number} [options.x1=2.0] - threshold between "flat" and "jagged", between 0 and 1000000
* @param {number} [options.y2=10.0] - maximum amount of brightening, between 0 and 1000000
* @param {number} [options.y3=20.0] - maximum amount of darkening, between 0 and 1000000
* @param {number} [flat] - (deprecated) see `options.m1`.
* @param {number} [jagged] - (deprecated) see `options.m2`.
* @returns {Sharp}
* @throws {Error} Invalid parameters
*/
function sharpen (options, flat, jagged) {
if (!is.defined(options)) {
// No arguments: default to mild sharpen
this.options.sharpenSigma = -1;
} else if (is.bool(options)) {
// Deprecated boolean argument: apply mild sharpen?
this.options.sharpenSigma = options ? -1 : 0;
} else if (is.number(options) && is.inRange(options, 0.01, 10000)) {
// Deprecated numeric argument: specific sigma
this.options.sharpenSigma = options;
// Deprecated control over flat areas
if (is.defined(flat)) {
if (is.number(flat) && is.inRange(flat, 0, 10000)) {
this.options.sharpenM1 = flat;
} else {
throw is.invalidParameterError('flat', 'number between 0 and 10000', flat);
}
}
// Deprecated control over jagged areas
if (is.defined(jagged)) {
if (is.number(jagged) && is.inRange(jagged, 0, 10000)) {
this.options.sharpenM2 = jagged;
} else {
throw is.invalidParameterError('jagged', 'number between 0 and 10000', jagged);
}
}
} else if (is.plainObject(options)) {
if (is.number(options.sigma) && is.inRange(options.sigma, 0.000001, 10)) {
this.options.sharpenSigma = options.sigma;
} else {
throw is.invalidParameterError('options.sigma', 'number between 0.000001 and 10', options.sigma);
}
if (is.defined(options.m1)) {
if (is.number(options.m1) && is.inRange(options.m1, 0, 1000000)) {
this.options.sharpenM1 = options.m1;
} else {
throw is.invalidParameterError('options.m1', 'number between 0 and 1000000', options.m1);
}
}
if (is.defined(options.m2)) {
if (is.number(options.m2) && is.inRange(options.m2, 0, 1000000)) {
this.options.sharpenM2 = options.m2;
} else {
throw is.invalidParameterError('options.m2', 'number between 0 and 1000000', options.m2);
}
}
if (is.defined(options.x1)) {
if (is.number(options.x1) && is.inRange(options.x1, 0, 1000000)) {
this.options.sharpenX1 = options.x1;
} else {
throw is.invalidParameterError('options.x1', 'number between 0 and 1000000', options.x1);
}
}
if (is.defined(options.y2)) {
if (is.number(options.y2) && is.inRange(options.y2, 0, 1000000)) {
this.options.sharpenY2 = options.y2;
} else {
throw is.invalidParameterError('options.y2', 'number between 0 and 1000000', options.y2);
}
}
if (is.defined(options.y3)) {
if (is.number(options.y3) && is.inRange(options.y3, 0, 1000000)) {
this.options.sharpenY3 = options.y3;
} else {
throw is.invalidParameterError('options.y3', 'number between 0 and 1000000', options.y3);
}
}
} else {
throw is.invalidParameterError('sigma', 'number between 0.01 and 10000', options);
}
return this;
}
/**
* Apply median filter.
* When used without parameters the default window is 3x3.
*
* @example
* const output = await sharp(input).median().toBuffer();
*
* @example
* const output = await sharp(input).median(5).toBuffer();
*
* @param {number} [size=3] square mask size: size x size
* @returns {Sharp}
* @throws {Error} Invalid parameters
*/
function median (size) {
if (!is.defined(size)) {
// No arguments: default to 3x3
this.options.medianSize = 3;
} else if (is.integer(size) && is.inRange(size, 1, 1000)) {
// Numeric argument: specific sigma
this.options.medianSize = size;
} else {
throw is.invalidParameterError('size', 'integer between 1 and 1000', size);
}
return this;
}
/**
* Blur the image.
*
* When used without parameters, performs a fast 3x3 box blur (equivalent to a box linear filter).
*
* When a `sigma` is provided, performs a slower, more accurate Gaussian blur.
*
* @example
* const boxBlurred = await sharp(input)
* .blur()
* .toBuffer();
*
* @example
* const gaussianBlurred = await sharp(input)
* .blur(5)
* .toBuffer();
*
* @param {Object|number|Boolean} [options]
* @param {number} [options.sigma] a value between 0.3 and 1000 representing the sigma of the Gaussian mask, where `sigma = 1 + radius / 2`.
* @param {string} [options.precision='integer'] How accurate the operation should be, one of: integer, float, approximate.
* @param {number} [options.minAmplitude=0.2] A value between 0.001 and 1. A smaller value will generate a larger, more accurate mask.
* @returns {Sharp}
* @throws {Error} Invalid parameters
*/
function blur (options) {
let sigma;
if (is.number(options)) {
sigma = options;
} else if (is.plainObject(options)) {
if (!is.number(options.sigma)) {
throw is.invalidParameterError('options.sigma', 'number between 0.3 and 1000', sigma);
}
sigma = options.sigma;
if ('precision' in options) {
if (is.string(vipsPrecision[options.precision])) {
this.options.precision = vipsPrecision[options.precision];
} else {
throw is.invalidParameterError('precision', 'one of: integer, float, approximate', options.precision);
}
}
if ('minAmplitude' in options) {
if (is.number(options.minAmplitude) && is.inRange(options.minAmplitude, 0.001, 1)) {
this.options.minAmpl = options.minAmplitude;
} else {
throw is.invalidParameterError('minAmplitude', 'number between 0.001 and 1', options.minAmplitude);
}
}
}
if (!is.defined(options)) {
// No arguments: default to mild blur
this.options.blurSigma = -1;
} else if (is.bool(options)) {
// Boolean argument: apply mild blur?
this.options.blurSigma = options ? -1 : 0;
} else if (is.number(sigma) && is.inRange(sigma, 0.3, 1000)) {
// Numeric argument: specific sigma
this.options.blurSigma = sigma;
} else {
throw is.invalidParameterError('sigma', 'number between 0.3 and 1000', sigma);
}
return this;
}
/**
* Expand foreground objects using the dilate morphological operator.
*
* @example
* const output = await sharp(input)
* .dilate()
* .toBuffer();
*
* @param {Number} [width=1] dilation width in pixels.
* @returns {Sharp}
* @throws {Error} Invalid parameters
*/
function dilate (width) {
if (!is.defined(width)) {
this.options.dilateWidth = 1;
} else if (is.integer(width) && width > 0) {
this.options.dilateWidth = width;
} else {
throw is.invalidParameterError('dilate', 'positive integer', dilate);
}
return this;
}
/**
* Shrink foreground objects using the erode morphological operator.
*
* @example
* const output = await sharp(input)
* .erode()
* .toBuffer();
*
* @param {Number} [width=1] erosion width in pixels.
* @returns {Sharp}
* @throws {Error} Invalid parameters
*/
function erode (width) {
if (!is.defined(width)) {
this.options.erodeWidth = 1;
} else if (is.integer(width) && width > 0) {
this.options.erodeWidth = width;
} else {
throw is.invalidParameterError('erode', 'positive integer', erode);
}
return this;
}
/**
* Merge alpha transparency channel, if any, with a background, then remove the alpha channel.
*
* See also {@link /api-channel#removealpha removeAlpha}.
*
* @example
* await sharp(rgbaInput)
* .flatten({ background: '#F0A703' })
* .toBuffer();
*
* @param {Object} [options]
* @param {string|Object} [options.background={r: 0, g: 0, b: 0}] - background colour, parsed by the [color](https://www.npmjs.org/package/color) module, defaults to black.
* @returns {Sharp}
*/
function flatten (options) {
this.options.flatten = is.bool(options) ? options : true;
if (is.object(options)) {
this._setBackgroundColourOption('flattenBackground', options.background);
}
return this;
}
/**
* Ensure the image has an alpha channel
* with all white pixel values made fully transparent.
*
* Existing alpha channel values for non-white pixels remain unchanged.
*
* This feature is experimental and the API may change.
*
* @since 0.32.1
*
* @example
* await sharp(rgbInput)
* .unflatten()
* .toBuffer();
*
* @example
* await sharp(rgbInput)
* .threshold(128, { grayscale: false }) // converter bright pixels to white
* .unflatten()
* .toBuffer();
*/
function unflatten () {
this.options.unflatten = true;
return this;
}
/**
* Apply a gamma correction by reducing the encoding (darken) pre-resize at a factor of `1/gamma`
* then increasing the encoding (brighten) post-resize at a factor of `gamma`.
* This can improve the perceived brightness of a resized image in non-linear colour spaces.
* JPEG and WebP input images will not take advantage of the shrink-on-load performance optimisation
* when applying a gamma correction.
*
* Supply a second argument to use a different output gamma value, otherwise the first value is used in both cases.
*
* @param {number} [gamma=2.2] value between 1.0 and 3.0.
* @param {number} [gammaOut] value between 1.0 and 3.0. (optional, defaults to same as `gamma`)
* @returns {Sharp}
* @throws {Error} Invalid parameters
*/
function gamma (gamma, gammaOut) {
if (!is.defined(gamma)) {
// Default gamma correction of 2.2 (sRGB)
this.options.gamma = 2.2;
} else if (is.number(gamma) && is.inRange(gamma, 1, 3)) {
this.options.gamma = gamma;
} else {
throw is.invalidParameterError('gamma', 'number between 1.0 and 3.0', gamma);
}
if (!is.defined(gammaOut)) {
// Default gamma correction for output is same as input
this.options.gammaOut = this.options.gamma;
} else if (is.number(gammaOut) && is.inRange(gammaOut, 1, 3)) {
this.options.gammaOut = gammaOut;
} else {
throw is.invalidParameterError('gammaOut', 'number between 1.0 and 3.0', gammaOut);
}
return this;
}
/**
* Produce the "negative" of the image.
*
* @example
* const output = await sharp(input)
* .negate()
* .toBuffer();
*
* @example
* const output = await sharp(input)
* .negate({ alpha: false })
* .toBuffer();
*
* @param {Object} [options]
* @param {Boolean} [options.alpha=true] Whether or not to negate any alpha channel
* @returns {Sharp}
*/
function negate (options) {
this.options.negate = is.bool(options) ? options : true;
if (is.plainObject(options) && 'alpha' in options) {
if (!is.bool(options.alpha)) {
throw is.invalidParameterError('alpha', 'should be boolean value', options.alpha);
} else {
this.options.negateAlpha = options.alpha;
}
}
return this;
}
/**
* Enhance output image contrast by stretching its luminance to cover a full dynamic range.
*
* Uses a histogram-based approach, taking a default range of 1% to 99% to reduce sensitivity to noise at the extremes.
*
* Luminance values below the `lower` percentile will be underexposed by clipping to zero.
* Luminance values above the `upper` percentile will be overexposed by clipping to the max pixel value.
*
* @example
* const output = await sharp(input)
* .normalise()
* .toBuffer();
*
* @example
* const output = await sharp(input)
* .normalise({ lower: 0, upper: 100 })
* .toBuffer();
*
* @param {Object} [options]
* @param {number} [options.lower=1] - Percentile below which luminance values will be underexposed.
* @param {number} [options.upper=99] - Percentile above which luminance values will be overexposed.
* @returns {Sharp}
*/
function normalise (options) {
if (is.plainObject(options)) {
if (is.defined(options.lower)) {
if (is.number(options.lower) && is.inRange(options.lower, 0, 99)) {
this.options.normaliseLower = options.lower;
} else {
throw is.invalidParameterError('lower', 'number between 0 and 99', options.lower);
}
}
if (is.defined(options.upper)) {
if (is.number(options.upper) && is.inRange(options.upper, 1, 100)) {
this.options.normaliseUpper = options.upper;
} else {
throw is.invalidParameterError('upper', 'number between 1 and 100', options.upper);
}
}
}
if (this.options.normaliseLower >= this.options.normaliseUpper) {
throw is.invalidParameterError('range', 'lower to be less than upper',
`${this.options.normaliseLower} >= ${this.options.normaliseUpper}`);
}
this.options.normalise = true;
return this;
}
/**
* Alternative spelling of normalise.
*
* @example
* const output = await sharp(input)
* .normalize()
* .toBuffer();
*
* @param {Object} [options]
* @param {number} [options.lower=1] - Percentile below which luminance values will be underexposed.
* @param {number} [options.upper=99] - Percentile above which luminance values will be overexposed.
* @returns {Sharp}
*/
function normalize (options) {
return this.normalise(options);
}
/**
* Perform contrast limiting adaptive histogram equalization
* {@link https://en.wikipedia.org/wiki/Adaptive_histogram_equalization#Contrast_Limited_AHE CLAHE}.
*
* This will, in general, enhance the clarity of the image by bringing out darker details.
*
* @since 0.28.3
*
* @example
* const output = await sharp(input)
* .clahe({
* width: 3,
* height: 3,
* })
* .toBuffer();
*
* @param {Object} options
* @param {number} options.width - Integral width of the search window, in pixels.
* @param {number} options.height - Integral height of the search window, in pixels.
* @param {number} [options.maxSlope=3] - Integral level of brightening, between 0 and 100, where 0 disables contrast limiting.
* @returns {Sharp}
* @throws {Error} Invalid parameters
*/
function clahe (options) {
if (is.plainObject(options)) {
if (is.integer(options.width) && options.width > 0) {
this.options.claheWidth = options.width;
} else {
throw is.invalidParameterError('width', 'integer greater than zero', options.width);
}
if (is.integer(options.height) && options.height > 0) {
this.options.claheHeight = options.height;
} else {
throw is.invalidParameterError('height', 'integer greater than zero', options.height);
}
if (is.defined(options.maxSlope)) {
if (is.integer(options.maxSlope) && is.inRange(options.maxSlope, 0, 100)) {
this.options.claheMaxSlope = options.maxSlope;
} else {
throw is.invalidParameterError('maxSlope', 'integer between 0 and 100', options.maxSlope);
}
}
} else {
throw is.invalidParameterError('options', 'plain object', options);
}
return this;
}
/**
* Convolve the image with the specified kernel.
*
* @example
* sharp(input)
* .convolve({
* width: 3,
* height: 3,
* kernel: [-1, 0, 1, -2, 0, 2, -1, 0, 1]
* })
* .raw()
* .toBuffer(function(err, data, info) {
* // data contains the raw pixel data representing the convolution
* // of the input image with the horizontal Sobel operator
* });
*
* @param {Object} kernel
* @param {number} kernel.width - width of the kernel in pixels.
* @param {number} kernel.height - height of the kernel in pixels.
* @param {Array<number>} kernel.kernel - Array of length `width*height` containing the kernel values.
* @param {number} [kernel.scale=sum] - the scale of the kernel in pixels.
* @param {number} [kernel.offset=0] - the offset of the kernel in pixels.
* @returns {Sharp}
* @throws {Error} Invalid parameters
*/
function convolve (kernel) {
if (!is.object(kernel) || !Array.isArray(kernel.kernel) ||
!is.integer(kernel.width) || !is.integer(kernel.height) ||
!is.inRange(kernel.width, 3, 1001) || !is.inRange(kernel.height, 3, 1001) ||
kernel.height * kernel.width !== kernel.kernel.length
) {
// must pass in a kernel
throw new Error('Invalid convolution kernel');
}
// Default scale is sum of kernel values
if (!is.integer(kernel.scale)) {
kernel.scale = kernel.kernel.reduce((a, b) => a + b, 0);
}
// Clip scale to a minimum value of 1
if (kernel.scale < 1) {
kernel.scale = 1;
}
if (!is.integer(kernel.offset)) {
kernel.offset = 0;
}
this.options.convKernel = kernel;
return this;
}
/**
* Any pixel value greater than or equal to the threshold value will be set to 255, otherwise it will be set to 0.
* @param {number} [threshold=128] - a value in the range 0-255 representing the level at which the threshold will be applied.
* @param {Object} [options]
* @param {Boolean} [options.greyscale=true] - convert to single channel greyscale.
* @param {Boolean} [options.grayscale=true] - alternative spelling for greyscale.
* @returns {Sharp}
* @throws {Error} Invalid parameters
*/
function threshold (threshold, options) {
if (!is.defined(threshold)) {
this.options.threshold = 128;
} else if (is.bool(threshold)) {
this.options.threshold = threshold ? 128 : 0;
} else if (is.integer(threshold) && is.inRange(threshold, 0, 255)) {
this.options.threshold = threshold;
} else {
throw is.invalidParameterError('threshold', 'integer between 0 and 255', threshold);
}
if (!is.object(options) || options.greyscale === true || options.grayscale === true) {
this.options.thresholdGrayscale = true;
} else {
this.options.thresholdGrayscale = false;
}
return this;
}
/**
* Perform a bitwise boolean operation with operand image.
*
* This operation creates an output image where each pixel is the result of
* the selected bitwise boolean `operation` between the corresponding pixels of the input images.
*
* @param {Buffer|string} operand - Buffer containing image data or string containing the path to an image file.
* @param {string} operator - one of `and`, `or` or `eor` to perform that bitwise operation, like the C logic operators `&`, `|` and `^` respectively.
* @param {Object} [options]
* @param {Object} [options.raw] - describes operand when using raw pixel data.
* @param {number} [options.raw.width]
* @param {number} [options.raw.height]
* @param {number} [options.raw.channels]
* @returns {Sharp}
* @throws {Error} Invalid parameters
*/
function boolean (operand, operator, options) {
this.options.boolean = this._createInputDescriptor(operand, options);
if (is.string(operator) && is.inArray(operator, ['and', 'or', 'eor'])) {
this.options.booleanOp = operator;
} else {
throw is.invalidParameterError('operator', 'one of: and, or, eor', operator);
}
return this;
}
/**
* Apply the linear formula `a` * input + `b` to the image to adjust image levels.
*
* When a single number is provided, it will be used for all image channels.
* When an array of numbers is provided, the array length must match the number of channels.
*
* @example
* await sharp(input)
* .linear(0.5, 2)
* .toBuffer();
*
* @example
* await sharp(rgbInput)
* .linear(
* [0.25, 0.5, 0.75],
* [150, 100, 50]
* )
* .toBuffer();
*
* @param {(number|number[])} [a=[]] multiplier
* @param {(number|number[])} [b=[]] offset
* @returns {Sharp}
* @throws {Error} Invalid parameters
*/
function linear (a, b) {
if (!is.defined(a) && is.number(b)) {
a = 1.0;
} else if (is.number(a) && !is.defined(b)) {
b = 0.0;
}
if (!is.defined(a)) {
this.options.linearA = [];
} else if (is.number(a)) {
this.options.linearA = [a];
} else if (Array.isArray(a) && a.length && a.every(is.number)) {
this.options.linearA = a;
} else {
throw is.invalidParameterError('a', 'number or array of numbers', a);
}
if (!is.defined(b)) {
this.options.linearB = [];
} else if (is.number(b)) {
this.options.linearB = [b];
} else if (Array.isArray(b) && b.length && b.every(is.number)) {
this.options.linearB = b;
} else {
throw is.invalidParameterError('b', 'number or array of numbers', b);
}
if (this.options.linearA.length !== this.options.linearB.length) {
throw new Error('Expected a and b to be arrays of the same length');
}
return this;
}
/**
* Recombine the image with the specified matrix.
*
* @since 0.21.1
*
* @example
* sharp(input)
* .recomb([
* [0.3588, 0.7044, 0.1368],
* [0.2990, 0.5870, 0.1140],
* [0.2392, 0.4696, 0.0912],
* ])
* .raw()
* .toBuffer(function(err, data, info) {
* // data contains the raw pixel data after applying the matrix
* // With this example input, a sepia filter has been applied
* });
*
* @param {Array<Array<number>>} inputMatrix - 3x3 or 4x4 Recombination matrix
* @returns {Sharp}
* @throws {Error} Invalid parameters
*/
function recomb (inputMatrix) {
if (!Array.isArray(inputMatrix)) {
throw is.invalidParameterError('inputMatrix', 'array', inputMatrix);
}
if (inputMatrix.length !== 3 && inputMatrix.length !== 4) {
throw is.invalidParameterError('inputMatrix', '3x3 or 4x4 array', inputMatrix.length);
}
const recombMatrix = inputMatrix.flat().map(Number);
if (recombMatrix.length !== 9 && recombMatrix.length !== 16) {
throw is.invalidParameterError('inputMatrix', 'cardinality of 9 or 16', recombMatrix.length);
}
this.options.recombMatrix = recombMatrix;
return this;
}
/**
* Transforms the image using brightness, saturation, hue rotation, and lightness.
* Brightness and lightness both operate on luminance, with the difference being that
* brightness is multiplicative whereas lightness is additive.
*
* @since 0.22.1
*
* @example
* // increase brightness by a factor of 2
* const output = await sharp(input)
* .modulate({
* brightness: 2
* })
* .toBuffer();
*
* @example
* // hue-rotate by 180 degrees
* const output = await sharp(input)
* .modulate({
* hue: 180
* })
* .toBuffer();
*
* @example
* // increase lightness by +50
* const output = await sharp(input)
* .modulate({
* lightness: 50
* })
* .toBuffer();
*
* @example
* // decrease brightness and saturation while also hue-rotating by 90 degrees
* const output = await sharp(input)
* .modulate({
* brightness: 0.5,
* saturation: 0.5,
* hue: 90,
* })
* .toBuffer();
*
* @param {Object} [options]
* @param {number} [options.brightness] Brightness multiplier
* @param {number} [options.saturation] Saturation multiplier
* @param {number} [options.hue] Degrees for hue rotation
* @param {number} [options.lightness] Lightness addend
* @returns {Sharp}
*/
function modulate (options) {
if (!is.plainObject(options)) {
throw is.invalidParameterError('options', 'plain object', options);
}
if ('brightness' in options) {
if (is.number(options.brightness) && options.brightness >= 0) {
this.options.brightness = options.brightness;
} else {
throw is.invalidParameterError('brightness', 'number above zero', options.brightness);
}
}
if ('saturation' in options) {
if (is.number(options.saturation) && options.saturation >= 0) {
this.options.saturation = options.saturation;
} else {
throw is.invalidParameterError('saturation', 'number above zero', options.saturation);
}
}
if ('hue' in options) {
if (is.integer(options.hue)) {
this.options.hue = options.hue % 360;
} else {
throw is.invalidParameterError('hue', 'number', options.hue);
}
}
if ('lightness' in options) {
if (is.number(options.lightness)) {
this.options.lightness = options.lightness;
} else {
throw is.invalidParameterError('lightness', 'number', options.lightness);
}
}
return this;
}
/**
* Decorate the Sharp prototype with operation-related functions.
* @module Sharp
* @private
*/
operation = (Sharp) => {
Object.assign(Sharp.prototype, {
autoOrient,
rotate,
flip,
flop,
affine,
sharpen,
erode,
dilate,
median,
blur,
flatten,
unflatten,
gamma,
negate,
normalise,
normalize,
clahe,
convolve,
threshold,
boolean,
linear,
recomb,
modulate
});
};
return operation;
}
var color;
var hasRequiredColor;
function requireColor () {
if (hasRequiredColor) return color;
hasRequiredColor = 1;
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __export = (target, all) => {
for (var name in all)
__defProp(target, name, { get: all[name], enumerable: true });
};
var __copyProps = (to, from, except, desc) => {
if (from && typeof from === "object" || typeof from === "function") {
for (let key of __getOwnPropNames(from))
if (!__hasOwnProp.call(to, key) && key !== except)
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
}
return to;
};
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
// node_modules/color/index.js
var index_exports = {};
__export(index_exports, {
default: () => index_default
});
color = __toCommonJS(index_exports);
// node_modules/color-name/index.js
var colors = {
aliceblue: [240, 248, 255],
antiquewhite: [250, 235, 215],
aqua: [0, 255, 255],
aquamarine: [127, 255, 212],
azure: [240, 255, 255],
beige: [245, 245, 220],
bisque: [255, 228, 196],
black: [0, 0, 0],
blanchedalmond: [255, 235, 205],
blue: [0, 0, 255],
blueviolet: [138, 43, 226],
brown: [165, 42, 42],
burlywood: [222, 184, 135],
cadetblue: [95, 158, 160],
chartreuse: [127, 255, 0],
chocolate: [210, 105, 30],
coral: [255, 127, 80],
cornflowerblue: [100, 149, 237],
cornsilk: [255, 248, 220],
crimson: [220, 20, 60],
cyan: [0, 255, 255],
darkblue: [0, 0, 139],
darkcyan: [0, 139, 139],
darkgoldenrod: [184, 134, 11],
darkgray: [169, 169, 169],
darkgreen: [0, 100, 0],
darkgrey: [169, 169, 169],
darkkhaki: [189, 183, 107],
darkmagenta: [139, 0, 139],
darkolivegreen: [85, 107, 47],
darkorange: [255, 140, 0],
darkorchid: [153, 50, 204],
darkred: [139, 0, 0],
darksalmon: [233, 150, 122],
darkseagreen: [143, 188, 143],
darkslateblue: [72, 61, 139],
darkslategray: [47, 79, 79],
darkslategrey: [47, 79, 79],
darkturquoise: [0, 206, 209],
darkviolet: [148, 0, 211],
deeppink: [255, 20, 147],
deepskyblue: [0, 191, 255],
dimgray: [105, 105, 105],
dimgrey: [105, 105, 105],
dodgerblue: [30, 144, 255],
firebrick: [178, 34, 34],
floralwhite: [255, 250, 240],
forestgreen: [34, 139, 34],
fuchsia: [255, 0, 255],
gainsboro: [220, 220, 220],
ghostwhite: [248, 248, 255],
gold: [255, 215, 0],
goldenrod: [218, 165, 32],
gray: [128, 128, 128],
green: [0, 128, 0],
greenyellow: [173, 255, 47],
grey: [128, 128, 128],
honeydew: [240, 255, 240],
hotpink: [255, 105, 180],
indianred: [205, 92, 92],
indigo: [75, 0, 130],
ivory: [255, 255, 240],
khaki: [240, 230, 140],
lavender: [230, 230, 250],
lavenderblush: [255, 240, 245],
lawngreen: [124, 252, 0],
lemonchiffon: [255, 250, 205],
lightblue: [173, 216, 230],
lightcoral: [240, 128, 128],
lightcyan: [224, 255, 255],
lightgoldenrodyellow: [250, 250, 210],
lightgray: [211, 211, 211],
lightgreen: [144, 238, 144],
lightgrey: [211, 211, 211],
lightpink: [255, 182, 193],
lightsalmon: [255, 160, 122],
lightseagreen: [32, 178, 170],
lightskyblue: [135, 206, 250],
lightslategray: [119, 136, 153],
lightslategrey: [119, 136, 153],
lightsteelblue: [176, 196, 222],
lightyellow: [255, 255, 224],
lime: [0, 255, 0],
limegreen: [50, 205, 50],
linen: [250, 240, 230],
magenta: [255, 0, 255],
maroon: [128, 0, 0],
mediumaquamarine: [102, 205, 170],
mediumblue: [0, 0, 205],
mediumorchid: [186, 85, 211],
mediumpurple: [147, 112, 219],
mediumseagreen: [60, 179, 113],
mediumslateblue: [123, 104, 238],
mediumspringgreen: [0, 250, 154],
mediumturquoise: [72, 209, 204],
mediumvioletred: [199, 21, 133],
midnightblue: [25, 25, 112],
mintcream: [245, 255, 250],
mistyrose: [255, 228, 225],
moccasin: [255, 228, 181],
navajowhite: [255, 222, 173],
navy: [0, 0, 128],
oldlace: [253, 245, 230],
olive: [128, 128, 0],
olivedrab: [107, 142, 35],
orange: [255, 165, 0],
orangered: [255, 69, 0],
orchid: [218, 112, 214],
palegoldenrod: [238, 232, 170],
palegreen: [152, 251, 152],
paleturquoise: [175, 238, 238],
palevioletred: [219, 112, 147],
papayawhip: [255, 239, 213],
peachpuff: [255, 218, 185],
peru: [205, 133, 63],
pink: [255, 192, 203],
plum: [221, 160, 221],
powderblue: [176, 224, 230],
purple: [128, 0, 128],
rebeccapurple: [102, 51, 153],
red: [255, 0, 0],
rosybrown: [188, 143, 143],
royalblue: [65, 105, 225],
saddlebrown: [139, 69, 19],
salmon: [250, 128, 114],
sandybrown: [244, 164, 96],
seagreen: [46, 139, 87],
seashell: [255, 245, 238],
sienna: [160, 82, 45],
silver: [192, 192, 192],
skyblue: [135, 206, 235],
slateblue: [106, 90, 205],
slategray: [112, 128, 144],
slategrey: [112, 128, 144],
snow: [255, 250, 250],
springgreen: [0, 255, 127],
steelblue: [70, 130, 180],
tan: [210, 180, 140],
teal: [0, 128, 128],
thistle: [216, 191, 216],
tomato: [255, 99, 71],
turquoise: [64, 224, 208],
violet: [238, 130, 238],
wheat: [245, 222, 179],
white: [255, 255, 255],
whitesmoke: [245, 245, 245],
yellow: [255, 255, 0],
yellowgreen: [154, 205, 50]
};
for (const key in colors) Object.freeze(colors[key]);
var color_name_default = Object.freeze(colors);
// node_modules/color-string/index.js
var reverseNames = /* @__PURE__ */ Object.create(null);
for (const name in color_name_default) {
if (Object.hasOwn(color_name_default, name)) {
reverseNames[color_name_default[name]] = name;
}
}
var cs = {
to: {},
get: {}
};
cs.get = function(string) {
const prefix = string.slice(0, 3).toLowerCase();
let value;
let model;
switch (prefix) {
case "hsl": {
value = cs.get.hsl(string);
model = "hsl";
break;
}
case "hwb": {
value = cs.get.hwb(string);
model = "hwb";
break;
}
default: {
value = cs.get.rgb(string);
model = "rgb";
break;
}
}
if (!value) {
return null;
}
return { model, value };
};
cs.get.rgb = function(string) {
if (!string) {
return null;
}
const abbr = /^#([a-f\d]{3,4})$/i;
const hex = /^#([a-f\d]{6})([a-f\d]{2})?$/i;
const rgba = /^rgba?\(\s*([+-]?(?:\d*\.)?\d+(?:e\d+)?)(?=[\s,])\s*(?:,\s*)?([+-]?(?:\d*\.)?\d+(?:e\d+)?)(?=[\s,])\s*(?:,\s*)?([+-]?(?:\d*\.)?\d+(?:e\d+)?)\s*(?:[\s,|/]\s*([+-]?(?:\d*\.)?\d+(?:e\d+)?)(%?)\s*)?\)$/i;
const per = /^rgba?\(\s*([+-]?[\d.]+)%\s*,?\s*([+-]?[\d.]+)%\s*,?\s*([+-]?[\d.]+)%\s*(?:[\s,|/]\s*([+-]?[\d.]+)(%?)\s*)?\)$/i;
const keyword = /^(\w+)$/;
let rgb = [0, 0, 0, 1];
let match;
let i;
let hexAlpha;
if (match = string.match(hex)) {
hexAlpha = match[2];
match = match[1];
for (i = 0; i < 3; i++) {
const i2 = i * 2;
rgb[i] = Number.parseInt(match.slice(i2, i2 + 2), 16);
}
if (hexAlpha) {
rgb[3] = Number.parseInt(hexAlpha, 16) / 255;
}
} else if (match = string.match(abbr)) {
match = match[1];
hexAlpha = match[3];
for (i = 0; i < 3; i++) {
rgb[i] = Number.parseInt(match[i] + match[i], 16);
}
if (hexAlpha) {
rgb[3] = Number.parseInt(hexAlpha + hexAlpha, 16) / 255;
}
} else if (match = string.match(rgba)) {
for (i = 0; i < 3; i++) {
rgb[i] = Number.parseFloat(match[i + 1]);
}
if (match[4]) {
rgb[3] = match[5] ? Number.parseFloat(match[4]) * 0.01 : Number.parseFloat(match[4]);
}
} else if (match = string.match(per)) {
for (i = 0; i < 3; i++) {
rgb[i] = Math.round(Number.parseFloat(match[i + 1]) * 2.55);
}
if (match[4]) {
rgb[3] = match[5] ? Number.parseFloat(match[4]) * 0.01 : Number.parseFloat(match[4]);
}
} else if (match = string.toLowerCase().match(keyword)) {
if (match[1] === "transparent") {
return [0, 0, 0, 0];
}
if (!Object.hasOwn(color_name_default, match[1])) {
return null;
}
rgb = color_name_default[match[1]].slice();
rgb[3] = 1;
return rgb;
} else {
return null;
}
for (i = 0; i < 3; i++) {
rgb[i] = clamp(rgb[i], 0, 255);
}
rgb[3] = clamp(rgb[3], 0, 1);
return rgb;
};
cs.get.hsl = function(string) {
if (!string) {
return null;
}
const hsl = /^hsla?\(\s*([+-]?(?:\d{0,3}\.)?\d+)(?:deg)?\s*,?\s*([+-]?[\d.]+)%\s*,?\s*([+-]?[\d.]+)%\s*(?:[,|/]\s*([+-]?(?=\.\d|\d)(?:0|[1-9]\d*)?(?:\.\d*)?(?:e[+-]?\d+)?)\s*)?\)$/i;
const match = string.match(hsl);
if (match) {
const alpha = Number.parseFloat(match[4]);
const h = (Number.parseFloat(match[1]) % 360 + 360) % 360;
const s = clamp(Number.parseFloat(match[2]), 0, 100);
const l = clamp(Number.parseFloat(match[3]), 0, 100);
const a = clamp(Number.isNaN(alpha) ? 1 : alpha, 0, 1);
return [h, s, l, a];
}
return null;
};
cs.get.hwb = function(string) {
if (!string) {
return null;
}
const hwb = /^hwb\(\s*([+-]?\d{0,3}(?:\.\d+)?)(?:deg)?\s*[\s,]\s*([+-]?[\d.]+)%\s*[\s,]\s*([+-]?[\d.]+)%\s*(?:[\s,]\s*([+-]?(?=\.\d|\d)(?:0|[1-9]\d*)?(?:\.\d*)?(?:e[+-]?\d+)?)\s*)?\)$/i;
const match = string.match(hwb);
if (match) {
const alpha = Number.parseFloat(match[4]);
const h = (Number.parseFloat(match[1]) % 360 + 360) % 360;
const w = clamp(Number.parseFloat(match[2]), 0, 100);
const b = clamp(Number.parseFloat(match[3]), 0, 100);
const a = clamp(Number.isNaN(alpha) ? 1 : alpha, 0, 1);
return [h, w, b, a];
}
return null;
};
cs.to.hex = function(...rgba) {
return "#" + hexDouble(rgba[0]) + hexDouble(rgba[1]) + hexDouble(rgba[2]) + (rgba[3] < 1 ? hexDouble(Math.round(rgba[3] * 255)) : "");
};
cs.to.rgb = function(...rgba) {
return rgba.length < 4 || rgba[3] === 1 ? "rgb(" + Math.round(rgba[0]) + ", " + Math.round(rgba[1]) + ", " + Math.round(rgba[2]) + ")" : "rgba(" + Math.round(rgba[0]) + ", " + Math.round(rgba[1]) + ", " + Math.round(rgba[2]) + ", " + rgba[3] + ")";
};
cs.to.rgb.percent = function(...rgba) {
const r = Math.round(rgba[0] / 255 * 100);
const g = Math.round(rgba[1] / 255 * 100);
const b = Math.round(rgba[2] / 255 * 100);
return rgba.length < 4 || rgba[3] === 1 ? "rgb(" + r + "%, " + g + "%, " + b + "%)" : "rgba(" + r + "%, " + g + "%, " + b + "%, " + rgba[3] + ")";
};
cs.to.hsl = function(...hsla) {
return hsla.length < 4 || hsla[3] === 1 ? "hsl(" + hsla[0] + ", " + hsla[1] + "%, " + hsla[2] + "%)" : "hsla(" + hsla[0] + ", " + hsla[1] + "%, " + hsla[2] + "%, " + hsla[3] + ")";
};
cs.to.hwb = function(...hwba) {
let a = "";
if (hwba.length >= 4 && hwba[3] !== 1) {
a = ", " + hwba[3];
}
return "hwb(" + hwba[0] + ", " + hwba[1] + "%, " + hwba[2] + "%" + a + ")";
};
cs.to.keyword = function(...rgb) {
return reverseNames[rgb.slice(0, 3)];
};
function clamp(number_, min, max) {
return Math.min(Math.max(min, number_), max);
}
function hexDouble(number_) {
const string_ = Math.round(number_).toString(16).toUpperCase();
return string_.length < 2 ? "0" + string_ : string_;
}
var color_string_default = cs;
// node_modules/color-convert/conversions.js
var reverseKeywords = {};
for (const key of Object.keys(color_name_default)) {
reverseKeywords[color_name_default[key]] = key;
}
var convert = {
rgb: { channels: 3, labels: "rgb" },
hsl: { channels: 3, labels: "hsl" },
hsv: { channels: 3, labels: "hsv" },
hwb: { channels: 3, labels: "hwb" },
cmyk: { channels: 4, labels: "cmyk" },
xyz: { channels: 3, labels: "xyz" },
lab: { channels: 3, labels: "lab" },
oklab: { channels: 3, labels: ["okl", "oka", "okb"] },
lch: { channels: 3, labels: "lch" },
oklch: { channels: 3, labels: ["okl", "okc", "okh"] },
hex: { channels: 1, labels: ["hex"] },
keyword: { channels: 1, labels: ["keyword"] },
ansi16: { channels: 1, labels: ["ansi16"] },
ansi256: { channels: 1, labels: ["ansi256"] },
hcg: { channels: 3, labels: ["h", "c", "g"] },
apple: { channels: 3, labels: ["r16", "g16", "b16"] },
gray: { channels: 1, labels: ["gray"] }
};
var conversions_default = convert;
var LAB_FT = (6 / 29) ** 3;
function srgbNonlinearTransform(c) {
const cc = c > 31308e-7 ? 1.055 * c ** (1 / 2.4) - 0.055 : c * 12.92;
return Math.min(Math.max(0, cc), 1);
}
function srgbNonlinearTransformInv(c) {
return c > 0.04045 ? ((c + 0.055) / 1.055) ** 2.4 : c / 12.92;
}
for (const model of Object.keys(convert)) {
if (!("channels" in convert[model])) {
throw new Error("missing channels property: " + model);
}
if (!("labels" in convert[model])) {
throw new Error("missing channel labels property: " + model);
}
if (convert[model].labels.length !== convert[model].channels) {
throw new Error("channel and label counts mismatch: " + model);
}
const { channels, labels } = convert[model];
delete convert[model].channels;
delete convert[model].labels;
Object.defineProperty(convert[model], "channels", { value: channels });
Object.defineProperty(convert[model], "labels", { value: labels });
}
convert.rgb.hsl = function(rgb) {
const r = rgb[0] / 255;
const g = rgb[1] / 255;
const b = rgb[2] / 255;
const min = Math.min(r, g, b);
const max = Math.max(r, g, b);
const delta = max - min;
let h;
let s;
switch (max) {
case min: {
h = 0;
break;
}
case r: {
h = (g - b) / delta;
break;
}
case g: {
h = 2 + (b - r) / delta;
break;
}
case b: {
h = 4 + (r - g) / delta;
break;
}
}
h = Math.min(h * 60, 360);
if (h < 0) {
h += 360;
}
const l = (min + max) / 2;
if (max === min) {
s = 0;
} else if (l <= 0.5) {
s = delta / (max + min);
} else {
s = delta / (2 - max - min);
}
return [h, s * 100, l * 100];
};
convert.rgb.hsv = function(rgb) {
let rdif;
let gdif;
let bdif;
let h;
let s;
const r = rgb[0] / 255;
const g = rgb[1] / 255;
const b = rgb[2] / 255;
const v = Math.max(r, g, b);
const diff = v - Math.min(r, g, b);
const diffc = function(c) {
return (v - c) / 6 / diff + 1 / 2;
};
if (diff === 0) {
h = 0;
s = 0;
} else {
s = diff / v;
rdif = diffc(r);
gdif = diffc(g);
bdif = diffc(b);
switch (v) {
case r: {
h = bdif - gdif;
break;
}
case g: {
h = 1 / 3 + rdif - bdif;
break;
}
case b: {
h = 2 / 3 + gdif - rdif;
break;
}
}
if (h < 0) {
h += 1;
} else if (h > 1) {
h -= 1;
}
}
return [
h * 360,
s * 100,
v * 100
];
};
convert.rgb.hwb = function(rgb) {
const r = rgb[0];
const g = rgb[1];
let b = rgb[2];
const h = convert.rgb.hsl(rgb)[0];
const w = 1 / 255 * Math.min(r, Math.min(g, b));
b = 1 - 1 / 255 * Math.max(r, Math.max(g, b));
return [h, w * 100, b * 100];
};
convert.rgb.oklab = function(rgb) {
const r = srgbNonlinearTransformInv(rgb[0] / 255);
const g = srgbNonlinearTransformInv(rgb[1] / 255);
const b = srgbNonlinearTransformInv(rgb[2] / 255);
const lp = Math.cbrt(0.4122214708 * r + 0.5363325363 * g + 0.0514459929 * b);
const mp = Math.cbrt(0.2119034982 * r + 0.6806995451 * g + 0.1073969566 * b);
const sp = Math.cbrt(0.0883024619 * r + 0.2817188376 * g + 0.6299787005 * b);
const l = 0.2104542553 * lp + 0.793617785 * mp - 0.0040720468 * sp;
const aa = 1.9779984951 * lp - 2.428592205 * mp + 0.4505937099 * sp;
const bb = 0.0259040371 * lp + 0.7827717662 * mp - 0.808675766 * sp;
return [l * 100, aa * 100, bb * 100];
};
convert.rgb.cmyk = function(rgb) {
const r = rgb[0] / 255;
const g = rgb[1] / 255;
const b = rgb[2] / 255;
const k = Math.min(1 - r, 1 - g, 1 - b);
const c = (1 - r - k) / (1 - k) || 0;
const m = (1 - g - k) / (1 - k) || 0;
const y = (1 - b - k) / (1 - k) || 0;
return [c * 100, m * 100, y * 100, k * 100];
};
function comparativeDistance(x, y) {
return (x[0] - y[0]) ** 2 + (x[1] - y[1]) ** 2 + (x[2] - y[2]) ** 2;
}
convert.rgb.keyword = function(rgb) {
const reversed = reverseKeywords[rgb];
if (reversed) {
return reversed;
}
let currentClosestDistance = Number.POSITIVE_INFINITY;
let currentClosestKeyword;
for (const keyword of Object.keys(color_name_default)) {
const value = color_name_default[keyword];
const distance = comparativeDistance(rgb, value);
if (distance < currentClosestDistance) {
currentClosestDistance = distance;
currentClosestKeyword = keyword;
}
}
return currentClosestKeyword;
};
convert.keyword.rgb = function(keyword) {
return [...color_name_default[keyword]];
};
convert.rgb.xyz = function(rgb) {
const r = srgbNonlinearTransformInv(rgb[0] / 255);
const g = srgbNonlinearTransformInv(rgb[1] / 255);
const b = srgbNonlinearTransformInv(rgb[2] / 255);
const x = r * 0.4124564 + g * 0.3575761 + b * 0.1804375;
const y = r * 0.2126729 + g * 0.7151522 + b * 0.072175;
const z = r * 0.0193339 + g * 0.119192 + b * 0.9503041;
return [x * 100, y * 100, z * 100];
};
convert.rgb.lab = function(rgb) {
const xyz = convert.rgb.xyz(rgb);
let x = xyz[0];
let y = xyz[1];
let z = xyz[2];
x /= 95.047;
y /= 100;
z /= 108.883;
x = x > LAB_FT ? x ** (1 / 3) : 7.787 * x + 16 / 116;
y = y > LAB_FT ? y ** (1 / 3) : 7.787 * y + 16 / 116;
z = z > LAB_FT ? z ** (1 / 3) : 7.787 * z + 16 / 116;
const l = 116 * y - 16;
const a = 500 * (x - y);
const b = 200 * (y - z);
return [l, a, b];
};
convert.hsl.rgb = function(hsl) {
const h = hsl[0] / 360;
const s = hsl[1] / 100;
const l = hsl[2] / 100;
let t3;
let value;
if (s === 0) {
value = l * 255;
return [value, value, value];
}
const t2 = l < 0.5 ? l * (1 + s) : l + s - l * s;
const t1 = 2 * l - t2;
const rgb = [0, 0, 0];
for (let i = 0; i < 3; i++) {
t3 = h + 1 / 3 * -(i - 1);
if (t3 < 0) {
t3++;
}
if (t3 > 1) {
t3--;
}
if (6 * t3 < 1) {
value = t1 + (t2 - t1) * 6 * t3;
} else if (2 * t3 < 1) {
value = t2;
} else if (3 * t3 < 2) {
value = t1 + (t2 - t1) * (2 / 3 - t3) * 6;
} else {
value = t1;
}
rgb[i] = value * 255;
}
return rgb;
};
convert.hsl.hsv = function(hsl) {
const h = hsl[0];
let s = hsl[1] / 100;
let l = hsl[2] / 100;
let smin = s;
const lmin = Math.max(l, 0.01);
l *= 2;
s *= l <= 1 ? l : 2 - l;
smin *= lmin <= 1 ? lmin : 2 - lmin;
const v = (l + s) / 2;
const sv = l === 0 ? 2 * smin / (lmin + smin) : 2 * s / (l + s);
return [h, sv * 100, v * 100];
};
convert.hsv.rgb = function(hsv) {
const h = hsv[0] / 60;
const s = hsv[1] / 100;
let v = hsv[2] / 100;
const hi = Math.floor(h) % 6;
const f = h - Math.floor(h);
const p = 255 * v * (1 - s);
const q = 255 * v * (1 - s * f);
const t = 255 * v * (1 - s * (1 - f));
v *= 255;
switch (hi) {
case 0: {
return [v, t, p];
}
case 1: {
return [q, v, p];
}
case 2: {
return [p, v, t];
}
case 3: {
return [p, q, v];
}
case 4: {
return [t, p, v];
}
case 5: {
return [v, p, q];
}
}
};
convert.hsv.hsl = function(hsv) {
const h = hsv[0];
const s = hsv[1] / 100;
const v = hsv[2] / 100;
const vmin = Math.max(v, 0.01);
let sl;
let l;
l = (2 - s) * v;
const lmin = (2 - s) * vmin;
sl = s * vmin;
sl /= lmin <= 1 ? lmin : 2 - lmin;
sl = sl || 0;
l /= 2;
return [h, sl * 100, l * 100];
};
convert.hwb.rgb = function(hwb) {
const h = hwb[0] / 360;
let wh = hwb[1] / 100;
let bl = hwb[2] / 100;
const ratio = wh + bl;
let f;
if (ratio > 1) {
wh /= ratio;
bl /= ratio;
}
const i = Math.floor(6 * h);
const v = 1 - bl;
f = 6 * h - i;
if ((i & 1) !== 0) {
f = 1 - f;
}
const n = wh + f * (v - wh);
let r;
let g;
let b;
switch (i) {
default:
case 6:
case 0: {
r = v;
g = n;
b = wh;
break;
}
case 1: {
r = n;
g = v;
b = wh;
break;
}
case 2: {
r = wh;
g = v;
b = n;
break;
}
case 3: {
r = wh;
g = n;
b = v;
break;
}
case 4: {
r = n;
g = wh;
b = v;
break;
}
case 5: {
r = v;
g = wh;
b = n;
break;
}
}
return [r * 255, g * 255, b * 255];
};
convert.cmyk.rgb = function(cmyk) {
const c = cmyk[0] / 100;
const m = cmyk[1] / 100;
const y = cmyk[2] / 100;
const k = cmyk[3] / 100;
const r = 1 - Math.min(1, c * (1 - k) + k);
const g = 1 - Math.min(1, m * (1 - k) + k);
const b = 1 - Math.min(1, y * (1 - k) + k);
return [r * 255, g * 255, b * 255];
};
convert.xyz.rgb = function(xyz) {
const x = xyz[0] / 100;
const y = xyz[1] / 100;
const z = xyz[2] / 100;
let r;
let g;
let b;
r = x * 3.2404542 + y * -1.5371385 + z * -0.4985314;
g = x * -0.969266 + y * 1.8760108 + z * 0.041556;
b = x * 0.0556434 + y * -0.2040259 + z * 1.0572252;
r = srgbNonlinearTransform(r);
g = srgbNonlinearTransform(g);
b = srgbNonlinearTransform(b);
return [r * 255, g * 255, b * 255];
};
convert.xyz.lab = function(xyz) {
let x = xyz[0];
let y = xyz[1];
let z = xyz[2];
x /= 95.047;
y /= 100;
z /= 108.883;
x = x > LAB_FT ? x ** (1 / 3) : 7.787 * x + 16 / 116;
y = y > LAB_FT ? y ** (1 / 3) : 7.787 * y + 16 / 116;
z = z > LAB_FT ? z ** (1 / 3) : 7.787 * z + 16 / 116;
const l = 116 * y - 16;
const a = 500 * (x - y);
const b = 200 * (y - z);
return [l, a, b];
};
convert.xyz.oklab = function(xyz) {
const x = xyz[0] / 100;
const y = xyz[1] / 100;
const z = xyz[2] / 100;
const lp = Math.cbrt(0.8189330101 * x + 0.3618667424 * y - 0.1288597137 * z);
const mp = Math.cbrt(0.0329845436 * x + 0.9293118715 * y + 0.0361456387 * z);
const sp = Math.cbrt(0.0482003018 * x + 0.2643662691 * y + 0.633851707 * z);
const l = 0.2104542553 * lp + 0.793617785 * mp - 0.0040720468 * sp;
const a = 1.9779984951 * lp - 2.428592205 * mp + 0.4505937099 * sp;
const b = 0.0259040371 * lp + 0.7827717662 * mp - 0.808675766 * sp;
return [l * 100, a * 100, b * 100];
};
convert.oklab.oklch = function(oklab) {
return convert.lab.lch(oklab);
};
convert.oklab.xyz = function(oklab) {
const ll = oklab[0] / 100;
const a = oklab[1] / 100;
const b = oklab[2] / 100;
const l = (0.999999998 * ll + 0.396337792 * a + 0.215803758 * b) ** 3;
const m = (1.000000008 * ll - 0.105561342 * a - 0.063854175 * b) ** 3;
const s = (1.000000055 * ll - 0.089484182 * a - 1.291485538 * b) ** 3;
const x = 1.227013851 * l - 0.55779998 * m + 0.281256149 * s;
const y = -0.040580178 * l + 1.11225687 * m - 0.071676679 * s;
const z = -0.076381285 * l - 0.421481978 * m + 1.58616322 * s;
return [x * 100, y * 100, z * 100];
};
convert.oklab.rgb = function(oklab) {
const ll = oklab[0] / 100;
const aa = oklab[1] / 100;
const bb = oklab[2] / 100;
const l = (ll + 0.3963377774 * aa + 0.2158037573 * bb) ** 3;
const m = (ll - 0.1055613458 * aa - 0.0638541728 * bb) ** 3;
const s = (ll - 0.0894841775 * aa - 1.291485548 * bb) ** 3;
const r = srgbNonlinearTransform(4.0767416621 * l - 3.3077115913 * m + 0.2309699292 * s);
const g = srgbNonlinearTransform(-1.2684380046 * l + 2.6097574011 * m - 0.3413193965 * s);
const b = srgbNonlinearTransform(-0.0041960863 * l - 0.7034186147 * m + 1.707614701 * s);
return [r * 255, g * 255, b * 255];
};
convert.oklch.oklab = function(oklch) {
return convert.lch.lab(oklch);
};
convert.lab.xyz = function(lab) {
const l = lab[0];
const a = lab[1];
const b = lab[2];
let x;
let y;
let z;
y = (l + 16) / 116;
x = a / 500 + y;
z = y - b / 200;
const y2 = y ** 3;
const x2 = x ** 3;
const z2 = z ** 3;
y = y2 > LAB_FT ? y2 : (y - 16 / 116) / 7.787;
x = x2 > LAB_FT ? x2 : (x - 16 / 116) / 7.787;
z = z2 > LAB_FT ? z2 : (z - 16 / 116) / 7.787;
x *= 95.047;
y *= 100;
z *= 108.883;
return [x, y, z];
};
convert.lab.lch = function(lab) {
const l = lab[0];
const a = lab[1];
const b = lab[2];
let h;
const hr = Math.atan2(b, a);
h = hr * 360 / 2 / Math.PI;
if (h < 0) {
h += 360;
}
const c = Math.sqrt(a * a + b * b);
return [l, c, h];
};
convert.lch.lab = function(lch) {
const l = lch[0];
const c = lch[1];
const h = lch[2];
const hr = h / 360 * 2 * Math.PI;
const a = c * Math.cos(hr);
const b = c * Math.sin(hr);
return [l, a, b];
};
convert.rgb.ansi16 = function(args, saturation = null) {
const [r, g, b] = args;
let value = saturation === null ? convert.rgb.hsv(args)[2] : saturation;
value = Math.round(value / 50);
if (value === 0) {
return 30;
}
let ansi = 30 + (Math.round(b / 255) << 2 | Math.round(g / 255) << 1 | Math.round(r / 255));
if (value === 2) {
ansi += 60;
}
return ansi;
};
convert.hsv.ansi16 = function(args) {
return convert.rgb.ansi16(convert.hsv.rgb(args), args[2]);
};
convert.rgb.ansi256 = function(args) {
const r = args[0];
const g = args[1];
const b = args[2];
if (r >> 4 === g >> 4 && g >> 4 === b >> 4) {
if (r < 8) {
return 16;
}
if (r > 248) {
return 231;
}
return Math.round((r - 8) / 247 * 24) + 232;
}
const ansi = 16 + 36 * Math.round(r / 255 * 5) + 6 * Math.round(g / 255 * 5) + Math.round(b / 255 * 5);
return ansi;
};
convert.ansi16.rgb = function(args) {
args = args[0];
let color = args % 10;
if (color === 0 || color === 7) {
if (args > 50) {
color += 3.5;
}
color = color / 10.5 * 255;
return [color, color, color];
}
const mult = (Math.trunc(args > 50) + 1) * 0.5;
const r = (color & 1) * mult * 255;
const g = (color >> 1 & 1) * mult * 255;
const b = (color >> 2 & 1) * mult * 255;
return [r, g, b];
};
convert.ansi256.rgb = function(args) {
args = args[0];
if (args >= 232) {
const c = (args - 232) * 10 + 8;
return [c, c, c];
}
args -= 16;
let rem;
const r = Math.floor(args / 36) / 5 * 255;
const g = Math.floor((rem = args % 36) / 6) / 5 * 255;
const b = rem % 6 / 5 * 255;
return [r, g, b];
};
convert.rgb.hex = function(args) {
const integer = ((Math.round(args[0]) & 255) << 16) + ((Math.round(args[1]) & 255) << 8) + (Math.round(args[2]) & 255);
const string = integer.toString(16).toUpperCase();
return "000000".slice(string.length) + string;
};
convert.hex.rgb = function(args) {
const match = args.toString(16).match(/[a-f\d]{6}|[a-f\d]{3}/i);
if (!match) {
return [0, 0, 0];
}
let colorString = match[0];
if (match[0].length === 3) {
colorString = [...colorString].map((char) => char + char).join("");
}
const integer = Number.parseInt(colorString, 16);
const r = integer >> 16 & 255;
const g = integer >> 8 & 255;
const b = integer & 255;
return [r, g, b];
};
convert.rgb.hcg = function(rgb) {
const r = rgb[0] / 255;
const g = rgb[1] / 255;
const b = rgb[2] / 255;
const max = Math.max(Math.max(r, g), b);
const min = Math.min(Math.min(r, g), b);
const chroma = max - min;
let hue;
const grayscale = chroma < 1 ? min / (1 - chroma) : 0;
if (chroma <= 0) {
hue = 0;
} else if (max === r) {
hue = (g - b) / chroma % 6;
} else if (max === g) {
hue = 2 + (b - r) / chroma;
} else {
hue = 4 + (r - g) / chroma;
}
hue /= 6;
hue %= 1;
return [hue * 360, chroma * 100, grayscale * 100];
};
convert.hsl.hcg = function(hsl) {
const s = hsl[1] / 100;
const l = hsl[2] / 100;
const c = l < 0.5 ? 2 * s * l : 2 * s * (1 - l);
let f = 0;
if (c < 1) {
f = (l - 0.5 * c) / (1 - c);
}
return [hsl[0], c * 100, f * 100];
};
convert.hsv.hcg = function(hsv) {
const s = hsv[1] / 100;
const v = hsv[2] / 100;
const c = s * v;
let f = 0;
if (c < 1) {
f = (v - c) / (1 - c);
}
return [hsv[0], c * 100, f * 100];
};
convert.hcg.rgb = function(hcg) {
const h = hcg[0] / 360;
const c = hcg[1] / 100;
const g = hcg[2] / 100;
if (c === 0) {
return [g * 255, g * 255, g * 255];
}
const pure = [0, 0, 0];
const hi = h % 1 * 6;
const v = hi % 1;
const w = 1 - v;
let mg = 0;
switch (Math.floor(hi)) {
case 0: {
pure[0] = 1;
pure[1] = v;
pure[2] = 0;
break;
}
case 1: {
pure[0] = w;
pure[1] = 1;
pure[2] = 0;
break;
}
case 2: {
pure[0] = 0;
pure[1] = 1;
pure[2] = v;
break;
}
case 3: {
pure[0] = 0;
pure[1] = w;
pure[2] = 1;
break;
}
case 4: {
pure[0] = v;
pure[1] = 0;
pure[2] = 1;
break;
}
default: {
pure[0] = 1;
pure[1] = 0;
pure[2] = w;
}
}
mg = (1 - c) * g;
return [
(c * pure[0] + mg) * 255,
(c * pure[1] + mg) * 255,
(c * pure[2] + mg) * 255
];
};
convert.hcg.hsv = function(hcg) {
const c = hcg[1] / 100;
const g = hcg[2] / 100;
const v = c + g * (1 - c);
let f = 0;
if (v > 0) {
f = c / v;
}
return [hcg[0], f * 100, v * 100];
};
convert.hcg.hsl = function(hcg) {
const c = hcg[1] / 100;
const g = hcg[2] / 100;
const l = g * (1 - c) + 0.5 * c;
let s = 0;
if (l > 0 && l < 0.5) {
s = c / (2 * l);
} else if (l >= 0.5 && l < 1) {
s = c / (2 * (1 - l));
}
return [hcg[0], s * 100, l * 100];
};
convert.hcg.hwb = function(hcg) {
const c = hcg[1] / 100;
const g = hcg[2] / 100;
const v = c + g * (1 - c);
return [hcg[0], (v - c) * 100, (1 - v) * 100];
};
convert.hwb.hcg = function(hwb) {
const w = hwb[1] / 100;
const b = hwb[2] / 100;
const v = 1 - b;
const c = v - w;
let g = 0;
if (c < 1) {
g = (v - c) / (1 - c);
}
return [hwb[0], c * 100, g * 100];
};
convert.apple.rgb = function(apple) {
return [apple[0] / 65535 * 255, apple[1] / 65535 * 255, apple[2] / 65535 * 255];
};
convert.rgb.apple = function(rgb) {
return [rgb[0] / 255 * 65535, rgb[1] / 255 * 65535, rgb[2] / 255 * 65535];
};
convert.gray.rgb = function(args) {
return [args[0] / 100 * 255, args[0] / 100 * 255, args[0] / 100 * 255];
};
convert.gray.hsl = function(args) {
return [0, 0, args[0]];
};
convert.gray.hsv = convert.gray.hsl;
convert.gray.hwb = function(gray) {
return [0, 100, gray[0]];
};
convert.gray.cmyk = function(gray) {
return [0, 0, 0, gray[0]];
};
convert.gray.lab = function(gray) {
return [gray[0], 0, 0];
};
convert.gray.hex = function(gray) {
const value = Math.round(gray[0] / 100 * 255) & 255;
const integer = (value << 16) + (value << 8) + value;
const string = integer.toString(16).toUpperCase();
return "000000".slice(string.length) + string;
};
convert.rgb.gray = function(rgb) {
const value = (rgb[0] + rgb[1] + rgb[2]) / 3;
return [value / 255 * 100];
};
// node_modules/color-convert/route.js
function buildGraph() {
const graph = {};
const models2 = Object.keys(conversions_default);
for (let { length } = models2, i = 0; i < length; i++) {
graph[models2[i]] = {
// http://jsperf.com/1-vs-infinity
// micro-opt, but this is simple.
distance: -1,
parent: null
};
}
return graph;
}
function deriveBFS(fromModel) {
const graph = buildGraph();
const queue = [fromModel];
graph[fromModel].distance = 0;
while (queue.length > 0) {
const current = queue.pop();
const adjacents = Object.keys(conversions_default[current]);
for (let { length } = adjacents, i = 0; i < length; i++) {
const adjacent = adjacents[i];
const node = graph[adjacent];
if (node.distance === -1) {
node.distance = graph[current].distance + 1;
node.parent = current;
queue.unshift(adjacent);
}
}
}
return graph;
}
function link(from, to) {
return function(args) {
return to(from(args));
};
}
function wrapConversion(toModel, graph) {
const path = [graph[toModel].parent, toModel];
let fn = conversions_default[graph[toModel].parent][toModel];
let cur = graph[toModel].parent;
while (graph[cur].parent) {
path.unshift(graph[cur].parent);
fn = link(conversions_default[graph[cur].parent][cur], fn);
cur = graph[cur].parent;
}
fn.conversion = path;
return fn;
}
function route(fromModel) {
const graph = deriveBFS(fromModel);
const conversion = {};
const models2 = Object.keys(graph);
for (let { length } = models2, i = 0; i < length; i++) {
const toModel = models2[i];
const node = graph[toModel];
if (node.parent === null) {
continue;
}
conversion[toModel] = wrapConversion(toModel, graph);
}
return conversion;
}
var route_default = route;
// node_modules/color-convert/index.js
var convert2 = {};
var models = Object.keys(conversions_default);
function wrapRaw(fn) {
const wrappedFn = function(...args) {
const arg0 = args[0];
if (arg0 === void 0 || arg0 === null) {
return arg0;
}
if (arg0.length > 1) {
args = arg0;
}
return fn(args);
};
if ("conversion" in fn) {
wrappedFn.conversion = fn.conversion;
}
return wrappedFn;
}
function wrapRounded(fn) {
const wrappedFn = function(...args) {
const arg0 = args[0];
if (arg0 === void 0 || arg0 === null) {
return arg0;
}
if (arg0.length > 1) {
args = arg0;
}
const result = fn(args);
if (typeof result === "object") {
for (let { length } = result, i = 0; i < length; i++) {
result[i] = Math.round(result[i]);
}
}
return result;
};
if ("conversion" in fn) {
wrappedFn.conversion = fn.conversion;
}
return wrappedFn;
}
for (const fromModel of models) {
convert2[fromModel] = {};
Object.defineProperty(convert2[fromModel], "channels", { value: conversions_default[fromModel].channels });
Object.defineProperty(convert2[fromModel], "labels", { value: conversions_default[fromModel].labels });
const routes = route_default(fromModel);
const routeModels = Object.keys(routes);
for (const toModel of routeModels) {
const fn = routes[toModel];
convert2[fromModel][toModel] = wrapRounded(fn);
convert2[fromModel][toModel].raw = wrapRaw(fn);
}
}
var color_convert_default = convert2;
// node_modules/color/index.js
var skippedModels = [
// To be honest, I don't really feel like keyword belongs in color convert, but eh.
"keyword",
// Gray conflicts with some method names, and has its own method defined.
"gray",
// Shouldn't really be in color-convert either...
"hex"
];
var hashedModelKeys = {};
for (const model of Object.keys(color_convert_default)) {
hashedModelKeys[[...color_convert_default[model].labels].sort().join("")] = model;
}
var limiters = {};
function Color(object, model) {
if (!(this instanceof Color)) {
return new Color(object, model);
}
if (model && model in skippedModels) {
model = null;
}
if (model && !(model in color_convert_default)) {
throw new Error("Unknown model: " + model);
}
let i;
let channels;
if (object == null) {
this.model = "rgb";
this.color = [0, 0, 0];
this.valpha = 1;
} else if (object instanceof Color) {
this.model = object.model;
this.color = [...object.color];
this.valpha = object.valpha;
} else if (typeof object === "string") {
const result = color_string_default.get(object);
if (result === null) {
throw new Error("Unable to parse color from string: " + object);
}
this.model = result.model;
channels = color_convert_default[this.model].channels;
this.color = result.value.slice(0, channels);
this.valpha = typeof result.value[channels] === "number" ? result.value[channels] : 1;
} else if (object.length > 0) {
this.model = model || "rgb";
channels = color_convert_default[this.model].channels;
const newArray = Array.prototype.slice.call(object, 0, channels);
this.color = zeroArray(newArray, channels);
this.valpha = typeof object[channels] === "number" ? object[channels] : 1;
} else if (typeof object === "number") {
this.model = "rgb";
this.color = [
object >> 16 & 255,
object >> 8 & 255,
object & 255
];
this.valpha = 1;
} else {
this.valpha = 1;
const keys = Object.keys(object);
if ("alpha" in object) {
keys.splice(keys.indexOf("alpha"), 1);
this.valpha = typeof object.alpha === "number" ? object.alpha : 0;
}
const hashedKeys = keys.sort().join("");
if (!(hashedKeys in hashedModelKeys)) {
throw new Error("Unable to parse color from object: " + JSON.stringify(object));
}
this.model = hashedModelKeys[hashedKeys];
const { labels } = color_convert_default[this.model];
const color = [];
for (i = 0; i < labels.length; i++) {
color.push(object[labels[i]]);
}
this.color = zeroArray(color);
}
if (limiters[this.model]) {
channels = color_convert_default[this.model].channels;
for (i = 0; i < channels; i++) {
const limit = limiters[this.model][i];
if (limit) {
this.color[i] = limit(this.color[i]);
}
}
}
this.valpha = Math.max(0, Math.min(1, this.valpha));
if (Object.freeze) {
Object.freeze(this);
}
}
Color.prototype = {
toString() {
return this.string();
},
toJSON() {
return this[this.model]();
},
string(places) {
let self = this.model in color_string_default.to ? this : this.rgb();
self = self.round(typeof places === "number" ? places : 1);
const arguments_ = self.valpha === 1 ? self.color : [...self.color, this.valpha];
return color_string_default.to[self.model](...arguments_);
},
percentString(places) {
const self = this.rgb().round(typeof places === "number" ? places : 1);
const arguments_ = self.valpha === 1 ? self.color : [...self.color, this.valpha];
return color_string_default.to.rgb.percent(...arguments_);
},
array() {
return this.valpha === 1 ? [...this.color] : [...this.color, this.valpha];
},
object() {
const result = {};
const { channels } = color_convert_default[this.model];
const { labels } = color_convert_default[this.model];
for (let i = 0; i < channels; i++) {
result[labels[i]] = this.color[i];
}
if (this.valpha !== 1) {
result.alpha = this.valpha;
}
return result;
},
unitArray() {
const rgb = this.rgb().color;
rgb[0] /= 255;
rgb[1] /= 255;
rgb[2] /= 255;
if (this.valpha !== 1) {
rgb.push(this.valpha);
}
return rgb;
},
unitObject() {
const rgb = this.rgb().object();
rgb.r /= 255;
rgb.g /= 255;
rgb.b /= 255;
if (this.valpha !== 1) {
rgb.alpha = this.valpha;
}
return rgb;
},
round(places) {
places = Math.max(places || 0, 0);
return new Color([...this.color.map(roundToPlace(places)), this.valpha], this.model);
},
alpha(value) {
if (value !== void 0) {
return new Color([...this.color, Math.max(0, Math.min(1, value))], this.model);
}
return this.valpha;
},
// Rgb
red: getset("rgb", 0, maxfn(255)),
green: getset("rgb", 1, maxfn(255)),
blue: getset("rgb", 2, maxfn(255)),
hue: getset(["hsl", "hsv", "hsl", "hwb", "hcg"], 0, (value) => (value % 360 + 360) % 360),
saturationl: getset("hsl", 1, maxfn(100)),
lightness: getset("hsl", 2, maxfn(100)),
saturationv: getset("hsv", 1, maxfn(100)),
value: getset("hsv", 2, maxfn(100)),
chroma: getset("hcg", 1, maxfn(100)),
gray: getset("hcg", 2, maxfn(100)),
white: getset("hwb", 1, maxfn(100)),
wblack: getset("hwb", 2, maxfn(100)),
cyan: getset("cmyk", 0, maxfn(100)),
magenta: getset("cmyk", 1, maxfn(100)),
yellow: getset("cmyk", 2, maxfn(100)),
black: getset("cmyk", 3, maxfn(100)),
x: getset("xyz", 0, maxfn(95.047)),
y: getset("xyz", 1, maxfn(100)),
z: getset("xyz", 2, maxfn(108.833)),
l: getset("lab", 0, maxfn(100)),
a: getset("lab", 1),
b: getset("lab", 2),
keyword(value) {
if (value !== void 0) {
return new Color(value);
}
return color_convert_default[this.model].keyword(this.color);
},
hex(value) {
if (value !== void 0) {
return new Color(value);
}
return color_string_default.to.hex(...this.rgb().round().color);
},
hexa(value) {
if (value !== void 0) {
return new Color(value);
}
const rgbArray = this.rgb().round().color;
let alphaHex = Math.round(this.valpha * 255).toString(16).toUpperCase();
if (alphaHex.length === 1) {
alphaHex = "0" + alphaHex;
}
return color_string_default.to.hex(...rgbArray) + alphaHex;
},
rgbNumber() {
const rgb = this.rgb().color;
return (rgb[0] & 255) << 16 | (rgb[1] & 255) << 8 | rgb[2] & 255;
},
luminosity() {
const rgb = this.rgb().color;
const lum = [];
for (const [i, element] of rgb.entries()) {
const chan = element / 255;
lum[i] = chan <= 0.04045 ? chan / 12.92 : ((chan + 0.055) / 1.055) ** 2.4;
}
return 0.2126 * lum[0] + 0.7152 * lum[1] + 0.0722 * lum[2];
},
contrast(color2) {
const lum1 = this.luminosity();
const lum2 = color2.luminosity();
if (lum1 > lum2) {
return (lum1 + 0.05) / (lum2 + 0.05);
}
return (lum2 + 0.05) / (lum1 + 0.05);
},
level(color2) {
const contrastRatio = this.contrast(color2);
if (contrastRatio >= 7) {
return "AAA";
}
return contrastRatio >= 4.5 ? "AA" : "";
},
isDark() {
const rgb = this.rgb().color;
const yiq = (rgb[0] * 2126 + rgb[1] * 7152 + rgb[2] * 722) / 1e4;
return yiq < 128;
},
isLight() {
return !this.isDark();
},
negate() {
const rgb = this.rgb();
for (let i = 0; i < 3; i++) {
rgb.color[i] = 255 - rgb.color[i];
}
return rgb;
},
lighten(ratio) {
const hsl = this.hsl();
hsl.color[2] += hsl.color[2] * ratio;
return hsl;
},
darken(ratio) {
const hsl = this.hsl();
hsl.color[2] -= hsl.color[2] * ratio;
return hsl;
},
saturate(ratio) {
const hsl = this.hsl();
hsl.color[1] += hsl.color[1] * ratio;
return hsl;
},
desaturate(ratio) {
const hsl = this.hsl();
hsl.color[1] -= hsl.color[1] * ratio;
return hsl;
},
whiten(ratio) {
const hwb = this.hwb();
hwb.color[1] += hwb.color[1] * ratio;
return hwb;
},
blacken(ratio) {
const hwb = this.hwb();
hwb.color[2] += hwb.color[2] * ratio;
return hwb;
},
grayscale() {
const rgb = this.rgb().color;
const value = rgb[0] * 0.3 + rgb[1] * 0.59 + rgb[2] * 0.11;
return Color.rgb(value, value, value);
},
fade(ratio) {
return this.alpha(this.valpha - this.valpha * ratio);
},
opaquer(ratio) {
return this.alpha(this.valpha + this.valpha * ratio);
},
rotate(degrees) {
const hsl = this.hsl();
let hue = hsl.color[0];
hue = (hue + degrees) % 360;
hue = hue < 0 ? 360 + hue : hue;
hsl.color[0] = hue;
return hsl;
},
mix(mixinColor, weight) {
if (!mixinColor || !mixinColor.rgb) {
throw new Error('Argument to "mix" was not a Color instance, but rather an instance of ' + typeof mixinColor);
}
const color1 = mixinColor.rgb();
const color2 = this.rgb();
const p = weight === void 0 ? 0.5 : weight;
const w = 2 * p - 1;
const a = color1.alpha() - color2.alpha();
const w1 = ((w * a === -1 ? w : (w + a) / (1 + w * a)) + 1) / 2;
const w2 = 1 - w1;
return Color.rgb(
w1 * color1.red() + w2 * color2.red(),
w1 * color1.green() + w2 * color2.green(),
w1 * color1.blue() + w2 * color2.blue(),
color1.alpha() * p + color2.alpha() * (1 - p)
);
}
};
for (const model of Object.keys(color_convert_default)) {
if (skippedModels.includes(model)) {
continue;
}
const { channels } = color_convert_default[model];
Color.prototype[model] = function(...arguments_) {
if (this.model === model) {
return new Color(this);
}
if (arguments_.length > 0) {
return new Color(arguments_, model);
}
return new Color([...assertArray(color_convert_default[this.model][model].raw(this.color)), this.valpha], model);
};
Color[model] = function(...arguments_) {
let color = arguments_[0];
if (typeof color === "number") {
color = zeroArray(arguments_, channels);
}
return new Color(color, model);
};
}
function roundTo(number, places) {
return Number(number.toFixed(places));
}
function roundToPlace(places) {
return function(number) {
return roundTo(number, places);
};
}
function getset(model, channel, modifier) {
model = Array.isArray(model) ? model : [model];
for (const m of model) {
(limiters[m] ||= [])[channel] = modifier;
}
model = model[0];
return function(value) {
let result;
if (value !== void 0) {
if (modifier) {
value = modifier(value);
}
result = this[model]();
result.color[channel] = value;
return result;
}
result = this[model]().color[channel];
if (modifier) {
result = modifier(result);
}
return result;
};
}
function maxfn(max) {
return function(v) {
return Math.max(0, Math.min(max, v));
};
}
function assertArray(value) {
return Array.isArray(value) ? value : [value];
}
function zeroArray(array, length) {
for (let i = 0; i < length; i++) {
if (typeof array[i] !== "number") {
array[i] = 0;
}
}
return array;
}
var index_default = Color;
return color;
}
var colour$1;
var hasRequiredColour$1;
function requireColour$1 () {
if (hasRequiredColour$1) return colour$1;
hasRequiredColour$1 = 1;
colour$1 = requireColor().default;
return colour$1;
}
/*!
Copyright 2013 Lovell Fuller and others.
SPDX-License-Identifier: Apache-2.0
*/
var colour;
var hasRequiredColour;
function requireColour () {
if (hasRequiredColour) return colour;
hasRequiredColour = 1;
const color = requireColour$1();
const is = requireIs();
/**
* Colourspaces.
* @private
*/
const colourspace = {
multiband: 'multiband',
'b-w': 'b-w',
bw: 'b-w',
cmyk: 'cmyk',
srgb: 'srgb'
};
/**
* Tint the image using the provided colour.
* An alpha channel may be present and will be unchanged by the operation.
*
* @example
* const output = await sharp(input)
* .tint({ r: 255, g: 240, b: 16 })
* .toBuffer();
*
* @param {string|Object} tint - Parsed by the [color](https://www.npmjs.org/package/color) module.
* @returns {Sharp}
* @throws {Error} Invalid parameter
*/
function tint (tint) {
this._setBackgroundColourOption('tint', tint);
return this;
}
/**
* Convert to 8-bit greyscale; 256 shades of grey.
* This is a linear operation. If the input image is in a non-linear colour space such as sRGB, use `gamma()` with `greyscale()` for the best results.
* By default the output image will be web-friendly sRGB and contain three (identical) colour channels.
* This may be overridden by other sharp operations such as `toColourspace('b-w')`,
* which will produce an output image containing one colour channel.
* An alpha channel may be present, and will be unchanged by the operation.
*
* @example
* const output = await sharp(input).greyscale().toBuffer();
*
* @param {Boolean} [greyscale=true]
* @returns {Sharp}
*/
function greyscale (greyscale) {
this.options.greyscale = is.bool(greyscale) ? greyscale : true;
return this;
}
/**
* Alternative spelling of `greyscale`.
* @param {Boolean} [grayscale=true]
* @returns {Sharp}
*/
function grayscale (grayscale) {
return this.greyscale(grayscale);
}
/**
* Set the pipeline colourspace.
*
* The input image will be converted to the provided colourspace at the start of the pipeline.
* All operations will use this colourspace before converting to the output colourspace,
* as defined by {@link #tocolourspace toColourspace}.
*
* @since 0.29.0
*
* @example
* // Run pipeline in 16 bits per channel RGB while converting final result to 8 bits per channel sRGB.
* await sharp(input)
* .pipelineColourspace('rgb16')
* .toColourspace('srgb')
* .toFile('16bpc-pipeline-to-8bpc-output.png')
*
* @param {string} [colourspace] - pipeline colourspace e.g. `rgb16`, `scrgb`, `lab`, `grey16` [...](https://www.libvips.org/API/current/enum.Interpretation.html)
* @returns {Sharp}
* @throws {Error} Invalid parameters
*/
function pipelineColourspace (colourspace) {
if (!is.string(colourspace)) {
throw is.invalidParameterError('colourspace', 'string', colourspace);
}
this.options.colourspacePipeline = colourspace;
return this;
}
/**
* Alternative spelling of `pipelineColourspace`.
* @param {string} [colorspace] - pipeline colorspace.
* @returns {Sharp}
* @throws {Error} Invalid parameters
*/
function pipelineColorspace (colorspace) {
return this.pipelineColourspace(colorspace);
}
/**
* Set the output colourspace.
* By default output image will be web-friendly sRGB, with additional channels interpreted as alpha channels.
*
* @example
* // Output 16 bits per pixel RGB
* await sharp(input)
* .toColourspace('rgb16')
* .toFile('16-bpp.png')
*
* @param {string} [colourspace] - output colourspace e.g. `srgb`, `rgb`, `cmyk`, `lab`, `b-w` [...](https://www.libvips.org/API/current/enum.Interpretation.html)
* @returns {Sharp}
* @throws {Error} Invalid parameters
*/
function toColourspace (colourspace) {
if (!is.string(colourspace)) {
throw is.invalidParameterError('colourspace', 'string', colourspace);
}
this.options.colourspace = colourspace;
return this;
}
/**
* Alternative spelling of `toColourspace`.
* @param {string} [colorspace] - output colorspace.
* @returns {Sharp}
* @throws {Error} Invalid parameters
*/
function toColorspace (colorspace) {
return this.toColourspace(colorspace);
}
/**
* Create a RGBA colour array from a given value.
* @private
* @param {string|Object} value
* @throws {Error} Invalid value
*/
function _getBackgroundColourOption (value) {
if (
is.object(value) ||
(is.string(value) && value.length >= 3 && value.length <= 200)
) {
const colour = color(value);
return [
colour.red(),
colour.green(),
colour.blue(),
Math.round(colour.alpha() * 255)
];
} else {
throw is.invalidParameterError('background', 'object or string', value);
}
}
/**
* Update a colour attribute of the this.options Object.
* @private
* @param {string} key
* @param {string|Object} value
* @throws {Error} Invalid value
*/
function _setBackgroundColourOption (key, value) {
if (is.defined(value)) {
this.options[key] = _getBackgroundColourOption(value);
}
}
/**
* Decorate the Sharp prototype with colour-related functions.
* @module Sharp
* @private
*/
colour = (Sharp) => {
Object.assign(Sharp.prototype, {
// Public
tint,
greyscale,
grayscale,
pipelineColourspace,
pipelineColorspace,
toColourspace,
toColorspace,
// Private
_getBackgroundColourOption,
_setBackgroundColourOption
});
// Class attributes
Sharp.colourspace = colourspace;
Sharp.colorspace = colourspace;
};
return colour;
}
/*!
Copyright 2013 Lovell Fuller and others.
SPDX-License-Identifier: Apache-2.0
*/
var channel;
var hasRequiredChannel;
function requireChannel () {
if (hasRequiredChannel) return channel;
hasRequiredChannel = 1;
const is = requireIs();
/**
* Boolean operations for bandbool.
* @private
*/
const bool = {
and: 'and',
or: 'or',
eor: 'eor'
};
/**
* Remove alpha channels, if any. This is a no-op if the image does not have an alpha channel.
*
* See also {@link /api-operation/#flatten flatten}.
*
* @example
* sharp('rgba.png')
* .removeAlpha()
* .toFile('rgb.png', function(err, info) {
* // rgb.png is a 3 channel image without an alpha channel
* });
*
* @returns {Sharp}
*/
function removeAlpha () {
this.options.removeAlpha = true;
return this;
}
/**
* Ensure the output image has an alpha transparency channel.
* If missing, the added alpha channel will have the specified
* transparency level, defaulting to fully-opaque (1).
* This is a no-op if the image already has an alpha channel.
*
* @since 0.21.2
*
* @example
* // rgba.png will be a 4 channel image with a fully-opaque alpha channel
* await sharp('rgb.jpg')
* .ensureAlpha()
* .toFile('rgba.png')
*
* @example
* // rgba is a 4 channel image with a fully-transparent alpha channel
* const rgba = await sharp(rgb)
* .ensureAlpha(0)
* .toBuffer();
*
* @param {number} [alpha=1] - alpha transparency level (0=fully-transparent, 1=fully-opaque)
* @returns {Sharp}
* @throws {Error} Invalid alpha transparency level
*/
function ensureAlpha (alpha) {
if (is.defined(alpha)) {
if (is.number(alpha) && is.inRange(alpha, 0, 1)) {
this.options.ensureAlpha = alpha;
} else {
throw is.invalidParameterError('alpha', 'number between 0 and 1', alpha);
}
} else {
this.options.ensureAlpha = 1;
}
return this;
}
/**
* Extract a single channel from a multi-channel image.
*
* The output colourspace will be either `b-w` (8-bit) or `grey16` (16-bit).
*
* @example
* // green.jpg is a greyscale image containing the green channel of the input
* await sharp(input)
* .extractChannel('green')
* .toFile('green.jpg');
*
* @example
* // red1 is the red value of the first pixel, red2 the second pixel etc.
* const [red1, red2, ...] = await sharp(input)
* .extractChannel(0)
* .raw()
* .toBuffer();
*
* @param {number|string} channel - zero-indexed channel/band number to extract, or `red`, `green`, `blue` or `alpha`.
* @returns {Sharp}
* @throws {Error} Invalid channel
*/
function extractChannel (channel) {
const channelMap = { red: 0, green: 1, blue: 2, alpha: 3 };
if (Object.keys(channelMap).includes(channel)) {
channel = channelMap[channel];
}
if (is.integer(channel) && is.inRange(channel, 0, 4)) {
this.options.extractChannel = channel;
} else {
throw is.invalidParameterError('channel', 'integer or one of: red, green, blue, alpha', channel);
}
return this;
}
/**
* Join one or more channels to the image.
* The meaning of the added channels depends on the output colourspace, set with `toColourspace()`.
* By default the output image will be web-friendly sRGB, with additional channels interpreted as alpha channels.
* Channel ordering follows vips convention:
* - sRGB: 0: Red, 1: Green, 2: Blue, 3: Alpha.
* - CMYK: 0: Magenta, 1: Cyan, 2: Yellow, 3: Black, 4: Alpha.
*
* Buffers may be any of the image formats supported by sharp.
* For raw pixel input, the `options` object should contain a `raw` attribute, which follows the format of the attribute of the same name in the `sharp()` constructor.
*
* @param {Array<string|Buffer>|string|Buffer} images - one or more images (file paths, Buffers).
* @param {Object} options - image options, see `sharp()` constructor.
* @returns {Sharp}
* @throws {Error} Invalid parameters
*/
function joinChannel (images, options) {
if (Array.isArray(images)) {
images.forEach(function (image) {
this.options.joinChannelIn.push(this._createInputDescriptor(image, options));
}, this);
} else {
this.options.joinChannelIn.push(this._createInputDescriptor(images, options));
}
return this;
}
/**
* Perform a bitwise boolean operation on all input image channels (bands) to produce a single channel output image.
*
* @example
* sharp('3-channel-rgb-input.png')
* .bandbool(sharp.bool.and)
* .toFile('1-channel-output.png', function (err, info) {
* // The output will be a single channel image where each pixel `P = R & G & B`.
* // If `I(1,1) = [247, 170, 14] = [0b11110111, 0b10101010, 0b00001111]`
* // then `O(1,1) = 0b11110111 & 0b10101010 & 0b00001111 = 0b00000010 = 2`.
* });
*
* @param {string} boolOp - one of `and`, `or` or `eor` to perform that bitwise operation, like the C logic operators `&`, `|` and `^` respectively.
* @returns {Sharp}
* @throws {Error} Invalid parameters
*/
function bandbool (boolOp) {
if (is.string(boolOp) && is.inArray(boolOp, ['and', 'or', 'eor'])) {
this.options.bandBoolOp = boolOp;
} else {
throw is.invalidParameterError('boolOp', 'one of: and, or, eor', boolOp);
}
return this;
}
/**
* Decorate the Sharp prototype with channel-related functions.
* @module Sharp
* @private
*/
channel = (Sharp) => {
Object.assign(Sharp.prototype, {
// Public instance functions
removeAlpha,
ensureAlpha,
extractChannel,
joinChannel,
bandbool
});
// Class attributes
Sharp.bool = bool;
};
return channel;
}
/*!
Copyright 2013 Lovell Fuller and others.
SPDX-License-Identifier: Apache-2.0
*/
var output;
var hasRequiredOutput;
function requireOutput () {
if (hasRequiredOutput) return output;
hasRequiredOutput = 1;
const path = nodePath;
const is = requireIs();
const sharp = requireSharp();
const formats = new Map([
['heic', 'heif'],
['heif', 'heif'],
['avif', 'avif'],
['jpeg', 'jpeg'],
['jpg', 'jpeg'],
['jpe', 'jpeg'],
['tile', 'tile'],
['dz', 'tile'],
['png', 'png'],
['raw', 'raw'],
['tiff', 'tiff'],
['tif', 'tiff'],
['webp', 'webp'],
['gif', 'gif'],
['jp2', 'jp2'],
['jpx', 'jp2'],
['j2k', 'jp2'],
['j2c', 'jp2'],
['jxl', 'jxl']
]);
const jp2Regex = /\.(jp[2x]|j2[kc])$/i;
const errJp2Save = () => new Error('JP2 output requires libvips with support for OpenJPEG');
const bitdepthFromColourCount = (colours) => 1 << 31 - Math.clz32(Math.ceil(Math.log2(colours)));
/**
* Write output image data to a file.
*
* If an explicit output format is not selected, it will be inferred from the extension,
* with JPEG, PNG, WebP, AVIF, TIFF, GIF, DZI, and libvips' V format supported.
* Note that raw pixel data is only supported for buffer output.
*
* By default all metadata will be removed, which includes EXIF-based orientation.
* See {@link #withmetadata withMetadata} for control over this.
*
* The caller is responsible for ensuring directory structures and permissions exist.
*
* A `Promise` is returned when `callback` is not provided.
*
* @example
* sharp(input)
* .toFile('output.png', (err, info) => { ... });
*
* @example
* sharp(input)
* .toFile('output.png')
* .then(info => { ... })
* .catch(err => { ... });
*
* @param {string} fileOut - the path to write the image data to.
* @param {Function} [callback] - called on completion with two arguments `(err, info)`.
* `info` contains the output image `format`, `size` (bytes), `width`, `height`,
* `channels` and `premultiplied` (indicating if premultiplication was used).
* When using a crop strategy also contains `cropOffsetLeft` and `cropOffsetTop`.
* When using the attention crop strategy also contains `attentionX` and `attentionY`, the focal point of the cropped region.
* Animated output will also contain `pageHeight` and `pages`.
* May also contain `textAutofitDpi` (dpi the font was rendered at) if image was created from text.
* @returns {Promise<Object>} - when no callback is provided
* @throws {Error} Invalid parameters
*/
function toFile (fileOut, callback) {
let err;
if (!is.string(fileOut)) {
err = new Error('Missing output file path');
} else if (is.string(this.options.input.file) && path.resolve(this.options.input.file) === path.resolve(fileOut)) {
err = new Error('Cannot use same file for input and output');
} else if (jp2Regex.test(path.extname(fileOut)) && !this.constructor.format.jp2k.output.file) {
err = errJp2Save();
}
if (err) {
if (is.fn(callback)) {
callback(err);
} else {
return Promise.reject(err);
}
} else {
this.options.fileOut = fileOut;
const stack = Error();
return this._pipeline(callback, stack);
}
return this;
}
/**
* Write output to a Buffer.
* JPEG, PNG, WebP, AVIF, TIFF, GIF and raw pixel data output are supported.
*
* Use {@link #toformat toFormat} or one of the format-specific functions such as {@link #jpeg jpeg}, {@link #png png} etc. to set the output format.
*
* If no explicit format is set, the output format will match the input image, except SVG input which becomes PNG output.
*
* By default all metadata will be removed, which includes EXIF-based orientation.
* See {@link #withmetadata withMetadata} for control over this.
*
* `callback`, if present, gets three arguments `(err, data, info)` where:
* - `err` is an error, if any.
* - `data` is the output image data.
* - `info` contains the output image `format`, `size` (bytes), `width`, `height`,
* `channels` and `premultiplied` (indicating if premultiplication was used).
* When using a crop strategy also contains `cropOffsetLeft` and `cropOffsetTop`.
* Animated output will also contain `pageHeight` and `pages`.
* May also contain `textAutofitDpi` (dpi the font was rendered at) if image was created from text.
*
* A `Promise` is returned when `callback` is not provided.
*
* @example
* sharp(input)
* .toBuffer((err, data, info) => { ... });
*
* @example
* sharp(input)
* .toBuffer()
* .then(data => { ... })
* .catch(err => { ... });
*
* @example
* sharp(input)
* .png()
* .toBuffer({ resolveWithObject: true })
* .then(({ data, info }) => { ... })
* .catch(err => { ... });
*
* @example
* const { data, info } = await sharp('my-image.jpg')
* // output the raw pixels
* .raw()
* .toBuffer({ resolveWithObject: true });
*
* // create a more type safe way to work with the raw pixel data
* // this will not copy the data, instead it will change `data`s underlying ArrayBuffer
* // so `data` and `pixelArray` point to the same memory location
* const pixelArray = new Uint8ClampedArray(data.buffer);
*
* // When you are done changing the pixelArray, sharp takes the `pixelArray` as an input
* const { width, height, channels } = info;
* await sharp(pixelArray, { raw: { width, height, channels } })
* .toFile('my-changed-image.jpg');
*
* @param {Object} [options]
* @param {boolean} [options.resolveWithObject] Resolve the Promise with an Object containing `data` and `info` properties instead of resolving only with `data`.
* @param {Function} [callback]
* @returns {Promise<Buffer>} - when no callback is provided
*/
function toBuffer (options, callback) {
if (is.object(options)) {
this._setBooleanOption('resolveWithObject', options.resolveWithObject);
} else if (this.options.resolveWithObject) {
this.options.resolveWithObject = false;
}
this.options.fileOut = '';
const stack = Error();
return this._pipeline(is.fn(options) ? options : callback, stack);
}
/**
* Keep all EXIF metadata from the input image in the output image.
*
* EXIF metadata is unsupported for TIFF output.
*
* @since 0.33.0
*
* @example
* const outputWithExif = await sharp(inputWithExif)
* .keepExif()
* .toBuffer();
*
* @returns {Sharp}
*/
function keepExif () {
this.options.keepMetadata |= 0b00001;
return this;
}
/**
* Set EXIF metadata in the output image, ignoring any EXIF in the input image.
*
* @since 0.33.0
*
* @example
* const dataWithExif = await sharp(input)
* .withExif({
* IFD0: {
* Copyright: 'The National Gallery'
* },
* IFD3: {
* GPSLatitudeRef: 'N',
* GPSLatitude: '51/1 30/1 3230/100',
* GPSLongitudeRef: 'W',
* GPSLongitude: '0/1 7/1 4366/100'
* }
* })
* .toBuffer();
*
* @param {Object<string, Object<string, string>>} exif Object keyed by IFD0, IFD1 etc. of key/value string pairs to write as EXIF data.
* @returns {Sharp}
* @throws {Error} Invalid parameters
*/
function withExif (exif) {
if (is.object(exif)) {
for (const [ifd, entries] of Object.entries(exif)) {
if (is.object(entries)) {
for (const [k, v] of Object.entries(entries)) {
if (is.string(v)) {
this.options.withExif[`exif-${ifd.toLowerCase()}-${k}`] = v;
} else {
throw is.invalidParameterError(`${ifd}.${k}`, 'string', v);
}
}
} else {
throw is.invalidParameterError(ifd, 'object', entries);
}
}
} else {
throw is.invalidParameterError('exif', 'object', exif);
}
this.options.withExifMerge = false;
return this.keepExif();
}
/**
* Update EXIF metadata from the input image in the output image.
*
* @since 0.33.0
*
* @example
* const dataWithMergedExif = await sharp(inputWithExif)
* .withExifMerge({
* IFD0: {
* Copyright: 'The National Gallery'
* }
* })
* .toBuffer();
*
* @param {Object<string, Object<string, string>>} exif Object keyed by IFD0, IFD1 etc. of key/value string pairs to write as EXIF data.
* @returns {Sharp}
* @throws {Error} Invalid parameters
*/
function withExifMerge (exif) {
this.withExif(exif);
this.options.withExifMerge = true;
return this;
}
/**
* Keep ICC profile from the input image in the output image.
*
* When input and output colour spaces differ, use with {@link /api-colour/#tocolourspace toColourspace} and optionally {@link /api-colour/#pipelinecolourspace pipelineColourspace}.
*
* @since 0.33.0
*
* @example
* const outputWithIccProfile = await sharp(inputWithIccProfile)
* .keepIccProfile()
* .toBuffer();
*
* @example
* const cmykOutputWithIccProfile = await sharp(cmykInputWithIccProfile)
* .pipelineColourspace('cmyk')
* .toColourspace('cmyk')
* .keepIccProfile()
* .toBuffer();
*
* @returns {Sharp}
*/
function keepIccProfile () {
this.options.keepMetadata |= 0b01000;
return this;
}
/**
* Transform using an ICC profile and attach to the output image.
*
* This can either be an absolute filesystem path or
* built-in profile name (`srgb`, `p3`, `cmyk`).
*
* @since 0.33.0
*
* @example
* const outputWithP3 = await sharp(input)
* .withIccProfile('p3')
* .toBuffer();
*
* @param {string} icc - Absolute filesystem path to output ICC profile or built-in profile name (srgb, p3, cmyk).
* @param {Object} [options]
* @param {number} [options.attach=true] Should the ICC profile be included in the output image metadata?
* @returns {Sharp}
* @throws {Error} Invalid parameters
*/
function withIccProfile (icc, options) {
if (is.string(icc)) {
this.options.withIccProfile = icc;
} else {
throw is.invalidParameterError('icc', 'string', icc);
}
this.keepIccProfile();
if (is.object(options)) {
if (is.defined(options.attach)) {
if (is.bool(options.attach)) {
if (!options.attach) {
this.options.keepMetadata &= -9;
}
} else {
throw is.invalidParameterError('attach', 'boolean', options.attach);
}
}
}
return this;
}
/**
* Keep XMP metadata from the input image in the output image.
*
* @since 0.34.3
*
* @example
* const outputWithXmp = await sharp(inputWithXmp)
* .keepXmp()
* .toBuffer();
*
* @returns {Sharp}
*/
function keepXmp () {
this.options.keepMetadata |= 0b00010;
return this;
}
/**
* Set XMP metadata in the output image.
*
* Supported by PNG, JPEG, WebP, and TIFF output.
*
* @since 0.34.3
*
* @example
* const xmpString = `
* <?xml version="1.0"?>
* <x:xmpmeta xmlns:x="adobe:ns:meta/">
* <rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#">
* <rdf:Description rdf:about="" xmlns:dc="http://purl.org/dc/elements/1.1/">
* <dc:creator><rdf:Seq><rdf:li>John Doe</rdf:li></rdf:Seq></dc:creator>
* </rdf:Description>
* </rdf:RDF>
* </x:xmpmeta>`;
*
* const data = await sharp(input)
* .withXmp(xmpString)
* .toBuffer();
*
* @param {string} xmp String containing XMP metadata to be embedded in the output image.
* @returns {Sharp}
* @throws {Error} Invalid parameters
*/
function withXmp (xmp) {
if (is.string(xmp) && xmp.length > 0) {
this.options.withXmp = xmp;
this.options.keepMetadata |= 0b00010;
} else {
throw is.invalidParameterError('xmp', 'non-empty string', xmp);
}
return this;
}
/**
* Keep all metadata (EXIF, ICC, XMP, IPTC) from the input image in the output image.
*
* The default behaviour, when `keepMetadata` is not used, is to convert to the device-independent
* sRGB colour space and strip all metadata, including the removal of any ICC profile.
*
* @since 0.33.0
*
* @example
* const outputWithMetadata = await sharp(inputWithMetadata)
* .keepMetadata()
* .toBuffer();
*
* @returns {Sharp}
*/
function keepMetadata () {
this.options.keepMetadata = 0b11111;
return this;
}
/**
* Keep most metadata (EXIF, XMP, IPTC) from the input image in the output image.
*
* This will also convert to and add a web-friendly sRGB ICC profile if appropriate.
*
* Allows orientation and density to be set or updated.
*
* @example
* const outputSrgbWithMetadata = await sharp(inputRgbWithMetadata)
* .withMetadata()
* .toBuffer();
*
* @example
* // Set output metadata to 96 DPI
* const data = await sharp(input)
* .withMetadata({ density: 96 })
* .toBuffer();
*
* @param {Object} [options]
* @param {number} [options.orientation] Used to update the EXIF `Orientation` tag, integer between 1 and 8.
* @param {number} [options.density] Number of pixels per inch (DPI).
* @returns {Sharp}
* @throws {Error} Invalid parameters
*/
function withMetadata (options) {
this.keepMetadata();
this.withIccProfile('srgb');
if (is.object(options)) {
if (is.defined(options.orientation)) {
if (is.integer(options.orientation) && is.inRange(options.orientation, 1, 8)) {
this.options.withMetadataOrientation = options.orientation;
} else {
throw is.invalidParameterError('orientation', 'integer between 1 and 8', options.orientation);
}
}
if (is.defined(options.density)) {
if (is.number(options.density) && options.density > 0) {
this.options.withMetadataDensity = options.density;
} else {
throw is.invalidParameterError('density', 'positive number', options.density);
}
}
if (is.defined(options.icc)) {
this.withIccProfile(options.icc);
}
if (is.defined(options.exif)) {
this.withExifMerge(options.exif);
}
}
return this;
}
/**
* Force output to a given format.
*
* @example
* // Convert any input to PNG output
* const data = await sharp(input)
* .toFormat('png')
* .toBuffer();
*
* @param {(string|Object)} format - as a string or an Object with an 'id' attribute
* @param {Object} options - output options
* @returns {Sharp}
* @throws {Error} unsupported format or options
*/
function toFormat (format, options) {
const actualFormat = formats.get((is.object(format) && is.string(format.id) ? format.id : format).toLowerCase());
if (!actualFormat) {
throw is.invalidParameterError('format', `one of: ${[...formats.keys()].join(', ')}`, format);
}
return this[actualFormat](options);
}
/**
* Use these JPEG options for output image.
*
* @example
* // Convert any input to very high quality JPEG output
* const data = await sharp(input)
* .jpeg({
* quality: 100,
* chromaSubsampling: '4:4:4'
* })
* .toBuffer();
*
* @example
* // Use mozjpeg to reduce output JPEG file size (slower)
* const data = await sharp(input)
* .jpeg({ mozjpeg: true })
* .toBuffer();
*
* @param {Object} [options] - output options
* @param {number} [options.quality=80] - quality, integer 1-100
* @param {boolean} [options.progressive=false] - use progressive (interlace) scan
* @param {string} [options.chromaSubsampling='4:2:0'] - set to '4:4:4' to prevent chroma subsampling otherwise defaults to '4:2:0' chroma subsampling
* @param {boolean} [options.optimiseCoding=true] - optimise Huffman coding tables
* @param {boolean} [options.optimizeCoding=true] - alternative spelling of optimiseCoding
* @param {boolean} [options.mozjpeg=false] - use mozjpeg defaults, equivalent to `{ trellisQuantisation: true, overshootDeringing: true, optimiseScans: true, quantisationTable: 3 }`
* @param {boolean} [options.trellisQuantisation=false] - apply trellis quantisation
* @param {boolean} [options.overshootDeringing=false] - apply overshoot deringing
* @param {boolean} [options.optimiseScans=false] - optimise progressive scans, forces progressive
* @param {boolean} [options.optimizeScans=false] - alternative spelling of optimiseScans
* @param {number} [options.quantisationTable=0] - quantization table to use, integer 0-8
* @param {number} [options.quantizationTable=0] - alternative spelling of quantisationTable
* @param {boolean} [options.force=true] - force JPEG output, otherwise attempt to use input format
* @returns {Sharp}
* @throws {Error} Invalid options
*/
function jpeg (options) {
if (is.object(options)) {
if (is.defined(options.quality)) {
if (is.integer(options.quality) && is.inRange(options.quality, 1, 100)) {
this.options.jpegQuality = options.quality;
} else {
throw is.invalidParameterError('quality', 'integer between 1 and 100', options.quality);
}
}
if (is.defined(options.progressive)) {
this._setBooleanOption('jpegProgressive', options.progressive);
}
if (is.defined(options.chromaSubsampling)) {
if (is.string(options.chromaSubsampling) && is.inArray(options.chromaSubsampling, ['4:2:0', '4:4:4'])) {
this.options.jpegChromaSubsampling = options.chromaSubsampling;
} else {
throw is.invalidParameterError('chromaSubsampling', 'one of: 4:2:0, 4:4:4', options.chromaSubsampling);
}
}
const optimiseCoding = is.bool(options.optimizeCoding) ? options.optimizeCoding : options.optimiseCoding;
if (is.defined(optimiseCoding)) {
this._setBooleanOption('jpegOptimiseCoding', optimiseCoding);
}
if (is.defined(options.mozjpeg)) {
if (is.bool(options.mozjpeg)) {
if (options.mozjpeg) {
this.options.jpegTrellisQuantisation = true;
this.options.jpegOvershootDeringing = true;
this.options.jpegOptimiseScans = true;
this.options.jpegProgressive = true;
this.options.jpegQuantisationTable = 3;
}
} else {
throw is.invalidParameterError('mozjpeg', 'boolean', options.mozjpeg);
}
}
const trellisQuantisation = is.bool(options.trellisQuantization) ? options.trellisQuantization : options.trellisQuantisation;
if (is.defined(trellisQuantisation)) {
this._setBooleanOption('jpegTrellisQuantisation', trellisQuantisation);
}
if (is.defined(options.overshootDeringing)) {
this._setBooleanOption('jpegOvershootDeringing', options.overshootDeringing);
}
const optimiseScans = is.bool(options.optimizeScans) ? options.optimizeScans : options.optimiseScans;
if (is.defined(optimiseScans)) {
this._setBooleanOption('jpegOptimiseScans', optimiseScans);
if (optimiseScans) {
this.options.jpegProgressive = true;
}
}
const quantisationTable = is.number(options.quantizationTable) ? options.quantizationTable : options.quantisationTable;
if (is.defined(quantisationTable)) {
if (is.integer(quantisationTable) && is.inRange(quantisationTable, 0, 8)) {
this.options.jpegQuantisationTable = quantisationTable;
} else {
throw is.invalidParameterError('quantisationTable', 'integer between 0 and 8', quantisationTable);
}
}
}
return this._updateFormatOut('jpeg', options);
}
/**
* Use these PNG options for output image.
*
* By default, PNG output is full colour at 8 bits per pixel.
*
* Indexed PNG input at 1, 2 or 4 bits per pixel is converted to 8 bits per pixel.
* Set `palette` to `true` for slower, indexed PNG output.
*
* For 16 bits per pixel output, convert to `rgb16` via
* {@link /api-colour/#tocolourspace toColourspace}.
*
* @example
* // Convert any input to full colour PNG output
* const data = await sharp(input)
* .png()
* .toBuffer();
*
* @example
* // Convert any input to indexed PNG output (slower)
* const data = await sharp(input)
* .png({ palette: true })
* .toBuffer();
*
* @example
* // Output 16 bits per pixel RGB(A)
* const data = await sharp(input)
* .toColourspace('rgb16')
* .png()
* .toBuffer();
*
* @param {Object} [options]
* @param {boolean} [options.progressive=false] - use progressive (interlace) scan
* @param {number} [options.compressionLevel=6] - zlib compression level, 0 (fastest, largest) to 9 (slowest, smallest)
* @param {boolean} [options.adaptiveFiltering=false] - use adaptive row filtering
* @param {boolean} [options.palette=false] - quantise to a palette-based image with alpha transparency support
* @param {number} [options.quality=100] - use the lowest number of colours needed to achieve given quality, sets `palette` to `true`
* @param {number} [options.effort=7] - CPU effort, between 1 (fastest) and 10 (slowest), sets `palette` to `true`
* @param {number} [options.colours=256] - maximum number of palette entries, sets `palette` to `true`
* @param {number} [options.colors=256] - alternative spelling of `options.colours`, sets `palette` to `true`
* @param {number} [options.dither=1.0] - level of Floyd-Steinberg error diffusion, sets `palette` to `true`
* @param {boolean} [options.force=true] - force PNG output, otherwise attempt to use input format
* @returns {Sharp}
* @throws {Error} Invalid options
*/
function png (options) {
if (is.object(options)) {
if (is.defined(options.progressive)) {
this._setBooleanOption('pngProgressive', options.progressive);
}
if (is.defined(options.compressionLevel)) {
if (is.integer(options.compressionLevel) && is.inRange(options.compressionLevel, 0, 9)) {
this.options.pngCompressionLevel = options.compressionLevel;
} else {
throw is.invalidParameterError('compressionLevel', 'integer between 0 and 9', options.compressionLevel);
}
}
if (is.defined(options.adaptiveFiltering)) {
this._setBooleanOption('pngAdaptiveFiltering', options.adaptiveFiltering);
}
const colours = options.colours || options.colors;
if (is.defined(colours)) {
if (is.integer(colours) && is.inRange(colours, 2, 256)) {
this.options.pngBitdepth = bitdepthFromColourCount(colours);
} else {
throw is.invalidParameterError('colours', 'integer between 2 and 256', colours);
}
}
if (is.defined(options.palette)) {
this._setBooleanOption('pngPalette', options.palette);
} else if ([options.quality, options.effort, options.colours, options.colors, options.dither].some(is.defined)) {
this._setBooleanOption('pngPalette', true);
}
if (this.options.pngPalette) {
if (is.defined(options.quality)) {
if (is.integer(options.quality) && is.inRange(options.quality, 0, 100)) {
this.options.pngQuality = options.quality;
} else {
throw is.invalidParameterError('quality', 'integer between 0 and 100', options.quality);
}
}
if (is.defined(options.effort)) {
if (is.integer(options.effort) && is.inRange(options.effort, 1, 10)) {
this.options.pngEffort = options.effort;
} else {
throw is.invalidParameterError('effort', 'integer between 1 and 10', options.effort);
}
}
if (is.defined(options.dither)) {
if (is.number(options.dither) && is.inRange(options.dither, 0, 1)) {
this.options.pngDither = options.dither;
} else {
throw is.invalidParameterError('dither', 'number between 0.0 and 1.0', options.dither);
}
}
}
}
return this._updateFormatOut('png', options);
}
/**
* Use these WebP options for output image.
*
* @example
* // Convert any input to lossless WebP output
* const data = await sharp(input)
* .webp({ lossless: true })
* .toBuffer();
*
* @example
* // Optimise the file size of an animated WebP
* const outputWebp = await sharp(inputWebp, { animated: true })
* .webp({ effort: 6 })
* .toBuffer();
*
* @param {Object} [options] - output options
* @param {number} [options.quality=80] - quality, integer 1-100
* @param {number} [options.alphaQuality=100] - quality of alpha layer, integer 0-100
* @param {boolean} [options.lossless=false] - use lossless compression mode
* @param {boolean} [options.nearLossless=false] - use near_lossless compression mode
* @param {boolean} [options.smartSubsample=false] - use high quality chroma subsampling
* @param {boolean} [options.smartDeblock=false] - auto-adjust the deblocking filter, can improve low contrast edges (slow)
* @param {string} [options.preset='default'] - named preset for preprocessing/filtering, one of: default, photo, picture, drawing, icon, text
* @param {number} [options.effort=4] - CPU effort, between 0 (fastest) and 6 (slowest)
* @param {number} [options.loop=0] - number of animation iterations, use 0 for infinite animation
* @param {number|number[]} [options.delay] - delay(s) between animation frames (in milliseconds)
* @param {boolean} [options.minSize=false] - prevent use of animation key frames to minimise file size (slow)
* @param {boolean} [options.mixed=false] - allow mixture of lossy and lossless animation frames (slow)
* @param {boolean} [options.force=true] - force WebP output, otherwise attempt to use input format
* @returns {Sharp}
* @throws {Error} Invalid options
*/
function webp (options) {
if (is.object(options)) {
if (is.defined(options.quality)) {
if (is.integer(options.quality) && is.inRange(options.quality, 1, 100)) {
this.options.webpQuality = options.quality;
} else {
throw is.invalidParameterError('quality', 'integer between 1 and 100', options.quality);
}
}
if (is.defined(options.alphaQuality)) {
if (is.integer(options.alphaQuality) && is.inRange(options.alphaQuality, 0, 100)) {
this.options.webpAlphaQuality = options.alphaQuality;
} else {
throw is.invalidParameterError('alphaQuality', 'integer between 0 and 100', options.alphaQuality);
}
}
if (is.defined(options.lossless)) {
this._setBooleanOption('webpLossless', options.lossless);
}
if (is.defined(options.nearLossless)) {
this._setBooleanOption('webpNearLossless', options.nearLossless);
}
if (is.defined(options.smartSubsample)) {
this._setBooleanOption('webpSmartSubsample', options.smartSubsample);
}
if (is.defined(options.smartDeblock)) {
this._setBooleanOption('webpSmartDeblock', options.smartDeblock);
}
if (is.defined(options.preset)) {
if (is.string(options.preset) && is.inArray(options.preset, ['default', 'photo', 'picture', 'drawing', 'icon', 'text'])) {
this.options.webpPreset = options.preset;
} else {
throw is.invalidParameterError('preset', 'one of: default, photo, picture, drawing, icon, text', options.preset);
}
}
if (is.defined(options.effort)) {
if (is.integer(options.effort) && is.inRange(options.effort, 0, 6)) {
this.options.webpEffort = options.effort;
} else {
throw is.invalidParameterError('effort', 'integer between 0 and 6', options.effort);
}
}
if (is.defined(options.minSize)) {
this._setBooleanOption('webpMinSize', options.minSize);
}
if (is.defined(options.mixed)) {
this._setBooleanOption('webpMixed', options.mixed);
}
}
trySetAnimationOptions(options, this.options);
return this._updateFormatOut('webp', options);
}
/**
* Use these GIF options for the output image.
*
* The first entry in the palette is reserved for transparency.
*
* The palette of the input image will be re-used if possible.
*
* @since 0.30.0
*
* @example
* // Convert PNG to GIF
* await sharp(pngBuffer)
* .gif()
* .toBuffer();
*
* @example
* // Convert animated WebP to animated GIF
* await sharp('animated.webp', { animated: true })
* .toFile('animated.gif');
*
* @example
* // Create a 128x128, cropped, non-dithered, animated thumbnail of an animated GIF
* const out = await sharp('in.gif', { animated: true })
* .resize({ width: 128, height: 128 })
* .gif({ dither: 0 })
* .toBuffer();
*
* @example
* // Lossy file size reduction of animated GIF
* await sharp('in.gif', { animated: true })
* .gif({ interFrameMaxError: 8 })
* .toFile('optim.gif');
*
* @param {Object} [options] - output options
* @param {boolean} [options.reuse=true] - re-use existing palette, otherwise generate new (slow)
* @param {boolean} [options.progressive=false] - use progressive (interlace) scan
* @param {number} [options.colours=256] - maximum number of palette entries, including transparency, between 2 and 256
* @param {number} [options.colors=256] - alternative spelling of `options.colours`
* @param {number} [options.effort=7] - CPU effort, between 1 (fastest) and 10 (slowest)
* @param {number} [options.dither=1.0] - level of Floyd-Steinberg error diffusion, between 0 (least) and 1 (most)
* @param {number} [options.interFrameMaxError=0] - maximum inter-frame error for transparency, between 0 (lossless) and 32
* @param {number} [options.interPaletteMaxError=3] - maximum inter-palette error for palette reuse, between 0 and 256
* @param {boolean} [options.keepDuplicateFrames=false] - keep duplicate frames in the output instead of combining them
* @param {number} [options.loop=0] - number of animation iterations, use 0 for infinite animation
* @param {number|number[]} [options.delay] - delay(s) between animation frames (in milliseconds)
* @param {boolean} [options.force=true] - force GIF output, otherwise attempt to use input format
* @returns {Sharp}
* @throws {Error} Invalid options
*/
function gif (options) {
if (is.object(options)) {
if (is.defined(options.reuse)) {
this._setBooleanOption('gifReuse', options.reuse);
}
if (is.defined(options.progressive)) {
this._setBooleanOption('gifProgressive', options.progressive);
}
const colours = options.colours || options.colors;
if (is.defined(colours)) {
if (is.integer(colours) && is.inRange(colours, 2, 256)) {
this.options.gifBitdepth = bitdepthFromColourCount(colours);
} else {
throw is.invalidParameterError('colours', 'integer between 2 and 256', colours);
}
}
if (is.defined(options.effort)) {
if (is.number(options.effort) && is.inRange(options.effort, 1, 10)) {
this.options.gifEffort = options.effort;
} else {
throw is.invalidParameterError('effort', 'integer between 1 and 10', options.effort);
}
}
if (is.defined(options.dither)) {
if (is.number(options.dither) && is.inRange(options.dither, 0, 1)) {
this.options.gifDither = options.dither;
} else {
throw is.invalidParameterError('dither', 'number between 0.0 and 1.0', options.dither);
}
}
if (is.defined(options.interFrameMaxError)) {
if (is.number(options.interFrameMaxError) && is.inRange(options.interFrameMaxError, 0, 32)) {
this.options.gifInterFrameMaxError = options.interFrameMaxError;
} else {
throw is.invalidParameterError('interFrameMaxError', 'number between 0.0 and 32.0', options.interFrameMaxError);
}
}
if (is.defined(options.interPaletteMaxError)) {
if (is.number(options.interPaletteMaxError) && is.inRange(options.interPaletteMaxError, 0, 256)) {
this.options.gifInterPaletteMaxError = options.interPaletteMaxError;
} else {
throw is.invalidParameterError('interPaletteMaxError', 'number between 0.0 and 256.0', options.interPaletteMaxError);
}
}
if (is.defined(options.keepDuplicateFrames)) {
if (is.bool(options.keepDuplicateFrames)) {
this._setBooleanOption('gifKeepDuplicateFrames', options.keepDuplicateFrames);
} else {
throw is.invalidParameterError('keepDuplicateFrames', 'boolean', options.keepDuplicateFrames);
}
}
}
trySetAnimationOptions(options, this.options);
return this._updateFormatOut('gif', options);
}
/**
* Use these JP2 options for output image.
*
* Requires libvips compiled with support for OpenJPEG.
* The prebuilt binaries do not include this - see
* {@link /install#custom-libvips installing a custom libvips}.
*
* @example
* // Convert any input to lossless JP2 output
* const data = await sharp(input)
* .jp2({ lossless: true })
* .toBuffer();
*
* @example
* // Convert any input to very high quality JP2 output
* const data = await sharp(input)
* .jp2({
* quality: 100,
* chromaSubsampling: '4:4:4'
* })
* .toBuffer();
*
* @since 0.29.1
*
* @param {Object} [options] - output options
* @param {number} [options.quality=80] - quality, integer 1-100
* @param {boolean} [options.lossless=false] - use lossless compression mode
* @param {number} [options.tileWidth=512] - horizontal tile size
* @param {number} [options.tileHeight=512] - vertical tile size
* @param {string} [options.chromaSubsampling='4:4:4'] - set to '4:2:0' to use chroma subsampling
* @returns {Sharp}
* @throws {Error} Invalid options
*/
function jp2 (options) {
/* node:coverage ignore next 41 */
if (!this.constructor.format.jp2k.output.buffer) {
throw errJp2Save();
}
if (is.object(options)) {
if (is.defined(options.quality)) {
if (is.integer(options.quality) && is.inRange(options.quality, 1, 100)) {
this.options.jp2Quality = options.quality;
} else {
throw is.invalidParameterError('quality', 'integer between 1 and 100', options.quality);
}
}
if (is.defined(options.lossless)) {
if (is.bool(options.lossless)) {
this.options.jp2Lossless = options.lossless;
} else {
throw is.invalidParameterError('lossless', 'boolean', options.lossless);
}
}
if (is.defined(options.tileWidth)) {
if (is.integer(options.tileWidth) && is.inRange(options.tileWidth, 1, 32768)) {
this.options.jp2TileWidth = options.tileWidth;
} else {
throw is.invalidParameterError('tileWidth', 'integer between 1 and 32768', options.tileWidth);
}
}
if (is.defined(options.tileHeight)) {
if (is.integer(options.tileHeight) && is.inRange(options.tileHeight, 1, 32768)) {
this.options.jp2TileHeight = options.tileHeight;
} else {
throw is.invalidParameterError('tileHeight', 'integer between 1 and 32768', options.tileHeight);
}
}
if (is.defined(options.chromaSubsampling)) {
if (is.string(options.chromaSubsampling) && is.inArray(options.chromaSubsampling, ['4:2:0', '4:4:4'])) {
this.options.jp2ChromaSubsampling = options.chromaSubsampling;
} else {
throw is.invalidParameterError('chromaSubsampling', 'one of: 4:2:0, 4:4:4', options.chromaSubsampling);
}
}
}
return this._updateFormatOut('jp2', options);
}
/**
* Set animation options if available.
* @private
*
* @param {Object} [source] - output options
* @param {number} [source.loop=0] - number of animation iterations, use 0 for infinite animation
* @param {number[]} [source.delay] - list of delays between animation frames (in milliseconds)
* @param {Object} [target] - target object for valid options
* @throws {Error} Invalid options
*/
function trySetAnimationOptions (source, target) {
if (is.object(source) && is.defined(source.loop)) {
if (is.integer(source.loop) && is.inRange(source.loop, 0, 65535)) {
target.loop = source.loop;
} else {
throw is.invalidParameterError('loop', 'integer between 0 and 65535', source.loop);
}
}
if (is.object(source) && is.defined(source.delay)) {
// We allow singular values as well
if (is.integer(source.delay) && is.inRange(source.delay, 0, 65535)) {
target.delay = [source.delay];
} else if (
Array.isArray(source.delay) &&
source.delay.every(is.integer) &&
source.delay.every(v => is.inRange(v, 0, 65535))) {
target.delay = source.delay;
} else {
throw is.invalidParameterError('delay', 'integer or an array of integers between 0 and 65535', source.delay);
}
}
}
/**
* Use these TIFF options for output image.
*
* The `density` can be set in pixels/inch via {@link #withmetadata withMetadata}
* instead of providing `xres` and `yres` in pixels/mm.
*
* @example
* // Convert SVG input to LZW-compressed, 1 bit per pixel TIFF output
* sharp('input.svg')
* .tiff({
* compression: 'lzw',
* bitdepth: 1
* })
* .toFile('1-bpp-output.tiff')
* .then(info => { ... });
*
* @param {Object} [options] - output options
* @param {number} [options.quality=80] - quality, integer 1-100
* @param {boolean} [options.force=true] - force TIFF output, otherwise attempt to use input format
* @param {string} [options.compression='jpeg'] - compression options: none, jpeg, deflate, packbits, ccittfax4, lzw, webp, zstd, jp2k
* @param {boolean} [options.bigtiff=false] - use BigTIFF variant (has no effect when compression is none)
* @param {string} [options.predictor='horizontal'] - compression predictor options: none, horizontal, float
* @param {boolean} [options.pyramid=false] - write an image pyramid
* @param {boolean} [options.tile=false] - write a tiled tiff
* @param {number} [options.tileWidth=256] - horizontal tile size
* @param {number} [options.tileHeight=256] - vertical tile size
* @param {number} [options.xres=1.0] - horizontal resolution in pixels/mm
* @param {number} [options.yres=1.0] - vertical resolution in pixels/mm
* @param {string} [options.resolutionUnit='inch'] - resolution unit options: inch, cm
* @param {number} [options.bitdepth=8] - reduce bitdepth to 1, 2 or 4 bit
* @param {boolean} [options.miniswhite=false] - write 1-bit images as miniswhite
* @returns {Sharp}
* @throws {Error} Invalid options
*/
function tiff (options) {
if (is.object(options)) {
if (is.defined(options.quality)) {
if (is.integer(options.quality) && is.inRange(options.quality, 1, 100)) {
this.options.tiffQuality = options.quality;
} else {
throw is.invalidParameterError('quality', 'integer between 1 and 100', options.quality);
}
}
if (is.defined(options.bitdepth)) {
if (is.integer(options.bitdepth) && is.inArray(options.bitdepth, [1, 2, 4, 8])) {
this.options.tiffBitdepth = options.bitdepth;
} else {
throw is.invalidParameterError('bitdepth', '1, 2, 4 or 8', options.bitdepth);
}
}
// tiling
if (is.defined(options.tile)) {
this._setBooleanOption('tiffTile', options.tile);
}
if (is.defined(options.tileWidth)) {
if (is.integer(options.tileWidth) && options.tileWidth > 0) {
this.options.tiffTileWidth = options.tileWidth;
} else {
throw is.invalidParameterError('tileWidth', 'integer greater than zero', options.tileWidth);
}
}
if (is.defined(options.tileHeight)) {
if (is.integer(options.tileHeight) && options.tileHeight > 0) {
this.options.tiffTileHeight = options.tileHeight;
} else {
throw is.invalidParameterError('tileHeight', 'integer greater than zero', options.tileHeight);
}
}
// miniswhite
if (is.defined(options.miniswhite)) {
this._setBooleanOption('tiffMiniswhite', options.miniswhite);
}
// pyramid
if (is.defined(options.pyramid)) {
this._setBooleanOption('tiffPyramid', options.pyramid);
}
// resolution
if (is.defined(options.xres)) {
if (is.number(options.xres) && options.xres > 0) {
this.options.tiffXres = options.xres;
} else {
throw is.invalidParameterError('xres', 'number greater than zero', options.xres);
}
}
if (is.defined(options.yres)) {
if (is.number(options.yres) && options.yres > 0) {
this.options.tiffYres = options.yres;
} else {
throw is.invalidParameterError('yres', 'number greater than zero', options.yres);
}
}
// compression
if (is.defined(options.compression)) {
if (is.string(options.compression) && is.inArray(options.compression, ['none', 'jpeg', 'deflate', 'packbits', 'ccittfax4', 'lzw', 'webp', 'zstd', 'jp2k'])) {
this.options.tiffCompression = options.compression;
} else {
throw is.invalidParameterError('compression', 'one of: none, jpeg, deflate, packbits, ccittfax4, lzw, webp, zstd, jp2k', options.compression);
}
}
// bigtiff
if (is.defined(options.bigtiff)) {
this._setBooleanOption('tiffBigtiff', options.bigtiff);
}
// predictor
if (is.defined(options.predictor)) {
if (is.string(options.predictor) && is.inArray(options.predictor, ['none', 'horizontal', 'float'])) {
this.options.tiffPredictor = options.predictor;
} else {
throw is.invalidParameterError('predictor', 'one of: none, horizontal, float', options.predictor);
}
}
// resolutionUnit
if (is.defined(options.resolutionUnit)) {
if (is.string(options.resolutionUnit) && is.inArray(options.resolutionUnit, ['inch', 'cm'])) {
this.options.tiffResolutionUnit = options.resolutionUnit;
} else {
throw is.invalidParameterError('resolutionUnit', 'one of: inch, cm', options.resolutionUnit);
}
}
}
return this._updateFormatOut('tiff', options);
}
/**
* Use these AVIF options for output image.
*
* AVIF image sequences are not supported.
* Prebuilt binaries support a bitdepth of 8 only.
*
* This feature is experimental on the Windows ARM64 platform
* and requires a CPU with ARM64v8.4 or later.
*
* @example
* const data = await sharp(input)
* .avif({ effort: 2 })
* .toBuffer();
*
* @example
* const data = await sharp(input)
* .avif({ lossless: true })
* .toBuffer();
*
* @since 0.27.0
*
* @param {Object} [options] - output options
* @param {number} [options.quality=50] - quality, integer 1-100
* @param {boolean} [options.lossless=false] - use lossless compression
* @param {number} [options.effort=4] - CPU effort, between 0 (fastest) and 9 (slowest)
* @param {string} [options.chromaSubsampling='4:4:4'] - set to '4:2:0' to use chroma subsampling
* @param {number} [options.bitdepth=8] - set bitdepth to 8, 10 or 12 bit
* @returns {Sharp}
* @throws {Error} Invalid options
*/
function avif (options) {
return this.heif({ ...options, compression: 'av1' });
}
/**
* Use these HEIF options for output image.
*
* Support for patent-encumbered HEIC images using `hevc` compression requires the use of a
* globally-installed libvips compiled with support for libheif, libde265 and x265.
*
* @example
* const data = await sharp(input)
* .heif({ compression: 'hevc' })
* .toBuffer();
*
* @since 0.23.0
*
* @param {Object} options - output options
* @param {string} options.compression - compression format: av1, hevc
* @param {number} [options.quality=50] - quality, integer 1-100
* @param {boolean} [options.lossless=false] - use lossless compression
* @param {number} [options.effort=4] - CPU effort, between 0 (fastest) and 9 (slowest)
* @param {string} [options.chromaSubsampling='4:4:4'] - set to '4:2:0' to use chroma subsampling
* @param {number} [options.bitdepth=8] - set bitdepth to 8, 10 or 12 bit
* @returns {Sharp}
* @throws {Error} Invalid options
*/
function heif (options) {
if (is.object(options)) {
if (is.string(options.compression) && is.inArray(options.compression, ['av1', 'hevc'])) {
this.options.heifCompression = options.compression;
} else {
throw is.invalidParameterError('compression', 'one of: av1, hevc', options.compression);
}
if (is.defined(options.quality)) {
if (is.integer(options.quality) && is.inRange(options.quality, 1, 100)) {
this.options.heifQuality = options.quality;
} else {
throw is.invalidParameterError('quality', 'integer between 1 and 100', options.quality);
}
}
if (is.defined(options.lossless)) {
if (is.bool(options.lossless)) {
this.options.heifLossless = options.lossless;
} else {
throw is.invalidParameterError('lossless', 'boolean', options.lossless);
}
}
if (is.defined(options.effort)) {
if (is.integer(options.effort) && is.inRange(options.effort, 0, 9)) {
this.options.heifEffort = options.effort;
} else {
throw is.invalidParameterError('effort', 'integer between 0 and 9', options.effort);
}
}
if (is.defined(options.chromaSubsampling)) {
if (is.string(options.chromaSubsampling) && is.inArray(options.chromaSubsampling, ['4:2:0', '4:4:4'])) {
this.options.heifChromaSubsampling = options.chromaSubsampling;
} else {
throw is.invalidParameterError('chromaSubsampling', 'one of: 4:2:0, 4:4:4', options.chromaSubsampling);
}
}
if (is.defined(options.bitdepth)) {
if (is.integer(options.bitdepth) && is.inArray(options.bitdepth, [8, 10, 12])) {
if (options.bitdepth !== 8 && this.constructor.versions.heif) {
throw is.invalidParameterError('bitdepth when using prebuilt binaries', 8, options.bitdepth);
}
this.options.heifBitdepth = options.bitdepth;
} else {
throw is.invalidParameterError('bitdepth', '8, 10 or 12', options.bitdepth);
}
}
} else {
throw is.invalidParameterError('options', 'Object', options);
}
return this._updateFormatOut('heif', options);
}
/**
* Use these JPEG-XL (JXL) options for output image.
*
* This feature is experimental, please do not use in production systems.
*
* Requires libvips compiled with support for libjxl.
* The prebuilt binaries do not include this - see
* {@link /install/#custom-libvips installing a custom libvips}.
*
* @since 0.31.3
*
* @param {Object} [options] - output options
* @param {number} [options.distance=1.0] - maximum encoding error, between 0 (highest quality) and 15 (lowest quality)
* @param {number} [options.quality] - calculate `distance` based on JPEG-like quality, between 1 and 100, overrides distance if specified
* @param {number} [options.decodingTier=0] - target decode speed tier, between 0 (highest quality) and 4 (lowest quality)
* @param {boolean} [options.lossless=false] - use lossless compression
* @param {number} [options.effort=7] - CPU effort, between 1 (fastest) and 9 (slowest)
* @param {number} [options.loop=0] - number of animation iterations, use 0 for infinite animation
* @param {number|number[]} [options.delay] - delay(s) between animation frames (in milliseconds)
* @returns {Sharp}
* @throws {Error} Invalid options
*/
function jxl (options) {
if (is.object(options)) {
if (is.defined(options.quality)) {
if (is.integer(options.quality) && is.inRange(options.quality, 1, 100)) {
// https://github.com/libjxl/libjxl/blob/0aeea7f180bafd6893c1db8072dcb67d2aa5b03d/tools/cjxl_main.cc#L640-L644
this.options.jxlDistance = options.quality >= 30
? 0.1 + (100 - options.quality) * 0.09
: 53 / 3000 * options.quality * options.quality - 23 / 20 * options.quality + 25;
} else {
throw is.invalidParameterError('quality', 'integer between 1 and 100', options.quality);
}
} else if (is.defined(options.distance)) {
if (is.number(options.distance) && is.inRange(options.distance, 0, 15)) {
this.options.jxlDistance = options.distance;
} else {
throw is.invalidParameterError('distance', 'number between 0.0 and 15.0', options.distance);
}
}
if (is.defined(options.decodingTier)) {
if (is.integer(options.decodingTier) && is.inRange(options.decodingTier, 0, 4)) {
this.options.jxlDecodingTier = options.decodingTier;
} else {
throw is.invalidParameterError('decodingTier', 'integer between 0 and 4', options.decodingTier);
}
}
if (is.defined(options.lossless)) {
if (is.bool(options.lossless)) {
this.options.jxlLossless = options.lossless;
} else {
throw is.invalidParameterError('lossless', 'boolean', options.lossless);
}
}
if (is.defined(options.effort)) {
if (is.integer(options.effort) && is.inRange(options.effort, 1, 9)) {
this.options.jxlEffort = options.effort;
} else {
throw is.invalidParameterError('effort', 'integer between 1 and 9', options.effort);
}
}
}
trySetAnimationOptions(options, this.options);
return this._updateFormatOut('jxl', options);
}
/**
* Force output to be raw, uncompressed pixel data.
* Pixel ordering is left-to-right, top-to-bottom, without padding.
* Channel ordering will be RGB or RGBA for non-greyscale colourspaces.
*
* @example
* // Extract raw, unsigned 8-bit RGB pixel data from JPEG input
* const { data, info } = await sharp('input.jpg')
* .raw()
* .toBuffer({ resolveWithObject: true });
*
* @example
* // Extract alpha channel as raw, unsigned 16-bit pixel data from PNG input
* const data = await sharp('input.png')
* .ensureAlpha()
* .extractChannel(3)
* .toColourspace('b-w')
* .raw({ depth: 'ushort' })
* .toBuffer();
*
* @param {Object} [options] - output options
* @param {string} [options.depth='uchar'] - bit depth, one of: char, uchar (default), short, ushort, int, uint, float, complex, double, dpcomplex
* @returns {Sharp}
* @throws {Error} Invalid options
*/
function raw (options) {
if (is.object(options)) {
if (is.defined(options.depth)) {
if (is.string(options.depth) && is.inArray(options.depth,
['char', 'uchar', 'short', 'ushort', 'int', 'uint', 'float', 'complex', 'double', 'dpcomplex']
)) {
this.options.rawDepth = options.depth;
} else {
throw is.invalidParameterError('depth', 'one of: char, uchar, short, ushort, int, uint, float, complex, double, dpcomplex', options.depth);
}
}
}
return this._updateFormatOut('raw');
}
/**
* Use tile-based deep zoom (image pyramid) output.
*
* Set the format and options for tile images via the `toFormat`, `jpeg`, `png` or `webp` functions.
* Use a `.zip` or `.szi` file extension with `toFile` to write to a compressed archive file format.
*
* The container will be set to `zip` when the output is a Buffer or Stream, otherwise it will default to `fs`.
*
* @example
* sharp('input.tiff')
* .png()
* .tile({
* size: 512
* })
* .toFile('output.dz', function(err, info) {
* // output.dzi is the Deep Zoom XML definition
* // output_files contains 512x512 tiles grouped by zoom level
* });
*
* @example
* const zipFileWithTiles = await sharp(input)
* .tile({ basename: "tiles" })
* .toBuffer();
*
* @example
* const iiififier = sharp().tile({ layout: "iiif" });
* readableStream
* .pipe(iiififier)
* .pipe(writeableStream);
*
* @param {Object} [options]
* @param {number} [options.size=256] tile size in pixels, a value between 1 and 8192.
* @param {number} [options.overlap=0] tile overlap in pixels, a value between 0 and 8192.
* @param {number} [options.angle=0] tile angle of rotation, must be a multiple of 90.
* @param {string|Object} [options.background={r: 255, g: 255, b: 255, alpha: 1}] - background colour, parsed by the [color](https://www.npmjs.org/package/color) module, defaults to white without transparency.
* @param {string} [options.depth] how deep to make the pyramid, possible values are `onepixel`, `onetile` or `one`, default based on layout.
* @param {number} [options.skipBlanks=-1] Threshold to skip tile generation. Range is 0-255 for 8-bit images, 0-65535 for 16-bit images. Default is 5 for `google` layout, -1 (no skip) otherwise.
* @param {string} [options.container='fs'] tile container, with value `fs` (filesystem) or `zip` (compressed file).
* @param {string} [options.layout='dz'] filesystem layout, possible values are `dz`, `iiif`, `iiif3`, `zoomify` or `google`.
* @param {boolean} [options.centre=false] centre image in tile.
* @param {boolean} [options.center=false] alternative spelling of centre.
* @param {string} [options.id='https://example.com/iiif'] when `layout` is `iiif`/`iiif3`, sets the `@id`/`id` attribute of `info.json`
* @param {string} [options.basename] the name of the directory within the zip file when container is `zip`.
* @returns {Sharp}
* @throws {Error} Invalid parameters
*/
function tile (options) {
if (is.object(options)) {
// Size of square tiles, in pixels
if (is.defined(options.size)) {
if (is.integer(options.size) && is.inRange(options.size, 1, 8192)) {
this.options.tileSize = options.size;
} else {
throw is.invalidParameterError('size', 'integer between 1 and 8192', options.size);
}
}
// Overlap of tiles, in pixels
if (is.defined(options.overlap)) {
if (is.integer(options.overlap) && is.inRange(options.overlap, 0, 8192)) {
if (options.overlap > this.options.tileSize) {
throw is.invalidParameterError('overlap', `<= size (${this.options.tileSize})`, options.overlap);
}
this.options.tileOverlap = options.overlap;
} else {
throw is.invalidParameterError('overlap', 'integer between 0 and 8192', options.overlap);
}
}
// Container
if (is.defined(options.container)) {
if (is.string(options.container) && is.inArray(options.container, ['fs', 'zip'])) {
this.options.tileContainer = options.container;
} else {
throw is.invalidParameterError('container', 'one of: fs, zip', options.container);
}
}
// Layout
if (is.defined(options.layout)) {
if (is.string(options.layout) && is.inArray(options.layout, ['dz', 'google', 'iiif', 'iiif3', 'zoomify'])) {
this.options.tileLayout = options.layout;
} else {
throw is.invalidParameterError('layout', 'one of: dz, google, iiif, iiif3, zoomify', options.layout);
}
}
// Angle of rotation,
if (is.defined(options.angle)) {
if (is.integer(options.angle) && !(options.angle % 90)) {
this.options.tileAngle = options.angle;
} else {
throw is.invalidParameterError('angle', 'positive/negative multiple of 90', options.angle);
}
}
// Background colour
this._setBackgroundColourOption('tileBackground', options.background);
// Depth of tiles
if (is.defined(options.depth)) {
if (is.string(options.depth) && is.inArray(options.depth, ['onepixel', 'onetile', 'one'])) {
this.options.tileDepth = options.depth;
} else {
throw is.invalidParameterError('depth', 'one of: onepixel, onetile, one', options.depth);
}
}
// Threshold to skip blank tiles
if (is.defined(options.skipBlanks)) {
if (is.integer(options.skipBlanks) && is.inRange(options.skipBlanks, -1, 65535)) {
this.options.tileSkipBlanks = options.skipBlanks;
} else {
throw is.invalidParameterError('skipBlanks', 'integer between -1 and 255/65535', options.skipBlanks);
}
} else if (is.defined(options.layout) && options.layout === 'google') {
this.options.tileSkipBlanks = 5;
}
// Center image in tile
const centre = is.bool(options.center) ? options.center : options.centre;
if (is.defined(centre)) {
this._setBooleanOption('tileCentre', centre);
}
// @id attribute for IIIF layout
if (is.defined(options.id)) {
if (is.string(options.id)) {
this.options.tileId = options.id;
} else {
throw is.invalidParameterError('id', 'string', options.id);
}
}
// Basename for zip container
if (is.defined(options.basename)) {
if (is.string(options.basename)) {
this.options.tileBasename = options.basename;
} else {
throw is.invalidParameterError('basename', 'string', options.basename);
}
}
}
// Format
if (is.inArray(this.options.formatOut, ['jpeg', 'png', 'webp'])) {
this.options.tileFormat = this.options.formatOut;
} else if (this.options.formatOut !== 'input') {
throw is.invalidParameterError('format', 'one of: jpeg, png, webp', this.options.formatOut);
}
return this._updateFormatOut('dz');
}
/**
* Set a timeout for processing, in seconds.
* Use a value of zero to continue processing indefinitely, the default behaviour.
*
* The clock starts when libvips opens an input image for processing.
* Time spent waiting for a libuv thread to become available is not included.
*
* @example
* // Ensure processing takes no longer than 3 seconds
* try {
* const data = await sharp(input)
* .blur(1000)
* .timeout({ seconds: 3 })
* .toBuffer();
* } catch (err) {
* if (err.message.includes('timeout')) { ... }
* }
*
* @since 0.29.2
*
* @param {Object} options
* @param {number} options.seconds - Number of seconds after which processing will be stopped
* @returns {Sharp}
*/
function timeout (options) {
if (!is.plainObject(options)) {
throw is.invalidParameterError('options', 'object', options);
}
if (is.integer(options.seconds) && is.inRange(options.seconds, 0, 3600)) {
this.options.timeoutSeconds = options.seconds;
} else {
throw is.invalidParameterError('seconds', 'integer between 0 and 3600', options.seconds);
}
return this;
}
/**
* Update the output format unless options.force is false,
* in which case revert to input format.
* @private
* @param {string} formatOut
* @param {Object} [options]
* @param {boolean} [options.force=true] - force output format, otherwise attempt to use input format
* @returns {Sharp}
*/
function _updateFormatOut (formatOut, options) {
if (!(is.object(options) && options.force === false)) {
this.options.formatOut = formatOut;
}
return this;
}
/**
* Update a boolean attribute of the this.options Object.
* @private
* @param {string} key
* @param {boolean} val
* @throws {Error} Invalid key
*/
function _setBooleanOption (key, val) {
if (is.bool(val)) {
this.options[key] = val;
} else {
throw is.invalidParameterError(key, 'boolean', val);
}
}
/**
* Called by a WriteableStream to notify us it is ready for data.
* @private
*/
function _read () {
if (!this.options.streamOut) {
this.options.streamOut = true;
const stack = Error();
this._pipeline(undefined, stack);
}
}
/**
* Invoke the C++ image processing pipeline
* Supports callback, stream and promise variants
* @private
*/
function _pipeline (callback, stack) {
if (typeof callback === 'function') {
// output=file/buffer
if (this._isStreamInput()) {
// output=file/buffer, input=stream
this.on('finish', () => {
this._flattenBufferIn();
sharp.pipeline(this.options, (err, data, info) => {
if (err) {
callback(is.nativeError(err, stack));
} else {
callback(null, data, info);
}
});
});
} else {
// output=file/buffer, input=file/buffer
sharp.pipeline(this.options, (err, data, info) => {
if (err) {
callback(is.nativeError(err, stack));
} else {
callback(null, data, info);
}
});
}
return this;
} else if (this.options.streamOut) {
// output=stream
if (this._isStreamInput()) {
// output=stream, input=stream
this.once('finish', () => {
this._flattenBufferIn();
sharp.pipeline(this.options, (err, data, info) => {
if (err) {
this.emit('error', is.nativeError(err, stack));
} else {
this.emit('info', info);
this.push(data);
}
this.push(null);
this.on('end', () => this.emit('close'));
});
});
if (this.streamInFinished) {
this.emit('finish');
}
} else {
// output=stream, input=file/buffer
sharp.pipeline(this.options, (err, data, info) => {
if (err) {
this.emit('error', is.nativeError(err, stack));
} else {
this.emit('info', info);
this.push(data);
}
this.push(null);
this.on('end', () => this.emit('close'));
});
}
return this;
} else {
// output=promise
if (this._isStreamInput()) {
// output=promise, input=stream
return new Promise((resolve, reject) => {
this.once('finish', () => {
this._flattenBufferIn();
sharp.pipeline(this.options, (err, data, info) => {
if (err) {
reject(is.nativeError(err, stack));
} else {
if (this.options.resolveWithObject) {
resolve({ data, info });
} else {
resolve(data);
}
}
});
});
});
} else {
// output=promise, input=file/buffer
return new Promise((resolve, reject) => {
sharp.pipeline(this.options, (err, data, info) => {
if (err) {
reject(is.nativeError(err, stack));
} else {
if (this.options.resolveWithObject) {
resolve({ data, info });
} else {
resolve(data);
}
}
});
});
}
}
}
/**
* Decorate the Sharp prototype with output-related functions.
* @module Sharp
* @private
*/
output = (Sharp) => {
Object.assign(Sharp.prototype, {
// Public
toFile,
toBuffer,
keepExif,
withExif,
withExifMerge,
keepIccProfile,
withIccProfile,
keepXmp,
withXmp,
keepMetadata,
withMetadata,
toFormat,
jpeg,
jp2,
png,
webp,
tiff,
avif,
heif,
jxl,
gif,
raw,
tile,
timeout,
// Private
_updateFormatOut,
_setBooleanOption,
_read,
_pipeline
});
};
return output;
}
/*!
Copyright 2013 Lovell Fuller and others.
SPDX-License-Identifier: Apache-2.0
*/
var utility;
var hasRequiredUtility;
function requireUtility () {
if (hasRequiredUtility) return utility;
hasRequiredUtility = 1;
const events = require$$0$3;
const detectLibc = requireDetectLibc();
const is = requireIs();
const { runtimePlatformArch } = requireLibvips();
const sharp = requireSharp();
const runtimePlatform = runtimePlatformArch();
const libvipsVersion = sharp.libvipsVersion();
/**
* An Object containing nested boolean values representing the available input and output formats/methods.
* @member
* @example
* console.log(sharp.format);
* @returns {Object}
*/
const format = sharp.format();
format.heif.output.alias = ['avif', 'heic'];
format.jpeg.output.alias = ['jpe', 'jpg'];
format.tiff.output.alias = ['tif'];
format.jp2k.output.alias = ['j2c', 'j2k', 'jp2', 'jpx'];
/**
* An Object containing the available interpolators and their proper values
* @readonly
* @enum {string}
*/
const interpolators = {
/** [Nearest neighbour interpolation](http://en.wikipedia.org/wiki/Nearest-neighbor_interpolation). Suitable for image enlargement only. */
nearest: 'nearest',
/** [Bilinear interpolation](http://en.wikipedia.org/wiki/Bilinear_interpolation). Faster than bicubic but with less smooth results. */
bilinear: 'bilinear',
/** [Bicubic interpolation](http://en.wikipedia.org/wiki/Bicubic_interpolation) (the default). */
bicubic: 'bicubic',
/** [LBB interpolation](https://github.com/libvips/libvips/blob/master/libvips/resample/lbb.cpp#L100). Prevents some "[acutance](http://en.wikipedia.org/wiki/Acutance)" but typically reduces performance by a factor of 2. */
locallyBoundedBicubic: 'lbb',
/** [Nohalo interpolation](http://eprints.soton.ac.uk/268086/). Prevents acutance but typically reduces performance by a factor of 3. */
nohalo: 'nohalo',
/** [VSQBS interpolation](https://github.com/libvips/libvips/blob/master/libvips/resample/vsqbs.cpp#L48). Prevents "staircasing" when enlarging. */
vertexSplitQuadraticBasisSpline: 'vsqbs'
};
/**
* An Object containing the version numbers of sharp, libvips
* and (when using prebuilt binaries) its dependencies.
*
* @member
* @example
* console.log(sharp.versions);
*/
let versions = {
vips: libvipsVersion.semver
};
/* node:coverage ignore next 15 */
if (!libvipsVersion.isGlobal) {
if (!libvipsVersion.isWasm) {
try {
versions = commonjsRequire(`@img/sharp-${runtimePlatform}/versions`);
} catch (_) {
try {
versions = commonjsRequire(`@img/sharp-libvips-${runtimePlatform}/versions`);
} catch (_) {}
}
} else {
try {
versions = require('@img/sharp-wasm32/versions');
} catch (_) {}
}
}
versions.sharp = require$$6.version;
/* node:coverage ignore next 5 */
if (versions.heif && format.heif) {
// Prebuilt binaries provide AV1
format.heif.input.fileSuffix = ['.avif'];
format.heif.output.alias = ['avif'];
}
/**
* Gets or, when options are provided, sets the limits of _libvips'_ operation cache.
* Existing entries in the cache will be trimmed after any change in limits.
* This method always returns cache statistics,
* useful for determining how much working memory is required for a particular task.
*
* @example
* const stats = sharp.cache();
* @example
* sharp.cache( { items: 200 } );
* sharp.cache( { files: 0 } );
* sharp.cache(false);
*
* @param {Object|boolean} [options=true] - Object with the following attributes, or boolean where true uses default cache settings and false removes all caching
* @param {number} [options.memory=50] - is the maximum memory in MB to use for this cache
* @param {number} [options.files=20] - is the maximum number of files to hold open
* @param {number} [options.items=100] - is the maximum number of operations to cache
* @returns {Object}
*/
function cache (options) {
if (is.bool(options)) {
if (options) {
// Default cache settings of 50MB, 20 files, 100 items
return sharp.cache(50, 20, 100);
} else {
return sharp.cache(0, 0, 0);
}
} else if (is.object(options)) {
return sharp.cache(options.memory, options.files, options.items);
} else {
return sharp.cache();
}
}
cache(true);
/**
* Gets or, when a concurrency is provided, sets
* the maximum number of threads _libvips_ should use to process _each image_.
* These are from a thread pool managed by glib,
* which helps avoid the overhead of creating new threads.
*
* This method always returns the current concurrency.
*
* The default value is the number of CPU cores,
* except when using glibc-based Linux without jemalloc,
* where the default is `1` to help reduce memory fragmentation.
*
* A value of `0` will reset this to the number of CPU cores.
*
* Some image format libraries spawn additional threads,
* e.g. libaom manages its own 4 threads when encoding AVIF images,
* and these are independent of the value set here.
*
* :::note
* Further {@link /performance/ control over performance} is available.
* :::
*
* @example
* const threads = sharp.concurrency(); // 4
* sharp.concurrency(2); // 2
* sharp.concurrency(0); // 4
*
* @param {number} [concurrency]
* @returns {number} concurrency
*/
function concurrency (concurrency) {
return sharp.concurrency(is.integer(concurrency) ? concurrency : null);
}
/* node:coverage ignore next 7 */
if (detectLibc.familySync() === detectLibc.GLIBC && !sharp._isUsingJemalloc()) {
// Reduce default concurrency to 1 when using glibc memory allocator
sharp.concurrency(1);
} else if (detectLibc.familySync() === detectLibc.MUSL && sharp.concurrency() === 1024) {
// Reduce default concurrency when musl thread over-subscription detected
sharp.concurrency(require$$7.availableParallelism());
}
/**
* An EventEmitter that emits a `change` event when a task is either:
* - queued, waiting for _libuv_ to provide a worker thread
* - complete
* @member
* @example
* sharp.queue.on('change', function(queueLength) {
* console.log('Queue contains ' + queueLength + ' task(s)');
* });
*/
const queue = new events.EventEmitter();
/**
* Provides access to internal task counters.
* - queue is the number of tasks this module has queued waiting for _libuv_ to provide a worker thread from its pool.
* - process is the number of resize tasks currently being processed.
*
* @example
* const counters = sharp.counters(); // { queue: 2, process: 4 }
*
* @returns {Object}
*/
function counters () {
return sharp.counters();
}
/**
* Get and set use of SIMD vector unit instructions.
* Requires libvips to have been compiled with highway support.
*
* Improves the performance of `resize`, `blur` and `sharpen` operations
* by taking advantage of the SIMD vector unit of the CPU, e.g. Intel SSE and ARM NEON.
*
* @example
* const simd = sharp.simd();
* // simd is `true` if the runtime use of highway is currently enabled
* @example
* const simd = sharp.simd(false);
* // prevent libvips from using highway at runtime
*
* @param {boolean} [simd=true]
* @returns {boolean}
*/
function simd (simd) {
return sharp.simd(is.bool(simd) ? simd : null);
}
/**
* Block libvips operations at runtime.
*
* This is in addition to the `VIPS_BLOCK_UNTRUSTED` environment variable,
* which when set will block all "untrusted" operations.
*
* @since 0.32.4
*
* @example <caption>Block all TIFF input.</caption>
* sharp.block({
* operation: ['VipsForeignLoadTiff']
* });
*
* @param {Object} options
* @param {Array<string>} options.operation - List of libvips low-level operation names to block.
*/
function block (options) {
if (is.object(options)) {
if (Array.isArray(options.operation) && options.operation.every(is.string)) {
sharp.block(options.operation, true);
} else {
throw is.invalidParameterError('operation', 'Array<string>', options.operation);
}
} else {
throw is.invalidParameterError('options', 'object', options);
}
}
/**
* Unblock libvips operations at runtime.
*
* This is useful for defining a list of allowed operations.
*
* @since 0.32.4
*
* @example <caption>Block all input except WebP from the filesystem.</caption>
* sharp.block({
* operation: ['VipsForeignLoad']
* });
* sharp.unblock({
* operation: ['VipsForeignLoadWebpFile']
* });
*
* @example <caption>Block all input except JPEG and PNG from a Buffer or Stream.</caption>
* sharp.block({
* operation: ['VipsForeignLoad']
* });
* sharp.unblock({
* operation: ['VipsForeignLoadJpegBuffer', 'VipsForeignLoadPngBuffer']
* });
*
* @param {Object} options
* @param {Array<string>} options.operation - List of libvips low-level operation names to unblock.
*/
function unblock (options) {
if (is.object(options)) {
if (Array.isArray(options.operation) && options.operation.every(is.string)) {
sharp.block(options.operation, false);
} else {
throw is.invalidParameterError('operation', 'Array<string>', options.operation);
}
} else {
throw is.invalidParameterError('options', 'object', options);
}
}
/**
* Decorate the Sharp class with utility-related functions.
* @module Sharp
* @private
*/
utility = (Sharp) => {
Sharp.cache = cache;
Sharp.concurrency = concurrency;
Sharp.counters = counters;
Sharp.simd = simd;
Sharp.format = format;
Sharp.interpolators = interpolators;
Sharp.versions = versions;
Sharp.queue = queue;
Sharp.block = block;
Sharp.unblock = unblock;
};
return utility;
}
/*!
Copyright 2013 Lovell Fuller and others.
SPDX-License-Identifier: Apache-2.0
*/
var lib;
var hasRequiredLib;
function requireLib () {
if (hasRequiredLib) return lib;
hasRequiredLib = 1;
const Sharp = requireConstructor();
requireInput()(Sharp);
requireResize()(Sharp);
requireComposite()(Sharp);
requireOperation()(Sharp);
requireColour()(Sharp);
requireChannel()(Sharp);
requireOutput()(Sharp);
requireUtility()(Sharp);
lib = Sharp;
return lib;
}
var libExports = requireLib();
const index = /*@__PURE__*/getDefaultExportFromCjs(libExports);
const index$1 = /*#__PURE__*/_mergeNamespaces({
__proto__: null,
default: index
}, [libExports]);
export { index$1 as i };