import { isRemotePath, joinPaths } from '@astrojs/internal-helpers/path'; import { A as AstroError, E as ExpectedImage, L as LocalImageUsedWrongly, M as MissingImageDimension, U as UnsupportedImageFormat, I as InvalidImageService, a as ExpectedImageOptions, b as MissingSharp } from './astro_bfe7ba8a.mjs'; const VALID_SUPPORTED_FORMATS = [ "jpeg", "jpg", "png", "tiff", "webp", "gif", "svg", "avif" ]; function isLocalService(service) { if (!service) { return false; } return "transform" in service; } function parseQuality(quality) { let result = parseInt(quality); if (Number.isNaN(result)) { return quality; } return result; } const baseService = { validateOptions(options) { if (!options.src || typeof options.src !== "string" && typeof options.src !== "object") { throw new AstroError({ ...ExpectedImage, message: ExpectedImage.message( JSON.stringify(options.src), typeof options.src, JSON.stringify(options, (_, v) => v === void 0 ? null : v) ) }); } if (!isESMImportedImage(options.src)) { if (options.src.startsWith("/@fs/") || !isRemotePath(options.src) && !options.src.startsWith("/")) { throw new AstroError({ ...LocalImageUsedWrongly, message: LocalImageUsedWrongly.message(options.src) }); } let missingDimension; if (!options.width && !options.height) { missingDimension = "both"; } else if (!options.width && options.height) { missingDimension = "width"; } else if (options.width && !options.height) { missingDimension = "height"; } if (missingDimension) { throw new AstroError({ ...MissingImageDimension, message: MissingImageDimension.message(missingDimension, options.src) }); } } else { if (!VALID_SUPPORTED_FORMATS.includes(options.src.format)) { throw new AstroError({ ...UnsupportedImageFormat, message: UnsupportedImageFormat.message( options.src.format, options.src.src, VALID_SUPPORTED_FORMATS ) }); } if (options.src.format === "svg") { options.format = "svg"; } } if (!options.format) { options.format = "webp"; } return options; }, getHTMLAttributes(options) { let targetWidth = options.width; let targetHeight = options.height; if (isESMImportedImage(options.src)) { const aspectRatio = options.src.width / options.src.height; if (targetHeight && !targetWidth) { targetWidth = Math.round(targetHeight * aspectRatio); } else if (targetWidth && !targetHeight) { targetHeight = Math.round(targetWidth / aspectRatio); } else if (!targetWidth && !targetHeight) { targetWidth = options.src.width; targetHeight = options.src.height; } } const { src, width, height, format, quality, ...attributes } = options; return { ...attributes, width: targetWidth, height: targetHeight, loading: attributes.loading ?? "lazy", decoding: attributes.decoding ?? "async" }; }, getURL(options, imageConfig) { const searchParams = new URLSearchParams(); if (isESMImportedImage(options.src)) { searchParams.append("href", options.src.src); } else if (isRemoteAllowed(options.src, imageConfig)) { searchParams.append("href", options.src); } else { return options.src; } const params = { w: "width", h: "height", q: "quality", f: "format" }; Object.entries(params).forEach(([param, key]) => { options[key] && searchParams.append(param, options[key].toString()); }); const imageEndpoint = joinPaths("/", "/_image"); return `${imageEndpoint}?${searchParams}`; }, parseURL(url) { const params = url.searchParams; if (!params.has("href")) { return void 0; } const transform = { src: params.get("href"), width: params.has("w") ? parseInt(params.get("w")) : void 0, height: params.has("h") ? parseInt(params.get("h")) : void 0, format: params.get("f"), quality: params.get("q") }; return transform; } }; function matchPattern(url, remotePattern) { return matchProtocol(url, remotePattern.protocol) && matchHostname(url, remotePattern.hostname, true) && matchPort(url, remotePattern.port) && matchPathname(url, remotePattern.pathname, true); } function matchPort(url, port) { return !port || port === url.port; } function matchProtocol(url, protocol) { return !protocol || protocol === url.protocol.slice(0, -1); } function matchHostname(url, hostname, allowWildcard) { if (!hostname) { return true; } else if (!allowWildcard || !hostname.startsWith("*")) { return hostname === url.hostname; } else if (hostname.startsWith("**.")) { const slicedHostname = hostname.slice(2); return slicedHostname !== url.hostname && url.hostname.endsWith(slicedHostname); } else if (hostname.startsWith("*.")) { const slicedHostname = hostname.slice(1); const additionalSubdomains = url.hostname.replace(slicedHostname, "").split(".").filter(Boolean); return additionalSubdomains.length === 1; } return false; } function matchPathname(url, pathname, allowWildcard) { if (!pathname) { return true; } else if (!allowWildcard || !pathname.endsWith("*")) { return pathname === url.pathname; } else if (pathname.endsWith("/**")) { const slicedPathname = pathname.slice(0, -2); return slicedPathname !== url.pathname && url.pathname.startsWith(slicedPathname); } else if (pathname.endsWith("/*")) { const slicedPathname = pathname.slice(0, -1); const additionalPathChunks = url.pathname.replace(slicedPathname, "").split("/").filter(Boolean); return additionalPathChunks.length === 1; } return false; } function isESMImportedImage(src) { return typeof src === "object"; } function isRemoteImage(src) { return typeof src === "string"; } function isRemoteAllowed(src, { domains = [], remotePatterns = [] }) { if (!isRemotePath(src)) return false; const url = new URL(src); return domains.some((domain) => matchHostname(url, domain)) || remotePatterns.some((remotePattern) => matchPattern(url, remotePattern)); } async function getConfiguredImageService() { if (!globalThis?.astroAsset?.imageService) { const { default: service } = await Promise.resolve().then(() => sharp$1).catch((e) => { const error = new AstroError(InvalidImageService); error.cause = e; throw error; }); if (!globalThis.astroAsset) globalThis.astroAsset = {}; globalThis.astroAsset.imageService = service; return service; } return globalThis.astroAsset.imageService; } async function getImage(options, imageConfig) { if (!options || typeof options !== "object") { throw new AstroError({ ...ExpectedImageOptions, message: ExpectedImageOptions.message(JSON.stringify(options)) }); } const service = await getConfiguredImageService(); const resolvedOptions = { ...options, src: typeof options.src === "object" && "then" in options.src ? (await options.src).default ?? await options.src : options.src }; const validatedOptions = service.validateOptions ? await service.validateOptions(resolvedOptions, imageConfig) : resolvedOptions; let imageURL = await service.getURL(validatedOptions, imageConfig); if (isLocalService(service) && globalThis.astroAsset.addStaticImage && // If `getURL` returned the same URL as the user provided, it means the service doesn't need to do anything !(isRemoteImage(validatedOptions.src) && imageURL === validatedOptions.src)) { imageURL = globalThis.astroAsset.addStaticImage(validatedOptions); } return { rawOptions: resolvedOptions, options: validatedOptions, src: imageURL, attributes: service.getHTMLAttributes !== void 0 ? service.getHTMLAttributes(validatedOptions, imageConfig) : {} }; } let sharp; const qualityTable = { low: 25, mid: 50, high: 80, max: 100 }; async function loadSharp() { let sharpImport; try { sharpImport = (await import('sharp')).default; } catch (e) { throw new AstroError(MissingSharp); } return sharpImport; } const sharpService = { validateOptions: baseService.validateOptions, getURL: baseService.getURL, parseURL: baseService.parseURL, getHTMLAttributes: baseService.getHTMLAttributes, async transform(inputBuffer, transformOptions) { if (!sharp) sharp = await loadSharp(); const transform = transformOptions; if (transform.format === "svg") return { data: inputBuffer, format: "svg" }; let result = sharp(inputBuffer, { failOnError: false, pages: -1 }); result.rotate(); if (transform.height && !transform.width) { result.resize({ height: transform.height }); } else if (transform.width) { result.resize({ width: transform.width }); } if (transform.format) { let quality = void 0; if (transform.quality) { const parsedQuality = parseQuality(transform.quality); if (typeof parsedQuality === "number") { quality = parsedQuality; } else { quality = transform.quality in qualityTable ? qualityTable[transform.quality] : void 0; } } result.toFormat(transform.format, { quality }); } const { data, info } = await result.toBuffer({ resolveWithObject: true }); return { data, format: info.format }; } }; var sharp_default = sharpService; const sharp$1 = /*#__PURE__*/Object.freeze(/*#__PURE__*/Object.defineProperty({ __proto__: null, default: sharp_default }, Symbol.toStringTag, { value: 'Module' })); export { getConfiguredImageService as a, getImage as g, isRemoteAllowed as i };