{"version":3,"sources":["../src/image-config.ts"],"sourcesContent":["export const DEFAULT_FARM_IMAGE_PATH = \"/_farm/image\";\nexport const DEFAULT_FARM_IMAGE_DEVICE_SIZES = [\n  640, 750, 828, 1080, 1200, 1920, 2048, 3840,\n] as const;\nexport const DEFAULT_FARM_IMAGE_SIZES = [16, 32, 48, 64, 96, 128, 256, 384] as const;\nexport const DEFAULT_FARM_IMAGE_QUALITIES = [75] as const;\nexport const DEFAULT_FARM_IMAGE_FORMATS = [\"image/webp\"] as const;\n\nexport type FarmImageFormat = \"image/avif\" | \"image/webp\";\nexport type FarmImageProvider = \"auto\" | \"node\" | \"cloudflare\" | \"none\";\n\nexport interface FarmImageRemotePattern {\n  protocol?: \"http\" | \"https\";\n  hostname: string;\n  port?: string;\n  pathname?: string;\n  search?: string;\n}\n\nexport interface FarmImageLocalPattern {\n  pathname: string;\n  search?: string;\n}\n\nexport interface FarmImageConfig {\n  /** Runtime optimizer. Auto selects Cloudflare Images on Cloudflare and Sharp elsewhere. */\n  provider?: FarmImageProvider;\n  /** Public optimizer endpoint. */\n  path?: string;\n  /** @deprecated Prefer remotePatterns, which also restricts protocol, path, port, and query. */\n  domains?: readonly string[];\n  remotePatterns?: readonly FarmImageRemotePattern[];\n  localPatterns?: readonly FarmImageLocalPattern[];\n  deviceSizes?: readonly number[];\n  imageSizes?: readonly number[];\n  qualities?: readonly number[];\n  formats?: readonly FarmImageFormat[];\n  minimumCacheTTL?: number;\n  maximumResponseBody?: number | string;\n  maximumRedirects?: number;\n  dangerouslyAllowSVG?: boolean;\n  dangerouslyAllowLocalIP?: boolean;\n}\n\nexport interface ResolvedFarmImageConfig {\n  provider: FarmImageProvider;\n  path: string;\n  domains: readonly string[];\n  remotePatterns: readonly FarmImageRemotePattern[];\n  localPatterns: readonly FarmImageLocalPattern[];\n  deviceSizes: readonly number[];\n  imageSizes: readonly number[];\n  qualities: readonly number[];\n  formats: readonly FarmImageFormat[];\n  minimumCacheTTL: number;\n  maximumResponseBody: number;\n  maximumRedirects: number;\n  dangerouslyAllowSVG: boolean;\n  dangerouslyAllowLocalIP: boolean;\n}\n\nexport type PublicFarmImageConfig = Pick<\n  ResolvedFarmImageConfig,\n  \"provider\" | \"path\" | \"deviceSizes\" | \"imageSizes\" | \"qualities\" | \"formats\"\n>;\n\nconst SIZE_UNITS: Record<string, number> = {\n  b: 1,\n  kb: 1_000,\n  mb: 1_000_000,\n  gb: 1_000_000_000,\n  kib: 1_024,\n  mib: 1_048_576,\n  gib: 1_073_741_824,\n};\n\nexport function resolveFarmImageConfig(\n  config: FarmImageConfig | undefined,\n): ResolvedFarmImageConfig {\n  const path = normalizeImagePath(config?.path ?? DEFAULT_FARM_IMAGE_PATH);\n  const deviceSizes = normalizeIntegerList(\n    config?.deviceSizes ?? DEFAULT_FARM_IMAGE_DEVICE_SIZES,\n    \"images.deviceSizes\",\n  );\n  const imageSizes = normalizeIntegerList(\n    config?.imageSizes ?? DEFAULT_FARM_IMAGE_SIZES,\n    \"images.imageSizes\",\n  );\n  const qualities = normalizeIntegerList(\n    config?.qualities ?? DEFAULT_FARM_IMAGE_QUALITIES,\n    \"images.qualities\",\n    100,\n  );\n  const formats = [...new Set(config?.formats ?? DEFAULT_FARM_IMAGE_FORMATS)];\n\n  if (formats.some((format) => format !== \"image/avif\" && format !== \"image/webp\")) {\n    throw new TypeError('images.formats only supports \"image/avif\" and \"image/webp\"');\n  }\n\n  const minimumCacheTTL = normalizeNonNegativeInteger(\n    config?.minimumCacheTTL ?? 60,\n    \"images.minimumCacheTTL\",\n  );\n  const maximumRedirects = normalizeNonNegativeInteger(\n    config?.maximumRedirects ?? 3,\n    \"images.maximumRedirects\",\n  );\n\n  return Object.freeze({\n    provider: config?.provider ?? \"auto\",\n    path,\n    domains: Object.freeze([\n      ...new Set(\n        (config?.domains ?? []).map((domain) => domain.trim().toLowerCase()).filter(Boolean),\n      ),\n    ]),\n    remotePatterns: Object.freeze(\n      (config?.remotePatterns ?? []).map((pattern) => normalizeRemotePattern(pattern)),\n    ),\n    localPatterns: Object.freeze(\n      (config?.localPatterns ?? [{ pathname: \"/**\" }]).map((pattern) =>\n        normalizeLocalPattern(pattern),\n      ),\n    ),\n    deviceSizes: Object.freeze(deviceSizes),\n    imageSizes: Object.freeze(imageSizes),\n    qualities: Object.freeze(qualities),\n    formats: Object.freeze(formats),\n    minimumCacheTTL,\n    maximumResponseBody: parseSize(\n      config?.maximumResponseBody ?? \"10mb\",\n      \"images.maximumResponseBody\",\n    ),\n    maximumRedirects,\n    dangerouslyAllowSVG: config?.dangerouslyAllowSVG ?? false,\n    dangerouslyAllowLocalIP: config?.dangerouslyAllowLocalIP ?? false,\n  });\n}\n\nexport function getPublicFarmImageConfig(\n  config: Pick<ResolvedFarmImageConfig, keyof PublicFarmImageConfig>,\n): PublicFarmImageConfig {\n  return {\n    provider: config.provider,\n    path: config.path,\n    deviceSizes: config.deviceSizes,\n    imageSizes: config.imageSizes,\n    qualities: config.qualities,\n    formats: config.formats,\n  };\n}\n\nfunction normalizeImagePath(value: string): string {\n  const hasUnstableCharacters = (candidate: string) =>\n    candidate.includes(\"\\\\\") ||\n    Array.from(candidate).some((character) => {\n      const code = character.charCodeAt(0);\n      return code <= 31 || (code >= 127 && code <= 159);\n    });\n\n  if (hasUnstableCharacters(value)) {\n    throw new TypeError(\"images.path cannot contain backslashes or control characters\");\n  }\n\n  const path = value.trim().replace(/\\/+$/, \"\") || \"/\";\n  if (!path.startsWith(\"/\") || path.startsWith(\"//\") || path.includes(\"?\") || path.includes(\"#\")) {\n    throw new TypeError(\"images.path must be an absolute pathname without a query or hash\");\n  }\n\n  for (const segment of path.split(\"/\")) {\n    let decoded = segment;\n    try {\n      decoded = decodeURIComponent(segment);\n    } catch {\n      // Malformed escapes remain literal and cannot conceal a path separator\n      // or dot segment.\n    }\n    if (hasUnstableCharacters(decoded)) {\n      throw new TypeError(\"images.path cannot contain backslashes or control characters\");\n    }\n    if (decoded.includes(\"/\")) {\n      throw new TypeError(\"images.path cannot contain percent-encoded path separators\");\n    }\n    if (decoded === \".\" || decoded === \"..\") {\n      throw new TypeError('images.path cannot contain \".\" or \"..\" path segments');\n    }\n  }\n\n  return path;\n}\n\nfunction normalizeIntegerList(values: readonly number[], name: string, max?: number): number[] {\n  if (values.length === 0) {\n    throw new TypeError(`${name} must contain at least one value`);\n  }\n\n  const normalized = [...new Set(values)].sort((left, right) => left - right);\n  for (const value of normalized) {\n    if (!Number.isSafeInteger(value) || value <= 0 || (max !== undefined && value > max)) {\n      throw new TypeError(\n        `${name} must contain positive integers${max ? ` no greater than ${max}` : \"\"}`,\n      );\n    }\n  }\n  return normalized;\n}\n\nfunction normalizeNonNegativeInteger(value: number, name: string): number {\n  if (!Number.isSafeInteger(value) || value < 0) {\n    throw new TypeError(`${name} must be a non-negative safe integer`);\n  }\n  return value;\n}\n\nfunction parseSize(value: number | string, name: string): number {\n  if (typeof value === \"number\") {\n    if (!Number.isSafeInteger(value) || value <= 0) {\n      throw new TypeError(`${name} must be a positive safe integer`);\n    }\n    return value;\n  }\n\n  const match = value\n    .trim()\n    .toLowerCase()\n    .match(/^(\\d+(?:\\.\\d+)?)\\s*(b|kb|mb|gb|kib|mib|gib)$/);\n  if (!match) {\n    throw new TypeError(`${name} must be bytes or a size string such as \"5mb\"`);\n  }\n\n  const bytes = Number(match[1]) * SIZE_UNITS[match[2]];\n  if (!Number.isSafeInteger(bytes) || bytes <= 0) {\n    throw new TypeError(`${name} must resolve to a positive safe integer`);\n  }\n  return bytes;\n}\n\nfunction normalizeRemotePattern(pattern: FarmImageRemotePattern): FarmImageRemotePattern {\n  const hostname = pattern.hostname.trim().toLowerCase();\n  if (!hostname || hostname.includes(\"/\") || hostname.includes(\":\")) {\n    throw new TypeError(\"images.remotePatterns[].hostname must be a hostname or wildcard hostname\");\n  }\n  if (pattern.pathname && !pattern.pathname.startsWith(\"/\")) {\n    throw new TypeError(\"images.remotePatterns[].pathname must start with /\");\n  }\n  if (pattern.search && !pattern.search.startsWith(\"?\")) {\n    throw new TypeError(\"images.remotePatterns[].search must start with ?\");\n  }\n  return Object.freeze({ ...pattern, hostname });\n}\n\nfunction normalizeLocalPattern(pattern: FarmImageLocalPattern): FarmImageLocalPattern {\n  if (!pattern.pathname.startsWith(\"/\")) {\n    throw new TypeError(\"images.localPatterns[].pathname must start with /\");\n  }\n  if (pattern.search && !pattern.search.startsWith(\"?\")) {\n    throw new TypeError(\"images.localPatterns[].search must start with ?\");\n  }\n  return Object.freeze({ ...pattern });\n}\n"],"mappings":"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","names":[]}