Files

1939 lines
58 KiB
JavaScript

import { i as isRemoteAllowed, j as joinPaths, a as isRemotePath, r as removeQueryString, b as isParentDirectory } from './remote_BIGaRndY.mjs';
import { A as AstroError, E as ExpectedImage, L as LocalImageUsedWrongly, M as MissingImageDimension, U as UnsupportedImageFormat, I as IncompatibleDescriptorOptions, a as UnsupportedImageConversion, t as toStyleString, N as NoImageMetadata, F as FailedToFetchRemoteImageDimensions, R as RemoteImageNotAllowed, b as ExpectedImageOptions, c as ExpectedNotESMImage, d as InvalidImageService, e as createComponent, f as ImageMissingAlt, m as maybeRenderHead, g as addAttribute, s as spreadAttributes, r as renderTemplate, h as createAstro, i as ExperimentalFontsNotEnabled, j as FontFamilyNotFound, u as unescapeHTML } from './astro/server_CtsYB3PN.mjs';
import 'clsx';
import * as mime from 'mrmime';
import 'piccolore';
import { readFile } from 'node:fs/promises';
import path from 'node:path';
import { fileURLToPath } from 'node:url';
import '../renderers.mjs';
const VALID_SUPPORTED_FORMATS = [
"jpeg",
"jpg",
"png",
"tiff",
"webp",
"gif",
"svg",
"avif"
];
const DEFAULT_OUTPUT_FORMAT = "webp";
const DEFAULT_HASH_PROPS = [
"src",
"width",
"height",
"format",
"quality",
"fit",
"position",
"background"
];
const DEFAULT_RESOLUTIONS = [
640,
// older and lower-end phones
750,
// iPhone 6-8
828,
// iPhone XR/11
960,
// older horizontal phones
1080,
// iPhone 6-8 Plus
1280,
// 720p
1668,
// Various iPads
1920,
// 1080p
2048,
// QXGA
2560,
// WQXGA
3200,
// QHD+
3840,
// 4K
4480,
// 4.5K
5120,
// 5K
6016
// 6K
];
const LIMITED_RESOLUTIONS = [
640,
// older and lower-end phones
750,
// iPhone 6-8
828,
// iPhone XR/11
1080,
// iPhone 6-8 Plus
1280,
// 720p
1668,
// Various iPads
2048,
// QXGA
2560
// WQXGA
];
const getWidths = ({
width,
layout,
breakpoints = DEFAULT_RESOLUTIONS,
originalWidth
}) => {
const smallerThanOriginal = (w) => !originalWidth || w <= originalWidth;
if (layout === "full-width") {
return breakpoints.filter(smallerThanOriginal);
}
if (!width) {
return [];
}
const doubleWidth = width * 2;
const maxSize = originalWidth ? Math.min(doubleWidth, originalWidth) : doubleWidth;
if (layout === "fixed") {
return originalWidth && width > originalWidth ? [originalWidth] : [width, maxSize];
}
if (layout === "constrained") {
return [
// Always include the image at 1x and 2x the specified width
width,
doubleWidth,
...breakpoints
].filter((w) => w <= maxSize).sort((a, b) => a - b);
}
return [];
};
const getSizesAttribute = ({
width,
layout
}) => {
if (!width || !layout) {
return void 0;
}
switch (layout) {
// If screen is wider than the max size then image width is the max size,
// otherwise it's the width of the screen
case "constrained":
return `(min-width: ${width}px) ${width}px, 100vw`;
// Image is always the same width, whatever the size of the screen
case "fixed":
return `${width}px`;
// Image is always the width of the screen
case "full-width":
return `100vw`;
case "none":
default:
return void 0;
}
};
function isESMImportedImage(src) {
return typeof src === "object" || typeof src === "function" && "src" in src;
}
function isRemoteImage(src) {
return typeof src === "string";
}
async function resolveSrc(src) {
if (typeof src === "object" && "then" in src) {
const resource = await src;
return resource.default ?? resource;
}
return src;
}
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 sortNumeric = (a, b) => a - b;
function verifyOptions(options) {
if (!options.src || !isRemoteImage(options.src) && !isESMImportedImage(options.src)) {
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.widths && options.densities) {
throw new AstroError(IncompatibleDescriptorOptions);
}
if (options.src.format === "svg" && options.format !== "svg" || options.src.format !== "svg" && options.format === "svg") {
throw new AstroError(UnsupportedImageConversion);
}
}
}
const baseService = {
validateOptions(options) {
if (isESMImportedImage(options.src) && options.src.format === "svg") {
options.format = "svg";
}
verifyOptions(options);
if (!options.format) {
options.format = DEFAULT_OUTPUT_FORMAT;
}
if (options.width) options.width = Math.round(options.width);
if (options.height) options.height = Math.round(options.height);
if (options.layout && options.width && options.height) {
options.fit ??= "cover";
delete options.layout;
}
if (options.fit === "none") {
delete options.fit;
}
return options;
},
getHTMLAttributes(options) {
const { targetWidth, targetHeight } = getTargetDimensions(options);
const {
src,
width,
height,
format,
quality,
densities,
widths,
formats,
layout,
priority,
fit,
position,
background,
...attributes
} = options;
return {
...attributes,
width: targetWidth,
height: targetHeight,
loading: attributes.loading ?? "lazy",
decoding: attributes.decoding ?? "async"
};
},
getSrcSet(options) {
const { targetWidth, targetHeight } = getTargetDimensions(options);
const aspectRatio = targetWidth / targetHeight;
const { widths, densities } = options;
const targetFormat = options.format ?? DEFAULT_OUTPUT_FORMAT;
let transformedWidths = (widths ?? []).sort(sortNumeric);
let imageWidth = options.width;
let maxWidth = Infinity;
if (isESMImportedImage(options.src)) {
imageWidth = options.src.width;
maxWidth = imageWidth;
if (transformedWidths.length > 0 && transformedWidths.at(-1) > maxWidth) {
transformedWidths = transformedWidths.filter((width) => width <= maxWidth);
transformedWidths.push(maxWidth);
}
}
transformedWidths = Array.from(new Set(transformedWidths));
const {
width: transformWidth,
height: transformHeight,
...transformWithoutDimensions
} = options;
let allWidths = [];
if (densities) {
const densityValues = densities.map((density) => {
if (typeof density === "number") {
return density;
} else {
return parseFloat(density);
}
});
const densityWidths = densityValues.sort(sortNumeric).map((density) => Math.round(targetWidth * density));
allWidths = densityWidths.map((width, index) => ({
width,
descriptor: `${densityValues[index]}x`
}));
} else if (transformedWidths.length > 0) {
allWidths = transformedWidths.map((width) => ({
width,
descriptor: `${width}w`
}));
}
return allWidths.map(({ width, descriptor }) => {
const height = Math.round(width / aspectRatio);
const transform = { ...transformWithoutDimensions, width, height };
return {
transform,
descriptor,
attributes: {
type: `image/${targetFormat}`
}
};
});
},
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",
fit: "fit",
position: "position",
background: "background"
};
Object.entries(params).forEach(([param, key]) => {
options[key] && searchParams.append(param, options[key].toString());
});
const imageEndpoint = joinPaths("/", imageConfig.endpoint.route);
let url = `${imageEndpoint}?${searchParams}`;
if (imageConfig.assetQueryParams) {
const assetQueryString = imageConfig.assetQueryParams.toString();
if (assetQueryString) {
url += "&" + assetQueryString;
}
}
return url;
},
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"),
fit: params.get("fit"),
position: params.get("position") ?? void 0,
background: params.get("background") ?? void 0
};
return transform;
}
};
function getTargetDimensions(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;
}
}
return {
targetWidth,
targetHeight
};
}
function isImageMetadata(src) {
return src.fsPath && !("fsPath" in src);
}
const cssFitValues = ["fill", "contain", "cover", "scale-down"];
function addCSSVarsToStyle(vars, styles) {
const cssVars = Object.entries(vars).filter(([_, value]) => value !== void 0 && value !== false).map(([key, value]) => `--${key}: ${value};`).join(" ");
if (!styles) {
return cssVars;
}
const style = typeof styles === "string" ? styles : toStyleString(styles);
return `${cssVars} ${style}`;
}
const decoder = new TextDecoder();
const toUTF8String = (input, start = 0, end = input.length) => decoder.decode(input.slice(start, end));
const toHexString = (input, start = 0, end = input.length) => input.slice(start, end).reduce((memo, i) => memo + `0${i.toString(16)}`.slice(-2), "");
const getView = (input, offset) => new DataView(input.buffer, input.byteOffset + offset);
const readInt16LE = (input, offset = 0) => getView(input, offset).getInt16(0, true);
const readUInt16BE = (input, offset = 0) => getView(input, offset).getUint16(0, false);
const readUInt16LE = (input, offset = 0) => getView(input, offset).getUint16(0, true);
const readUInt24LE = (input, offset = 0) => {
const view = getView(input, offset);
return view.getUint16(0, true) + (view.getUint8(2) << 16);
};
const readInt32LE = (input, offset = 0) => getView(input, offset).getInt32(0, true);
const readUInt32BE = (input, offset = 0) => getView(input, offset).getUint32(0, false);
const readUInt32LE = (input, offset = 0) => getView(input, offset).getUint32(0, true);
const readUInt64 = (input, offset, isBigEndian) => getView(input, offset).getBigUint64(0, !isBigEndian);
const methods = {
readUInt16BE,
readUInt16LE,
readUInt32BE,
readUInt32LE
};
function readUInt(input, bits, offset = 0, isBigEndian = false) {
const endian = isBigEndian ? "BE" : "LE";
const methodName = `readUInt${bits}${endian}`;
return methods[methodName](input, offset);
}
function readBox(input, offset) {
if (input.length - offset < 4) return;
const boxSize = readUInt32BE(input, offset);
if (input.length - offset < boxSize) return;
return {
name: toUTF8String(input, 4 + offset, 8 + offset),
offset,
size: boxSize
};
}
function findBox(input, boxName, currentOffset) {
while (currentOffset < input.length) {
const box = readBox(input, currentOffset);
if (!box) break;
if (box.name === boxName) return box;
currentOffset += box.size > 0 ? box.size : 8;
}
}
const BMP = {
validate: (input) => toUTF8String(input, 0, 2) === "BM",
calculate: (input) => ({
height: Math.abs(readInt32LE(input, 22)),
width: readUInt32LE(input, 18)
})
};
const TYPE_ICON = 1;
const SIZE_HEADER$1 = 2 + 2 + 2;
const SIZE_IMAGE_ENTRY = 1 + 1 + 1 + 1 + 2 + 2 + 4 + 4;
function getSizeFromOffset(input, offset) {
const value = input[offset];
return value === 0 ? 256 : value;
}
function getImageSize$1(input, imageIndex) {
const offset = SIZE_HEADER$1 + imageIndex * SIZE_IMAGE_ENTRY;
return {
height: getSizeFromOffset(input, offset + 1),
width: getSizeFromOffset(input, offset)
};
}
const ICO = {
validate(input) {
const reserved = readUInt16LE(input, 0);
const imageCount = readUInt16LE(input, 4);
if (reserved !== 0 || imageCount === 0) return false;
const imageType = readUInt16LE(input, 2);
return imageType === TYPE_ICON;
},
calculate(input) {
const nbImages = readUInt16LE(input, 4);
const imageSize = getImageSize$1(input, 0);
if (nbImages === 1) return imageSize;
const images = [];
for (let imageIndex = 0; imageIndex < nbImages; imageIndex += 1) {
images.push(getImageSize$1(input, imageIndex));
}
return {
width: imageSize.width,
height: imageSize.height,
images
};
}
};
const TYPE_CURSOR = 2;
const CUR = {
validate(input) {
const reserved = readUInt16LE(input, 0);
const imageCount = readUInt16LE(input, 4);
if (reserved !== 0 || imageCount === 0) return false;
const imageType = readUInt16LE(input, 2);
return imageType === TYPE_CURSOR;
},
calculate: (input) => ICO.calculate(input)
};
const DDS = {
validate: (input) => readUInt32LE(input, 0) === 542327876,
calculate: (input) => ({
height: readUInt32LE(input, 12),
width: readUInt32LE(input, 16)
})
};
const gifRegexp = /^GIF8[79]a/;
const GIF = {
validate: (input) => gifRegexp.test(toUTF8String(input, 0, 6)),
calculate: (input) => ({
height: readUInt16LE(input, 8),
width: readUInt16LE(input, 6)
})
};
const brandMap = {
avif: "avif",
avis: "avif",
// avif-sequence
mif1: "heif",
msf1: "heif",
// heif-sequence
heic: "heic",
heix: "heic",
hevc: "heic",
// heic-sequence
hevx: "heic"
// heic-sequence
};
function detectType(input, start, end) {
let hasAvif = false;
let hasHeic = false;
let hasHeif = false;
for (let i = start; i <= end; i += 4) {
const brand = toUTF8String(input, i, i + 4);
if (brand === "avif" || brand === "avis") hasAvif = true;
else if (brand === "heic" || brand === "heix" || brand === "hevc" || brand === "hevx") hasHeic = true;
else if (brand === "mif1" || brand === "msf1") hasHeif = true;
}
if (hasAvif) return "avif";
if (hasHeic) return "heic";
if (hasHeif) return "heif";
}
const HEIF = {
validate(input) {
const boxType = toUTF8String(input, 4, 8);
if (boxType !== "ftyp") return false;
const ftypBox = findBox(input, "ftyp", 0);
if (!ftypBox) return false;
const brand = toUTF8String(input, ftypBox.offset + 8, ftypBox.offset + 12);
return brand in brandMap;
},
calculate(input) {
const metaBox = findBox(input, "meta", 0);
const iprpBox = metaBox && findBox(input, "iprp", metaBox.offset + 12);
const ipcoBox = iprpBox && findBox(input, "ipco", iprpBox.offset + 8);
if (!ipcoBox) {
throw new TypeError("Invalid HEIF, no ipco box found");
}
const type = detectType(input, 8, metaBox.offset);
const images = [];
let currentOffset = ipcoBox.offset + 8;
while (currentOffset < ipcoBox.offset + ipcoBox.size) {
const ispeBox = findBox(input, "ispe", currentOffset);
if (!ispeBox) break;
const rawWidth = readUInt32BE(input, ispeBox.offset + 12);
const rawHeight = readUInt32BE(input, ispeBox.offset + 16);
const clapBox = findBox(input, "clap", currentOffset);
let width = rawWidth;
let height = rawHeight;
if (clapBox && clapBox.offset < ipcoBox.offset + ipcoBox.size) {
const cropRight = readUInt32BE(input, clapBox.offset + 12);
width = rawWidth - cropRight;
}
images.push({ height, width });
currentOffset = ispeBox.offset + ispeBox.size;
}
if (images.length === 0) {
throw new TypeError("Invalid HEIF, no sizes found");
}
return {
width: images[0].width,
height: images[0].height,
type,
...images.length > 1 ? { images } : {}
};
}
};
const SIZE_HEADER = 4 + 4;
const FILE_LENGTH_OFFSET = 4;
const ENTRY_LENGTH_OFFSET = 4;
const ICON_TYPE_SIZE = {
ICON: 32,
"ICN#": 32,
// m => 16 x 16
"icm#": 16,
icm4: 16,
icm8: 16,
// s => 16 x 16
"ics#": 16,
ics4: 16,
ics8: 16,
is32: 16,
s8mk: 16,
icp4: 16,
// l => 32 x 32
icl4: 32,
icl8: 32,
il32: 32,
l8mk: 32,
icp5: 32,
ic11: 32,
// h => 48 x 48
ich4: 48,
ich8: 48,
ih32: 48,
h8mk: 48,
// . => 64 x 64
icp6: 64,
ic12: 32,
// t => 128 x 128
it32: 128,
t8mk: 128,
ic07: 128,
// . => 256 x 256
ic08: 256,
ic13: 256,
// . => 512 x 512
ic09: 512,
ic14: 512,
// . => 1024 x 1024
ic10: 1024
};
function readImageHeader(input, imageOffset) {
const imageLengthOffset = imageOffset + ENTRY_LENGTH_OFFSET;
return [
toUTF8String(input, imageOffset, imageLengthOffset),
readUInt32BE(input, imageLengthOffset)
];
}
function getImageSize(type) {
const size = ICON_TYPE_SIZE[type];
return { width: size, height: size, type };
}
const ICNS = {
validate: (input) => toUTF8String(input, 0, 4) === "icns",
calculate(input) {
const inputLength = input.length;
const fileLength = readUInt32BE(input, FILE_LENGTH_OFFSET);
let imageOffset = SIZE_HEADER;
const images = [];
while (imageOffset < fileLength && imageOffset < inputLength) {
const imageHeader = readImageHeader(input, imageOffset);
const imageSize = getImageSize(imageHeader[0]);
images.push(imageSize);
imageOffset += imageHeader[1];
}
if (images.length === 0) {
throw new TypeError("Invalid ICNS, no sizes found");
}
return {
width: images[0].width,
height: images[0].height,
...images.length > 1 ? { images } : {}
};
}
};
const J2C = {
// TODO: this doesn't seem right. SIZ marker doesn't have to be right after the SOC
validate: (input) => readUInt32BE(input, 0) === 4283432785,
calculate: (input) => ({
height: readUInt32BE(input, 12),
width: readUInt32BE(input, 8)
})
};
const JP2 = {
validate(input) {
const boxType = toUTF8String(input, 4, 8);
if (boxType !== "jP ") return false;
const ftypBox = findBox(input, "ftyp", 0);
if (!ftypBox) return false;
const brand = toUTF8String(input, ftypBox.offset + 8, ftypBox.offset + 12);
return brand === "jp2 ";
},
calculate(input) {
const jp2hBox = findBox(input, "jp2h", 0);
const ihdrBox = jp2hBox && findBox(input, "ihdr", jp2hBox.offset + 8);
if (ihdrBox) {
return {
height: readUInt32BE(input, ihdrBox.offset + 8),
width: readUInt32BE(input, ihdrBox.offset + 12)
};
}
throw new TypeError("Unsupported JPEG 2000 format");
}
};
const EXIF_MARKER = "45786966";
const APP1_DATA_SIZE_BYTES = 2;
const EXIF_HEADER_BYTES = 6;
const TIFF_BYTE_ALIGN_BYTES = 2;
const BIG_ENDIAN_BYTE_ALIGN = "4d4d";
const LITTLE_ENDIAN_BYTE_ALIGN = "4949";
const IDF_ENTRY_BYTES = 12;
const NUM_DIRECTORY_ENTRIES_BYTES = 2;
function isEXIF(input) {
return toHexString(input, 2, 6) === EXIF_MARKER;
}
function extractSize(input, index) {
return {
height: readUInt16BE(input, index),
width: readUInt16BE(input, index + 2)
};
}
function extractOrientation(exifBlock, isBigEndian) {
const idfOffset = 8;
const offset = EXIF_HEADER_BYTES + idfOffset;
const idfDirectoryEntries = readUInt(exifBlock, 16, offset, isBigEndian);
for (let directoryEntryNumber = 0; directoryEntryNumber < idfDirectoryEntries; directoryEntryNumber++) {
const start = offset + NUM_DIRECTORY_ENTRIES_BYTES + directoryEntryNumber * IDF_ENTRY_BYTES;
const end = start + IDF_ENTRY_BYTES;
if (start > exifBlock.length) {
return;
}
const block = exifBlock.slice(start, end);
const tagNumber = readUInt(block, 16, 0, isBigEndian);
if (tagNumber === 274) {
const dataFormat = readUInt(block, 16, 2, isBigEndian);
if (dataFormat !== 3) {
return;
}
const numberOfComponents = readUInt(block, 32, 4, isBigEndian);
if (numberOfComponents !== 1) {
return;
}
return readUInt(block, 16, 8, isBigEndian);
}
}
}
function validateExifBlock(input, index) {
const exifBlock = input.slice(APP1_DATA_SIZE_BYTES, index);
const byteAlign = toHexString(
exifBlock,
EXIF_HEADER_BYTES,
EXIF_HEADER_BYTES + TIFF_BYTE_ALIGN_BYTES
);
const isBigEndian = byteAlign === BIG_ENDIAN_BYTE_ALIGN;
const isLittleEndian = byteAlign === LITTLE_ENDIAN_BYTE_ALIGN;
if (isBigEndian || isLittleEndian) {
return extractOrientation(exifBlock, isBigEndian);
}
}
function validateInput(input, index) {
if (index > input.length) {
throw new TypeError("Corrupt JPG, exceeded buffer limits");
}
}
const JPG = {
validate: (input) => toHexString(input, 0, 2) === "ffd8",
calculate(_input) {
let input = _input.slice(4);
let orientation;
let next;
while (input.length) {
const i = readUInt16BE(input, 0);
validateInput(input, i);
if (input[i] !== 255) {
input = input.slice(1);
continue;
}
if (isEXIF(input)) {
orientation = validateExifBlock(input, i);
}
next = input[i + 1];
if (next === 192 || next === 193 || next === 194) {
const size = extractSize(input, i + 5);
if (!orientation) {
return size;
}
return {
height: size.height,
orientation,
width: size.width
};
}
input = input.slice(i + 2);
}
throw new TypeError("Invalid JPG, no size found");
}
};
class BitReader {
constructor(input, endianness) {
this.input = input;
this.endianness = endianness;
}
// Skip the first 16 bits (2 bytes) of signature
byteOffset = 2;
bitOffset = 0;
/** Reads a specified number of bits, and move the offset */
getBits(length = 1) {
let result = 0;
let bitsRead = 0;
while (bitsRead < length) {
if (this.byteOffset >= this.input.length) {
throw new Error("Reached end of input");
}
const currentByte = this.input[this.byteOffset];
const bitsLeft = 8 - this.bitOffset;
const bitsToRead = Math.min(length - bitsRead, bitsLeft);
if (this.endianness === "little-endian") {
const mask = (1 << bitsToRead) - 1;
const bits = currentByte >> this.bitOffset & mask;
result |= bits << bitsRead;
} else {
const mask = (1 << bitsToRead) - 1 << 8 - this.bitOffset - bitsToRead;
const bits = (currentByte & mask) >> 8 - this.bitOffset - bitsToRead;
result = result << bitsToRead | bits;
}
bitsRead += bitsToRead;
this.bitOffset += bitsToRead;
if (this.bitOffset === 8) {
this.byteOffset++;
this.bitOffset = 0;
}
}
return result;
}
}
function calculateImageDimension(reader, isSmallImage) {
if (isSmallImage) {
return 8 * (1 + reader.getBits(5));
}
const sizeClass = reader.getBits(2);
const extraBits = [9, 13, 18, 30][sizeClass];
return 1 + reader.getBits(extraBits);
}
function calculateImageWidth(reader, isSmallImage, widthMode, height) {
if (isSmallImage && widthMode === 0) {
return 8 * (1 + reader.getBits(5));
}
if (widthMode === 0) {
return calculateImageDimension(reader, false);
}
const aspectRatios = [1, 1.2, 4 / 3, 1.5, 16 / 9, 5 / 4, 2];
return Math.floor(height * aspectRatios[widthMode - 1]);
}
const JXLStream = {
validate: (input) => {
return toHexString(input, 0, 2) === "ff0a";
},
calculate(input) {
const reader = new BitReader(input, "little-endian");
const isSmallImage = reader.getBits(1) === 1;
const height = calculateImageDimension(reader, isSmallImage);
const widthMode = reader.getBits(3);
const width = calculateImageWidth(reader, isSmallImage, widthMode, height);
return { width, height };
}
};
function extractCodestream(input) {
const jxlcBox = findBox(input, "jxlc", 0);
if (jxlcBox) {
return input.slice(jxlcBox.offset + 8, jxlcBox.offset + jxlcBox.size);
}
const partialStreams = extractPartialStreams(input);
if (partialStreams.length > 0) {
return concatenateCodestreams(partialStreams);
}
return void 0;
}
function extractPartialStreams(input) {
const partialStreams = [];
let offset = 0;
while (offset < input.length) {
const jxlpBox = findBox(input, "jxlp", offset);
if (!jxlpBox) break;
partialStreams.push(
input.slice(jxlpBox.offset + 12, jxlpBox.offset + jxlpBox.size)
);
offset = jxlpBox.offset + jxlpBox.size;
}
return partialStreams;
}
function concatenateCodestreams(partialCodestreams) {
const totalLength = partialCodestreams.reduce(
(acc, curr) => acc + curr.length,
0
);
const codestream = new Uint8Array(totalLength);
let position = 0;
for (const partial of partialCodestreams) {
codestream.set(partial, position);
position += partial.length;
}
return codestream;
}
const JXL = {
validate: (input) => {
const boxType = toUTF8String(input, 4, 8);
if (boxType !== "JXL ") return false;
const ftypBox = findBox(input, "ftyp", 0);
if (!ftypBox) return false;
const brand = toUTF8String(input, ftypBox.offset + 8, ftypBox.offset + 12);
return brand === "jxl ";
},
calculate(input) {
const codestream = extractCodestream(input);
if (codestream) return JXLStream.calculate(codestream);
throw new Error("No codestream found in JXL container");
}
};
const KTX = {
validate: (input) => {
const signature = toUTF8String(input, 1, 7);
return ["KTX 11", "KTX 20"].includes(signature);
},
calculate: (input) => {
const type = input[5] === 49 ? "ktx" : "ktx2";
const offset = type === "ktx" ? 36 : 20;
return {
height: readUInt32LE(input, offset + 4),
width: readUInt32LE(input, offset),
type
};
}
};
const pngSignature = "PNG\r\n\n";
const pngImageHeaderChunkName = "IHDR";
const pngFriedChunkName = "CgBI";
const PNG = {
validate(input) {
if (pngSignature === toUTF8String(input, 1, 8)) {
let chunkName = toUTF8String(input, 12, 16);
if (chunkName === pngFriedChunkName) {
chunkName = toUTF8String(input, 28, 32);
}
if (chunkName !== pngImageHeaderChunkName) {
throw new TypeError("Invalid PNG");
}
return true;
}
return false;
},
calculate(input) {
if (toUTF8String(input, 12, 16) === pngFriedChunkName) {
return {
height: readUInt32BE(input, 36),
width: readUInt32BE(input, 32)
};
}
return {
height: readUInt32BE(input, 20),
width: readUInt32BE(input, 16)
};
}
};
const PNMTypes = {
P1: "pbm/ascii",
P2: "pgm/ascii",
P3: "ppm/ascii",
P4: "pbm",
P5: "pgm",
P6: "ppm",
P7: "pam",
PF: "pfm"
};
const handlers = {
default: (lines) => {
let dimensions = [];
while (lines.length > 0) {
const line = lines.shift();
if (line[0] === "#") {
continue;
}
dimensions = line.split(" ");
break;
}
if (dimensions.length === 2) {
return {
height: Number.parseInt(dimensions[1], 10),
width: Number.parseInt(dimensions[0], 10)
};
}
throw new TypeError("Invalid PNM");
},
pam: (lines) => {
const size = {};
while (lines.length > 0) {
const line = lines.shift();
if (line.length > 16 || line.charCodeAt(0) > 128) {
continue;
}
const [key, value] = line.split(" ");
if (key && value) {
size[key.toLowerCase()] = Number.parseInt(value, 10);
}
if (size.height && size.width) {
break;
}
}
if (size.height && size.width) {
return {
height: size.height,
width: size.width
};
}
throw new TypeError("Invalid PAM");
}
};
const PNM = {
validate: (input) => toUTF8String(input, 0, 2) in PNMTypes,
calculate(input) {
const signature = toUTF8String(input, 0, 2);
const type = PNMTypes[signature];
const lines = toUTF8String(input, 3).split(/[\r\n]+/);
const handler = handlers[type] || handlers.default;
return handler(lines);
}
};
const PSD = {
validate: (input) => toUTF8String(input, 0, 4) === "8BPS",
calculate: (input) => ({
height: readUInt32BE(input, 14),
width: readUInt32BE(input, 18)
})
};
const svgReg = /<svg\s([^>"']|"[^"]*"|'[^']*')*>/;
const extractorRegExps = {
height: /\sheight=(['"])([^%]+?)\1/,
root: svgReg,
viewbox: /\sviewBox=(['"])(.+?)\1/i,
width: /\swidth=(['"])([^%]+?)\1/
};
const INCH_CM = 2.54;
const units = {
in: 96,
cm: 96 / INCH_CM,
em: 16,
ex: 8,
m: 96 / INCH_CM * 100,
mm: 96 / INCH_CM / 10,
pc: 96 / 72 / 12,
pt: 96 / 72,
px: 1
};
const unitsReg = new RegExp(
`^([0-9.]+(?:e\\d+)?)(${Object.keys(units).join("|")})?$`
);
function parseLength(len) {
const m = unitsReg.exec(len);
if (!m) {
return void 0;
}
return Math.round(Number(m[1]) * (units[m[2]] || 1));
}
function parseViewbox(viewbox) {
const bounds = viewbox.split(" ");
return {
height: parseLength(bounds[3]),
width: parseLength(bounds[2])
};
}
function parseAttributes(root) {
const width = extractorRegExps.width.exec(root);
const height = extractorRegExps.height.exec(root);
const viewbox = extractorRegExps.viewbox.exec(root);
return {
height: height && parseLength(height[2]),
viewbox: viewbox && parseViewbox(viewbox[2]),
width: width && parseLength(width[2])
};
}
function calculateByDimensions(attrs) {
return {
height: attrs.height,
width: attrs.width
};
}
function calculateByViewbox(attrs, viewbox) {
const ratio = viewbox.width / viewbox.height;
if (attrs.width) {
return {
height: Math.floor(attrs.width / ratio),
width: attrs.width
};
}
if (attrs.height) {
return {
height: attrs.height,
width: Math.floor(attrs.height * ratio)
};
}
return {
height: viewbox.height,
width: viewbox.width
};
}
const SVG = {
// Scan only the first kilo-byte to speed up the check on larger files
validate: (input) => svgReg.test(toUTF8String(input, 0, 1e3)),
calculate(input) {
const root = extractorRegExps.root.exec(toUTF8String(input));
if (root) {
const attrs = parseAttributes(root[0]);
if (attrs.width && attrs.height) {
return calculateByDimensions(attrs);
}
if (attrs.viewbox) {
return calculateByViewbox(attrs, attrs.viewbox);
}
}
throw new TypeError("Invalid SVG");
}
};
const TGA = {
validate(input) {
return readUInt16LE(input, 0) === 0 && readUInt16LE(input, 4) === 0;
},
calculate(input) {
return {
height: readUInt16LE(input, 14),
width: readUInt16LE(input, 12)
};
}
};
const CONSTANTS = {
TAG: {
WIDTH: 256,
HEIGHT: 257,
COMPRESSION: 259
},
TYPE: {
SHORT: 3,
LONG: 4,
LONG8: 16
},
ENTRY_SIZE: {
STANDARD: 12,
BIG: 20
},
COUNT_SIZE: {
STANDARD: 2,
BIG: 8
}
};
function readIFD(input, { isBigEndian, isBigTiff }) {
const ifdOffset = isBigTiff ? Number(readUInt64(input, 8, isBigEndian)) : readUInt(input, 32, 4, isBigEndian);
const entryCountSize = isBigTiff ? CONSTANTS.COUNT_SIZE.BIG : CONSTANTS.COUNT_SIZE.STANDARD;
return input.slice(ifdOffset + entryCountSize);
}
function readTagValue(input, type, offset, isBigEndian) {
switch (type) {
case CONSTANTS.TYPE.SHORT:
return readUInt(input, 16, offset, isBigEndian);
case CONSTANTS.TYPE.LONG:
return readUInt(input, 32, offset, isBigEndian);
case CONSTANTS.TYPE.LONG8: {
const value = Number(readUInt64(input, offset, isBigEndian));
if (value > Number.MAX_SAFE_INTEGER) {
throw new TypeError("Value too large");
}
return value;
}
default:
return 0;
}
}
function nextTag(input, isBigTiff) {
const entrySize = isBigTiff ? CONSTANTS.ENTRY_SIZE.BIG : CONSTANTS.ENTRY_SIZE.STANDARD;
if (input.length > entrySize) {
return input.slice(entrySize);
}
}
function extractTags(input, { isBigEndian, isBigTiff }) {
const tags = {};
let temp = input;
while (temp?.length) {
const code = readUInt(temp, 16, 0, isBigEndian);
const type = readUInt(temp, 16, 2, isBigEndian);
const length = isBigTiff ? Number(readUInt64(temp, 4, isBigEndian)) : readUInt(temp, 32, 4, isBigEndian);
if (code === 0) break;
if (length === 1 && (type === CONSTANTS.TYPE.SHORT || type === CONSTANTS.TYPE.LONG || isBigTiff && type === CONSTANTS.TYPE.LONG8)) {
const valueOffset = isBigTiff ? 12 : 8;
tags[code] = readTagValue(temp, type, valueOffset, isBigEndian);
}
temp = nextTag(temp, isBigTiff);
}
return tags;
}
function determineFormat(input) {
const signature = toUTF8String(input, 0, 2);
const version = readUInt(input, 16, 2, signature === "MM");
return {
isBigEndian: signature === "MM",
isBigTiff: version === 43
};
}
function validateBigTIFFHeader(input, isBigEndian) {
const byteSize = readUInt(input, 16, 4, isBigEndian);
const reserved = readUInt(input, 16, 6, isBigEndian);
if (byteSize !== 8 || reserved !== 0) {
throw new TypeError("Invalid BigTIFF header");
}
}
const signatures = /* @__PURE__ */ new Set([
"49492a00",
// Little Endian
"4d4d002a",
// Big Endian
"49492b00",
// BigTIFF Little Endian
"4d4d002b"
// BigTIFF Big Endian
]);
const TIFF = {
validate: (input) => {
const signature = toHexString(input, 0, 4);
return signatures.has(signature);
},
calculate(input) {
const format = determineFormat(input);
if (format.isBigTiff) {
validateBigTIFFHeader(input, format.isBigEndian);
}
const ifdBuffer = readIFD(input, format);
const tags = extractTags(ifdBuffer, format);
const info = {
height: tags[CONSTANTS.TAG.HEIGHT],
width: tags[CONSTANTS.TAG.WIDTH],
type: format.isBigTiff ? "bigtiff" : "tiff"
};
if (tags[CONSTANTS.TAG.COMPRESSION]) {
info.compression = tags[CONSTANTS.TAG.COMPRESSION];
}
if (!info.width || !info.height) {
throw new TypeError("Invalid Tiff. Missing tags");
}
return info;
}
};
function calculateExtended(input) {
return {
height: 1 + readUInt24LE(input, 7),
width: 1 + readUInt24LE(input, 4)
};
}
function calculateLossless(input) {
return {
height: 1 + ((input[4] & 15) << 10 | input[3] << 2 | (input[2] & 192) >> 6),
width: 1 + ((input[2] & 63) << 8 | input[1])
};
}
function calculateLossy(input) {
return {
height: readInt16LE(input, 8) & 16383,
width: readInt16LE(input, 6) & 16383
};
}
const WEBP = {
validate(input) {
const riffHeader = "RIFF" === toUTF8String(input, 0, 4);
const webpHeader = "WEBP" === toUTF8String(input, 8, 12);
const vp8Header = "VP8" === toUTF8String(input, 12, 15);
return riffHeader && webpHeader && vp8Header;
},
calculate(_input) {
const chunkHeader = toUTF8String(_input, 12, 16);
const input = _input.slice(20, 30);
if (chunkHeader === "VP8X") {
const extendedHeader = input[0];
const validStart = (extendedHeader & 192) === 0;
const validEnd = (extendedHeader & 1) === 0;
if (validStart && validEnd) {
return calculateExtended(input);
}
throw new TypeError("Invalid WebP");
}
if (chunkHeader === "VP8 " && input[0] !== 47) {
return calculateLossy(input);
}
const signature = toHexString(input, 3, 6);
if (chunkHeader === "VP8L" && signature !== "9d012a") {
return calculateLossless(input);
}
throw new TypeError("Invalid WebP");
}
};
const typeHandlers = /* @__PURE__ */ new Map([
["bmp", BMP],
["cur", CUR],
["dds", DDS],
["gif", GIF],
["heif", HEIF],
["icns", ICNS],
["ico", ICO],
["j2c", J2C],
["jp2", JP2],
["jpg", JPG],
["jxl", JXL],
["jxl-stream", JXLStream],
["ktx", KTX],
["png", PNG],
["pnm", PNM],
["psd", PSD],
["svg", SVG],
["tga", TGA],
["tiff", TIFF],
["webp", WEBP]
]);
const types = Array.from(typeHandlers.keys());
const firstBytes = /* @__PURE__ */ new Map([
[0, "heif"],
[56, "psd"],
[66, "bmp"],
[68, "dds"],
[71, "gif"],
[73, "tiff"],
[77, "tiff"],
[82, "webp"],
[105, "icns"],
[137, "png"],
[255, "jpg"]
]);
function detector(input) {
const byte = input[0];
const type = firstBytes.get(byte);
if (type && typeHandlers.get(type).validate(input)) {
return type;
}
return types.find((imageType) => typeHandlers.get(imageType).validate(input));
}
function lookup(input) {
const type = detector(input);
if (typeof type !== "undefined") {
const size = typeHandlers.get(type).calculate(input);
if (size !== void 0) {
size.type = size.type ?? type;
return size;
}
}
throw new TypeError("unsupported file type: " + type);
}
async function imageMetadata(data, src) {
let result;
try {
result = lookup(data);
} catch {
throw new AstroError({
...NoImageMetadata,
message: NoImageMetadata.message(src)
});
}
if (!result.height || !result.width || !result.type) {
throw new AstroError({
...NoImageMetadata,
message: NoImageMetadata.message(src)
});
}
const { width, height, type, orientation } = result;
const isPortrait = (orientation || 0) >= 5;
return {
width: isPortrait ? height : width,
height: isPortrait ? width : height,
format: type,
orientation
};
}
async function inferRemoteSize(url, imageConfig) {
if (!URL.canParse(url)) {
throw new AstroError({
...FailedToFetchRemoteImageDimensions,
message: FailedToFetchRemoteImageDimensions.message(url)
});
}
const allowlistConfig = imageConfig ? {
domains: imageConfig.domains ?? [],
remotePatterns: imageConfig.remotePatterns ?? []
} : void 0;
if (!allowlistConfig) {
const parsedUrl = new URL(url);
if (!["http:", "https:"].includes(parsedUrl.protocol)) {
throw new AstroError({
...FailedToFetchRemoteImageDimensions,
message: FailedToFetchRemoteImageDimensions.message(url)
});
}
}
if (allowlistConfig && !isRemoteAllowed(url, allowlistConfig)) {
throw new AstroError({
...RemoteImageNotAllowed,
message: RemoteImageNotAllowed.message(url)
});
}
const response = await fetch(url, { redirect: "manual" });
if (response.status >= 300 && response.status < 400) {
throw new AstroError({
...FailedToFetchRemoteImageDimensions,
message: FailedToFetchRemoteImageDimensions.message(url)
});
}
if (!response.body || !response.ok) {
throw new AstroError({
...FailedToFetchRemoteImageDimensions,
message: FailedToFetchRemoteImageDimensions.message(url)
});
}
const reader = response.body.getReader();
let done, value;
let accumulatedChunks = new Uint8Array();
while (!done) {
const readResult = await reader.read();
done = readResult.done;
if (done) break;
if (readResult.value) {
value = readResult.value;
let tmp = new Uint8Array(accumulatedChunks.length + value.length);
tmp.set(accumulatedChunks, 0);
tmp.set(value, accumulatedChunks.length);
accumulatedChunks = tmp;
try {
const dimensions = await imageMetadata(accumulatedChunks, url);
if (dimensions) {
await reader.cancel();
return dimensions;
}
} catch {
}
}
}
throw new AstroError({
...NoImageMetadata,
message: NoImageMetadata.message(url)
});
}
const PLACEHOLDER_BASE = "astro://placeholder";
function createPlaceholderURL(pathOrUrl) {
return new URL(pathOrUrl, PLACEHOLDER_BASE);
}
function stringifyPlaceholderURL(url) {
return url.href.replace(PLACEHOLDER_BASE, "");
}
async function getConfiguredImageService() {
if (!globalThis?.astroAsset?.imageService) {
const { default: service } = await import(
// @ts-expect-error
'./sharp_Btg2Ob7H.mjs'
).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$1(options, imageConfig) {
if (!options || typeof options !== "object") {
throw new AstroError({
...ExpectedImageOptions,
message: ExpectedImageOptions.message(JSON.stringify(options))
});
}
if (typeof options.src === "undefined") {
throw new AstroError({
...ExpectedImage,
message: ExpectedImage.message(
options.src,
"undefined",
JSON.stringify(options)
)
});
}
if (isImageMetadata(options)) {
throw new AstroError(ExpectedNotESMImage);
}
const service = await getConfiguredImageService();
const resolvedOptions = {
...options,
src: await resolveSrc(options.src)
};
let originalWidth;
let originalHeight;
if (options.inferSize) {
delete resolvedOptions.inferSize;
if (isRemoteImage(resolvedOptions.src) && isRemotePath(resolvedOptions.src)) {
if (!isRemoteAllowed(resolvedOptions.src, imageConfig)) {
throw new AstroError({
...RemoteImageNotAllowed,
message: RemoteImageNotAllowed.message(resolvedOptions.src)
});
}
const result = await inferRemoteSize(resolvedOptions.src, imageConfig);
resolvedOptions.width ??= result.width;
resolvedOptions.height ??= result.height;
originalWidth = result.width;
originalHeight = result.height;
}
}
const originalFilePath = isESMImportedImage(resolvedOptions.src) ? resolvedOptions.src.fsPath : void 0;
const clonedSrc = isESMImportedImage(resolvedOptions.src) ? (
// @ts-expect-error - clone is a private, hidden prop
resolvedOptions.src.clone ?? resolvedOptions.src
) : resolvedOptions.src;
if (isESMImportedImage(clonedSrc)) {
originalWidth = clonedSrc.width;
originalHeight = clonedSrc.height;
}
if (originalWidth && originalHeight) {
const aspectRatio = originalWidth / originalHeight;
if (resolvedOptions.height && !resolvedOptions.width) {
resolvedOptions.width = Math.round(resolvedOptions.height * aspectRatio);
} else if (resolvedOptions.width && !resolvedOptions.height) {
resolvedOptions.height = Math.round(resolvedOptions.width / aspectRatio);
} else if (!resolvedOptions.width && !resolvedOptions.height) {
resolvedOptions.width = originalWidth;
resolvedOptions.height = originalHeight;
}
}
resolvedOptions.src = clonedSrc;
const layout = options.layout ?? imageConfig.layout ?? "none";
if (resolvedOptions.priority) {
resolvedOptions.loading ??= "eager";
resolvedOptions.decoding ??= "sync";
resolvedOptions.fetchpriority ??= "high";
delete resolvedOptions.priority;
} else {
resolvedOptions.loading ??= "lazy";
resolvedOptions.decoding ??= "async";
resolvedOptions.fetchpriority ??= "auto";
}
if (layout !== "none") {
resolvedOptions.widths ||= getWidths({
width: resolvedOptions.width,
layout,
originalWidth,
breakpoints: imageConfig.breakpoints?.length ? imageConfig.breakpoints : isLocalService(service) ? LIMITED_RESOLUTIONS : DEFAULT_RESOLUTIONS
});
resolvedOptions.sizes ||= getSizesAttribute({ width: resolvedOptions.width, layout });
delete resolvedOptions.densities;
resolvedOptions.style = addCSSVarsToStyle(
{
fit: cssFitValues.includes(resolvedOptions.fit ?? "") && resolvedOptions.fit,
pos: resolvedOptions.position
},
resolvedOptions.style
);
resolvedOptions["data-astro-image"] = layout;
}
const validatedOptions = service.validateOptions ? await service.validateOptions(resolvedOptions, imageConfig) : resolvedOptions;
const srcSetTransforms = service.getSrcSet ? await service.getSrcSet(validatedOptions, imageConfig) : [];
let imageURL = await service.getURL(validatedOptions, imageConfig);
const matchesValidatedTransform = (transform) => transform.width === validatedOptions.width && transform.height === validatedOptions.height && transform.format === validatedOptions.format;
let srcSets = await Promise.all(
srcSetTransforms.map(async (srcSet) => {
return {
transform: srcSet.transform,
url: matchesValidatedTransform(srcSet.transform) ? imageURL : await service.getURL(srcSet.transform, imageConfig),
descriptor: srcSet.descriptor,
attributes: srcSet.attributes
};
})
);
if (isLocalService(service) && globalThis.astroAsset.addStaticImage && !(isRemoteImage(validatedOptions.src) && imageURL === validatedOptions.src)) {
const propsToHash = service.propertiesToHash ?? DEFAULT_HASH_PROPS;
imageURL = globalThis.astroAsset.addStaticImage(
validatedOptions,
propsToHash,
originalFilePath
);
srcSets = srcSetTransforms.map((srcSet) => {
return {
transform: srcSet.transform,
url: matchesValidatedTransform(srcSet.transform) ? imageURL : globalThis.astroAsset.addStaticImage(srcSet.transform, propsToHash, originalFilePath),
descriptor: srcSet.descriptor,
attributes: srcSet.attributes
};
});
} else if (imageConfig.assetQueryParams) {
const imageURLObj = createPlaceholderURL(imageURL);
imageConfig.assetQueryParams.forEach((value, key) => {
imageURLObj.searchParams.set(key, value);
});
imageURL = stringifyPlaceholderURL(imageURLObj);
srcSets = srcSets.map((srcSet) => {
const urlObj = createPlaceholderURL(srcSet.url);
imageConfig.assetQueryParams.forEach((value, key) => {
urlObj.searchParams.set(key, value);
});
return {
...srcSet,
url: stringifyPlaceholderURL(urlObj)
};
});
}
return {
rawOptions: resolvedOptions,
options: validatedOptions,
src: imageURL,
srcSet: {
values: srcSets,
attribute: srcSets.map((srcSet) => `${srcSet.url} ${srcSet.descriptor}`).join(", ")
},
attributes: service.getHTMLAttributes !== void 0 ? await service.getHTMLAttributes(validatedOptions, imageConfig) : {}
};
}
const $$Astro$2 = createAstro();
const $$Image = createComponent(async ($$result, $$props, $$slots) => {
const Astro2 = $$result.createAstro($$Astro$2, $$props, $$slots);
Astro2.self = $$Image;
const props = Astro2.props;
if (props.alt === void 0 || props.alt === null) {
throw new AstroError(ImageMissingAlt);
}
if (typeof props.width === "string") {
props.width = parseInt(props.width);
}
if (typeof props.height === "string") {
props.height = parseInt(props.height);
}
const layout = props.layout ?? imageConfig.layout ?? "none";
if (layout !== "none") {
props.layout ??= imageConfig.layout;
props.fit ??= imageConfig.objectFit ?? "cover";
props.position ??= imageConfig.objectPosition ?? "center";
}
const image = await getImage(props);
const additionalAttributes = {};
if (image.srcSet.values.length > 0) {
additionalAttributes.srcset = image.srcSet.attribute;
}
const { class: className, ...attributes } = { ...additionalAttributes, ...image.attributes };
return renderTemplate`${maybeRenderHead()}<img${addAttribute(image.src, "src")}${spreadAttributes(attributes)}${addAttribute(className, "class")}>`;
}, "/tmp/astro-directus/node_modules/astro/components/Image.astro", void 0);
const $$Astro$1 = createAstro();
const $$Picture = createComponent(async ($$result, $$props, $$slots) => {
const Astro2 = $$result.createAstro($$Astro$1, $$props, $$slots);
Astro2.self = $$Picture;
const defaultFormats = ["webp"];
const defaultFallbackFormat = "png";
const specialFormatsFallback = ["gif", "svg", "jpg", "jpeg"];
const { formats = defaultFormats, pictureAttributes = {}, fallbackFormat, ...props } = Astro2.props;
if (props.alt === void 0 || props.alt === null) {
throw new AstroError(ImageMissingAlt);
}
const scopedStyleClass = props.class?.match(/\bastro-\w{8}\b/)?.[0];
if (scopedStyleClass) {
if (pictureAttributes.class) {
pictureAttributes.class = `${pictureAttributes.class} ${scopedStyleClass}`;
} else {
pictureAttributes.class = scopedStyleClass;
}
}
const layout = props.layout ?? imageConfig.layout ?? "none";
const useResponsive = layout !== "none";
if (useResponsive) {
props.layout ??= imageConfig.layout;
props.fit ??= imageConfig.objectFit ?? "cover";
props.position ??= imageConfig.objectPosition ?? "center";
}
for (const key in props) {
if (key.startsWith("data-astro-cid")) {
pictureAttributes[key] = props[key];
}
}
const originalSrc = await resolveSrc(props.src);
const optimizedImages = await Promise.all(
formats.map(
async (format) => await getImage({
...props,
src: originalSrc,
format,
widths: props.widths,
densities: props.densities
})
)
);
let resultFallbackFormat = fallbackFormat ?? defaultFallbackFormat;
if (!fallbackFormat && isESMImportedImage(originalSrc) && specialFormatsFallback.includes(originalSrc.format)) {
resultFallbackFormat = originalSrc.format;
}
const fallbackImage = await getImage({
...props,
format: resultFallbackFormat,
widths: props.widths,
densities: props.densities
});
const imgAdditionalAttributes = {};
const sourceAdditionalAttributes = {};
if (props.sizes) {
sourceAdditionalAttributes.sizes = props.sizes;
}
if (fallbackImage.srcSet.values.length > 0) {
imgAdditionalAttributes.srcset = fallbackImage.srcSet.attribute;
}
const { class: className, ...attributes } = {
...imgAdditionalAttributes,
...fallbackImage.attributes
};
return renderTemplate`${maybeRenderHead()}<picture${spreadAttributes(pictureAttributes)}> ${Object.entries(optimizedImages).map(([_, image]) => {
const srcsetAttribute = props.densities || !props.densities && !props.widths && !useResponsive ? `${image.src}${image.srcSet.values.length > 0 ? ", " + image.srcSet.attribute : ""}` : image.srcSet.attribute;
return renderTemplate`<source${addAttribute(srcsetAttribute, "srcset")}${addAttribute(mime.lookup(image.options.format ?? image.src) ?? `image/${image.options.format}`, "type")}${spreadAttributes(sourceAdditionalAttributes)}>`;
})} <img${addAttribute(fallbackImage.src, "src")}${spreadAttributes(attributes)}${addAttribute(className, "class")}> </picture>`;
}, "/tmp/astro-directus/node_modules/astro/components/Picture.astro", void 0);
const mod = /*#__PURE__*/Object.freeze(/*#__PURE__*/Object.defineProperty({
__proto__: null
}, Symbol.toStringTag, { value: 'Module' }));
function filterPreloads(data, preload) {
if (!preload) {
return null;
}
if (preload === true) {
return data;
}
return data.filter(
({ weight, style, subset }) => preload.some((p) => {
if (p.weight !== void 0 && weight !== void 0 && !checkWeight(p.weight.toString(), weight)) {
return false;
}
if (p.style !== void 0 && p.style !== style) {
return false;
}
if (p.subset !== void 0 && p.subset !== subset) {
return false;
}
return true;
})
);
}
function checkWeight(input, target) {
const trimmedInput = input.trim();
if (trimmedInput.includes(" ")) {
return trimmedInput === target;
}
if (target.includes(" ")) {
const [a, b] = target.split(" ");
const parsedInput = Number.parseInt(input);
return parsedInput >= Number.parseInt(a) && parsedInput <= Number.parseInt(b);
}
return input === target;
}
const $$Astro = createAstro();
const $$Font = createComponent(($$result, $$props, $$slots) => {
const Astro2 = $$result.createAstro($$Astro, $$props, $$slots);
Astro2.self = $$Font;
const { componentDataByCssVariable } = mod;
if (!componentDataByCssVariable) {
throw new AstroError(ExperimentalFontsNotEnabled);
}
const { cssVariable, preload = false } = Astro2.props;
const data = componentDataByCssVariable.get(cssVariable);
if (!data) {
throw new AstroError({
...FontFamilyNotFound,
message: FontFamilyNotFound.message(cssVariable)
});
}
const filteredPreloadData = filterPreloads(data.preloads, preload);
return renderTemplate`<style>${unescapeHTML(data.css)}</style>${filteredPreloadData?.map(({ url, type }) => renderTemplate`<link rel="preload"${addAttribute(url, "href")} as="font"${addAttribute(`font/${type}`, "type")} crossorigin>`)}`;
}, "/tmp/astro-directus/node_modules/astro/components/Font.astro", void 0);
const assetQueryParams = undefined;
const imageConfig = {"endpoint":{"route":"/_image","entrypoint":"astro/assets/endpoint/node"},"service":{"entrypoint":"astro/assets/services/sharp","config":{}},"domains":[],"remotePatterns":[],"responsiveStyles":false};
Object.defineProperty(imageConfig, 'assetQueryParams', {
value: assetQueryParams,
enumerable: false,
configurable: true,
});
// This is used by the @astrojs/node integration to locate images.
// It's unused on other platforms, but on some platforms like Netlify (and presumably also Vercel)
// new URL("dist/...") is interpreted by the bundler as a signal to include that directory
// in the Lambda bundle, which would bloat the bundle with images.
// To prevent this, we mark the URL construction as pure,
// so that it's tree-shaken away for all platforms that don't need it.
const outDir = /* #__PURE__ */ new URL("file:///tmp/astro-directus/dist/client/");
const serverDir = /* #__PURE__ */ new URL("file:///tmp/astro-directus/dist/server/");
const getImage = async (options) => await getImage$1(options, imageConfig);
const fnv1a52 = (str) => {
const len = str.length;
let i = 0, t0 = 0, v0 = 8997, t1 = 0, v1 = 33826, t2 = 0, v2 = 40164, t3 = 0, v3 = 52210;
while (i < len) {
v0 ^= str.charCodeAt(i++);
t0 = v0 * 435;
t1 = v1 * 435;
t2 = v2 * 435;
t3 = v3 * 435;
t2 += v0 << 8;
t3 += v1 << 8;
t1 += t0 >>> 16;
v0 = t0 & 65535;
t2 += t1 >>> 16;
v1 = t1 & 65535;
v3 = t3 + (t2 >>> 16) & 65535;
v2 = t2 & 65535;
}
return (v3 & 15) * 281474976710656 + v2 * 4294967296 + v1 * 65536 + (v0 ^ v3 >> 4);
};
const etag = (payload, weak = false) => {
const prefix = weak ? 'W/"' : '"';
return prefix + fnv1a52(payload).toString(36) + payload.length.toString(36) + '"';
};
async function loadRemoteImage(src) {
try {
const res = await fetch(src, { redirect: "manual" });
if (res.status >= 300 && res.status < 400) {
return void 0;
}
if (!res.ok) {
return void 0;
}
return Buffer.from(await res.arrayBuffer());
} catch {
return void 0;
}
}
const handleImageRequest = async ({
request,
loadLocalImage
}) => {
const imageService = await getConfiguredImageService();
if (!("transform" in imageService)) {
throw new Error("Configured image service is not a local service");
}
const url = new URL(request.url);
const transform = await imageService.parseURL(url, imageConfig);
if (!transform?.src) {
return new Response("Invalid request", { status: 400 });
}
let inputBuffer = void 0;
if (isRemotePath(transform.src)) {
if (!isRemoteAllowed(transform.src, imageConfig)) {
return new Response("Forbidden", { status: 403 });
}
inputBuffer = await loadRemoteImage(new URL(transform.src));
} else {
inputBuffer = await loadLocalImage(removeQueryString(transform.src), url);
}
if (!inputBuffer) {
return new Response("Internal Server Error", { status: 500 });
}
const { data, format } = await imageService.transform(inputBuffer, transform, imageConfig);
return new Response(data, {
status: 200,
headers: {
"Content-Type": mime.lookup(format) ?? `image/${format}`,
"Cache-Control": "public, max-age=31536000",
ETag: etag(data.toString()),
Date: (/* @__PURE__ */ new Date()).toUTCString()
}
});
};
async function loadLocalImage(src, url) {
const outDirURL = resolveOutDir();
const idx = url.pathname.indexOf("/_image");
if (idx > 0) {
src = src.slice(idx);
}
if (!URL.canParse("." + src, outDirURL)) {
return void 0;
}
const fileUrl = new URL("." + src, outDirURL);
if (fileUrl.protocol !== "file:") {
return void 0;
}
if (!isParentDirectory(fileURLToPath(outDirURL), fileURLToPath(fileUrl))) {
return void 0;
}
try {
return await readFile(fileUrl);
} catch {
return void 0;
}
}
const GET = async ({ request }) => {
try {
return await handleImageRequest({ request, loadLocalImage });
} catch (err) {
console.error("Could not process image request:", err);
return new Response("Internal Server Error", {
status: 500
});
}
};
function resolveOutDir() {
const serverDirPath = fileURLToPath(serverDir);
const rel = path.relative(serverDirPath, fileURLToPath(outDir));
const serverFolder = path.basename(serverDirPath);
let serverEntryFolderURL = path.dirname(import.meta.url);
while (!serverEntryFolderURL.endsWith(serverFolder)) {
serverEntryFolderURL = path.dirname(serverEntryFolderURL);
}
const serverEntryURL = serverEntryFolderURL + "/entry.mjs";
const outDirURL = new URL(appendForwardSlash(rel), serverEntryURL);
return outDirURL;
}
function appendForwardSlash(pth) {
return pth.endsWith("/") ? pth : pth + "/";
}
const _page = /*#__PURE__*/Object.freeze(/*#__PURE__*/Object.defineProperty({
__proto__: null,
GET
}, Symbol.toStringTag, { value: 'Module' }));
const page = () => _page;
export { page as a, baseService as b, parseQuality as p };