img-upload-kit 1.1.1 → 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (111) hide show
  1. package/CHANGELOG.md +56 -41
  2. package/MIGRATION.md +192 -0
  3. package/README.md +138 -154
  4. package/dist/bin.js +1186 -0
  5. package/dist/bin.js.map +1 -0
  6. package/dist/blur.cjs +40 -412
  7. package/dist/blur.cjs.map +1 -1
  8. package/dist/blur.d.cts +5 -5
  9. package/dist/blur.d.ts +5 -5
  10. package/dist/blur.js +39 -393
  11. package/dist/blur.js.map +1 -1
  12. package/dist/blurDataUtils-DveBRtqE.d.cts +43 -0
  13. package/dist/blurDataUtils-yx9Q6HlU.d.ts +43 -0
  14. package/dist/blurService-D5TpMNji.d.ts +83 -0
  15. package/dist/blurService-DacwylJx.d.cts +83 -0
  16. package/dist/chunk-2OVNXHTN.cjs +514 -0
  17. package/dist/chunk-2OVNXHTN.cjs.map +1 -0
  18. package/dist/chunk-3ICG2SLL.cjs +135 -0
  19. package/dist/chunk-3ICG2SLL.cjs.map +1 -0
  20. package/dist/chunk-4ASFG3KB.js +514 -0
  21. package/dist/chunk-4ASFG3KB.js.map +1 -0
  22. package/dist/chunk-AI3JJLSA.js +2370 -0
  23. package/dist/chunk-AI3JJLSA.js.map +1 -0
  24. package/dist/chunk-CV23O4YU.js +145 -0
  25. package/dist/chunk-CV23O4YU.js.map +1 -0
  26. package/dist/chunk-G7LLSKBV.js +158 -0
  27. package/dist/chunk-G7LLSKBV.js.map +1 -0
  28. package/dist/chunk-IB3TEDLR.js +294 -0
  29. package/dist/chunk-IB3TEDLR.js.map +1 -0
  30. package/dist/chunk-KJVYWKIG.cjs +158 -0
  31. package/dist/chunk-KJVYWKIG.cjs.map +1 -0
  32. package/dist/chunk-NEDBIIE3.cjs +226 -0
  33. package/dist/chunk-NEDBIIE3.cjs.map +1 -0
  34. package/dist/chunk-QHU7FUO4.cjs +37 -0
  35. package/dist/chunk-QHU7FUO4.cjs.map +1 -0
  36. package/dist/chunk-QQHZJKNA.js +135 -0
  37. package/dist/chunk-QQHZJKNA.js.map +1 -0
  38. package/dist/chunk-S2IFAWJK.js +37 -0
  39. package/dist/chunk-S2IFAWJK.js.map +1 -0
  40. package/dist/chunk-TT5C7K3Q.js +226 -0
  41. package/dist/chunk-TT5C7K3Q.js.map +1 -0
  42. package/dist/chunk-U2TWTEBY.cjs +2370 -0
  43. package/dist/chunk-U2TWTEBY.cjs.map +1 -0
  44. package/dist/chunk-WISG74VL.cjs +294 -0
  45. package/dist/chunk-WISG74VL.cjs.map +1 -0
  46. package/dist/chunk-YRXXQUDE.cjs +145 -0
  47. package/dist/chunk-YRXXQUDE.cjs.map +1 -0
  48. package/dist/cli.cjs +273 -76
  49. package/dist/cli.cjs.map +1 -1
  50. package/dist/cli.js +269 -73
  51. package/dist/cli.js.map +1 -1
  52. package/dist/headless.cjs +8 -1880
  53. package/dist/headless.cjs.map +1 -1
  54. package/dist/headless.d.cts +3 -1
  55. package/dist/headless.d.ts +3 -1
  56. package/dist/headless.js +25 -1886
  57. package/dist/headless.js.map +1 -1
  58. package/dist/index-BIdnqtW8.d.ts +234 -0
  59. package/dist/index-Brk_uHs6.d.cts +234 -0
  60. package/dist/{index-FVXXKf2v.d.ts → index-CPMlaQsC.d.ts} +76 -153
  61. package/dist/{index-FVXXKf2v.d.cts → index-DHINe-rA.d.cts} +76 -153
  62. package/dist/index.cjs +64 -532
  63. package/dist/index.cjs.map +1 -1
  64. package/dist/index.d.cts +7 -4
  65. package/dist/index.d.ts +7 -4
  66. package/dist/index.js +63 -504
  67. package/dist/index.js.map +1 -1
  68. package/dist/{memoryAdapter-CBu7_3Pp.d.ts → memoryAdapter-Ck0ccZbF.d.cts} +3 -2
  69. package/dist/{memoryAdapter-BJjyLR-1.d.cts → memoryAdapter-DxEKaG0b.d.ts} +3 -2
  70. package/dist/placeholders-C4ftwaII.d.ts +5 -0
  71. package/dist/placeholders-x_3rt65M.d.cts +5 -0
  72. package/dist/react.cjs +1952 -3257
  73. package/dist/react.cjs.map +1 -1
  74. package/dist/react.d.cts +188 -50
  75. package/dist/react.d.ts +188 -50
  76. package/dist/react.js +1763 -3013
  77. package/dist/react.js.map +1 -1
  78. package/dist/server-handler.cjs +14 -0
  79. package/dist/server-handler.cjs.map +1 -0
  80. package/dist/server-handler.d.cts +3 -0
  81. package/dist/server-handler.d.ts +3 -0
  82. package/dist/server-handler.js +14 -0
  83. package/dist/server-handler.js.map +1 -0
  84. package/dist/server.cjs +1096 -532
  85. package/dist/server.cjs.map +1 -1
  86. package/dist/server.d.cts +77 -154
  87. package/dist/server.d.ts +77 -154
  88. package/dist/server.js +1050 -448
  89. package/dist/server.js.map +1 -1
  90. package/dist/storage.cjs +239 -232
  91. package/dist/storage.cjs.map +1 -1
  92. package/dist/storage.d.cts +21 -5
  93. package/dist/storage.d.ts +21 -5
  94. package/dist/storage.js +227 -207
  95. package/dist/storage.js.map +1 -1
  96. package/dist/styles/animated-image.css +112 -0
  97. package/dist/styles/dropzone-and-tiles.css +247 -0
  98. package/dist/styles/modals-and-dock.css +248 -0
  99. package/dist/styles/primitives.css +197 -0
  100. package/dist/styles/quality-and-inputs.css +184 -0
  101. package/dist/styles/tokens.css +123 -0
  102. package/dist/styles.css +39 -24
  103. package/dist/types-BrP-o9nd.d.cts +85 -0
  104. package/dist/types-sZ3KoFQY.d.ts +85 -0
  105. package/dist/types-yShGNQnW.d.cts +61 -0
  106. package/dist/types-yShGNQnW.d.ts +61 -0
  107. package/dist/{types-CiLY0DwB.d.cts → upload-C5Trd6Dh.d.cts} +27 -53
  108. package/dist/{types-CiLY0DwB.d.ts → upload-C5Trd6Dh.d.ts} +27 -53
  109. package/package.json +92 -32
  110. package/dist/blurService-DqsnfPTd.d.cts +0 -172
  111. package/dist/blurService-DqsnfPTd.d.ts +0 -172
package/dist/cli.cjs.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/cli/index.ts","../src/cli/commands.ts","../src/blur/pathMapper.ts","../src/cli/generator.ts","../src/server/optimization/types.ts","../src/server/optimization/blurGenerator.ts","../src/server/optimization/edgeColorExtractor.ts","../src/utils/color.ts","../src/server/optimization/colorHistogram.ts","../src/server/semaphore.ts","../src/cli/scanner.ts","../src/cli/validation.ts"],"sourcesContent":["import { createCliProgram } from './commands';\n\nexport * from './scanner';\nexport * from './generator';\nexport * from './validation';\nexport { createCliProgram };\n\nconst CLI_SUBCOMMANDS = new Set([\n 'generate',\n 'validate',\n 'sync',\n 'cleanup',\n 'health',\n 'inspect',\n 'help',\n]);\n\nexport async function runCli(argv?: string[]): Promise<void> {\n const program = createCliProgram();\n if (!argv) {\n await program.parseAsync(process.argv);\n return;\n }\n const isUserArgs =\n argv.length === 0 ||\n CLI_SUBCOMMANDS.has(argv[0]) ||\n argv[0].startsWith('-');\n await program.parseAsync(argv, isUserArgs ? { from: 'user' } : undefined);\n}\n\nfunction isDirectCliExecution(entryArg?: string): boolean {\n if (!entryArg) return false;\n const normalized = entryArg.replace(/\\\\/g, '/');\n return (\n normalized === 'cli.js' ||\n normalized === 'cli.cjs' ||\n normalized.endsWith('/cli.js') ||\n normalized.endsWith('/cli.cjs') ||\n normalized.endsWith('/cli/index.ts') ||\n normalized.endsWith('/bin/img-upload-kit') ||\n normalized.endsWith('/.bin/img-upload-kit') ||\n normalized.endsWith('/bin/image-upload-kit') ||\n normalized.endsWith('/.bin/image-upload-kit') ||\n normalized.endsWith('/bin/blur-cli') ||\n normalized.endsWith('/.bin/blur-cli')\n );\n}\n\nif (typeof process !== 'undefined' && isDirectCliExecution(process.argv?.[1])) {\n void runCli().catch((err: unknown) => {\n console.error(err instanceof Error ? err.message : String(err));\n process.exitCode = 1;\n });\n}\n","import fs from 'node:fs/promises';\nimport path from 'node:path';\nimport { Command } from 'commander';\nimport { normalizeImagePath } from '../blur/pathMapper';\nimport type { BlurManifestData, BlurManifestEntry } from '../blur/types';\nimport { generateBlurManifest } from './generator';\nimport {\n autoSyncCheck,\n cleanupOrphanedHashes,\n generateHealthReport,\n validateBlurHashFile,\n} from './validation';\n\nconst DEFAULT_IMAGES_DIR = 'public/images';\nconst DEFAULT_MANIFEST_FILE = 'src/blur-hashes.json';\n\nfunction findManifestEntry(\n hashes: Record<string, BlurManifestEntry>,\n imageKey: string,\n): { key: string; entry: BlurManifestEntry } | null {\n const candidates = [\n imageKey,\n normalizeImagePath(imageKey).normalizedPath,\n normalizeImagePath(imageKey, { stripPrefixes: [] }).normalizedPath,\n ];\n for (const candidate of candidates) {\n if (hashes[candidate]) return { key: candidate, entry: hashes[candidate] };\n const noSlash = candidate.replace(/^\\/+/, '');\n if (hashes[noSlash]) return { key: noSlash, entry: hashes[noSlash] };\n }\n return null;\n}\n\nexport function createCliProgram(): Command {\n const program = new Command();\n\n program\n .name('img-upload-kit')\n .description('LQIP, BlurHash, and dominant color manifest CLI tooling')\n .version('1.1.1');\n\n program\n .command('generate')\n .description('Scan an image directory and generate a blur-hashes JSON manifest')\n .option('-d, --dir <dir>', 'Source images directory', DEFAULT_IMAGES_DIR)\n .option('-o, --out <file>', 'Output manifest JSON path', DEFAULT_MANIFEST_FILE)\n .option('-f, --force', 'Force regenerate all entries ignoring cache', false)\n .option('-c, --concurrency <n>', 'Max parallel processing workers', '4')\n .option('--no-medium', 'Skip generating medium (128px) LQIP variants')\n .action(\n async (opts: {\n dir: string;\n out: string;\n force?: boolean;\n concurrency: string;\n medium: boolean;\n }) => {\n const concurrency = Math.max(1, Number.parseInt(opts.concurrency, 10) || 4);\n const result = await generateBlurManifest(opts.dir, opts.out, {\n concurrency,\n force: Boolean(opts.force),\n includeMedium: opts.medium,\n });\n console.log(\n `Generated ${result.outputPath} (total: ${result.manifest.totalImages}, processed: ${result.processed}, cached: ${result.cached}, failed: ${result.failed})`,\n );\n },\n );\n\n program\n .command('validate')\n .description('Validate a blur manifest JSON file')\n .option('-f, --file <file>', 'Manifest JSON path', DEFAULT_MANIFEST_FILE)\n .action(async (opts: { file: string }) => {\n const result = await validateBlurHashFile(opts.file);\n for (const warning of result.warnings) console.warn(`Warning: ${warning}`);\n if (!result.valid) {\n for (const error of result.errors) console.error(`Error: ${error}`);\n process.exitCode = 1;\n return;\n }\n console.log(`Manifest is valid (${result.totalEntries} entries).`);\n });\n\n program\n .command('sync')\n .description('Check whether disk images and manifest entries are in sync')\n .option('-d, --dir <dir>', 'Source images directory', DEFAULT_IMAGES_DIR)\n .option('-f, --file <file>', 'Manifest JSON path', DEFAULT_MANIFEST_FILE)\n .option('--fix', 'Generate missing blur manifest entries automatically', false)\n .action(async (opts: { dir: string; file: string; fix?: boolean }) => {\n const sync = await autoSyncCheck(opts.dir, opts.file);\n if (sync.inSync) {\n console.log(`In sync (${sync.totalEntries} entries).`);\n return;\n }\n console.log(\n `Out of sync: ${sync.missingKeys.length} missing, ${sync.orphanedKeys.length} orphaned.`,\n );\n if (opts.fix && sync.missingKeys.length > 0) {\n const result = await generateBlurManifest(opts.dir, opts.file);\n console.log(\n `Synced manifest (processed: ${result.processed}, cached: ${result.cached}).`,\n );\n return;\n }\n if (!opts.fix) process.exitCode = 1;\n });\n\n program\n .command('cleanup')\n .description('Remove orphaned entries from the blur manifest')\n .option('-d, --dir <dir>', 'Source images directory', DEFAULT_IMAGES_DIR)\n .option('-f, --file <file>', 'Manifest JSON path', DEFAULT_MANIFEST_FILE)\n .option('--dry-run', 'Preview orphaned keys without writing changes', false)\n .action(async (opts: { dir: string; file: string; dryRun?: boolean }) => {\n const result = await cleanupOrphanedHashes(opts.dir, opts.file, {\n dryRun: Boolean(opts.dryRun),\n });\n const prefix = result.dryRun ? '[Dry Run] Would remove' : 'Removed';\n console.log(`${prefix} ${result.removedCount} orphaned entries.`);\n });\n\n program\n .command('health')\n .description('Generate a comprehensive manifest health and coverage report')\n .option('-d, --dir <dir>', 'Source images directory', DEFAULT_IMAGES_DIR)\n .option('-f, --file <file>', 'Manifest JSON path', DEFAULT_MANIFEST_FILE)\n .action(async (opts: { dir: string; file: string }) => {\n const report = await generateHealthReport(opts.dir, opts.file);\n console.log(\n `Health: valid=${report.validManifest}, coverage=${report.coveragePercent}% (${report.totalManifestEntries} entries / ${report.totalImagesOnDisk} disk images)`,\n );\n for (const warning of report.warnings) console.warn(`Warning: ${warning}`);\n for (const error of report.errors) console.error(`Error: ${error}`);\n if (!report.validManifest || report.errors.length > 0) {\n process.exitCode = 1;\n }\n });\n\n program\n .command('inspect <imageKey>')\n .description('Inspect a single image entry inside the blur manifest')\n .option('-f, --file <file>', 'Manifest JSON path', DEFAULT_MANIFEST_FILE)\n .action(async (imageKey: string, opts: { file: string }) => {\n const raw = await fs.readFile(path.resolve(opts.file), 'utf8');\n const parsed = JSON.parse(raw) as Partial<BlurManifestData>;\n const match = parsed.hashes ? findManifestEntry(parsed.hashes, imageKey) : null;\n if (!match) {\n console.error(`Entry not found for key: ${imageKey}`);\n process.exitCode = 1;\n return;\n }\n console.log(\n JSON.stringify(\n {\n key: match.key,\n hash: match.entry.hash,\n color: match.entry.color,\n width: match.entry.width,\n height: match.entry.height,\n variants: {\n smallChars: match.entry.variants.small.length,\n mediumChars: match.entry.variants.medium?.length ?? 0,\n hasMedium: Boolean(match.entry.variants.medium),\n },\n metadata: match.entry.metadata,\n },\n null,\n 2,\n ),\n );\n });\n\n return program;\n}\n","export const DEFAULT_CDN_PATTERNS: RegExp[] = [\n /^https?:\\/\\/[^/]+\\.supabase\\.co\\/storage\\/v1\\/object\\/public\\/([^/]+)\\/(.+)$/,\n /^https?:\\/\\/[^/]+\\.r2\\.dev\\/(.+)$/,\n /^https?:\\/\\/[^/]+\\.amazonaws\\.com\\/(.+)$/,\n /^https?:\\/\\/[^/]+\\.cloudflare\\.com\\/(.+)$/,\n /^https?:\\/\\/cdn\\.[^/]+\\/(.+)$/,\n];\n\nexport interface NormalizePathOptions {\n cdnPatterns?: RegExp[];\n stripPrefixes?: string[];\n}\n\nexport interface PathInfo {\n normalizedPath: string;\n folder?: string;\n filename?: string;\n extension?: string;\n isExternal: boolean;\n originalPath: string;\n}\n\n/**\n * Project-agnostic image URL / path normalizer.\n * Strips query parameters, leading `/public/`, relative `./`, and common CDN prefixes\n * so manifest keys match consistently across local and CDN environments.\n */\nexport function normalizeImagePath(\n imageSrc: string,\n options: NormalizePathOptions = {},\n): PathInfo {\n const originalPath = imageSrc;\n let normalizedPath = imageSrc;\n let isExternal = false;\n\n try {\n if (imageSrc.startsWith('data:') || imageSrc.startsWith('blob:')) {\n return {\n normalizedPath: imageSrc,\n isExternal: false,\n originalPath,\n };\n }\n\n const patterns = options.cdnPatterns || DEFAULT_CDN_PATTERNS;\n\n if (imageSrc.startsWith('http://') || imageSrc.startsWith('https://')) {\n const url = new URL(imageSrc);\n isExternal = true;\n const cleanUrlNoQuery = `${url.origin}${url.pathname}`;\n\n for (const pattern of patterns) {\n const match = cleanUrlNoQuery.match(pattern);\n if (match) {\n const pathPart = match[match.length - 1];\n normalizedPath = '/' + pathPart.replace(/^\\/+/, '');\n break;\n }\n }\n\n if (normalizedPath === imageSrc) {\n normalizedPath = url.pathname;\n }\n } else {\n normalizedPath = normalizedPath.split('?')[0].split('#')[0];\n }\n\n normalizedPath = normalizedPath.replace(/\\\\/g, '/').replace(/^\\.\\.?\\//g, '');\n\n if (!normalizedPath.startsWith('/')) {\n normalizedPath = '/' + normalizedPath;\n }\n\n const prefixesToStrip = options.stripPrefixes || ['/public/'];\n for (const prefix of prefixesToStrip) {\n if (normalizedPath.startsWith(prefix)) {\n normalizedPath = '/' + normalizedPath.slice(prefix.length).replace(/^\\/+/, '');\n }\n }\n\n const segments = normalizedPath.split('/').filter(Boolean);\n const filename = segments[segments.length - 1];\n const folder =\n segments.length > 1 ? segments.slice(0, -1).join('/') : undefined;\n const dotIdx = filename ? filename.lastIndexOf('.') : -1;\n const extension =\n filename && dotIdx > 0 ? filename.slice(dotIdx + 1).toLowerCase() : undefined;\n\n return {\n normalizedPath,\n folder,\n filename,\n extension,\n isExternal,\n originalPath,\n };\n } catch {\n return {\n normalizedPath: imageSrc,\n isExternal: false,\n originalPath,\n };\n }\n}\n","import fs from 'node:fs/promises';\nimport path from 'node:path';\nimport sharp from 'sharp';\nimport type { BlurManifestData, BlurManifestEntry } from '../blur/types';\nimport {\n encodeBlurHashString,\n generateBlurPlaceholder,\n} from '../server/optimization';\nimport { AsyncSemaphore } from '../server/semaphore';\nimport { scanImageDirectory, type ScannedImageFile } from './scanner';\n\nexport interface GenerateManifestOptions {\n concurrency?: number;\n force?: boolean;\n includeMedium?: boolean;\n onProgress?: (done: number, total: number, key: string) => void;\n}\n\nexport interface GenerateManifestResult {\n manifest: BlurManifestData;\n processed: number;\n cached: number;\n failed: number;\n outputPath: string;\n}\n\nconst FALLBACK_BLURHASH = 'LEHV6nWB2yk8pyo0adR*.7kCMdnj';\nconst MANIFEST_VERSION = '2.0';\n\nasync function loadExistingHashes(\n manifestPath: string,\n): Promise<Record<string, BlurManifestEntry>> {\n try {\n const parsed: unknown = JSON.parse(await fs.readFile(manifestPath, 'utf8'));\n if (\n parsed &&\n typeof parsed === 'object' &&\n !Array.isArray(parsed) &&\n 'hashes' in parsed &&\n parsed.hashes &&\n typeof parsed.hashes === 'object' &&\n !Array.isArray(parsed.hashes)\n ) {\n return parsed.hashes as Record<string, BlurManifestEntry>;\n }\n return {};\n } catch {\n return {};\n }\n}\n\nfunction canReuseCachedEntry(\n entry: BlurManifestEntry | undefined,\n file: ScannedImageFile,\n includeMedium: boolean,\n): entry is BlurManifestEntry {\n if (!entry || typeof entry !== 'object') return false;\n if (!entry.hash || !entry.color || !entry.variants?.small) return false;\n if (entry.metadata) {\n if (entry.metadata.size && entry.metadata.size !== String(file.size)) {\n return false;\n }\n if (\n entry.metadata.mtimeMs &&\n entry.metadata.mtimeMs !== String(Math.round(file.mtimeMs))\n ) {\n return false;\n }\n if (includeMedium && !entry.variants.medium) return false;\n }\n return true;\n}\n\nasync function resolveImageDimensions(\n buffer: Buffer,\n isSvg: boolean,\n): Promise<{ width?: number; height?: number }> {\n const metadata = await sharp(buffer).metadata();\n let { width, height } = metadata;\n\n if ((!width || !height) && isSvg) {\n const match = buffer\n .toString('utf8')\n .match(/viewBox\\s*=\\s*[\"']\\s*([\\d.-]+)\\s+([\\d.-]+)\\s+([\\d.-]+)\\s+([\\d.-]+)\\s*[\"']/i);\n if (match) {\n width = Math.round(Number.parseFloat(match[3]));\n height = Math.round(Number.parseFloat(match[4]));\n }\n }\n\n return { width, height };\n}\n\n/**\n * Generates or updates a `blur-hashes.json` manifest from an image directory\n * with bounded concurrency and incremental cache reuse.\n */\nexport async function generateBlurManifest(\n imagesDir: string,\n manifestFile: string,\n options: GenerateManifestOptions = {},\n): Promise<GenerateManifestResult> {\n const outputPath = path.resolve(manifestFile);\n const concurrency = Math.max(1, Math.floor(options.concurrency ?? 4));\n const includeMedium = options.includeMedium ?? true;\n const force = Boolean(options.force);\n\n const files = await scanImageDirectory(imagesDir);\n const existingHashes = force ? {} : await loadExistingHashes(outputPath);\n const semaphore = new AsyncSemaphore(concurrency);\n const generatedEntries = new Map<string, BlurManifestEntry>();\n\n let processed = 0;\n let cached = 0;\n let failed = 0;\n let done = 0;\n const total = files.length;\n\n await Promise.all(\n files.map((file) =>\n semaphore.run(async () => {\n try {\n const prior = force ? undefined : existingHashes[file.relativeKey];\n if (canReuseCachedEntry(prior, file, includeMedium)) {\n generatedEntries.set(file.relativeKey, prior);\n cached += 1;\n return;\n }\n\n const buffer = await fs.readFile(file.absolutePath);\n const isSvg = file.relativeKey.toLowerCase().endsWith('.svg');\n const { width, height } = await resolveImageDimensions(buffer, isSvg);\n\n const small = await generateBlurPlaceholder(buffer, {\n width: 64,\n fallbackWidth: 48,\n includeBlurHash: true,\n });\n const medium = includeMedium\n ? await generateBlurPlaceholder(buffer, {\n width: 128,\n fallbackWidth: 96,\n includeBlurHash: false,\n })\n : undefined;\n\n const hash =\n small.blurHash ||\n (await encodeBlurHashString(buffer).catch(() => FALLBACK_BLURHASH));\n\n generatedEntries.set(file.relativeKey, {\n hash,\n variants: {\n small: small.dataURL,\n ...(medium ? { medium: medium.dataURL } : {}),\n },\n color: small.color,\n ...(width ? { width } : {}),\n ...(height ? { height } : {}),\n metadata: {\n size: String(file.size),\n mtimeMs: String(Math.round(file.mtimeMs)),\n },\n });\n processed += 1;\n } catch {\n failed += 1;\n } finally {\n done += 1;\n options.onProgress?.(done, total, file.relativeKey);\n }\n }),\n ),\n );\n\n const orderedHashes: Record<string, BlurManifestEntry> = {};\n for (const file of files) {\n const entry = generatedEntries.get(file.relativeKey);\n if (entry) orderedHashes[file.relativeKey] = entry;\n }\n\n const manifest: BlurManifestData = {\n version: MANIFEST_VERSION,\n generatedAt: new Date().toISOString(),\n totalImages: Object.keys(orderedHashes).length,\n processedImages: processed,\n cachedImages: cached,\n failedImages: failed,\n hashes: orderedHashes,\n };\n\n await fs.mkdir(path.dirname(outputPath), { recursive: true });\n await fs.writeFile(outputPath, `${JSON.stringify(manifest, null, 2)}\\n`, 'utf8');\n\n return { manifest, processed, cached, failed, outputPath };\n}\n","import type {\n ExplicitOutputFormat,\n ImageDimensions,\n ImageOutputFormat,\n} from '../../types';\n\nexport interface OptimizedImageResult {\n buffer: Buffer;\n width: number;\n height: number;\n format: ImageOutputFormat;\n size: number;\n}\n\nexport interface BlurHashResult {\n dataURL: string;\n color: string;\n blurHash?: string;\n}\n\nexport interface OptimizeImageOptions {\n /** Max bounding box dimensions (fit: 'inside', withoutEnlargement: true). Defaults to 1920x1080. */\n maxDimensions?: ImageDimensions;\n /** Explicit output format; omit for smart size-guarded AVIF/WebP selection */\n outputFormat?: ExplicitOutputFormat;\n /** 1–100 compression quality. Defaults to smart photo/graphic heuristic (85–88). */\n quality?: number;\n}\n\nexport interface GenerateBlurOptions {\n width?: number;\n fallbackWidth?: number;\n quality?: number;\n maxDataUrlChars?: number;\n includeBlurHash?: boolean;\n}\n\nexport type ColorSamplingMode = 'balanced' | 'edge';\nexport type EdgeKey = 'top' | 'right' | 'bottom' | 'left';\nexport type EdgeColorSet = Record<EdgeKey, string>;\n\nexport const PRIMARY_AVIF_EFFORT = 6;\nexport const RETRY_AVIF_EFFORT = 5;\n\nexport const BLUR_PLACEHOLDER_WIDTH = 128;\nexport const BLUR_PLACEHOLDER_FALLBACK_WIDTH = 96;\nexport const BLUR_PLACEHOLDER_QUALITY = 80;\nexport const BLUR_PLACEHOLDER_MAX_CHARS = 8000;\n\nexport const EDGE_COLOR_FALLBACK = '#6B7280';\nexport const EDGE_WHITE_FALLBACK = '#F9FAFB';\nexport const WHITE_DOMINANCE_RATIO = 1.35;\n","import { encode as encodeBlurHash } from 'blurhash';\nimport lqip from 'lqip-modern';\nimport sharp from 'sharp';\nimport {\n blendEdgeColors,\n extractEdgeColorsFromBuffer,\n} from './edgeColorExtractor';\nimport {\n BLUR_PLACEHOLDER_FALLBACK_WIDTH,\n BLUR_PLACEHOLDER_MAX_CHARS,\n BLUR_PLACEHOLDER_QUALITY,\n BLUR_PLACEHOLDER_WIDTH,\n type BlurHashResult,\n type GenerateBlurOptions,\n} from './types';\n\nexport async function encodeBlurHashString(\n buffer: Buffer,\n componentX: number = 4,\n componentY: number = 3,\n): Promise<string> {\n const { data, info } = await sharp(buffer)\n .resize(32, 32, { fit: 'inside' })\n .ensureAlpha()\n .raw()\n .toBuffer({ resolveWithObject: true });\n\n const clampedX = Math.max(1, Math.min(Math.round(componentX), info.width, 9));\n const clampedY = Math.max(1, Math.min(Math.round(componentY), info.height, 9));\n\n return encodeBlurHash(\n new Uint8ClampedArray(data),\n info.width,\n info.height,\n clampedX,\n clampedY,\n );\n}\n\nexport async function generateBlurPlaceholder(\n buffer: Buffer,\n options: GenerateBlurOptions = {},\n): Promise<BlurHashResult> {\n const targetWidth = options.width ?? BLUR_PLACEHOLDER_WIDTH;\n const fallbackWidth =\n options.fallbackWidth ?? BLUR_PLACEHOLDER_FALLBACK_WIDTH;\n const quality = options.quality ?? BLUR_PLACEHOLDER_QUALITY;\n const maxChars = options.maxDataUrlChars ?? BLUR_PLACEHOLDER_MAX_CHARS;\n\n try {\n let processBuffer = buffer;\n const isSvg = /<svg[\\s>]/i.test(buffer.toString('utf8', 0, 1024));\n if (isSvg) {\n const normalizedSvg = buffer\n .toString('utf8')\n .replace(/<svg\\b/gi, '<svg')\n .replace(/<\\/svg\\s*>/gi, '</svg>');\n processBuffer = await sharp(Buffer.from(normalizedSvg, 'utf8'))\n .resize(targetWidth, targetWidth, { fit: 'inside' })\n .png()\n .toBuffer();\n }\n\n const buildPlaceholder = (width: number, q: number) =>\n lqip(processBuffer, {\n outputFormat: 'webp',\n resize: width,\n outputOptions: { quality: q, effort: 6, smartSubsample: true },\n });\n\n let result = await buildPlaceholder(targetWidth, quality);\n if (result.metadata.dataURIBase64.length > maxChars) {\n result = await buildPlaceholder(fallbackWidth, quality);\n }\n\n const dataURL = result.metadata.dataURIBase64;\n const edgeColors = await extractEdgeColorsFromBuffer(processBuffer);\n const color = blendEdgeColors(edgeColors);\n\n let blurHash: string | undefined;\n if (options.includeBlurHash) {\n try {\n blurHash = await encodeBlurHashString(processBuffer);\n } catch {\n blurHash = undefined;\n }\n }\n\n return {\n dataURL,\n color,\n ...(blurHash && { blurHash }),\n };\n } catch {\n return generateFallbackBlur(buffer, options.includeBlurHash);\n }\n}\n\nexport const generateBlurHash = generateBlurPlaceholder;\n\nasync function generateFallbackBlur(\n buffer: Buffer,\n includeBlurHash?: boolean,\n): Promise<BlurHashResult> {\n const tinyBuffer = await sharp(buffer)\n .resize(64, 64, { fit: 'cover', kernel: 'lanczos3' })\n .blur(1.0)\n .webp({\n quality: 75,\n effort: 6,\n smartSubsample: false,\n })\n .toBuffer();\n\n const dataURL = `data:image/webp;base64,${tinyBuffer.toString('base64')}`;\n const edgeColors = await extractEdgeColorsFromBuffer(buffer);\n const color = blendEdgeColors(edgeColors);\n\n let blurHash: string | undefined;\n if (includeBlurHash) {\n try {\n blurHash = await encodeBlurHashString(tinyBuffer);\n } catch {\n blurHash = undefined;\n }\n }\n\n return { dataURL, color, ...(blurHash && { blurHash }) };\n}\n","import sharp from 'sharp';\nimport { hexToRgb, rgbToHex } from '../../utils/color';\nimport { extractEnhancedColorFromBuffer } from './colorHistogram';\nimport {\n EDGE_COLOR_FALLBACK,\n EDGE_WHITE_FALLBACK,\n type EdgeColorSet,\n type EdgeKey,\n WHITE_DOMINANCE_RATIO,\n} from './types';\n\nexport async function extractEdgeColorsFromBuffer(\n buffer: Buffer,\n): Promise<EdgeColorSet> {\n try {\n const sample = await sharp(buffer)\n .resize(400, 400, {\n fit: 'inside',\n kernel: 'lanczos3',\n withoutEnlargement: true,\n })\n .ensureAlpha()\n .raw()\n .toBuffer({ resolveWithObject: true });\n\n return computeEdgeColorSet(\n sample.data,\n sample.info.width,\n sample.info.height,\n );\n } catch {\n try {\n const fallback = await extractEnhancedColorFromBuffer(buffer, 'edge');\n return {\n top: fallback,\n right: fallback,\n bottom: fallback,\n left: fallback,\n };\n } catch {\n return {\n top: EDGE_COLOR_FALLBACK,\n right: EDGE_COLOR_FALLBACK,\n bottom: EDGE_COLOR_FALLBACK,\n left: EDGE_COLOR_FALLBACK,\n };\n }\n }\n}\n\nfunction computeEdgeColorSet(\n data: Buffer,\n width: number,\n height: number,\n): EdgeColorSet {\n const thicknessX = Math.max(2, Math.floor(width * 0.12));\n const thicknessY = Math.max(2, Math.floor(height * 0.12));\n const bucketSize = 16;\n\n const edgeMaps: Record<EdgeKey, Map<string, number>> = {\n top: new Map(),\n right: new Map(),\n bottom: new Map(),\n left: new Map(),\n };\n\n const whiteEdgeStrength: Record<EdgeKey, number> = {\n top: 0,\n right: 0,\n bottom: 0,\n left: 0,\n };\n\n const accumulateEdgeSample = (\n edge: EdgeKey,\n weight: number,\n key: string,\n isWhiteSample: boolean,\n ) => {\n if (weight <= 0) return;\n if (isWhiteSample) {\n whiteEdgeStrength[edge] += weight;\n return;\n }\n edgeMaps[edge].set(key, (edgeMaps[edge].get(key) || 0) + weight);\n };\n\n for (let i = 0; i < data.length; i += 4) {\n const r = data[i];\n const g = data[i + 1];\n const b = data[i + 2];\n const alpha = data[i + 3];\n\n if (alpha !== undefined && alpha < 8) continue;\n\n const brightness = (r + g + b) / 3;\n if (brightness < 12) continue;\n\n const isNearWhite = r >= 245 && g >= 245 && b >= 245;\n\n const pixelIndex = i / 4;\n const x = pixelIndex % width;\n const y = Math.floor(pixelIndex / width);\n\n const bucketR = Math.floor(r / bucketSize) * bucketSize;\n const bucketG = Math.floor(g / bucketSize) * bucketSize;\n const bucketB = Math.floor(b / bucketSize) * bucketSize;\n const key = `${bucketR},${bucketG},${bucketB}`;\n\n const topWeight = y < thicknessY ? 1 + (thicknessY - y) / thicknessY : 0;\n if (topWeight > 0) {\n accumulateEdgeSample('top', topWeight, key, isNearWhite);\n }\n\n const bottomWeight =\n y >= height - thicknessY\n ? 1 + (y - (height - thicknessY)) / thicknessY\n : 0;\n if (bottomWeight > 0) {\n accumulateEdgeSample('bottom', bottomWeight, key, isNearWhite);\n }\n\n const leftWeight = x < thicknessX ? 1 + (thicknessX - x) / thicknessX : 0;\n if (leftWeight > 0) {\n accumulateEdgeSample('left', leftWeight, key, isNearWhite);\n }\n\n const rightWeight =\n x >= width - thicknessX\n ? 1 + (x - (width - thicknessX)) / thicknessX\n : 0;\n if (rightWeight > 0) {\n accumulateEdgeSample('right', rightWeight, key, isNearWhite);\n }\n }\n\n return {\n top: resolveEdgeColor(edgeMaps.top, whiteEdgeStrength.top),\n right: resolveEdgeColor(edgeMaps.right, whiteEdgeStrength.right),\n bottom: resolveEdgeColor(edgeMaps.bottom, whiteEdgeStrength.bottom),\n left: resolveEdgeColor(edgeMaps.left, whiteEdgeStrength.left),\n };\n}\n\nfunction resolveEdgeColor(\n map: Map<string, number>,\n whiteStrength: number,\n): string {\n if (!map.size) {\n return whiteStrength > 0 ? EDGE_WHITE_FALLBACK : EDGE_COLOR_FALLBACK;\n }\n\n let dominantColor = '';\n let maxCount = 0;\n let totalColorWeight = 0;\n\n map.forEach((count, color) => {\n totalColorWeight += count;\n if (count > maxCount) {\n maxCount = count;\n dominantColor = color;\n }\n });\n\n if (!dominantColor) {\n return whiteStrength > 0 ? EDGE_WHITE_FALLBACK : EDGE_COLOR_FALLBACK;\n }\n\n const whiteDominates =\n whiteStrength > 0 &&\n (totalColorWeight === 0 ||\n whiteStrength >= totalColorWeight * WHITE_DOMINANCE_RATIO);\n\n if (whiteDominates) {\n return EDGE_WHITE_FALLBACK;\n }\n\n if (dominantColor.startsWith('#')) {\n return dominantColor.toUpperCase();\n }\n\n const [r, g, b] = dominantColor.split(',').map(Number);\n return rgbToHex(r, g, b);\n}\n\nexport function blendEdgeColors(edgeColors: EdgeColorSet): string {\n const colors = Object.values(edgeColors);\n if (!colors.length) {\n return EDGE_COLOR_FALLBACK;\n }\n\n const totals = colors.reduce(\n (acc, hex) => {\n const { r, g, b } = hexToRgb(hex || EDGE_COLOR_FALLBACK);\n acc.r += r;\n acc.g += g;\n acc.b += b;\n return acc;\n },\n { r: 0, g: 0, b: 0 },\n );\n\n const count = colors.length;\n return rgbToHex(totals.r / count, totals.g / count, totals.b / count);\n}\n","export const DEFAULT_BLUR_COLOR = '#F5F5F5';\n\nexport function normalizeColorInput(value?: string | null): string | null {\n if (!value) return null;\n let next = value.trim();\n if (!next) return null;\n if (!next.startsWith('#')) {\n next = `#${next}`;\n }\n const threeHex = /^#([0-9a-fA-F]{3})$/;\n const sixHex = /^#([0-9a-fA-F]{6})$/;\n if (threeHex.test(next)) {\n const [, triplet] = next.match(threeHex) ?? [];\n if (triplet) {\n next = `#${triplet\n .split('')\n .map((char) => char + char)\n .join('')}`;\n }\n } else if (!sixHex.test(next)) {\n return null;\n }\n return next.toUpperCase();\n}\n\nexport function hexToRgb(hex: string): { r: number; g: number; b: number } {\n const normalized = hex.replace('#', '');\n const expanded =\n normalized.length === 3\n ? normalized\n .split('')\n .map((char) => char + char)\n .join('')\n : normalized;\n const padded = expanded.padEnd(6, '0').slice(0, 6);\n const value = Number.parseInt(padded, 16);\n if (Number.isNaN(value)) {\n return { r: 245, g: 245, b: 245 };\n }\n return {\n r: (value >> 16) & 255,\n g: (value >> 8) & 255,\n b: value & 255,\n };\n}\n\nexport function rgbToHex(r: number, g: number, b: number): string {\n const clamp = (v: number) => Math.max(0, Math.min(255, Math.round(v)));\n const value = (clamp(r) << 16) | (clamp(g) << 8) | clamp(b);\n return `#${value.toString(16).padStart(6, '0').toUpperCase()}`;\n}\n\nfunction encodeBase64Ascii(str: string): string {\n if (typeof btoa === 'function') {\n return btoa(str);\n }\n return Buffer.from(str, 'utf8').toString('base64');\n}\n\nexport function generateColorSvgDataURL(color: string = DEFAULT_BLUR_COLOR): string {\n const safeColor = normalizeColorInput(color) || DEFAULT_BLUR_COLOR;\n const svg = `<svg width=\"100\" height=\"100\" xmlns=\"http://www.w3.org/2000/svg\"><rect width=\"100%\" height=\"100%\" fill=\"${safeColor}\"/></svg>`;\n return `data:image/svg+xml;base64,${encodeBase64Ascii(svg)}`;\n}\n","import sharp from 'sharp';\nimport { rgbToHex } from '../../utils/color';\nimport {\n type ColorSamplingMode,\n EDGE_COLOR_FALLBACK,\n EDGE_WHITE_FALLBACK,\n WHITE_DOMINANCE_RATIO,\n} from './types';\n\nexport async function extractEnhancedColorFromBuffer(\n buffer: Buffer,\n mode: ColorSamplingMode = 'balanced',\n): Promise<string> {\n try {\n const metadata = await sharp(buffer).metadata();\n const maxSize = 400;\n const needsResize =\n metadata.width &&\n metadata.height &&\n (metadata.width > maxSize || metadata.height > maxSize);\n\n if (needsResize) {\n const resized = await sharp(buffer)\n .resize(maxSize, maxSize, {\n fit: 'inside',\n kernel: 'lanczos3',\n withoutEnlargement: true,\n })\n .ensureAlpha()\n .raw()\n .toBuffer({ resolveWithObject: true });\n\n return extractDominantColorHistogramAdvanced(\n resized.data,\n resized.info.width,\n resized.info.height,\n mode,\n );\n }\n\n const raw = await sharp(buffer)\n .ensureAlpha()\n .raw()\n .toBuffer({ resolveWithObject: true });\n\n return extractDominantColorHistogramAdvanced(\n raw.data,\n raw.info.width,\n raw.info.height,\n mode,\n );\n } catch {\n const stats = await sharp(buffer)\n .resize(64, 64, { fit: 'cover', kernel: 'lanczos3' })\n .ensureAlpha()\n .raw()\n .toBuffer({ resolveWithObject: true });\n return extractDominantColorHistogram(stats.data);\n }\n}\n\nfunction extractDominantColorHistogramAdvanced(\n data: Buffer,\n width: number,\n height: number,\n mode: ColorSamplingMode,\n): string {\n const colorMap = new Map<string, number>();\n const bucketSize = 16;\n\n const centerWeight = 1.5;\n const centerX = width / 2 || 1;\n const centerY = height / 2 || 1;\n let whiteWeight = 0;\n\n for (let i = 0; i < data.length; i += 4) {\n const alpha = data[i + 3];\n if (alpha !== undefined && alpha < 8) continue;\n\n const r = data[i];\n const g = data[i + 1];\n const b = data[i + 2];\n const brightness = (r + g + b) / 3;\n if (brightness < 10) continue;\n\n const pixelIndex = i / 4;\n const x = pixelIndex % width;\n const y = Math.floor(pixelIndex / width);\n const dx = (x - centerX) / centerX;\n const dy = (y - centerY) / centerY;\n const distanceFromCenter = Math.sqrt(dx * dx + dy * dy);\n let weight = 1.0;\n\n if (mode === 'edge') {\n const edgeThicknessX = Math.max(1, Math.floor(width * 0.12));\n const edgeThicknessY = Math.max(1, Math.floor(height * 0.12));\n\n const onLeftEdge = x < edgeThicknessX;\n const onRightEdge = x >= width - edgeThicknessX;\n const onTopEdge = y < edgeThicknessY;\n const onBottomEdge = y >= height - edgeThicknessY;\n\n if (!(onLeftEdge || onRightEdge || onTopEdge || onBottomEdge)) {\n continue;\n }\n\n const distanceToNearestEdge = Math.min(\n x,\n y,\n width - 1 - x,\n height - 1 - y,\n );\n const maxEdgeDistance = Math.max(edgeThicknessX, edgeThicknessY) || 1;\n const edgeFactor = 1 - distanceToNearestEdge / maxEdgeDistance;\n weight = 1 + edgeFactor * 3;\n } else {\n weight = distanceFromCenter < 0.5 ? centerWeight : 1.0;\n }\n\n const isNearWhite = r >= 245 && g >= 245 && b >= 245;\n if (isNearWhite) {\n whiteWeight += weight;\n continue;\n }\n\n const bucketR = Math.floor(r / bucketSize) * bucketSize;\n const bucketG = Math.floor(g / bucketSize) * bucketSize;\n const bucketB = Math.floor(b / bucketSize) * bucketSize;\n\n const key = `${bucketR},${bucketG},${bucketB}`;\n colorMap.set(key, (colorMap.get(key) || 0) + weight);\n }\n\n if (!colorMap.size) {\n return whiteWeight > 0 ? EDGE_WHITE_FALLBACK : EDGE_COLOR_FALLBACK;\n }\n\n let maxCount = 0;\n let dominantColor = '64,64,80';\n let totalColorWeight = 0;\n\n colorMap.forEach((count, color) => {\n totalColorWeight += count;\n if (count > maxCount) {\n maxCount = count;\n dominantColor = color;\n }\n });\n\n const whiteDominates =\n whiteWeight > 0 && whiteWeight >= totalColorWeight * WHITE_DOMINANCE_RATIO;\n if (whiteDominates) {\n return EDGE_WHITE_FALLBACK;\n }\n\n const [r, g, b] = dominantColor.split(',').map(Number);\n return rgbToHex(r, g, b);\n}\n\nfunction extractDominantColorHistogram(data: Buffer): string {\n const colorMap = new Map<string, number>();\n const bucketSize = 16;\n let whiteWeight = 0;\n\n for (let i = 0; i < data.length; i += 4) {\n const alpha = data[i + 3];\n if (alpha !== undefined && alpha < 8) continue;\n\n const r = Math.floor(data[i] / bucketSize) * bucketSize;\n const g = Math.floor(data[i + 1] / bucketSize) * bucketSize;\n const b = Math.floor(data[i + 2] / bucketSize) * bucketSize;\n\n if (r >= 240 && g >= 240 && b >= 240) {\n whiteWeight += 1;\n continue;\n }\n\n const key = `${r},${g},${b}`;\n colorMap.set(key, (colorMap.get(key) || 0) + 1);\n }\n\n if (!colorMap.size) {\n return whiteWeight > 0 ? EDGE_WHITE_FALLBACK : EDGE_COLOR_FALLBACK;\n }\n\n let maxCount = 0;\n let dominantColor = '64,64,80';\n let totalColorWeight = 0;\n\n colorMap.forEach((count, color) => {\n totalColorWeight += count;\n if (count > maxCount) {\n maxCount = count;\n dominantColor = color;\n }\n });\n\n const whiteDominates =\n whiteWeight > 0 && whiteWeight >= totalColorWeight * WHITE_DOMINANCE_RATIO;\n if (whiteDominates) {\n return EDGE_WHITE_FALLBACK;\n }\n\n const [r, g, b] = dominantColor.split(',').map(Number);\n return rgbToHex(r, g, b);\n}\n","export class AsyncSemaphore {\n private activeCount = 0;\n private readonly queue: Array<() => void> = [];\n\n constructor(private readonly concurrency: number) {\n if (concurrency < 1) {\n throw new Error('AsyncSemaphore concurrency must be >= 1');\n }\n }\n\n async run<T>(task: () => Promise<T>): Promise<T> {\n await this.acquire();\n\n try {\n return await task();\n } finally {\n this.release();\n }\n }\n\n private acquire(): Promise<void> {\n if (this.activeCount < this.concurrency) {\n this.activeCount += 1;\n return Promise.resolve();\n }\n\n return new Promise((resolve) => {\n this.queue.push(() => {\n this.activeCount += 1;\n resolve();\n });\n });\n }\n\n private release(): void {\n this.activeCount = Math.max(0, this.activeCount - 1);\n const next = this.queue.shift();\n next?.();\n }\n}\n\nexport async function withTimeout<T>(\n task: Promise<T>,\n timeoutMs: number,\n label: string,\n): Promise<T> {\n let timeoutHandle: NodeJS.Timeout | null = null;\n\n try {\n return await Promise.race([\n task,\n new Promise<never>((_, reject) => {\n timeoutHandle = setTimeout(() => {\n reject(new Error(`${label} timed out after ${timeoutMs}ms`));\n }, timeoutMs);\n }),\n ]);\n } finally {\n if (timeoutHandle) {\n clearTimeout(timeoutHandle);\n }\n }\n}\n","import fs from 'node:fs/promises';\nimport path from 'node:path';\n\nexport const SUPPORTED_IMAGE_EXTENSIONS = new Set([\n '.jpg',\n '.jpeg',\n '.png',\n '.webp',\n '.avif',\n '.svg',\n]);\n\nexport const IGNORED_DIRECTORIES = new Set(['node_modules', '.git', 'dist']);\n\nexport interface ScannedImageFile {\n absolutePath: string;\n relativeKey: string;\n size: number;\n mtimeMs: number;\n}\n\n/**\n * Recursively scans an image directory for supported image files,\n * ignoring `node_modules`, `.git`, and `dist`, and returning normalized\n * `/` forward-slash relative keys sorted alphabetically.\n */\nexport async function scanImageDirectory(\n imagesDir: string,\n): Promise<ScannedImageFile[]> {\n const rootDir = path.resolve(imagesDir);\n const rootStats = await fs.stat(rootDir);\n\n if (!rootStats.isDirectory()) {\n throw new Error(`Expected images path to be a directory: ${rootDir}`);\n }\n\n const results: ScannedImageFile[] = [];\n\n async function walk(currentDir: string): Promise<void> {\n const entries = await fs.readdir(currentDir, { withFileTypes: true });\n\n for (const entry of entries) {\n const fullPath = path.join(currentDir, entry.name);\n\n if (entry.isDirectory()) {\n if (IGNORED_DIRECTORIES.has(entry.name)) {\n continue;\n }\n await walk(fullPath);\n continue;\n }\n\n if (!entry.isFile()) {\n continue;\n }\n\n const ext = path.extname(entry.name).toLowerCase();\n if (!SUPPORTED_IMAGE_EXTENSIONS.has(ext)) {\n continue;\n }\n\n const fileStats = await fs.stat(fullPath);\n const relPath = path\n .relative(rootDir, fullPath)\n .split(path.sep)\n .join('/')\n .replace(/\\\\/g, '/');\n const relativeKey = relPath.startsWith('/') ? relPath : `/${relPath}`;\n\n results.push({\n absolutePath: fullPath,\n relativeKey,\n size: fileStats.size,\n mtimeMs: fileStats.mtimeMs,\n });\n }\n }\n\n await walk(rootDir);\n results.sort((a, b) => a.relativeKey.localeCompare(b.relativeKey));\n\n return results;\n}\n","import fs from 'node:fs/promises';\nimport path from 'node:path';\nimport { normalizeImagePath } from '../blur/pathMapper';\nimport type { BlurManifestData, BlurManifestEntry } from '../blur/types';\nimport { scanImageDirectory, type ScannedImageFile } from './scanner';\n\nexport interface ValidationResult {\n valid: boolean;\n totalEntries: number;\n errors: string[];\n warnings: string[];\n}\n\nexport interface SyncCheckResult {\n inSync: boolean;\n totalFiles: number;\n totalEntries: number;\n missingKeys: string[];\n orphanedKeys: string[];\n}\n\nexport interface CleanupResult {\n removedCount: number;\n removedKeys: string[];\n dryRun: boolean;\n}\n\nexport interface HealthReport {\n validManifest: boolean;\n totalImagesOnDisk: number;\n totalManifestEntries: number;\n coveragePercent: number;\n missingKeys: string[];\n orphanedKeys: string[];\n errors: string[];\n warnings: string[];\n}\n\nconst HEX_COLOR_RE = /^#([0-9a-fA-F]{3}|[0-9a-fA-F]{6}|[0-9a-fA-F]{8})$/;\n\nfunction isPlainObject(value: unknown): value is Record<string, unknown> {\n return Boolean(value) && typeof value === 'object' && !Array.isArray(value);\n}\n\nfunction toNormalizedKey(key: string): string {\n return normalizeImagePath(key, { stripPrefixes: [] }).normalizedPath;\n}\n\nfunction failValidation(error: string): ValidationResult {\n return { valid: false, totalEntries: 0, errors: [error], warnings: [] };\n}\n\nfunction isDataImageUrl(value: unknown): value is string {\n return typeof value === 'string' && value.startsWith('data:image/');\n}\n\nfunction computeKeyDiff(\n files: ScannedImageFile[],\n hashes: Record<string, BlurManifestEntry>,\n): { missingKeys: string[]; orphanedKeys: string[] } {\n const diskKeys = new Set(files.map((f) => f.relativeKey));\n const manifestKeys = Object.keys(hashes);\n const normalizedSet = new Set(manifestKeys.map(toNormalizedKey));\n const missingKeys = files\n .map((f) => f.relativeKey)\n .filter((k) => !(k in hashes) && !normalizedSet.has(k));\n const orphanedKeys = manifestKeys.filter(\n (k) => !diskKeys.has(k) && !diskKeys.has(toNormalizedKey(k)),\n );\n return { missingKeys, orphanedKeys };\n}\n\nasync function readManifestSafe(manifestFile: string): Promise<{\n exists: boolean;\n manifest?: BlurManifestData;\n hashes: Record<string, BlurManifestEntry>;\n}> {\n try {\n const parsed: unknown = JSON.parse(await fs.readFile(path.resolve(manifestFile), 'utf8'));\n if (isPlainObject(parsed) && isPlainObject(parsed.hashes)) {\n return {\n exists: true,\n manifest: parsed as unknown as BlurManifestData,\n hashes: parsed.hashes as Record<string, BlurManifestEntry>,\n };\n }\n return { exists: true, hashes: {} };\n } catch (err) {\n return { exists: (err as NodeJS.ErrnoException).code !== 'ENOENT', hashes: {} };\n }\n}\n\nfunction validateEntry(key: string, value: unknown, errors: string[], warnings: string[]): void {\n if (!key.startsWith('/')) warnings.push(`Entry key \"${key}\" should start with \"/\"`);\n if (!isPlainObject(value)) {\n errors.push(`Entry \"${key}\" must be an object`);\n return;\n }\n const entry = value as Partial<BlurManifestEntry>;\n if (typeof entry.hash !== 'string' || entry.hash.trim().length < 6) {\n errors.push(`Entry \"${key}\" has missing or invalid BlurHash \"hash\"`);\n }\n if (typeof entry.color !== 'string' || !HEX_COLOR_RE.test(entry.color)) {\n errors.push(`Entry \"${key}\" has missing or invalid hex \"color\"`);\n }\n if (!isPlainObject(entry.variants)) {\n errors.push(`Entry \"${key}\" is missing \"variants\" object`);\n return;\n }\n if (!isDataImageUrl(entry.variants.small)) {\n errors.push(`Entry \"${key}\" is missing valid \"variants.small\" data URL`);\n }\n if (entry.variants.medium !== undefined && !isDataImageUrl(entry.variants.medium)) {\n errors.push(`Entry \"${key}\" has invalid \"variants.medium\" data URL`);\n }\n}\n\nexport async function validateBlurHashFile(manifestFile: string): Promise<ValidationResult> {\n const resolvedPath = path.resolve(manifestFile);\n let raw: string;\n try {\n raw = await fs.readFile(resolvedPath, 'utf8');\n } catch {\n return failValidation(`Manifest file not found or unreadable: ${resolvedPath}`);\n }\n let parsed: unknown;\n try {\n parsed = JSON.parse(raw);\n } catch (err) {\n return failValidation(`Invalid JSON in manifest: ${(err as Error).message}`);\n }\n if (!isPlainObject(parsed)) return failValidation('Manifest root must be a JSON object');\n if (!isPlainObject(parsed.hashes)) return failValidation('Manifest is missing a valid \"hashes\" object');\n\n const errors: string[] = [];\n const warnings: string[] = [];\n if (typeof parsed.version !== 'string' || !parsed.version.trim()) {\n warnings.push('Manifest is missing a \"version\" string');\n }\n if (typeof parsed.generatedAt !== 'string' || Number.isNaN(Date.parse(parsed.generatedAt))) {\n warnings.push('Manifest has missing or invalid \"generatedAt\" timestamp');\n }\n\n const entries = Object.entries(parsed.hashes);\n if (typeof parsed.totalImages === 'number' && parsed.totalImages !== entries.length) {\n warnings.push(`Manifest totalImages (${parsed.totalImages}) differs from count (${entries.length})`);\n }\n if (entries.length === 0) warnings.push('Manifest contains 0 image entries');\n for (const [key, value] of entries) validateEntry(key, value, errors, warnings);\n\n return { valid: errors.length === 0, totalEntries: entries.length, errors, warnings };\n}\n\nexport async function autoSyncCheck(imagesDir: string, manifestFile: string): Promise<SyncCheckResult> {\n const [files, loaded] = await Promise.all([scanImageDirectory(imagesDir), readManifestSafe(manifestFile)]);\n const { missingKeys, orphanedKeys } = computeKeyDiff(files, loaded.hashes);\n const inSync = loaded.exists && missingKeys.length === 0 && orphanedKeys.length === 0;\n return { inSync, totalFiles: files.length, totalEntries: Object.keys(loaded.hashes).length, missingKeys, orphanedKeys };\n}\n\nexport async function findMissingBlurHashes(imagesDir: string, manifestFile: string): Promise<string[]> {\n return (await autoSyncCheck(imagesDir, manifestFile)).missingKeys;\n}\n\nexport async function cleanupOrphanedHashes(\n imagesDir: string,\n manifestFile: string,\n options?: { dryRun?: boolean },\n): Promise<CleanupResult> {\n const dryRun = Boolean(options?.dryRun);\n const resolvedPath = path.resolve(manifestFile);\n const [files, loaded] = await Promise.all([scanImageDirectory(imagesDir), readManifestSafe(resolvedPath)]);\n if (!loaded.exists || !loaded.manifest) return { removedCount: 0, removedKeys: [], dryRun };\n\n const { orphanedKeys } = computeKeyDiff(files, loaded.hashes);\n if (!dryRun && orphanedKeys.length > 0) {\n const orphanSet = new Set(orphanedKeys);\n const hashes = Object.fromEntries(Object.entries(loaded.hashes).filter(([k]) => !orphanSet.has(k)));\n const updated: BlurManifestData = { ...loaded.manifest, generatedAt: new Date().toISOString(), totalImages: Object.keys(hashes).length, hashes };\n await fs.writeFile(resolvedPath, `${JSON.stringify(updated, null, 2)}\\n`, 'utf8');\n }\n return { removedCount: orphanedKeys.length, removedKeys: orphanedKeys, dryRun };\n}\n\nexport async function generateHealthReport(imagesDir: string, manifestFile: string): Promise<HealthReport> {\n const [validation, loaded] = await Promise.all([validateBlurHashFile(manifestFile), readManifestSafe(manifestFile)]);\n const errors = [...validation.errors];\n const warnings = [...validation.warnings];\n let files: ScannedImageFile[] = [];\n try {\n files = await scanImageDirectory(imagesDir);\n } catch (err) {\n errors.push(`Failed to scan \"${path.resolve(imagesDir)}\": ${(err as Error).message}`);\n }\n\n const { missingKeys, orphanedKeys } = computeKeyDiff(files, loaded.hashes);\n if (missingKeys.length > 0) warnings.push(`${missingKeys.length} missing image(s)`);\n if (orphanedKeys.length > 0) warnings.push(`${orphanedKeys.length} orphaned entry(ies)`);\n\n const totalImagesOnDisk = files.length;\n const covered = Math.max(0, totalImagesOnDisk - missingKeys.length);\n const coveragePercent =\n totalImagesOnDisk === 0 ? (validation.valid ? 100 : 0) : Number(((covered / totalImagesOnDisk) * 100).toFixed(2));\n\n return {\n validManifest: validation.valid,\n totalImagesOnDisk,\n totalManifestEntries: validation.totalEntries,\n coveragePercent,\n missingKeys,\n orphanedKeys,\n errors,\n warnings,\n };\n}\n"],"mappings":"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","names":["import_promises","import_node_path","import_promises","import_node_path","import_sharp","import_sharp","import_sharp","sharp","r","g","b","sharp","sharp","encodeBlurHash","lqip","sharp","path","fs","fs","sharp","path","import_promises","import_node_path","fs","path","fs","path"]}
1
+ {"version":3,"sources":["../src/cli/index.ts","../src/cli/commands.ts","../src/blur/pathMapper.ts","../src/cli/generator.ts","../src/server/optimization/types.ts","../src/server/optimization/loadSharp.ts","../src/server/optimization/blurGenerator.ts","../src/utils/color.ts","../src/server/optimization/colorHistogram.ts","../src/server/optimization/edgeColorExtractor.ts","../src/server/semaphore.ts","../src/cli/scanner.ts","../src/cli/validation.ts"],"sourcesContent":["import { createCliProgram } from './commands';\n\nexport * from './scanner';\nexport * from './generator';\nexport * from './validation';\nexport { createCliProgram };\n\nconst CLI_SUBCOMMANDS = new Set([\n 'generate',\n 'validate',\n 'sync',\n 'cleanup',\n 'health',\n 'inspect',\n 'help',\n]);\n\nexport async function runCli(argv?: string[]): Promise<void> {\n const program = createCliProgram();\n if (!argv) {\n await program.parseAsync(process.argv);\n return;\n }\n const isUserArgs =\n argv.length === 0 ||\n CLI_SUBCOMMANDS.has(argv[0]) ||\n argv[0].startsWith('-');\n await program.parseAsync(argv, isUserArgs ? { from: 'user' } : undefined);\n}\n","import fs from 'node:fs/promises';\nimport path from 'node:path';\nimport { Command } from 'commander';\nimport { normalizeImagePath } from '../blur/pathMapper';\nimport type { BlurManifestData, BlurManifestEntry } from '../blur/types';\nimport { generateBlurManifest } from './generator';\nimport {\n autoSyncCheck,\n cleanupOrphanedHashes,\n generateHealthReport,\n validateBlurHashFile,\n} from './validation';\n\ndeclare const __PKG_VERSION__: string | undefined;\n\nconst CLI_VERSION =\n typeof __PKG_VERSION__ === 'string' && __PKG_VERSION__.length > 0\n ? __PKG_VERSION__\n : '2.0.0';\n\nconst DEFAULT_IMAGES_DIR = 'public/images';\nconst DEFAULT_MANIFEST_FILE = 'src/blur-hashes.json';\n\nfunction findManifestEntry(\n hashes: Record<string, BlurManifestEntry>,\n imageKey: string,\n): { key: string; entry: BlurManifestEntry } | null {\n const candidates = [\n imageKey,\n normalizeImagePath(imageKey).normalizedPath,\n normalizeImagePath(imageKey, { stripPrefixes: [] }).normalizedPath,\n ];\n for (const candidate of candidates) {\n if (hashes[candidate]) return { key: candidate, entry: hashes[candidate] };\n const noSlash = candidate.replace(/^\\/+/, '');\n if (hashes[noSlash]) return { key: noSlash, entry: hashes[noSlash] };\n }\n return null;\n}\n\nexport function createCliProgram(): Command {\n const program = new Command();\n\n program\n .name('img-upload-kit')\n .description('LQIP, BlurHash, and dominant color manifest CLI tooling')\n .version(CLI_VERSION);\n\n program\n .command('generate')\n .description('Scan an image directory and generate a blur-hashes JSON manifest')\n .option('-d, --dir <dir>', 'Source images directory', DEFAULT_IMAGES_DIR)\n .option('-o, --out <file>', 'Output manifest JSON path', DEFAULT_MANIFEST_FILE)\n .option('-f, --force', 'Force regenerate all entries ignoring cache', false)\n .option('-c, --concurrency <n>', 'Max parallel processing workers', '4')\n .option('--no-medium', 'Skip generating medium (128px) LQIP variants')\n .action(\n async (opts: {\n dir: string;\n out: string;\n force?: boolean;\n concurrency: string;\n medium: boolean;\n }) => {\n const concurrency = Math.max(1, Number.parseInt(opts.concurrency, 10) || 4);\n const result = await generateBlurManifest(opts.dir, opts.out, {\n concurrency,\n force: Boolean(opts.force),\n includeMedium: opts.medium,\n });\n console.log(\n `Generated ${result.outputPath} (total: ${result.manifest.totalImages}, processed: ${result.processed}, cached: ${result.cached}, failed: ${result.failed})`,\n );\n },\n );\n\n program\n .command('validate')\n .description('Validate a blur manifest JSON file')\n .option('-f, --file <file>', 'Manifest JSON path', DEFAULT_MANIFEST_FILE)\n .action(async (opts: { file: string }) => {\n const result = await validateBlurHashFile(opts.file);\n for (const warning of result.warnings) console.warn(`Warning: ${warning}`);\n if (!result.valid) {\n for (const error of result.errors) console.error(`Error: ${error}`);\n process.exitCode = 1;\n return;\n }\n console.log(`Manifest is valid (${result.totalEntries} entries).`);\n });\n\n program\n .command('sync')\n .description('Check whether disk images and manifest entries are in sync')\n .option('-d, --dir <dir>', 'Source images directory', DEFAULT_IMAGES_DIR)\n .option('-f, --file <file>', 'Manifest JSON path', DEFAULT_MANIFEST_FILE)\n .option('--fix', 'Generate missing and remove orphaned blur manifest entries automatically', false)\n .action(async (opts: { dir: string; file: string; fix?: boolean }) => {\n const sync = await autoSyncCheck(opts.dir, opts.file);\n if (sync.inSync) {\n console.log(`In sync (${sync.totalEntries} entries).`);\n return;\n }\n console.log(\n `Out of sync: ${sync.missingKeys.length} missing, ${sync.orphanedKeys.length} orphaned.`,\n );\n if (opts.fix) {\n if (sync.missingKeys.length > 0) {\n const result = await generateBlurManifest(opts.dir, opts.file);\n console.log(\n `Synced manifest (processed: ${result.processed}, cached: ${result.cached}, removed orphans: ${sync.orphanedKeys.length}).`,\n );\n return;\n }\n if (sync.orphanedKeys.length > 0) {\n const cleaned = await cleanupOrphanedHashes(opts.dir, opts.file);\n console.log(\n `Synced manifest (removed ${cleaned.removedCount} orphaned entries).`,\n );\n return;\n }\n }\n if (!opts.fix) process.exitCode = 1;\n });\n\n program\n .command('cleanup')\n .description('Remove orphaned entries from the blur manifest')\n .option('-d, --dir <dir>', 'Source images directory', DEFAULT_IMAGES_DIR)\n .option('-f, --file <file>', 'Manifest JSON path', DEFAULT_MANIFEST_FILE)\n .option('--dry-run', 'Preview orphaned keys without writing changes', false)\n .action(async (opts: { dir: string; file: string; dryRun?: boolean }) => {\n const result = await cleanupOrphanedHashes(opts.dir, opts.file, {\n dryRun: Boolean(opts.dryRun),\n });\n const prefix = result.dryRun ? '[Dry Run] Would remove' : 'Removed';\n console.log(`${prefix} ${result.removedCount} orphaned entries.`);\n });\n\n program\n .command('health')\n .description('Generate a comprehensive manifest health and coverage report')\n .option('-d, --dir <dir>', 'Source images directory', DEFAULT_IMAGES_DIR)\n .option('-f, --file <file>', 'Manifest JSON path', DEFAULT_MANIFEST_FILE)\n .action(async (opts: { dir: string; file: string }) => {\n const report = await generateHealthReport(opts.dir, opts.file);\n console.log(\n `Health: valid=${report.validManifest}, coverage=${report.coveragePercent}% (${report.totalManifestEntries} entries / ${report.totalImagesOnDisk} disk images)`,\n );\n for (const warning of report.warnings) console.warn(`Warning: ${warning}`);\n for (const error of report.errors) console.error(`Error: ${error}`);\n if (!report.validManifest || report.errors.length > 0) {\n process.exitCode = 1;\n }\n });\n\n program\n .command('inspect <imageKey>')\n .description('Inspect a single image entry inside the blur manifest')\n .option('-f, --file <file>', 'Manifest JSON path', DEFAULT_MANIFEST_FILE)\n .action(async (imageKey: string, opts: { file: string }) => {\n const raw = await fs.readFile(path.resolve(opts.file), 'utf8');\n const parsed = JSON.parse(raw) as Partial<BlurManifestData>;\n const match = parsed.hashes ? findManifestEntry(parsed.hashes, imageKey) : null;\n if (!match) {\n console.error(`Entry not found for key: ${imageKey}`);\n process.exitCode = 1;\n return;\n }\n console.log(\n JSON.stringify(\n {\n key: match.key,\n hash: match.entry.hash,\n color: match.entry.color,\n width: match.entry.width,\n height: match.entry.height,\n variants: {\n smallChars: match.entry.variants.small.length,\n mediumChars: match.entry.variants.medium?.length ?? 0,\n hasMedium: Boolean(match.entry.variants.medium),\n },\n metadata: match.entry.metadata,\n },\n null,\n 2,\n ),\n );\n });\n\n return program;\n}\n","export const DEFAULT_CDN_PATTERNS: RegExp[] = [\n /^https?:\\/\\/[^/]+\\.supabase\\.co\\/storage\\/v1\\/object\\/public\\/([^/]+)\\/(.+)$/,\n /^https?:\\/\\/[^/]+\\.r2\\.dev\\/(.+)$/,\n /^https?:\\/\\/[^/]+\\.amazonaws\\.com\\/(.+)$/,\n /^https?:\\/\\/[^/]+\\.cloudflare\\.com\\/(.+)$/,\n /^https?:\\/\\/cdn\\.[^/]+\\/(.+)$/,\n];\n\nexport interface NormalizePathOptions {\n cdnPatterns?: RegExp[];\n stripPrefixes?: string[];\n}\n\nexport interface PathInfo {\n normalizedPath: string;\n folder?: string;\n filename?: string;\n extension?: string;\n isExternal: boolean;\n originalPath: string;\n}\n\n/**\n * Project-agnostic image URL / path normalizer.\n * Strips query parameters, leading `/public/`, relative `./`, and common CDN prefixes\n * so manifest keys match consistently across local and CDN environments.\n */\nexport function normalizeImagePath(\n imageSrc: string,\n options: NormalizePathOptions = {},\n): PathInfo {\n const originalPath = imageSrc;\n let normalizedPath = imageSrc;\n let isExternal = false;\n\n try {\n if (imageSrc.startsWith('data:') || imageSrc.startsWith('blob:')) {\n return {\n normalizedPath: imageSrc,\n isExternal: false,\n originalPath,\n };\n }\n\n const patterns = options.cdnPatterns || DEFAULT_CDN_PATTERNS;\n\n if (imageSrc.startsWith('http://') || imageSrc.startsWith('https://')) {\n const url = new URL(imageSrc);\n isExternal = true;\n const cleanUrlNoQuery = `${url.origin}${url.pathname}`;\n\n for (const pattern of patterns) {\n const match = cleanUrlNoQuery.match(pattern);\n if (match) {\n const groups = match.slice(1).filter(Boolean);\n const pathPart = groups.join('/');\n normalizedPath = '/' + pathPart.replace(/^\\/+/, '');\n break;\n }\n }\n\n if (normalizedPath === imageSrc) {\n normalizedPath = url.pathname;\n }\n } else {\n normalizedPath = normalizedPath.split('?')[0].split('#')[0];\n }\n\n normalizedPath = normalizedPath.replace(/\\\\/g, '/').replace(/^\\.\\.?\\//g, '');\n\n if (!normalizedPath.startsWith('/')) {\n normalizedPath = '/' + normalizedPath;\n }\n\n const prefixesToStrip = options.stripPrefixes || ['/public/'];\n for (const prefix of prefixesToStrip) {\n if (normalizedPath.startsWith(prefix)) {\n normalizedPath = '/' + normalizedPath.slice(prefix.length).replace(/^\\/+/, '');\n }\n }\n\n const segments = normalizedPath.split('/').filter(Boolean);\n const filename = segments[segments.length - 1];\n const folder =\n segments.length > 1 ? segments.slice(0, -1).join('/') : undefined;\n const dotIdx = filename ? filename.lastIndexOf('.') : -1;\n const extension =\n filename && dotIdx > 0 ? filename.slice(dotIdx + 1).toLowerCase() : undefined;\n\n return {\n normalizedPath,\n folder,\n filename,\n extension,\n isExternal,\n originalPath,\n };\n } catch {\n return {\n normalizedPath: imageSrc,\n isExternal: false,\n originalPath,\n };\n }\n}\n","import fs from 'node:fs/promises';\nimport path from 'node:path';\nimport type { BlurManifestData, BlurManifestEntry } from '../blur/types';\nimport {\n encodeBlurHashString,\n generateBlurPlaceholder,\n} from '../server/optimization';\nimport { loadSharp } from '../server/optimization/loadSharp';\nimport { AsyncSemaphore } from '../server/semaphore';\nimport { scanImageDirectory, type ScannedImageFile } from './scanner';\n\nexport interface GenerateManifestOptions {\n concurrency?: number;\n force?: boolean;\n includeMedium?: boolean;\n onProgress?: (done: number, total: number, key: string) => void;\n}\n\nexport interface GenerateManifestResult {\n manifest: BlurManifestData;\n processed: number;\n cached: number;\n failed: number;\n outputPath: string;\n}\n\nconst FALLBACK_BLURHASH = 'LEHV6nWB2yk8pyo0adR*.7kCMdnj';\nconst MANIFEST_VERSION = '2.0';\n\nasync function loadExistingHashes(\n manifestPath: string,\n): Promise<Record<string, BlurManifestEntry>> {\n try {\n const parsed: unknown = JSON.parse(await fs.readFile(manifestPath, 'utf8'));\n if (\n parsed &&\n typeof parsed === 'object' &&\n !Array.isArray(parsed) &&\n 'hashes' in parsed &&\n parsed.hashes &&\n typeof parsed.hashes === 'object' &&\n !Array.isArray(parsed.hashes)\n ) {\n return parsed.hashes as Record<string, BlurManifestEntry>;\n }\n return {};\n } catch {\n return {};\n }\n}\n\nfunction canReuseCachedEntry(\n entry: BlurManifestEntry | undefined,\n file: ScannedImageFile,\n includeMedium: boolean,\n): entry is BlurManifestEntry {\n if (!entry || typeof entry !== 'object') return false;\n if (!entry.hash || !entry.color || !entry.variants?.small) return false;\n if (!entry.metadata || !entry.metadata.size || !entry.metadata.mtimeMs) {\n return false;\n }\n if (entry.metadata.size !== String(file.size)) {\n return false;\n }\n if (entry.metadata.mtimeMs !== String(Math.round(file.mtimeMs))) {\n return false;\n }\n if (includeMedium && !entry.variants.medium) return false;\n return true;\n}\n\nasync function resolveImageDimensions(\n buffer: Buffer,\n isSvg: boolean,\n): Promise<{ width?: number; height?: number }> {\n const sharp = await loadSharp();\n const metadata = await sharp(buffer).metadata();\n let { width, height } = metadata;\n\n if ((!width || !height) && isSvg) {\n const svgText = buffer.toString('utf8');\n const match = svgText.match(\n /viewBox\\s*=\\s*[\"']\\s*([\\d.-]+)[\\s,]+([\\d.-]+)[\\s,]+([\\d.-]+)[\\s,]+([\\d.-]+)\\s*[\"']/i,\n );\n if (match) {\n width = Math.round(Number.parseFloat(match[3]));\n height = Math.round(Number.parseFloat(match[4]));\n }\n }\n\n return { width, height };\n}\n\n/**\n * Generates or updates a `blur-hashes.json` manifest from an image directory\n * with bounded concurrency and incremental cache reuse.\n */\nexport async function generateBlurManifest(\n imagesDir: string,\n manifestFile: string,\n options: GenerateManifestOptions = {},\n): Promise<GenerateManifestResult> {\n const outputPath = path.resolve(manifestFile);\n const concurrency = Math.max(1, Math.floor(options.concurrency ?? 4));\n const includeMedium = options.includeMedium ?? true;\n const force = Boolean(options.force);\n\n const files = await scanImageDirectory(imagesDir);\n const existingHashes = force ? {} : await loadExistingHashes(outputPath);\n const semaphore = new AsyncSemaphore(concurrency);\n const generatedEntries = new Map<string, BlurManifestEntry>();\n\n let processed = 0;\n let cached = 0;\n let failed = 0;\n let done = 0;\n const total = files.length;\n\n await Promise.all(\n files.map((file) =>\n semaphore.run(async () => {\n const prior = force ? undefined : existingHashes[file.relativeKey];\n try {\n if (canReuseCachedEntry(prior, file, includeMedium)) {\n generatedEntries.set(file.relativeKey, prior);\n cached += 1;\n return;\n }\n\n const buffer = await fs.readFile(file.absolutePath);\n const isSvg = file.relativeKey.toLowerCase().endsWith('.svg');\n const { width, height } = await resolveImageDimensions(buffer, isSvg);\n\n const small = await generateBlurPlaceholder(buffer, {\n width: 64,\n fallbackWidth: 48,\n includeBlurHash: true,\n });\n const medium = includeMedium\n ? await generateBlurPlaceholder(buffer, {\n width: 128,\n fallbackWidth: 96,\n includeBlurHash: false,\n })\n : undefined;\n\n const hash =\n small.blurHash ||\n (await encodeBlurHashString(buffer).catch(() => FALLBACK_BLURHASH));\n\n generatedEntries.set(file.relativeKey, {\n hash,\n variants: {\n small: small.dataURL,\n ...(medium ? { medium: medium.dataURL } : {}),\n },\n color: small.color,\n ...(width ? { width } : {}),\n ...(height ? { height } : {}),\n metadata: {\n size: String(file.size),\n mtimeMs: String(Math.round(file.mtimeMs)),\n },\n });\n processed += 1;\n } catch (err) {\n failed += 1;\n const existing = existingHashes[file.relativeKey];\n if (existing) {\n generatedEntries.set(file.relativeKey, existing);\n }\n const reason = err instanceof Error ? err.message : String(err);\n console.warn(\n `Warning: Failed to generate blur manifest entry for \"${file.relativeKey}\": ${reason}`,\n );\n } finally {\n done += 1;\n options.onProgress?.(done, total, file.relativeKey);\n }\n }),\n ),\n );\n\n const orderedHashes: Record<string, BlurManifestEntry> = {};\n for (const file of files) {\n const entry = generatedEntries.get(file.relativeKey);\n if (entry) orderedHashes[file.relativeKey] = entry;\n }\n\n const manifest: BlurManifestData = {\n version: MANIFEST_VERSION,\n generatedAt: new Date().toISOString(),\n totalImages: Object.keys(orderedHashes).length,\n processedImages: processed,\n cachedImages: cached,\n failedImages: failed,\n hashes: orderedHashes,\n };\n\n await fs.mkdir(path.dirname(outputPath), { recursive: true });\n await fs.writeFile(outputPath, `${JSON.stringify(manifest, null, 2)}\\n`, 'utf8');\n\n return { manifest, processed, cached, failed, outputPath };\n}\n","import type {\n ExplicitOutputFormat,\n ImageDimensions,\n ImageOutputFormat,\n} from '../../types';\n\nexport interface OptimizedImageResult {\n buffer: Buffer;\n width: number;\n height: number;\n format: ImageOutputFormat;\n size: number;\n quality?: number;\n}\n\nexport interface BlurHashResult {\n dataURL: string;\n color: string;\n blurHash?: string;\n}\n\nexport interface OptimizeImageOptions {\n /** Max bounding box dimensions (fit: 'inside', withoutEnlargement: true). Defaults to 1920x1080. */\n maxDimensions?: ImageDimensions;\n /** Explicit output format; omit for smart size-guarded AVIF/WebP selection */\n outputFormat?: ExplicitOutputFormat;\n /** 1–100 compression quality. Defaults to smart photo/graphic heuristic (85–88). */\n quality?: number;\n /** Maximum input pixel count allowed by Sharp (defaults to 50 MP = 50,000,000) */\n limitInputPixels?: number;\n /** AVIF/WebP encoding effort (defaults to 6) */\n effort?: number;\n}\n\nexport const DEFAULT_LIMIT_INPUT_PIXELS = 50_000_000;\n\nexport interface GenerateBlurOptions {\n width?: number;\n fallbackWidth?: number;\n quality?: number;\n maxDataUrlChars?: number;\n includeBlurHash?: boolean;\n}\n\nexport type ColorSamplingMode = 'balanced' | 'edge';\nexport type EdgeKey = 'top' | 'right' | 'bottom' | 'left';\nexport type EdgeColorSet = Record<EdgeKey, string>;\n\nexport const PRIMARY_AVIF_EFFORT = 6;\nexport const RETRY_AVIF_EFFORT = 5;\n\nexport const BLUR_PLACEHOLDER_WIDTH = 128;\nexport const BLUR_PLACEHOLDER_FALLBACK_WIDTH = 96;\nexport const BLUR_PLACEHOLDER_QUALITY = 80;\nexport const BLUR_PLACEHOLDER_MAX_CHARS = 8000;\n\nexport const EDGE_COLOR_FALLBACK = '#6B7280';\nexport const EDGE_WHITE_FALLBACK = '#F9FAFB';\nexport const WHITE_DOMINANCE_RATIO = 1.35;\n","/**\n * Lazy loaders for the optional native peer dependencies (`sharp`,\n * `lqip-modern`). They are resolved on first use via dynamic `import()` so\n * that merely importing `img-upload-kit/server` (or bundling it) never\n * requires the native binaries to be present, and so that missing peers\n * produce an actionable error instead of an opaque module-resolution crash.\n */\nimport type LqipDefault from 'lqip-modern';\nimport type SharpDefault from 'sharp';\n\nexport type SharpFactory = typeof SharpDefault;\nexport type LqipFn = typeof LqipDefault;\n\nexport const SHARP_MISSING_MESSAGE =\n 'img-upload-kit/server requires `sharp` — run `npm i sharp`';\nexport const LQIP_MISSING_MESSAGE =\n 'img-upload-kit/server requires `lqip-modern` for LQIP generation — run `npm i lqip-modern`';\n\nlet sharpPromise: Promise<SharpFactory> | null = null;\nlet lqipPromise: Promise<LqipFn> | null = null;\n\nfunction unwrapDefault<T>(mod: unknown): T {\n const record = mod as { default?: unknown };\n return (record && record.default !== undefined ? record.default : mod) as T;\n}\n\n/** Resolves the `sharp` factory, throwing a descriptive error if it is not installed. */\nexport function loadSharp(): Promise<SharpFactory> {\n if (!sharpPromise) {\n sharpPromise = import('sharp').then(\n (mod) => unwrapDefault<SharpFactory>(mod),\n (error: unknown) => {\n sharpPromise = null;\n throw new Error(SHARP_MISSING_MESSAGE, { cause: error });\n },\n );\n }\n return sharpPromise;\n}\n\n/** Resolves the `lqip-modern` function, throwing a descriptive error if it is not installed. */\nexport function loadLqip(): Promise<LqipFn> {\n if (!lqipPromise) {\n lqipPromise = import('lqip-modern').then(\n (mod) => unwrapDefault<LqipFn>(mod),\n (error: unknown) => {\n lqipPromise = null;\n throw new Error(LQIP_MISSING_MESSAGE, { cause: error });\n },\n );\n }\n return lqipPromise;\n}\n","import { encode as encodeBlurHash } from 'blurhash';\nimport {\n blendEdgeColors,\n computeEdgeColorSet,\n extractEdgeColorsFromBuffer,\n} from './edgeColorExtractor';\nimport { loadSharp } from './loadSharp';\nimport {\n BLUR_PLACEHOLDER_FALLBACK_WIDTH,\n BLUR_PLACEHOLDER_MAX_CHARS,\n BLUR_PLACEHOLDER_QUALITY,\n BLUR_PLACEHOLDER_WIDTH,\n type BlurHashResult,\n type GenerateBlurOptions,\n} from './types';\n\nfunction encodeRawBlurHash(\n data: Buffer,\n width: number,\n height: number,\n componentX: number = 4,\n componentY: number = 3,\n): string {\n const clampedX = Math.max(1, Math.min(Math.round(componentX), width, 9));\n const clampedY = Math.max(1, Math.min(Math.round(componentY), height, 9));\n return encodeBlurHash(\n new Uint8ClampedArray(data),\n width,\n height,\n clampedX,\n clampedY,\n );\n}\n\nexport async function encodeBlurHashString(\n buffer: Buffer,\n componentX: number = 4,\n componentY: number = 3,\n): Promise<string> {\n const sharp = await loadSharp();\n const { data, info } = await sharp(buffer)\n .resize(32, 32, { fit: 'inside' })\n .ensureAlpha()\n .raw()\n .toBuffer({ resolveWithObject: true });\n\n return encodeRawBlurHash(\n data,\n info.width,\n info.height,\n componentX,\n componentY,\n );\n}\n\nexport async function generateBlurPlaceholder(\n buffer: Buffer,\n options: GenerateBlurOptions = {},\n): Promise<BlurHashResult> {\n const targetWidth = options.width ?? BLUR_PLACEHOLDER_WIDTH;\n const fallbackWidth =\n options.fallbackWidth ?? BLUR_PLACEHOLDER_FALLBACK_WIDTH;\n const quality = options.quality ?? BLUR_PLACEHOLDER_QUALITY;\n const maxChars = options.maxDataUrlChars ?? BLUR_PLACEHOLDER_MAX_CHARS;\n const sharp = await loadSharp();\n\n try {\n let processBuffer = buffer;\n const isSvg = /<svg[\\s>]/i.test(buffer.toString('utf8', 0, 1024));\n if (isSvg) {\n const normalizedSvg = buffer\n .toString('utf8')\n .replace(/<svg\\b/gi, '<svg')\n .replace(/<\\/svg\\s*>/gi, '</svg>');\n processBuffer = Buffer.from(normalizedSvg, 'utf8');\n }\n\n const baseSize = Math.max(targetWidth, 128);\n const rawSample = await sharp(processBuffer)\n .rotate()\n .resize(baseSize, baseSize, { fit: 'inside', withoutEnlargement: true })\n .ensureAlpha()\n .raw()\n .toBuffer({ resolveWithObject: true });\n\n const rawInput = {\n raw: {\n width: rawSample.info.width,\n height: rawSample.info.height,\n channels: 4 as const,\n },\n };\n\n const buildPlaceholderDataUrl = async (width: number): Promise<string> => {\n const webpBuffer = await sharp(rawSample.data, rawInput)\n .resize(width, width, { fit: 'inside', withoutEnlargement: true })\n .webp({ quality, effort: 6, smartSubsample: true })\n .toBuffer();\n return `data:image/webp;base64,${webpBuffer.toString('base64')}`;\n };\n\n let dataURL = await buildPlaceholderDataUrl(targetWidth);\n if (dataURL.length > maxChars) {\n dataURL = await buildPlaceholderDataUrl(fallbackWidth);\n }\n\n const edgeColors = computeEdgeColorSet(\n rawSample.data,\n rawSample.info.width,\n rawSample.info.height,\n );\n const color = blendEdgeColors(edgeColors);\n\n let blurHash: string | undefined;\n if (options.includeBlurHash) {\n try {\n const hashSample =\n rawSample.info.width <= 32 && rawSample.info.height <= 32\n ? rawSample\n : await sharp(rawSample.data, rawInput)\n .resize(32, 32, { fit: 'inside' })\n .ensureAlpha()\n .raw()\n .toBuffer({ resolveWithObject: true });\n blurHash = encodeRawBlurHash(\n hashSample.data,\n hashSample.info.width,\n hashSample.info.height,\n );\n } catch {\n blurHash = undefined;\n }\n }\n\n return {\n dataURL,\n color,\n ...(blurHash && { blurHash }),\n };\n } catch {\n return generateFallbackBlur(buffer, options.includeBlurHash);\n }\n}\n\nexport const generateBlurHash = generateBlurPlaceholder;\n\nasync function generateFallbackBlur(\n buffer: Buffer,\n includeBlurHash?: boolean,\n): Promise<BlurHashResult> {\n const sharp = await loadSharp();\n const tinyBuffer = await sharp(buffer)\n .resize(64, 64, { fit: 'cover', kernel: 'lanczos3' })\n .blur(1.0)\n .webp({\n quality: 75,\n effort: 6,\n smartSubsample: false,\n })\n .toBuffer();\n\n const dataURL = `data:image/webp;base64,${tinyBuffer.toString('base64')}`;\n const edgeColors = await extractEdgeColorsFromBuffer(buffer);\n const color = blendEdgeColors(edgeColors);\n\n let blurHash: string | undefined;\n if (includeBlurHash) {\n try {\n blurHash = await encodeBlurHashString(tinyBuffer);\n } catch {\n blurHash = undefined;\n }\n }\n\n return { dataURL, color, ...(blurHash && { blurHash }) };\n}\n","export const DEFAULT_BLUR_COLOR = '#F5F5F5';\n\nexport function normalizeColorInput(value?: string | null): string | null {\n if (!value) return null;\n let next = value.trim();\n if (!next) return null;\n if (!next.startsWith('#')) {\n next = `#${next}`;\n }\n const threeHex = /^#([0-9a-fA-F]{3})$/;\n const sixHex = /^#([0-9a-fA-F]{6})$/;\n if (threeHex.test(next)) {\n const [, triplet] = next.match(threeHex) ?? [];\n if (triplet) {\n next = `#${triplet\n .split('')\n .map((char) => char + char)\n .join('')}`;\n }\n } else if (!sixHex.test(next)) {\n return null;\n }\n return next.toUpperCase();\n}\n\nexport function hexToRgb(hex: string): { r: number; g: number; b: number } {\n const normalized = hex.replace('#', '');\n const expanded =\n normalized.length === 3\n ? normalized\n .split('')\n .map((char) => char + char)\n .join('')\n : normalized;\n const padded = expanded.padEnd(6, '0').slice(0, 6);\n const value = Number.parseInt(padded, 16);\n if (Number.isNaN(value)) {\n return { r: 245, g: 245, b: 245 };\n }\n return {\n r: (value >> 16) & 255,\n g: (value >> 8) & 255,\n b: value & 255,\n };\n}\n\nexport function rgbToHex(r: number, g: number, b: number): string {\n const clamp = (v: number) => Math.max(0, Math.min(255, Math.round(v)));\n const value = (clamp(r) << 16) | (clamp(g) << 8) | clamp(b);\n return `#${value.toString(16).padStart(6, '0').toUpperCase()}`;\n}\n\nfunction encodeBase64Ascii(str: string): string {\n if (typeof btoa === 'function') {\n return btoa(str);\n }\n return Buffer.from(str, 'utf8').toString('base64');\n}\n\nexport function generateColorSvgDataURL(color: string = DEFAULT_BLUR_COLOR): string {\n const safeColor = normalizeColorInput(color) || DEFAULT_BLUR_COLOR;\n const svg = `<svg width=\"100\" height=\"100\" xmlns=\"http://www.w3.org/2000/svg\"><rect width=\"100%\" height=\"100%\" fill=\"${safeColor}\"/></svg>`;\n return `data:image/svg+xml;base64,${encodeBase64Ascii(svg)}`;\n}\n","import { rgbToHex } from '../../utils/color';\nimport {\n type ColorSamplingMode,\n EDGE_COLOR_FALLBACK,\n EDGE_WHITE_FALLBACK,\n WHITE_DOMINANCE_RATIO,\n} from './types';\nimport { loadSharp } from './loadSharp';\n\nexport async function extractEnhancedColorFromBuffer(\n buffer: Buffer,\n mode: ColorSamplingMode = 'balanced',\n): Promise<string> {\n const sharp = await loadSharp();\n try {\n const metadata = await sharp(buffer).metadata();\n const maxSize = 400;\n const needsResize =\n metadata.width &&\n metadata.height &&\n (metadata.width > maxSize || metadata.height > maxSize);\n\n if (needsResize) {\n const resized = await sharp(buffer)\n .resize(maxSize, maxSize, {\n fit: 'inside',\n kernel: 'lanczos3',\n withoutEnlargement: true,\n })\n .ensureAlpha()\n .raw()\n .toBuffer({ resolveWithObject: true });\n\n return extractDominantColorHistogramAdvanced(\n resized.data,\n resized.info.width,\n resized.info.height,\n mode,\n );\n }\n\n const raw = await sharp(buffer)\n .ensureAlpha()\n .raw()\n .toBuffer({ resolveWithObject: true });\n\n return extractDominantColorHistogramAdvanced(\n raw.data,\n raw.info.width,\n raw.info.height,\n mode,\n );\n } catch {\n const stats = await sharp(buffer)\n .resize(64, 64, { fit: 'cover', kernel: 'lanczos3' })\n .ensureAlpha()\n .raw()\n .toBuffer({ resolveWithObject: true });\n return extractDominantColorHistogram(stats.data);\n }\n}\n\nfunction extractDominantColorHistogramAdvanced(\n data: Buffer,\n width: number,\n height: number,\n mode: ColorSamplingMode,\n): string {\n const colorMap = new Map<string, number>();\n const bucketSize = 16;\n\n const centerWeight = 1.5;\n const centerX = width / 2 || 1;\n const centerY = height / 2 || 1;\n let whiteWeight = 0;\n\n for (let i = 0; i < data.length; i += 4) {\n const alpha = data[i + 3];\n if (alpha !== undefined && alpha < 8) continue;\n\n const r = data[i];\n const g = data[i + 1];\n const b = data[i + 2];\n const brightness = (r + g + b) / 3;\n if (brightness < 10) continue;\n\n const pixelIndex = i / 4;\n const x = pixelIndex % width;\n const y = Math.floor(pixelIndex / width);\n const dx = (x - centerX) / centerX;\n const dy = (y - centerY) / centerY;\n const distanceFromCenter = Math.sqrt(dx * dx + dy * dy);\n let weight = 1.0;\n\n if (mode === 'edge') {\n const edgeThicknessX = Math.max(1, Math.floor(width * 0.12));\n const edgeThicknessY = Math.max(1, Math.floor(height * 0.12));\n\n const onLeftEdge = x < edgeThicknessX;\n const onRightEdge = x >= width - edgeThicknessX;\n const onTopEdge = y < edgeThicknessY;\n const onBottomEdge = y >= height - edgeThicknessY;\n\n if (!(onLeftEdge || onRightEdge || onTopEdge || onBottomEdge)) {\n continue;\n }\n\n const distanceToNearestEdge = Math.min(\n x,\n y,\n width - 1 - x,\n height - 1 - y,\n );\n const maxEdgeDistance = Math.max(edgeThicknessX, edgeThicknessY) || 1;\n const edgeFactor = 1 - distanceToNearestEdge / maxEdgeDistance;\n weight = 1 + edgeFactor * 3;\n } else {\n weight = distanceFromCenter < 0.5 ? centerWeight : 1.0;\n }\n\n const isNearWhite = r >= 245 && g >= 245 && b >= 245;\n if (isNearWhite) {\n whiteWeight += weight;\n continue;\n }\n\n const bucketR = Math.floor(r / bucketSize) * bucketSize;\n const bucketG = Math.floor(g / bucketSize) * bucketSize;\n const bucketB = Math.floor(b / bucketSize) * bucketSize;\n\n const key = `${bucketR},${bucketG},${bucketB}`;\n colorMap.set(key, (colorMap.get(key) || 0) + weight);\n }\n\n if (!colorMap.size) {\n return whiteWeight > 0 ? EDGE_WHITE_FALLBACK : EDGE_COLOR_FALLBACK;\n }\n\n let maxCount = 0;\n let dominantColor = '64,64,80';\n let totalColorWeight = 0;\n\n colorMap.forEach((count, color) => {\n totalColorWeight += count;\n if (count > maxCount) {\n maxCount = count;\n dominantColor = color;\n }\n });\n\n const whiteDominates =\n whiteWeight > 0 && whiteWeight >= totalColorWeight * WHITE_DOMINANCE_RATIO;\n if (whiteDominates) {\n return EDGE_WHITE_FALLBACK;\n }\n\n const [r, g, b] = dominantColor.split(',').map(Number);\n return rgbToHex(r, g, b);\n}\n\nfunction extractDominantColorHistogram(data: Buffer): string {\n const colorMap = new Map<string, number>();\n const bucketSize = 16;\n let whiteWeight = 0;\n\n for (let i = 0; i < data.length; i += 4) {\n const alpha = data[i + 3];\n if (alpha !== undefined && alpha < 8) continue;\n\n const r = Math.floor(data[i] / bucketSize) * bucketSize;\n const g = Math.floor(data[i + 1] / bucketSize) * bucketSize;\n const b = Math.floor(data[i + 2] / bucketSize) * bucketSize;\n\n if (r >= 240 && g >= 240 && b >= 240) {\n whiteWeight += 1;\n continue;\n }\n\n const key = `${r},${g},${b}`;\n colorMap.set(key, (colorMap.get(key) || 0) + 1);\n }\n\n if (!colorMap.size) {\n return whiteWeight > 0 ? EDGE_WHITE_FALLBACK : EDGE_COLOR_FALLBACK;\n }\n\n let maxCount = 0;\n let dominantColor = '64,64,80';\n let totalColorWeight = 0;\n\n colorMap.forEach((count, color) => {\n totalColorWeight += count;\n if (count > maxCount) {\n maxCount = count;\n dominantColor = color;\n }\n });\n\n const whiteDominates =\n whiteWeight > 0 && whiteWeight >= totalColorWeight * WHITE_DOMINANCE_RATIO;\n if (whiteDominates) {\n return EDGE_WHITE_FALLBACK;\n }\n\n const [r, g, b] = dominantColor.split(',').map(Number);\n return rgbToHex(r, g, b);\n}\n","import { hexToRgb, rgbToHex } from '../../utils/color';\nimport { extractEnhancedColorFromBuffer } from './colorHistogram';\nimport {\n EDGE_COLOR_FALLBACK,\n EDGE_WHITE_FALLBACK,\n type EdgeColorSet,\n type EdgeKey,\n WHITE_DOMINANCE_RATIO,\n} from './types';\nimport { loadSharp } from './loadSharp';\n\nexport async function extractEdgeColorsFromBuffer(\n buffer: Buffer,\n): Promise<EdgeColorSet> {\n const sharp = await loadSharp();\n try {\n const sample = await sharp(buffer)\n .resize(400, 400, {\n fit: 'inside',\n kernel: 'lanczos3',\n withoutEnlargement: true,\n })\n .ensureAlpha()\n .raw()\n .toBuffer({ resolveWithObject: true });\n\n return computeEdgeColorSet(\n sample.data,\n sample.info.width,\n sample.info.height,\n );\n } catch {\n try {\n const fallback = await extractEnhancedColorFromBuffer(buffer, 'edge');\n return {\n top: fallback,\n right: fallback,\n bottom: fallback,\n left: fallback,\n };\n } catch {\n return {\n top: EDGE_COLOR_FALLBACK,\n right: EDGE_COLOR_FALLBACK,\n bottom: EDGE_COLOR_FALLBACK,\n left: EDGE_COLOR_FALLBACK,\n };\n }\n }\n}\n\nexport function computeEdgeColorSet(\n data: Buffer,\n width: number,\n height: number,\n): EdgeColorSet {\n const thicknessX = Math.max(2, Math.floor(width * 0.12));\n const thicknessY = Math.max(2, Math.floor(height * 0.12));\n const bucketSize = 16;\n\n const edgeMaps: Record<EdgeKey, Map<string, number>> = {\n top: new Map(),\n right: new Map(),\n bottom: new Map(),\n left: new Map(),\n };\n\n const whiteEdgeStrength: Record<EdgeKey, number> = {\n top: 0,\n right: 0,\n bottom: 0,\n left: 0,\n };\n\n const accumulateEdgeSample = (\n edge: EdgeKey,\n weight: number,\n key: string,\n isWhiteSample: boolean,\n ) => {\n if (weight <= 0) return;\n if (isWhiteSample) {\n whiteEdgeStrength[edge] += weight;\n return;\n }\n edgeMaps[edge].set(key, (edgeMaps[edge].get(key) || 0) + weight);\n };\n\n for (let i = 0; i < data.length; i += 4) {\n const r = data[i];\n const g = data[i + 1];\n const b = data[i + 2];\n const alpha = data[i + 3];\n\n if (alpha !== undefined && alpha < 8) continue;\n\n const brightness = (r + g + b) / 3;\n if (brightness < 12) continue;\n\n const isNearWhite = r >= 245 && g >= 245 && b >= 245;\n\n const pixelIndex = i / 4;\n const x = pixelIndex % width;\n const y = Math.floor(pixelIndex / width);\n\n const bucketR = Math.floor(r / bucketSize) * bucketSize;\n const bucketG = Math.floor(g / bucketSize) * bucketSize;\n const bucketB = Math.floor(b / bucketSize) * bucketSize;\n const key = `${bucketR},${bucketG},${bucketB}`;\n\n const topWeight = y < thicknessY ? 1 + (thicknessY - y) / thicknessY : 0;\n if (topWeight > 0) {\n accumulateEdgeSample('top', topWeight, key, isNearWhite);\n }\n\n const bottomWeight =\n y >= height - thicknessY\n ? 1 + (y - (height - thicknessY)) / thicknessY\n : 0;\n if (bottomWeight > 0) {\n accumulateEdgeSample('bottom', bottomWeight, key, isNearWhite);\n }\n\n const leftWeight = x < thicknessX ? 1 + (thicknessX - x) / thicknessX : 0;\n if (leftWeight > 0) {\n accumulateEdgeSample('left', leftWeight, key, isNearWhite);\n }\n\n const rightWeight =\n x >= width - thicknessX\n ? 1 + (x - (width - thicknessX)) / thicknessX\n : 0;\n if (rightWeight > 0) {\n accumulateEdgeSample('right', rightWeight, key, isNearWhite);\n }\n }\n\n return {\n top: resolveEdgeColor(edgeMaps.top, whiteEdgeStrength.top),\n right: resolveEdgeColor(edgeMaps.right, whiteEdgeStrength.right),\n bottom: resolveEdgeColor(edgeMaps.bottom, whiteEdgeStrength.bottom),\n left: resolveEdgeColor(edgeMaps.left, whiteEdgeStrength.left),\n };\n}\n\nfunction resolveEdgeColor(\n map: Map<string, number>,\n whiteStrength: number,\n): string {\n if (!map.size) {\n return whiteStrength > 0 ? EDGE_WHITE_FALLBACK : EDGE_COLOR_FALLBACK;\n }\n\n let dominantColor = '';\n let maxCount = 0;\n let totalColorWeight = 0;\n\n map.forEach((count, color) => {\n totalColorWeight += count;\n if (count > maxCount) {\n maxCount = count;\n dominantColor = color;\n }\n });\n\n if (!dominantColor) {\n return whiteStrength > 0 ? EDGE_WHITE_FALLBACK : EDGE_COLOR_FALLBACK;\n }\n\n const whiteDominates =\n whiteStrength > 0 &&\n (totalColorWeight === 0 ||\n whiteStrength >= totalColorWeight * WHITE_DOMINANCE_RATIO);\n\n if (whiteDominates) {\n return EDGE_WHITE_FALLBACK;\n }\n\n if (dominantColor.startsWith('#')) {\n return dominantColor.toUpperCase();\n }\n\n const [r, g, b] = dominantColor.split(',').map(Number);\n return rgbToHex(r, g, b);\n}\n\nexport function blendEdgeColors(edgeColors: EdgeColorSet): string {\n const colors = Object.values(edgeColors);\n if (!colors.length) {\n return EDGE_COLOR_FALLBACK;\n }\n\n const totals = colors.reduce(\n (acc, hex) => {\n const { r, g, b } = hexToRgb(hex || EDGE_COLOR_FALLBACK);\n acc.r += r;\n acc.g += g;\n acc.b += b;\n return acc;\n },\n { r: 0, g: 0, b: 0 },\n );\n\n const count = colors.length;\n return rgbToHex(totals.r / count, totals.g / count, totals.b / count);\n}\n","export class SemaphoreQueueFullError extends Error {\n readonly status = 503;\n\n constructor(\n message: string = 'Server upload queue is full. Please try again later.',\n ) {\n super(message);\n this.name = 'SemaphoreQueueFullError';\n }\n}\n\nexport interface SemaphoreOptions {\n maxQueue?: number;\n}\n\nexport interface SemaphoreRunOptions {\n signal?: AbortSignal;\n timeoutMs?: number;\n label?: string;\n}\n\ninterface QueueEntry {\n grant: () => void;\n cancel: (reason: unknown) => void;\n}\n\ninterface ErrorWithPendingTask {\n pendingTask?: Promise<unknown>;\n}\n\nfunction createAbortError(signal?: AbortSignal): Error {\n if (signal?.reason instanceof Error) {\n return signal.reason;\n }\n const err = new Error(\n typeof signal?.reason === 'string' ? signal.reason : 'Operation aborted',\n );\n err.name = 'AbortError';\n return err;\n}\n\nexport class AsyncSemaphore {\n private activeCount = 0;\n private readonly maxQueue?: number;\n private readonly queue: QueueEntry[] = [];\n\n constructor(\n private readonly concurrency: number,\n options?: SemaphoreOptions | number,\n ) {\n if (concurrency < 1) {\n throw new Error('AsyncSemaphore concurrency must be >= 1');\n }\n if (typeof options === 'number') {\n this.maxQueue = options;\n } else if (options && typeof options === 'object') {\n this.maxQueue = options.maxQueue;\n }\n }\n\n get active(): number {\n return this.activeCount;\n }\n\n get pending(): number {\n return this.queue.length;\n }\n\n async run<T>(\n task: () => Promise<T>,\n options: SemaphoreRunOptions = {},\n ): Promise<T> {\n const { signal, timeoutMs, label = 'Semaphore task' } = options;\n await this.acquire(signal);\n\n let rawTaskPromise: Promise<T>;\n try {\n rawTaskPromise = Promise.resolve().then(task);\n } catch (syncError) {\n this.release();\n throw syncError;\n }\n\n let released = false;\n const releaseOnce = () => {\n if (!released) {\n released = true;\n this.release();\n }\n };\n\n void rawTaskPromise.then(\n () => releaseOnce(),\n (err: unknown) => {\n const pending =\n err && typeof err === 'object' && 'pendingTask' in err\n ? (err as ErrorWithPendingTask).pendingTask\n : undefined;\n if (pending && typeof pending.then === 'function') {\n void pending.then(releaseOnce, releaseOnce);\n } else {\n releaseOnce();\n }\n },\n );\n\n let callerPromise = rawTaskPromise;\n if (timeoutMs !== undefined && timeoutMs > 0) {\n callerPromise = withTimeout(callerPromise, timeoutMs, label);\n }\n if (signal) {\n callerPromise = raceWithAbort(callerPromise, signal);\n }\n\n return callerPromise;\n }\n\n private acquire(signal?: AbortSignal): Promise<void> {\n if (signal?.aborted) {\n return Promise.reject(createAbortError(signal));\n }\n\n if (this.activeCount < this.concurrency) {\n this.activeCount += 1;\n return Promise.resolve();\n }\n\n if (this.maxQueue !== undefined && this.queue.length >= this.maxQueue) {\n return Promise.reject(new SemaphoreQueueFullError());\n }\n\n return new Promise<void>((resolve, reject) => {\n const onAbort = () => {\n const idx = this.queue.indexOf(entry);\n if (idx !== -1) {\n this.queue.splice(idx, 1);\n }\n entry.cancel(createAbortError(signal));\n };\n\n const entry: QueueEntry = {\n grant: () => {\n signal?.removeEventListener('abort', onAbort);\n this.activeCount += 1;\n resolve();\n },\n cancel: (reason: unknown) => {\n signal?.removeEventListener('abort', onAbort);\n reject(reason);\n },\n };\n\n signal?.addEventListener('abort', onAbort, { once: true });\n this.queue.push(entry);\n });\n }\n\n private release(): void {\n this.activeCount = Math.max(0, this.activeCount - 1);\n const next = this.queue.shift();\n next?.grant();\n }\n}\n\nfunction raceWithAbort<T>(\n task: Promise<T>,\n signal: AbortSignal,\n): Promise<T> {\n if (signal.aborted) {\n const err = createAbortError(signal) as Error & ErrorWithPendingTask;\n err.pendingTask = task.then(\n () => {},\n () => {},\n );\n return Promise.reject(err);\n }\n\n return new Promise<T>((resolve, reject) => {\n const onAbort = () => {\n const err = createAbortError(signal) as Error & ErrorWithPendingTask;\n err.pendingTask = task.then(\n () => {},\n () => {},\n );\n reject(err);\n };\n\n signal.addEventListener('abort', onAbort, { once: true });\n task.then(\n (val) => {\n signal.removeEventListener('abort', onAbort);\n resolve(val);\n },\n (err) => {\n signal.removeEventListener('abort', onAbort);\n reject(err);\n },\n );\n });\n}\n\nexport async function withTimeout<T>(\n task: Promise<T>,\n timeoutMs: number,\n label: string,\n): Promise<T> {\n let timeoutHandle: ReturnType<typeof setTimeout> | null = null;\n const settledTask = task.then(\n () => {},\n () => {},\n );\n\n try {\n return await Promise.race([\n task,\n new Promise<never>((_, reject) => {\n timeoutHandle = setTimeout(() => {\n const err = new Error(\n `${label} timed out after ${timeoutMs}ms`,\n ) as Error & ErrorWithPendingTask;\n err.pendingTask = settledTask;\n reject(err);\n }, timeoutMs);\n }),\n ]);\n } finally {\n if (timeoutHandle) {\n clearTimeout(timeoutHandle);\n }\n }\n}\n","import fs from 'node:fs/promises';\nimport path from 'node:path';\n\nexport const SUPPORTED_IMAGE_EXTENSIONS = new Set([\n '.jpg',\n '.jpeg',\n '.png',\n '.webp',\n '.avif',\n '.svg',\n]);\n\nexport const IGNORED_DIRECTORIES = new Set(['node_modules', '.git', 'dist']);\n\nexport interface ScannedImageFile {\n absolutePath: string;\n relativeKey: string;\n size: number;\n mtimeMs: number;\n}\n\n/**\n * Recursively scans an image directory for supported image files,\n * ignoring `node_modules`, `.git`, and `dist`, and returning normalized\n * `/` forward-slash relative keys sorted alphabetically.\n */\nexport async function scanImageDirectory(\n imagesDir: string,\n): Promise<ScannedImageFile[]> {\n const rootDir = path.resolve(imagesDir);\n const rootStats = await fs.stat(rootDir);\n\n if (!rootStats.isDirectory()) {\n throw new Error(`Expected images path to be a directory: ${rootDir}`);\n }\n\n const results: ScannedImageFile[] = [];\n\n async function walk(currentDir: string): Promise<void> {\n const entries = await fs.readdir(currentDir, { withFileTypes: true });\n\n for (const entry of entries) {\n const fullPath = path.join(currentDir, entry.name);\n\n if (entry.isDirectory()) {\n if (IGNORED_DIRECTORIES.has(entry.name)) {\n continue;\n }\n await walk(fullPath);\n continue;\n }\n\n if (!entry.isFile()) {\n continue;\n }\n\n const ext = path.extname(entry.name).toLowerCase();\n if (!SUPPORTED_IMAGE_EXTENSIONS.has(ext)) {\n continue;\n }\n\n const fileStats = await fs.stat(fullPath);\n const relPath = path\n .relative(rootDir, fullPath)\n .split(path.sep)\n .join('/')\n .replace(/\\\\/g, '/');\n const relativeKey = relPath.startsWith('/') ? relPath : `/${relPath}`;\n\n results.push({\n absolutePath: fullPath,\n relativeKey,\n size: fileStats.size,\n mtimeMs: fileStats.mtimeMs,\n });\n }\n }\n\n await walk(rootDir);\n results.sort((a, b) => a.relativeKey.localeCompare(b.relativeKey));\n\n return results;\n}\n","import fs from 'node:fs/promises';\nimport path from 'node:path';\nimport { normalizeImagePath } from '../blur/pathMapper';\nimport type { BlurManifestData, BlurManifestEntry } from '../blur/types';\nimport { scanImageDirectory, type ScannedImageFile } from './scanner';\n\nexport interface ValidationResult {\n valid: boolean;\n totalEntries: number;\n errors: string[];\n warnings: string[];\n}\n\nexport interface SyncCheckResult {\n inSync: boolean;\n totalFiles: number;\n totalEntries: number;\n missingKeys: string[];\n orphanedKeys: string[];\n}\n\nexport interface CleanupResult {\n removedCount: number;\n removedKeys: string[];\n dryRun: boolean;\n}\n\nexport interface HealthReport {\n validManifest: boolean;\n totalImagesOnDisk: number;\n totalManifestEntries: number;\n coveragePercent: number;\n missingKeys: string[];\n orphanedKeys: string[];\n errors: string[];\n warnings: string[];\n}\n\nconst HEX_COLOR_RE = /^#([0-9a-fA-F]{3}|[0-9a-fA-F]{6}|[0-9a-fA-F]{8})$/;\n\nfunction isPlainObject(value: unknown): value is Record<string, unknown> {\n return Boolean(value) && typeof value === 'object' && !Array.isArray(value);\n}\n\nfunction toNormalizedKey(key: string): string {\n return normalizeImagePath(key, { stripPrefixes: [] }).normalizedPath;\n}\n\nfunction failValidation(error: string): ValidationResult {\n return { valid: false, totalEntries: 0, errors: [error], warnings: [] };\n}\n\nfunction isDataImageUrl(value: unknown): value is string {\n return typeof value === 'string' && value.startsWith('data:image/');\n}\n\nfunction computeKeyDiff(\n files: ScannedImageFile[],\n hashes: Record<string, BlurManifestEntry>,\n): { missingKeys: string[]; orphanedKeys: string[] } {\n const diskKeys = new Set(files.map((f) => f.relativeKey));\n const manifestKeys = Object.keys(hashes);\n const normalizedSet = new Set(manifestKeys.map(toNormalizedKey));\n const missingKeys = files\n .map((f) => f.relativeKey)\n .filter((k) => !(k in hashes) && !normalizedSet.has(k));\n const orphanedKeys = manifestKeys.filter(\n (k) => !diskKeys.has(k) && !diskKeys.has(toNormalizedKey(k)),\n );\n return { missingKeys, orphanedKeys };\n}\n\nasync function readManifestSafe(manifestFile: string): Promise<{\n exists: boolean;\n manifest?: BlurManifestData;\n hashes: Record<string, BlurManifestEntry>;\n}> {\n try {\n const parsed: unknown = JSON.parse(await fs.readFile(path.resolve(manifestFile), 'utf8'));\n if (isPlainObject(parsed) && isPlainObject(parsed.hashes)) {\n return {\n exists: true,\n manifest: parsed as unknown as BlurManifestData,\n hashes: parsed.hashes as Record<string, BlurManifestEntry>,\n };\n }\n return { exists: true, hashes: {} };\n } catch (err) {\n return { exists: (err as NodeJS.ErrnoException).code !== 'ENOENT', hashes: {} };\n }\n}\n\nfunction validateEntry(key: string, value: unknown, errors: string[], warnings: string[]): void {\n if (!key.startsWith('/')) warnings.push(`Entry key \"${key}\" should start with \"/\"`);\n if (!isPlainObject(value)) {\n errors.push(`Entry \"${key}\" must be an object`);\n return;\n }\n const entry = value as Partial<BlurManifestEntry>;\n if (typeof entry.hash !== 'string' || entry.hash.trim().length < 6) {\n errors.push(`Entry \"${key}\" has missing or invalid BlurHash \"hash\"`);\n }\n if (typeof entry.color !== 'string' || !HEX_COLOR_RE.test(entry.color)) {\n errors.push(`Entry \"${key}\" has missing or invalid hex \"color\"`);\n }\n if (!isPlainObject(entry.variants)) {\n errors.push(`Entry \"${key}\" is missing \"variants\" object`);\n return;\n }\n if (!isDataImageUrl(entry.variants.small)) {\n errors.push(`Entry \"${key}\" is missing valid \"variants.small\" data URL`);\n }\n if (entry.variants.medium !== undefined && !isDataImageUrl(entry.variants.medium)) {\n errors.push(`Entry \"${key}\" has invalid \"variants.medium\" data URL`);\n }\n}\n\nexport async function validateBlurHashFile(manifestFile: string): Promise<ValidationResult> {\n const resolvedPath = path.resolve(manifestFile);\n let raw: string;\n try {\n raw = await fs.readFile(resolvedPath, 'utf8');\n } catch {\n return failValidation(`Manifest file not found or unreadable: ${resolvedPath}`);\n }\n let parsed: unknown;\n try {\n parsed = JSON.parse(raw);\n } catch (err) {\n return failValidation(`Invalid JSON in manifest: ${(err as Error).message}`);\n }\n if (!isPlainObject(parsed)) return failValidation('Manifest root must be a JSON object');\n if (!isPlainObject(parsed.hashes)) return failValidation('Manifest is missing a valid \"hashes\" object');\n\n const errors: string[] = [];\n const warnings: string[] = [];\n if (typeof parsed.version !== 'string' || !parsed.version.trim()) {\n warnings.push('Manifest is missing a \"version\" string');\n }\n if (typeof parsed.generatedAt !== 'string' || Number.isNaN(Date.parse(parsed.generatedAt))) {\n warnings.push('Manifest has missing or invalid \"generatedAt\" timestamp');\n }\n\n const entries = Object.entries(parsed.hashes);\n if (typeof parsed.totalImages === 'number' && parsed.totalImages !== entries.length) {\n warnings.push(`Manifest totalImages (${parsed.totalImages}) differs from count (${entries.length})`);\n }\n if (entries.length === 0) warnings.push('Manifest contains 0 image entries');\n for (const [key, value] of entries) validateEntry(key, value, errors, warnings);\n\n return { valid: errors.length === 0, totalEntries: entries.length, errors, warnings };\n}\n\nexport async function autoSyncCheck(imagesDir: string, manifestFile: string): Promise<SyncCheckResult> {\n const [files, loaded] = await Promise.all([scanImageDirectory(imagesDir), readManifestSafe(manifestFile)]);\n const { missingKeys, orphanedKeys } = computeKeyDiff(files, loaded.hashes);\n const inSync = loaded.exists && missingKeys.length === 0 && orphanedKeys.length === 0;\n return { inSync, totalFiles: files.length, totalEntries: Object.keys(loaded.hashes).length, missingKeys, orphanedKeys };\n}\n\nexport async function findMissingBlurHashes(imagesDir: string, manifestFile: string): Promise<string[]> {\n return (await autoSyncCheck(imagesDir, manifestFile)).missingKeys;\n}\n\nexport async function cleanupOrphanedHashes(\n imagesDir: string,\n manifestFile: string,\n options?: { dryRun?: boolean },\n): Promise<CleanupResult> {\n const dryRun = Boolean(options?.dryRun);\n const resolvedPath = path.resolve(manifestFile);\n const [files, loaded] = await Promise.all([scanImageDirectory(imagesDir), readManifestSafe(resolvedPath)]);\n if (!loaded.exists || !loaded.manifest) return { removedCount: 0, removedKeys: [], dryRun };\n\n const { orphanedKeys } = computeKeyDiff(files, loaded.hashes);\n if (!dryRun && orphanedKeys.length > 0) {\n const orphanSet = new Set(orphanedKeys);\n const hashes = Object.fromEntries(Object.entries(loaded.hashes).filter(([k]) => !orphanSet.has(k)));\n const updated: BlurManifestData = { ...loaded.manifest, generatedAt: new Date().toISOString(), totalImages: Object.keys(hashes).length, hashes };\n await fs.writeFile(resolvedPath, `${JSON.stringify(updated, null, 2)}\\n`, 'utf8');\n }\n return { removedCount: orphanedKeys.length, removedKeys: orphanedKeys, dryRun };\n}\n\nexport async function generateHealthReport(imagesDir: string, manifestFile: string): Promise<HealthReport> {\n const [validation, loaded] = await Promise.all([validateBlurHashFile(manifestFile), readManifestSafe(manifestFile)]);\n const errors = [...validation.errors];\n const warnings = [...validation.warnings];\n let files: ScannedImageFile[] = [];\n try {\n files = await scanImageDirectory(imagesDir);\n } catch (err) {\n errors.push(`Failed to scan \"${path.resolve(imagesDir)}\": ${(err as Error).message}`);\n }\n\n const { missingKeys, orphanedKeys } = computeKeyDiff(files, loaded.hashes);\n if (missingKeys.length > 0) warnings.push(`${missingKeys.length} missing image(s)`);\n if (orphanedKeys.length > 0) warnings.push(`${orphanedKeys.length} orphaned entry(ies)`);\n\n const totalImagesOnDisk = files.length;\n const covered = Math.max(0, totalImagesOnDisk - missingKeys.length);\n const coveragePercent =\n totalImagesOnDisk === 0 ? (validation.valid ? 100 : 0) : Number(((covered / totalImagesOnDisk) * 100).toFixed(2));\n\n return {\n validManifest: validation.valid,\n totalImagesOnDisk,\n totalManifestEntries: validation.totalEntries,\n coveragePercent,\n missingKeys,\n orphanedKeys,\n errors,\n warnings,\n };\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;;ACAA,IAAAA,mBAAe;AACf,IAAAC,oBAAiB;AACjB,uBAAwB;;;ACFjB,IAAM,uBAAiC;AAAA,EAC5C;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF;AAqBO,SAAS,mBACd,UACA,UAAgC,CAAC,GACvB;AACV,QAAM,eAAe;AACrB,MAAI,iBAAiB;AACrB,MAAI,aAAa;AAEjB,MAAI;AACF,QAAI,SAAS,WAAW,OAAO,KAAK,SAAS,WAAW,OAAO,GAAG;AAChE,aAAO;AAAA,QACL,gBAAgB;AAAA,QAChB,YAAY;AAAA,QACZ;AAAA,MACF;AAAA,IACF;AAEA,UAAM,WAAW,QAAQ,eAAe;AAExC,QAAI,SAAS,WAAW,SAAS,KAAK,SAAS,WAAW,UAAU,GAAG;AACrE,YAAM,MAAM,IAAI,IAAI,QAAQ;AAC5B,mBAAa;AACb,YAAM,kBAAkB,GAAG,IAAI,MAAM,GAAG,IAAI,QAAQ;AAEpD,iBAAW,WAAW,UAAU;AAC9B,cAAM,QAAQ,gBAAgB,MAAM,OAAO;AAC3C,YAAI,OAAO;AACT,gBAAM,SAAS,MAAM,MAAM,CAAC,EAAE,OAAO,OAAO;AAC5C,gBAAM,WAAW,OAAO,KAAK,GAAG;AAChC,2BAAiB,MAAM,SAAS,QAAQ,QAAQ,EAAE;AAClD;AAAA,QACF;AAAA,MACF;AAEA,UAAI,mBAAmB,UAAU;AAC/B,yBAAiB,IAAI;AAAA,MACvB;AAAA,IACF,OAAO;AACL,uBAAiB,eAAe,MAAM,GAAG,EAAE,CAAC,EAAE,MAAM,GAAG,EAAE,CAAC;AAAA,IAC5D;AAEA,qBAAiB,eAAe,QAAQ,OAAO,GAAG,EAAE,QAAQ,aAAa,EAAE;AAE3E,QAAI,CAAC,eAAe,WAAW,GAAG,GAAG;AACnC,uBAAiB,MAAM;AAAA,IACzB;AAEA,UAAM,kBAAkB,QAAQ,iBAAiB,CAAC,UAAU;AAC5D,eAAW,UAAU,iBAAiB;AACpC,UAAI,eAAe,WAAW,MAAM,GAAG;AACrC,yBAAiB,MAAM,eAAe,MAAM,OAAO,MAAM,EAAE,QAAQ,QAAQ,EAAE;AAAA,MAC/E;AAAA,IACF;AAEA,UAAM,WAAW,eAAe,MAAM,GAAG,EAAE,OAAO,OAAO;AACzD,UAAM,WAAW,SAAS,SAAS,SAAS,CAAC;AAC7C,UAAM,SACJ,SAAS,SAAS,IAAI,SAAS,MAAM,GAAG,EAAE,EAAE,KAAK,GAAG,IAAI;AAC1D,UAAM,SAAS,WAAW,SAAS,YAAY,GAAG,IAAI;AACtD,UAAM,YACJ,YAAY,SAAS,IAAI,SAAS,MAAM,SAAS,CAAC,EAAE,YAAY,IAAI;AAEtE,WAAO;AAAA,MACL;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IACF;AAAA,EACF,QAAQ;AACN,WAAO;AAAA,MACL,gBAAgB;AAAA,MAChB,YAAY;AAAA,MACZ;AAAA,IACF;AAAA,EACF;AACF;;;ACxGA,IAAAC,mBAAe;AACf,IAAAC,oBAAiB;;;ACkDV,IAAM,yBAAyB;AAC/B,IAAM,kCAAkC;AACxC,IAAM,2BAA2B;AACjC,IAAM,6BAA6B;AAEnC,IAAM,sBAAsB;AAC5B,IAAM,sBAAsB;AAC5B,IAAM,wBAAwB;;;AC7C9B,IAAM,wBACX;AAIF,IAAI,eAA6C;AAGjD,SAAS,cAAiB,KAAiB;AACzC,QAAM,SAAS;AACf,SAAQ,UAAU,OAAO,YAAY,SAAY,OAAO,UAAU;AACpE;AAGO,SAAS,YAAmC;AACjD,MAAI,CAAC,cAAc;AACjB,mBAAe,OAAO,OAAO,EAAE;AAAA,MAC7B,CAAC,QAAQ,cAA4B,GAAG;AAAA,MACxC,CAAC,UAAmB;AAClB,uBAAe;AACf,cAAM,IAAI,MAAM,uBAAuB,EAAE,OAAO,MAAM,CAAC;AAAA,MACzD;AAAA,IACF;AAAA,EACF;AACA,SAAO;AACT;;;ACtCA,sBAAyC;;;ACyBlC,SAAS,SAAS,KAAkD;AACzE,QAAM,aAAa,IAAI,QAAQ,KAAK,EAAE;AACtC,QAAM,WACJ,WAAW,WAAW,IAClB,WACG,MAAM,EAAE,EACR,IAAI,CAAC,SAAS,OAAO,IAAI,EACzB,KAAK,EAAE,IACV;AACN,QAAM,SAAS,SAAS,OAAO,GAAG,GAAG,EAAE,MAAM,GAAG,CAAC;AACjD,QAAM,QAAQ,OAAO,SAAS,QAAQ,EAAE;AACxC,MAAI,OAAO,MAAM,KAAK,GAAG;AACvB,WAAO,EAAE,GAAG,KAAK,GAAG,KAAK,GAAG,IAAI;AAAA,EAClC;AACA,SAAO;AAAA,IACL,GAAI,SAAS,KAAM;AAAA,IACnB,GAAI,SAAS,IAAK;AAAA,IAClB,GAAG,QAAQ;AAAA,EACb;AACF;AAEO,SAAS,SAAS,GAAW,GAAW,GAAmB;AAChE,QAAM,QAAQ,CAAC,MAAc,KAAK,IAAI,GAAG,KAAK,IAAI,KAAK,KAAK,MAAM,CAAC,CAAC,CAAC;AACrE,QAAM,QAAS,MAAM,CAAC,KAAK,KAAO,MAAM,CAAC,KAAK,IAAK,MAAM,CAAC;AAC1D,SAAO,IAAI,MAAM,SAAS,EAAE,EAAE,SAAS,GAAG,GAAG,EAAE,YAAY,CAAC;AAC9D;;;ACzCA,eAAsB,+BACpB,QACA,OAA0B,YACT;AACjB,QAAM,QAAQ,MAAM,UAAU;AAC9B,MAAI;AACF,UAAM,WAAW,MAAM,MAAM,MAAM,EAAE,SAAS;AAC9C,UAAM,UAAU;AAChB,UAAM,cACJ,SAAS,SACT,SAAS,WACR,SAAS,QAAQ,WAAW,SAAS,SAAS;AAEjD,QAAI,aAAa;AACf,YAAM,UAAU,MAAM,MAAM,MAAM,EAC/B,OAAO,SAAS,SAAS;AAAA,QACxB,KAAK;AAAA,QACL,QAAQ;AAAA,QACR,oBAAoB;AAAA,MACtB,CAAC,EACA,YAAY,EACZ,IAAI,EACJ,SAAS,EAAE,mBAAmB,KAAK,CAAC;AAEvC,aAAO;AAAA,QACL,QAAQ;AAAA,QACR,QAAQ,KAAK;AAAA,QACb,QAAQ,KAAK;AAAA,QACb;AAAA,MACF;AAAA,IACF;AAEA,UAAM,MAAM,MAAM,MAAM,MAAM,EAC3B,YAAY,EACZ,IAAI,EACJ,SAAS,EAAE,mBAAmB,KAAK,CAAC;AAEvC,WAAO;AAAA,MACL,IAAI;AAAA,MACJ,IAAI,KAAK;AAAA,MACT,IAAI,KAAK;AAAA,MACT;AAAA,IACF;AAAA,EACF,QAAQ;AACN,UAAM,QAAQ,MAAM,MAAM,MAAM,EAC7B,OAAO,IAAI,IAAI,EAAE,KAAK,SAAS,QAAQ,WAAW,CAAC,EACnD,YAAY,EACZ,IAAI,EACJ,SAAS,EAAE,mBAAmB,KAAK,CAAC;AACvC,WAAO,8BAA8B,MAAM,IAAI;AAAA,EACjD;AACF;AAEA,SAAS,sCACP,MACA,OACA,QACA,MACQ;AACR,QAAM,WAAW,oBAAI,IAAoB;AACzC,QAAM,aAAa;AAEnB,QAAM,eAAe;AACrB,QAAM,UAAU,QAAQ,KAAK;AAC7B,QAAM,UAAU,SAAS,KAAK;AAC9B,MAAI,cAAc;AAElB,WAAS,IAAI,GAAG,IAAI,KAAK,QAAQ,KAAK,GAAG;AACvC,UAAM,QAAQ,KAAK,IAAI,CAAC;AACxB,QAAI,UAAU,UAAa,QAAQ,EAAG;AAEtC,UAAMC,KAAI,KAAK,CAAC;AAChB,UAAMC,KAAI,KAAK,IAAI,CAAC;AACpB,UAAMC,KAAI,KAAK,IAAI,CAAC;AACpB,UAAM,cAAcF,KAAIC,KAAIC,MAAK;AACjC,QAAI,aAAa,GAAI;AAErB,UAAM,aAAa,IAAI;AACvB,UAAM,IAAI,aAAa;AACvB,UAAM,IAAI,KAAK,MAAM,aAAa,KAAK;AACvC,UAAM,MAAM,IAAI,WAAW;AAC3B,UAAM,MAAM,IAAI,WAAW;AAC3B,UAAM,qBAAqB,KAAK,KAAK,KAAK,KAAK,KAAK,EAAE;AACtD,QAAI,SAAS;AAEb,QAAI,SAAS,QAAQ;AACnB,YAAM,iBAAiB,KAAK,IAAI,GAAG,KAAK,MAAM,QAAQ,IAAI,CAAC;AAC3D,YAAM,iBAAiB,KAAK,IAAI,GAAG,KAAK,MAAM,SAAS,IAAI,CAAC;AAE5D,YAAM,aAAa,IAAI;AACvB,YAAM,cAAc,KAAK,QAAQ;AACjC,YAAM,YAAY,IAAI;AACtB,YAAM,eAAe,KAAK,SAAS;AAEnC,UAAI,EAAE,cAAc,eAAe,aAAa,eAAe;AAC7D;AAAA,MACF;AAEA,YAAM,wBAAwB,KAAK;AAAA,QACjC;AAAA,QACA;AAAA,QACA,QAAQ,IAAI;AAAA,QACZ,SAAS,IAAI;AAAA,MACf;AACA,YAAM,kBAAkB,KAAK,IAAI,gBAAgB,cAAc,KAAK;AACpE,YAAM,aAAa,IAAI,wBAAwB;AAC/C,eAAS,IAAI,aAAa;AAAA,IAC5B,OAAO;AACL,eAAS,qBAAqB,MAAM,eAAe;AAAA,IACrD;AAEA,UAAM,cAAcF,MAAK,OAAOC,MAAK,OAAOC,MAAK;AACjD,QAAI,aAAa;AACf,qBAAe;AACf;AAAA,IACF;AAEA,UAAM,UAAU,KAAK,MAAMF,KAAI,UAAU,IAAI;AAC7C,UAAM,UAAU,KAAK,MAAMC,KAAI,UAAU,IAAI;AAC7C,UAAM,UAAU,KAAK,MAAMC,KAAI,UAAU,IAAI;AAE7C,UAAM,MAAM,GAAG,OAAO,IAAI,OAAO,IAAI,OAAO;AAC5C,aAAS,IAAI,MAAM,SAAS,IAAI,GAAG,KAAK,KAAK,MAAM;AAAA,EACrD;AAEA,MAAI,CAAC,SAAS,MAAM;AAClB,WAAO,cAAc,IAAI,sBAAsB;AAAA,EACjD;AAEA,MAAI,WAAW;AACf,MAAI,gBAAgB;AACpB,MAAI,mBAAmB;AAEvB,WAAS,QAAQ,CAAC,OAAO,UAAU;AACjC,wBAAoB;AACpB,QAAI,QAAQ,UAAU;AACpB,iBAAW;AACX,sBAAgB;AAAA,IAClB;AAAA,EACF,CAAC;AAED,QAAM,iBACJ,cAAc,KAAK,eAAe,mBAAmB;AACvD,MAAI,gBAAgB;AAClB,WAAO;AAAA,EACT;AAEA,QAAM,CAAC,GAAG,GAAG,CAAC,IAAI,cAAc,MAAM,GAAG,EAAE,IAAI,MAAM;AACrD,SAAO,SAAS,GAAG,GAAG,CAAC;AACzB;AAEA,SAAS,8BAA8B,MAAsB;AAC3D,QAAM,WAAW,oBAAI,IAAoB;AACzC,QAAM,aAAa;AACnB,MAAI,cAAc;AAElB,WAAS,IAAI,GAAG,IAAI,KAAK,QAAQ,KAAK,GAAG;AACvC,UAAM,QAAQ,KAAK,IAAI,CAAC;AACxB,QAAI,UAAU,UAAa,QAAQ,EAAG;AAEtC,UAAMF,KAAI,KAAK,MAAM,KAAK,CAAC,IAAI,UAAU,IAAI;AAC7C,UAAMC,KAAI,KAAK,MAAM,KAAK,IAAI,CAAC,IAAI,UAAU,IAAI;AACjD,UAAMC,KAAI,KAAK,MAAM,KAAK,IAAI,CAAC,IAAI,UAAU,IAAI;AAEjD,QAAIF,MAAK,OAAOC,MAAK,OAAOC,MAAK,KAAK;AACpC,qBAAe;AACf;AAAA,IACF;AAEA,UAAM,MAAM,GAAGF,EAAC,IAAIC,EAAC,IAAIC,EAAC;AAC1B,aAAS,IAAI,MAAM,SAAS,IAAI,GAAG,KAAK,KAAK,CAAC;AAAA,EAChD;AAEA,MAAI,CAAC,SAAS,MAAM;AAClB,WAAO,cAAc,IAAI,sBAAsB;AAAA,EACjD;AAEA,MAAI,WAAW;AACf,MAAI,gBAAgB;AACpB,MAAI,mBAAmB;AAEvB,WAAS,QAAQ,CAAC,OAAO,UAAU;AACjC,wBAAoB;AACpB,QAAI,QAAQ,UAAU;AACpB,iBAAW;AACX,sBAAgB;AAAA,IAClB;AAAA,EACF,CAAC;AAED,QAAM,iBACJ,cAAc,KAAK,eAAe,mBAAmB;AACvD,MAAI,gBAAgB;AAClB,WAAO;AAAA,EACT;AAEA,QAAM,CAAC,GAAG,GAAG,CAAC,IAAI,cAAc,MAAM,GAAG,EAAE,IAAI,MAAM;AACrD,SAAO,SAAS,GAAG,GAAG,CAAC;AACzB;;;ACnMA,eAAsB,4BACpB,QACuB;AACvB,QAAM,QAAQ,MAAM,UAAU;AAC9B,MAAI;AACF,UAAM,SAAS,MAAM,MAAM,MAAM,EAC9B,OAAO,KAAK,KAAK;AAAA,MAChB,KAAK;AAAA,MACL,QAAQ;AAAA,MACR,oBAAoB;AAAA,IACtB,CAAC,EACA,YAAY,EACZ,IAAI,EACJ,SAAS,EAAE,mBAAmB,KAAK,CAAC;AAEvC,WAAO;AAAA,MACL,OAAO;AAAA,MACP,OAAO,KAAK;AAAA,MACZ,OAAO,KAAK;AAAA,IACd;AAAA,EACF,QAAQ;AACN,QAAI;AACF,YAAM,WAAW,MAAM,+BAA+B,QAAQ,MAAM;AACpE,aAAO;AAAA,QACL,KAAK;AAAA,QACL,OAAO;AAAA,QACP,QAAQ;AAAA,QACR,MAAM;AAAA,MACR;AAAA,IACF,QAAQ;AACN,aAAO;AAAA,QACL,KAAK;AAAA,QACL,OAAO;AAAA,QACP,QAAQ;AAAA,QACR,MAAM;AAAA,MACR;AAAA,IACF;AAAA,EACF;AACF;AAEO,SAAS,oBACd,MACA,OACA,QACc;AACd,QAAM,aAAa,KAAK,IAAI,GAAG,KAAK,MAAM,QAAQ,IAAI,CAAC;AACvD,QAAM,aAAa,KAAK,IAAI,GAAG,KAAK,MAAM,SAAS,IAAI,CAAC;AACxD,QAAM,aAAa;AAEnB,QAAM,WAAiD;AAAA,IACrD,KAAK,oBAAI,IAAI;AAAA,IACb,OAAO,oBAAI,IAAI;AAAA,IACf,QAAQ,oBAAI,IAAI;AAAA,IAChB,MAAM,oBAAI,IAAI;AAAA,EAChB;AAEA,QAAM,oBAA6C;AAAA,IACjD,KAAK;AAAA,IACL,OAAO;AAAA,IACP,QAAQ;AAAA,IACR,MAAM;AAAA,EACR;AAEA,QAAM,uBAAuB,CAC3B,MACA,QACA,KACA,kBACG;AACH,QAAI,UAAU,EAAG;AACjB,QAAI,eAAe;AACjB,wBAAkB,IAAI,KAAK;AAC3B;AAAA,IACF;AACA,aAAS,IAAI,EAAE,IAAI,MAAM,SAAS,IAAI,EAAE,IAAI,GAAG,KAAK,KAAK,MAAM;AAAA,EACjE;AAEA,WAAS,IAAI,GAAG,IAAI,KAAK,QAAQ,KAAK,GAAG;AACvC,UAAM,IAAI,KAAK,CAAC;AAChB,UAAM,IAAI,KAAK,IAAI,CAAC;AACpB,UAAM,IAAI,KAAK,IAAI,CAAC;AACpB,UAAM,QAAQ,KAAK,IAAI,CAAC;AAExB,QAAI,UAAU,UAAa,QAAQ,EAAG;AAEtC,UAAM,cAAc,IAAI,IAAI,KAAK;AACjC,QAAI,aAAa,GAAI;AAErB,UAAM,cAAc,KAAK,OAAO,KAAK,OAAO,KAAK;AAEjD,UAAM,aAAa,IAAI;AACvB,UAAM,IAAI,aAAa;AACvB,UAAM,IAAI,KAAK,MAAM,aAAa,KAAK;AAEvC,UAAM,UAAU,KAAK,MAAM,IAAI,UAAU,IAAI;AAC7C,UAAM,UAAU,KAAK,MAAM,IAAI,UAAU,IAAI;AAC7C,UAAM,UAAU,KAAK,MAAM,IAAI,UAAU,IAAI;AAC7C,UAAM,MAAM,GAAG,OAAO,IAAI,OAAO,IAAI,OAAO;AAE5C,UAAM,YAAY,IAAI,aAAa,KAAK,aAAa,KAAK,aAAa;AACvE,QAAI,YAAY,GAAG;AACjB,2BAAqB,OAAO,WAAW,KAAK,WAAW;AAAA,IACzD;AAEA,UAAM,eACJ,KAAK,SAAS,aACV,KAAK,KAAK,SAAS,eAAe,aAClC;AACN,QAAI,eAAe,GAAG;AACpB,2BAAqB,UAAU,cAAc,KAAK,WAAW;AAAA,IAC/D;AAEA,UAAM,aAAa,IAAI,aAAa,KAAK,aAAa,KAAK,aAAa;AACxE,QAAI,aAAa,GAAG;AAClB,2BAAqB,QAAQ,YAAY,KAAK,WAAW;AAAA,IAC3D;AAEA,UAAM,cACJ,KAAK,QAAQ,aACT,KAAK,KAAK,QAAQ,eAAe,aACjC;AACN,QAAI,cAAc,GAAG;AACnB,2BAAqB,SAAS,aAAa,KAAK,WAAW;AAAA,IAC7D;AAAA,EACF;AAEA,SAAO;AAAA,IACL,KAAK,iBAAiB,SAAS,KAAK,kBAAkB,GAAG;AAAA,IACzD,OAAO,iBAAiB,SAAS,OAAO,kBAAkB,KAAK;AAAA,IAC/D,QAAQ,iBAAiB,SAAS,QAAQ,kBAAkB,MAAM;AAAA,IAClE,MAAM,iBAAiB,SAAS,MAAM,kBAAkB,IAAI;AAAA,EAC9D;AACF;AAEA,SAAS,iBACP,KACA,eACQ;AACR,MAAI,CAAC,IAAI,MAAM;AACb,WAAO,gBAAgB,IAAI,sBAAsB;AAAA,EACnD;AAEA,MAAI,gBAAgB;AACpB,MAAI,WAAW;AACf,MAAI,mBAAmB;AAEvB,MAAI,QAAQ,CAAC,OAAO,UAAU;AAC5B,wBAAoB;AACpB,QAAI,QAAQ,UAAU;AACpB,iBAAW;AACX,sBAAgB;AAAA,IAClB;AAAA,EACF,CAAC;AAED,MAAI,CAAC,eAAe;AAClB,WAAO,gBAAgB,IAAI,sBAAsB;AAAA,EACnD;AAEA,QAAM,iBACJ,gBAAgB,MACf,qBAAqB,KACpB,iBAAiB,mBAAmB;AAExC,MAAI,gBAAgB;AAClB,WAAO;AAAA,EACT;AAEA,MAAI,cAAc,WAAW,GAAG,GAAG;AACjC,WAAO,cAAc,YAAY;AAAA,EACnC;AAEA,QAAM,CAAC,GAAG,GAAG,CAAC,IAAI,cAAc,MAAM,GAAG,EAAE,IAAI,MAAM;AACrD,SAAO,SAAS,GAAG,GAAG,CAAC;AACzB;AAEO,SAAS,gBAAgB,YAAkC;AAChE,QAAM,SAAS,OAAO,OAAO,UAAU;AACvC,MAAI,CAAC,OAAO,QAAQ;AAClB,WAAO;AAAA,EACT;AAEA,QAAM,SAAS,OAAO;AAAA,IACpB,CAAC,KAAK,QAAQ;AACZ,YAAM,EAAE,GAAG,GAAG,EAAE,IAAI,SAAS,OAAO,mBAAmB;AACvD,UAAI,KAAK;AACT,UAAI,KAAK;AACT,UAAI,KAAK;AACT,aAAO;AAAA,IACT;AAAA,IACA,EAAE,GAAG,GAAG,GAAG,GAAG,GAAG,EAAE;AAAA,EACrB;AAEA,QAAM,QAAQ,OAAO;AACrB,SAAO,SAAS,OAAO,IAAI,OAAO,OAAO,IAAI,OAAO,OAAO,IAAI,KAAK;AACtE;;;AH7LA,SAAS,kBACP,MACA,OACA,QACA,aAAqB,GACrB,aAAqB,GACb;AACR,QAAM,WAAW,KAAK,IAAI,GAAG,KAAK,IAAI,KAAK,MAAM,UAAU,GAAG,OAAO,CAAC,CAAC;AACvE,QAAM,WAAW,KAAK,IAAI,GAAG,KAAK,IAAI,KAAK,MAAM,UAAU,GAAG,QAAQ,CAAC,CAAC;AACxE,aAAO,gBAAAC;AAAA,IACL,IAAI,kBAAkB,IAAI;AAAA,IAC1B;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF;AACF;AAEA,eAAsB,qBACpB,QACA,aAAqB,GACrB,aAAqB,GACJ;AACjB,QAAM,QAAQ,MAAM,UAAU;AAC9B,QAAM,EAAE,MAAM,KAAK,IAAI,MAAM,MAAM,MAAM,EACtC,OAAO,IAAI,IAAI,EAAE,KAAK,SAAS,CAAC,EAChC,YAAY,EACZ,IAAI,EACJ,SAAS,EAAE,mBAAmB,KAAK,CAAC;AAEvC,SAAO;AAAA,IACL;AAAA,IACA,KAAK;AAAA,IACL,KAAK;AAAA,IACL;AAAA,IACA;AAAA,EACF;AACF;AAEA,eAAsB,wBACpB,QACA,UAA+B,CAAC,GACP;AACzB,QAAM,cAAc,QAAQ,SAAS;AACrC,QAAM,gBACJ,QAAQ,iBAAiB;AAC3B,QAAM,UAAU,QAAQ,WAAW;AACnC,QAAM,WAAW,QAAQ,mBAAmB;AAC5C,QAAM,QAAQ,MAAM,UAAU;AAE9B,MAAI;AACF,QAAI,gBAAgB;AACpB,UAAM,QAAQ,aAAa,KAAK,OAAO,SAAS,QAAQ,GAAG,IAAI,CAAC;AAChE,QAAI,OAAO;AACT,YAAM,gBAAgB,OACnB,SAAS,MAAM,EACf,QAAQ,YAAY,MAAM,EAC1B,QAAQ,gBAAgB,QAAQ;AACnC,sBAAgB,OAAO,KAAK,eAAe,MAAM;AAAA,IACnD;AAEA,UAAM,WAAW,KAAK,IAAI,aAAa,GAAG;AAC1C,UAAM,YAAY,MAAM,MAAM,aAAa,EACxC,OAAO,EACP,OAAO,UAAU,UAAU,EAAE,KAAK,UAAU,oBAAoB,KAAK,CAAC,EACtE,YAAY,EACZ,IAAI,EACJ,SAAS,EAAE,mBAAmB,KAAK,CAAC;AAEvC,UAAM,WAAW;AAAA,MACf,KAAK;AAAA,QACH,OAAO,UAAU,KAAK;AAAA,QACtB,QAAQ,UAAU,KAAK;AAAA,QACvB,UAAU;AAAA,MACZ;AAAA,IACF;AAEA,UAAM,0BAA0B,OAAO,UAAmC;AACxE,YAAM,aAAa,MAAM,MAAM,UAAU,MAAM,QAAQ,EACpD,OAAO,OAAO,OAAO,EAAE,KAAK,UAAU,oBAAoB,KAAK,CAAC,EAChE,KAAK,EAAE,SAAS,QAAQ,GAAG,gBAAgB,KAAK,CAAC,EACjD,SAAS;AACZ,aAAO,0BAA0B,WAAW,SAAS,QAAQ,CAAC;AAAA,IAChE;AAEA,QAAI,UAAU,MAAM,wBAAwB,WAAW;AACvD,QAAI,QAAQ,SAAS,UAAU;AAC7B,gBAAU,MAAM,wBAAwB,aAAa;AAAA,IACvD;AAEA,UAAM,aAAa;AAAA,MACjB,UAAU;AAAA,MACV,UAAU,KAAK;AAAA,MACf,UAAU,KAAK;AAAA,IACjB;AACA,UAAM,QAAQ,gBAAgB,UAAU;AAExC,QAAI;AACJ,QAAI,QAAQ,iBAAiB;AAC3B,UAAI;AACF,cAAM,aACJ,UAAU,KAAK,SAAS,MAAM,UAAU,KAAK,UAAU,KACnD,YACA,MAAM,MAAM,UAAU,MAAM,QAAQ,EACjC,OAAO,IAAI,IAAI,EAAE,KAAK,SAAS,CAAC,EAChC,YAAY,EACZ,IAAI,EACJ,SAAS,EAAE,mBAAmB,KAAK,CAAC;AAC7C,mBAAW;AAAA,UACT,WAAW;AAAA,UACX,WAAW,KAAK;AAAA,UAChB,WAAW,KAAK;AAAA,QAClB;AAAA,MACF,QAAQ;AACN,mBAAW;AAAA,MACb;AAAA,IACF;AAEA,WAAO;AAAA,MACL;AAAA,MACA;AAAA,MACA,GAAI,YAAY,EAAE,SAAS;AAAA,IAC7B;AAAA,EACF,QAAQ;AACN,WAAO,qBAAqB,QAAQ,QAAQ,eAAe;AAAA,EAC7D;AACF;AAIA,eAAe,qBACb,QACA,iBACyB;AACzB,QAAM,QAAQ,MAAM,UAAU;AAC9B,QAAM,aAAa,MAAM,MAAM,MAAM,EAClC,OAAO,IAAI,IAAI,EAAE,KAAK,SAAS,QAAQ,WAAW,CAAC,EACnD,KAAK,CAAG,EACR,KAAK;AAAA,IACJ,SAAS;AAAA,IACT,QAAQ;AAAA,IACR,gBAAgB;AAAA,EAClB,CAAC,EACA,SAAS;AAEZ,QAAM,UAAU,0BAA0B,WAAW,SAAS,QAAQ,CAAC;AACvE,QAAM,aAAa,MAAM,4BAA4B,MAAM;AAC3D,QAAM,QAAQ,gBAAgB,UAAU;AAExC,MAAI;AACJ,MAAI,iBAAiB;AACnB,QAAI;AACF,iBAAW,MAAM,qBAAqB,UAAU;AAAA,IAClD,QAAQ;AACN,iBAAW;AAAA,IACb;AAAA,EACF;AAEA,SAAO,EAAE,SAAS,OAAO,GAAI,YAAY,EAAE,SAAS,EAAG;AACzD;;;AI/KO,IAAM,0BAAN,cAAsC,MAAM;AAAA,EACxC,SAAS;AAAA,EAElB,YACE,UAAkB,wDAClB;AACA,UAAM,OAAO;AACb,SAAK,OAAO;AAAA,EACd;AACF;AAqBA,SAAS,iBAAiB,QAA6B;AACrD,MAAI,QAAQ,kBAAkB,OAAO;AACnC,WAAO,OAAO;AAAA,EAChB;AACA,QAAM,MAAM,IAAI;AAAA,IACd,OAAO,QAAQ,WAAW,WAAW,OAAO,SAAS;AAAA,EACvD;AACA,MAAI,OAAO;AACX,SAAO;AACT;AAEO,IAAM,iBAAN,MAAqB;AAAA,EAK1B,YACmB,aACjB,SACA;AAFiB;AAGjB,QAAI,cAAc,GAAG;AACnB,YAAM,IAAI,MAAM,yCAAyC;AAAA,IAC3D;AACA,QAAI,OAAO,YAAY,UAAU;AAC/B,WAAK,WAAW;AAAA,IAClB,WAAW,WAAW,OAAO,YAAY,UAAU;AACjD,WAAK,WAAW,QAAQ;AAAA,IAC1B;AAAA,EACF;AAAA,EAXmB;AAAA,EALX,cAAc;AAAA,EACL;AAAA,EACA,QAAsB,CAAC;AAAA,EAgBxC,IAAI,SAAiB;AACnB,WAAO,KAAK;AAAA,EACd;AAAA,EAEA,IAAI,UAAkB;AACpB,WAAO,KAAK,MAAM;AAAA,EACpB;AAAA,EAEA,MAAM,IACJ,MACA,UAA+B,CAAC,GACpB;AACZ,UAAM,EAAE,QAAQ,WAAW,QAAQ,iBAAiB,IAAI;AACxD,UAAM,KAAK,QAAQ,MAAM;AAEzB,QAAI;AACJ,QAAI;AACF,uBAAiB,QAAQ,QAAQ,EAAE,KAAK,IAAI;AAAA,IAC9C,SAAS,WAAW;AAClB,WAAK,QAAQ;AACb,YAAM;AAAA,IACR;AAEA,QAAI,WAAW;AACf,UAAM,cAAc,MAAM;AACxB,UAAI,CAAC,UAAU;AACb,mBAAW;AACX,aAAK,QAAQ;AAAA,MACf;AAAA,IACF;AAEA,SAAK,eAAe;AAAA,MAClB,MAAM,YAAY;AAAA,MAClB,CAAC,QAAiB;AAChB,cAAM,UACJ,OAAO,OAAO,QAAQ,YAAY,iBAAiB,MAC9C,IAA6B,cAC9B;AACN,YAAI,WAAW,OAAO,QAAQ,SAAS,YAAY;AACjD,eAAK,QAAQ,KAAK,aAAa,WAAW;AAAA,QAC5C,OAAO;AACL,sBAAY;AAAA,QACd;AAAA,MACF;AAAA,IACF;AAEA,QAAI,gBAAgB;AACpB,QAAI,cAAc,UAAa,YAAY,GAAG;AAC5C,sBAAgB,YAAY,eAAe,WAAW,KAAK;AAAA,IAC7D;AACA,QAAI,QAAQ;AACV,sBAAgB,cAAc,eAAe,MAAM;AAAA,IACrD;AAEA,WAAO;AAAA,EACT;AAAA,EAEQ,QAAQ,QAAqC;AACnD,QAAI,QAAQ,SAAS;AACnB,aAAO,QAAQ,OAAO,iBAAiB,MAAM,CAAC;AAAA,IAChD;AAEA,QAAI,KAAK,cAAc,KAAK,aAAa;AACvC,WAAK,eAAe;AACpB,aAAO,QAAQ,QAAQ;AAAA,IACzB;AAEA,QAAI,KAAK,aAAa,UAAa,KAAK,MAAM,UAAU,KAAK,UAAU;AACrE,aAAO,QAAQ,OAAO,IAAI,wBAAwB,CAAC;AAAA,IACrD;AAEA,WAAO,IAAI,QAAc,CAAC,SAAS,WAAW;AAC5C,YAAM,UAAU,MAAM;AACpB,cAAM,MAAM,KAAK,MAAM,QAAQ,KAAK;AACpC,YAAI,QAAQ,IAAI;AACd,eAAK,MAAM,OAAO,KAAK,CAAC;AAAA,QAC1B;AACA,cAAM,OAAO,iBAAiB,MAAM,CAAC;AAAA,MACvC;AAEA,YAAM,QAAoB;AAAA,QACxB,OAAO,MAAM;AACX,kBAAQ,oBAAoB,SAAS,OAAO;AAC5C,eAAK,eAAe;AACpB,kBAAQ;AAAA,QACV;AAAA,QACA,QAAQ,CAAC,WAAoB;AAC3B,kBAAQ,oBAAoB,SAAS,OAAO;AAC5C,iBAAO,MAAM;AAAA,QACf;AAAA,MACF;AAEA,cAAQ,iBAAiB,SAAS,SAAS,EAAE,MAAM,KAAK,CAAC;AACzD,WAAK,MAAM,KAAK,KAAK;AAAA,IACvB,CAAC;AAAA,EACH;AAAA,EAEQ,UAAgB;AACtB,SAAK,cAAc,KAAK,IAAI,GAAG,KAAK,cAAc,CAAC;AACnD,UAAM,OAAO,KAAK,MAAM,MAAM;AAC9B,UAAM,MAAM;AAAA,EACd;AACF;AAEA,SAAS,cACP,MACA,QACY;AACZ,MAAI,OAAO,SAAS;AAClB,UAAM,MAAM,iBAAiB,MAAM;AACnC,QAAI,cAAc,KAAK;AAAA,MACrB,MAAM;AAAA,MAAC;AAAA,MACP,MAAM;AAAA,MAAC;AAAA,IACT;AACA,WAAO,QAAQ,OAAO,GAAG;AAAA,EAC3B;AAEA,SAAO,IAAI,QAAW,CAAC,SAAS,WAAW;AACzC,UAAM,UAAU,MAAM;AACpB,YAAM,MAAM,iBAAiB,MAAM;AACnC,UAAI,cAAc,KAAK;AAAA,QACrB,MAAM;AAAA,QAAC;AAAA,QACP,MAAM;AAAA,QAAC;AAAA,MACT;AACA,aAAO,GAAG;AAAA,IACZ;AAEA,WAAO,iBAAiB,SAAS,SAAS,EAAE,MAAM,KAAK,CAAC;AACxD,SAAK;AAAA,MACH,CAAC,QAAQ;AACP,eAAO,oBAAoB,SAAS,OAAO;AAC3C,gBAAQ,GAAG;AAAA,MACb;AAAA,MACA,CAAC,QAAQ;AACP,eAAO,oBAAoB,SAAS,OAAO;AAC3C,eAAO,GAAG;AAAA,MACZ;AAAA,IACF;AAAA,EACF,CAAC;AACH;AAEA,eAAsB,YACpB,MACA,WACA,OACY;AACZ,MAAI,gBAAsD;AAC1D,QAAM,cAAc,KAAK;AAAA,IACvB,MAAM;AAAA,IAAC;AAAA,IACP,MAAM;AAAA,IAAC;AAAA,EACT;AAEA,MAAI;AACF,WAAO,MAAM,QAAQ,KAAK;AAAA,MACxB;AAAA,MACA,IAAI,QAAe,CAAC,GAAG,WAAW;AAChC,wBAAgB,WAAW,MAAM;AAC/B,gBAAM,MAAM,IAAI;AAAA,YACd,GAAG,KAAK,oBAAoB,SAAS;AAAA,UACvC;AACA,cAAI,cAAc;AAClB,iBAAO,GAAG;AAAA,QACZ,GAAG,SAAS;AAAA,MACd,CAAC;AAAA,IACH,CAAC;AAAA,EACH,UAAE;AACA,QAAI,eAAe;AACjB,mBAAa,aAAa;AAAA,IAC5B;AAAA,EACF;AACF;;;ACtOA,sBAAe;AACf,uBAAiB;AAEV,IAAM,6BAA6B,oBAAI,IAAI;AAAA,EAChD;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,CAAC;AAEM,IAAM,sBAAsB,oBAAI,IAAI,CAAC,gBAAgB,QAAQ,MAAM,CAAC;AAc3E,eAAsB,mBACpB,WAC6B;AAC7B,QAAM,UAAU,iBAAAC,QAAK,QAAQ,SAAS;AACtC,QAAM,YAAY,MAAM,gBAAAC,QAAG,KAAK,OAAO;AAEvC,MAAI,CAAC,UAAU,YAAY,GAAG;AAC5B,UAAM,IAAI,MAAM,2CAA2C,OAAO,EAAE;AAAA,EACtE;AAEA,QAAM,UAA8B,CAAC;AAErC,iBAAe,KAAK,YAAmC;AACrD,UAAM,UAAU,MAAM,gBAAAA,QAAG,QAAQ,YAAY,EAAE,eAAe,KAAK,CAAC;AAEpE,eAAW,SAAS,SAAS;AAC3B,YAAM,WAAW,iBAAAD,QAAK,KAAK,YAAY,MAAM,IAAI;AAEjD,UAAI,MAAM,YAAY,GAAG;AACvB,YAAI,oBAAoB,IAAI,MAAM,IAAI,GAAG;AACvC;AAAA,QACF;AACA,cAAM,KAAK,QAAQ;AACnB;AAAA,MACF;AAEA,UAAI,CAAC,MAAM,OAAO,GAAG;AACnB;AAAA,MACF;AAEA,YAAM,MAAM,iBAAAA,QAAK,QAAQ,MAAM,IAAI,EAAE,YAAY;AACjD,UAAI,CAAC,2BAA2B,IAAI,GAAG,GAAG;AACxC;AAAA,MACF;AAEA,YAAM,YAAY,MAAM,gBAAAC,QAAG,KAAK,QAAQ;AACxC,YAAM,UAAU,iBAAAD,QACb,SAAS,SAAS,QAAQ,EAC1B,MAAM,iBAAAA,QAAK,GAAG,EACd,KAAK,GAAG,EACR,QAAQ,OAAO,GAAG;AACrB,YAAM,cAAc,QAAQ,WAAW,GAAG,IAAI,UAAU,IAAI,OAAO;AAEnE,cAAQ,KAAK;AAAA,QACX,cAAc;AAAA,QACd;AAAA,QACA,MAAM,UAAU;AAAA,QAChB,SAAS,UAAU;AAAA,MACrB,CAAC;AAAA,IACH;AAAA,EACF;AAEA,QAAM,KAAK,OAAO;AAClB,UAAQ,KAAK,CAAC,GAAG,MAAM,EAAE,YAAY,cAAc,EAAE,WAAW,CAAC;AAEjE,SAAO;AACT;;;ARxDA,IAAM,oBAAoB;AAC1B,IAAM,mBAAmB;AAEzB,eAAe,mBACb,cAC4C;AAC5C,MAAI;AACF,UAAM,SAAkB,KAAK,MAAM,MAAM,iBAAAE,QAAG,SAAS,cAAc,MAAM,CAAC;AAC1E,QACE,UACA,OAAO,WAAW,YAClB,CAAC,MAAM,QAAQ,MAAM,KACrB,YAAY,UACZ,OAAO,UACP,OAAO,OAAO,WAAW,YACzB,CAAC,MAAM,QAAQ,OAAO,MAAM,GAC5B;AACA,aAAO,OAAO;AAAA,IAChB;AACA,WAAO,CAAC;AAAA,EACV,QAAQ;AACN,WAAO,CAAC;AAAA,EACV;AACF;AAEA,SAAS,oBACP,OACA,MACA,eAC4B;AAC5B,MAAI,CAAC,SAAS,OAAO,UAAU,SAAU,QAAO;AAChD,MAAI,CAAC,MAAM,QAAQ,CAAC,MAAM,SAAS,CAAC,MAAM,UAAU,MAAO,QAAO;AAClE,MAAI,CAAC,MAAM,YAAY,CAAC,MAAM,SAAS,QAAQ,CAAC,MAAM,SAAS,SAAS;AACtE,WAAO;AAAA,EACT;AACA,MAAI,MAAM,SAAS,SAAS,OAAO,KAAK,IAAI,GAAG;AAC7C,WAAO;AAAA,EACT;AACA,MAAI,MAAM,SAAS,YAAY,OAAO,KAAK,MAAM,KAAK,OAAO,CAAC,GAAG;AAC/D,WAAO;AAAA,EACT;AACA,MAAI,iBAAiB,CAAC,MAAM,SAAS,OAAQ,QAAO;AACpD,SAAO;AACT;AAEA,eAAe,uBACb,QACA,OAC8C;AAC9C,QAAM,QAAQ,MAAM,UAAU;AAC9B,QAAM,WAAW,MAAM,MAAM,MAAM,EAAE,SAAS;AAC9C,MAAI,EAAE,OAAO,OAAO,IAAI;AAExB,OAAK,CAAC,SAAS,CAAC,WAAW,OAAO;AAChC,UAAM,UAAU,OAAO,SAAS,MAAM;AACtC,UAAM,QAAQ,QAAQ;AAAA,MACpB;AAAA,IACF;AACA,QAAI,OAAO;AACT,cAAQ,KAAK,MAAM,OAAO,WAAW,MAAM,CAAC,CAAC,CAAC;AAC9C,eAAS,KAAK,MAAM,OAAO,WAAW,MAAM,CAAC,CAAC,CAAC;AAAA,IACjD;AAAA,EACF;AAEA,SAAO,EAAE,OAAO,OAAO;AACzB;AAMA,eAAsB,qBACpB,WACA,cACA,UAAmC,CAAC,GACH;AACjC,QAAM,aAAa,kBAAAC,QAAK,QAAQ,YAAY;AAC5C,QAAM,cAAc,KAAK,IAAI,GAAG,KAAK,MAAM,QAAQ,eAAe,CAAC,CAAC;AACpE,QAAM,gBAAgB,QAAQ,iBAAiB;AAC/C,QAAM,QAAQ,QAAQ,QAAQ,KAAK;AAEnC,QAAM,QAAQ,MAAM,mBAAmB,SAAS;AAChD,QAAM,iBAAiB,QAAQ,CAAC,IAAI,MAAM,mBAAmB,UAAU;AACvE,QAAM,YAAY,IAAI,eAAe,WAAW;AAChD,QAAM,mBAAmB,oBAAI,IAA+B;AAE5D,MAAI,YAAY;AAChB,MAAI,SAAS;AACb,MAAI,SAAS;AACb,MAAI,OAAO;AACX,QAAM,QAAQ,MAAM;AAEpB,QAAM,QAAQ;AAAA,IACZ,MAAM;AAAA,MAAI,CAAC,SACT,UAAU,IAAI,YAAY;AACxB,cAAM,QAAQ,QAAQ,SAAY,eAAe,KAAK,WAAW;AACjE,YAAI;AACF,cAAI,oBAAoB,OAAO,MAAM,aAAa,GAAG;AACnD,6BAAiB,IAAI,KAAK,aAAa,KAAK;AAC5C,sBAAU;AACV;AAAA,UACF;AAEA,gBAAM,SAAS,MAAM,iBAAAD,QAAG,SAAS,KAAK,YAAY;AAClD,gBAAM,QAAQ,KAAK,YAAY,YAAY,EAAE,SAAS,MAAM;AAC5D,gBAAM,EAAE,OAAO,OAAO,IAAI,MAAM,uBAAuB,QAAQ,KAAK;AAEpE,gBAAM,QAAQ,MAAM,wBAAwB,QAAQ;AAAA,YAClD,OAAO;AAAA,YACP,eAAe;AAAA,YACf,iBAAiB;AAAA,UACnB,CAAC;AACD,gBAAM,SAAS,gBACX,MAAM,wBAAwB,QAAQ;AAAA,YACpC,OAAO;AAAA,YACP,eAAe;AAAA,YACf,iBAAiB;AAAA,UACnB,CAAC,IACD;AAEJ,gBAAM,OACJ,MAAM,YACL,MAAM,qBAAqB,MAAM,EAAE,MAAM,MAAM,iBAAiB;AAEnE,2BAAiB,IAAI,KAAK,aAAa;AAAA,YACrC;AAAA,YACA,UAAU;AAAA,cACR,OAAO,MAAM;AAAA,cACb,GAAI,SAAS,EAAE,QAAQ,OAAO,QAAQ,IAAI,CAAC;AAAA,YAC7C;AAAA,YACA,OAAO,MAAM;AAAA,YACb,GAAI,QAAQ,EAAE,MAAM,IAAI,CAAC;AAAA,YACzB,GAAI,SAAS,EAAE,OAAO,IAAI,CAAC;AAAA,YAC3B,UAAU;AAAA,cACR,MAAM,OAAO,KAAK,IAAI;AAAA,cACtB,SAAS,OAAO,KAAK,MAAM,KAAK,OAAO,CAAC;AAAA,YAC1C;AAAA,UACF,CAAC;AACD,uBAAa;AAAA,QACf,SAAS,KAAK;AACZ,oBAAU;AACV,gBAAM,WAAW,eAAe,KAAK,WAAW;AAChD,cAAI,UAAU;AACZ,6BAAiB,IAAI,KAAK,aAAa,QAAQ;AAAA,UACjD;AACA,gBAAM,SAAS,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG;AAC9D,kBAAQ;AAAA,YACN,wDAAwD,KAAK,WAAW,MAAM,MAAM;AAAA,UACtF;AAAA,QACF,UAAE;AACA,kBAAQ;AACR,kBAAQ,aAAa,MAAM,OAAO,KAAK,WAAW;AAAA,QACpD;AAAA,MACF,CAAC;AAAA,IACH;AAAA,EACF;AAEA,QAAM,gBAAmD,CAAC;AAC1D,aAAW,QAAQ,OAAO;AACxB,UAAM,QAAQ,iBAAiB,IAAI,KAAK,WAAW;AACnD,QAAI,MAAO,eAAc,KAAK,WAAW,IAAI;AAAA,EAC/C;AAEA,QAAM,WAA6B;AAAA,IACjC,SAAS;AAAA,IACT,cAAa,oBAAI,KAAK,GAAE,YAAY;AAAA,IACpC,aAAa,OAAO,KAAK,aAAa,EAAE;AAAA,IACxC,iBAAiB;AAAA,IACjB,cAAc;AAAA,IACd,cAAc;AAAA,IACd,QAAQ;AAAA,EACV;AAEA,QAAM,iBAAAA,QAAG,MAAM,kBAAAC,QAAK,QAAQ,UAAU,GAAG,EAAE,WAAW,KAAK,CAAC;AAC5D,QAAM,iBAAAD,QAAG,UAAU,YAAY,GAAG,KAAK,UAAU,UAAU,MAAM,CAAC,CAAC;AAAA,GAAM,MAAM;AAE/E,SAAO,EAAE,UAAU,WAAW,QAAQ,QAAQ,WAAW;AAC3D;;;AS3MA,IAAAE,mBAAe;AACf,IAAAC,oBAAiB;AAqCjB,IAAM,eAAe;AAErB,SAAS,cAAc,OAAkD;AACvE,SAAO,QAAQ,KAAK,KAAK,OAAO,UAAU,YAAY,CAAC,MAAM,QAAQ,KAAK;AAC5E;AAEA,SAAS,gBAAgB,KAAqB;AAC5C,SAAO,mBAAmB,KAAK,EAAE,eAAe,CAAC,EAAE,CAAC,EAAE;AACxD;AAEA,SAAS,eAAe,OAAiC;AACvD,SAAO,EAAE,OAAO,OAAO,cAAc,GAAG,QAAQ,CAAC,KAAK,GAAG,UAAU,CAAC,EAAE;AACxE;AAEA,SAAS,eAAe,OAAiC;AACvD,SAAO,OAAO,UAAU,YAAY,MAAM,WAAW,aAAa;AACpE;AAEA,SAAS,eACP,OACA,QACmD;AACnD,QAAM,WAAW,IAAI,IAAI,MAAM,IAAI,CAAC,MAAM,EAAE,WAAW,CAAC;AACxD,QAAM,eAAe,OAAO,KAAK,MAAM;AACvC,QAAM,gBAAgB,IAAI,IAAI,aAAa,IAAI,eAAe,CAAC;AAC/D,QAAM,cAAc,MACjB,IAAI,CAAC,MAAM,EAAE,WAAW,EACxB,OAAO,CAAC,MAAM,EAAE,KAAK,WAAW,CAAC,cAAc,IAAI,CAAC,CAAC;AACxD,QAAM,eAAe,aAAa;AAAA,IAChC,CAAC,MAAM,CAAC,SAAS,IAAI,CAAC,KAAK,CAAC,SAAS,IAAI,gBAAgB,CAAC,CAAC;AAAA,EAC7D;AACA,SAAO,EAAE,aAAa,aAAa;AACrC;AAEA,eAAe,iBAAiB,cAI7B;AACD,MAAI;AACF,UAAM,SAAkB,KAAK,MAAM,MAAM,iBAAAC,QAAG,SAAS,kBAAAC,QAAK,QAAQ,YAAY,GAAG,MAAM,CAAC;AACxF,QAAI,cAAc,MAAM,KAAK,cAAc,OAAO,MAAM,GAAG;AACzD,aAAO;AAAA,QACL,QAAQ;AAAA,QACR,UAAU;AAAA,QACV,QAAQ,OAAO;AAAA,MACjB;AAAA,IACF;AACA,WAAO,EAAE,QAAQ,MAAM,QAAQ,CAAC,EAAE;AAAA,EACpC,SAAS,KAAK;AACZ,WAAO,EAAE,QAAS,IAA8B,SAAS,UAAU,QAAQ,CAAC,EAAE;AAAA,EAChF;AACF;AAEA,SAAS,cAAc,KAAa,OAAgB,QAAkB,UAA0B;AAC9F,MAAI,CAAC,IAAI,WAAW,GAAG,EAAG,UAAS,KAAK,cAAc,GAAG,yBAAyB;AAClF,MAAI,CAAC,cAAc,KAAK,GAAG;AACzB,WAAO,KAAK,UAAU,GAAG,qBAAqB;AAC9C;AAAA,EACF;AACA,QAAM,QAAQ;AACd,MAAI,OAAO,MAAM,SAAS,YAAY,MAAM,KAAK,KAAK,EAAE,SAAS,GAAG;AAClE,WAAO,KAAK,UAAU,GAAG,0CAA0C;AAAA,EACrE;AACA,MAAI,OAAO,MAAM,UAAU,YAAY,CAAC,aAAa,KAAK,MAAM,KAAK,GAAG;AACtE,WAAO,KAAK,UAAU,GAAG,sCAAsC;AAAA,EACjE;AACA,MAAI,CAAC,cAAc,MAAM,QAAQ,GAAG;AAClC,WAAO,KAAK,UAAU,GAAG,gCAAgC;AACzD;AAAA,EACF;AACA,MAAI,CAAC,eAAe,MAAM,SAAS,KAAK,GAAG;AACzC,WAAO,KAAK,UAAU,GAAG,8CAA8C;AAAA,EACzE;AACA,MAAI,MAAM,SAAS,WAAW,UAAa,CAAC,eAAe,MAAM,SAAS,MAAM,GAAG;AACjF,WAAO,KAAK,UAAU,GAAG,0CAA0C;AAAA,EACrE;AACF;AAEA,eAAsB,qBAAqB,cAAiD;AAC1F,QAAM,eAAe,kBAAAA,QAAK,QAAQ,YAAY;AAC9C,MAAI;AACJ,MAAI;AACF,UAAM,MAAM,iBAAAD,QAAG,SAAS,cAAc,MAAM;AAAA,EAC9C,QAAQ;AACN,WAAO,eAAe,0CAA0C,YAAY,EAAE;AAAA,EAChF;AACA,MAAI;AACJ,MAAI;AACF,aAAS,KAAK,MAAM,GAAG;AAAA,EACzB,SAAS,KAAK;AACZ,WAAO,eAAe,6BAA8B,IAAc,OAAO,EAAE;AAAA,EAC7E;AACA,MAAI,CAAC,cAAc,MAAM,EAAG,QAAO,eAAe,qCAAqC;AACvF,MAAI,CAAC,cAAc,OAAO,MAAM,EAAG,QAAO,eAAe,6CAA6C;AAEtG,QAAM,SAAmB,CAAC;AAC1B,QAAM,WAAqB,CAAC;AAC5B,MAAI,OAAO,OAAO,YAAY,YAAY,CAAC,OAAO,QAAQ,KAAK,GAAG;AAChE,aAAS,KAAK,wCAAwC;AAAA,EACxD;AACA,MAAI,OAAO,OAAO,gBAAgB,YAAY,OAAO,MAAM,KAAK,MAAM,OAAO,WAAW,CAAC,GAAG;AAC1F,aAAS,KAAK,yDAAyD;AAAA,EACzE;AAEA,QAAM,UAAU,OAAO,QAAQ,OAAO,MAAM;AAC5C,MAAI,OAAO,OAAO,gBAAgB,YAAY,OAAO,gBAAgB,QAAQ,QAAQ;AACnF,aAAS,KAAK,yBAAyB,OAAO,WAAW,yBAAyB,QAAQ,MAAM,GAAG;AAAA,EACrG;AACA,MAAI,QAAQ,WAAW,EAAG,UAAS,KAAK,mCAAmC;AAC3E,aAAW,CAAC,KAAK,KAAK,KAAK,QAAS,eAAc,KAAK,OAAO,QAAQ,QAAQ;AAE9E,SAAO,EAAE,OAAO,OAAO,WAAW,GAAG,cAAc,QAAQ,QAAQ,QAAQ,SAAS;AACtF;AAEA,eAAsB,cAAc,WAAmB,cAAgD;AACrG,QAAM,CAAC,OAAO,MAAM,IAAI,MAAM,QAAQ,IAAI,CAAC,mBAAmB,SAAS,GAAG,iBAAiB,YAAY,CAAC,CAAC;AACzG,QAAM,EAAE,aAAa,aAAa,IAAI,eAAe,OAAO,OAAO,MAAM;AACzE,QAAM,SAAS,OAAO,UAAU,YAAY,WAAW,KAAK,aAAa,WAAW;AACpF,SAAO,EAAE,QAAQ,YAAY,MAAM,QAAQ,cAAc,OAAO,KAAK,OAAO,MAAM,EAAE,QAAQ,aAAa,aAAa;AACxH;AAEA,eAAsB,sBAAsB,WAAmB,cAAyC;AACtG,UAAQ,MAAM,cAAc,WAAW,YAAY,GAAG;AACxD;AAEA,eAAsB,sBACpB,WACA,cACA,SACwB;AACxB,QAAM,SAAS,QAAQ,SAAS,MAAM;AACtC,QAAM,eAAe,kBAAAC,QAAK,QAAQ,YAAY;AAC9C,QAAM,CAAC,OAAO,MAAM,IAAI,MAAM,QAAQ,IAAI,CAAC,mBAAmB,SAAS,GAAG,iBAAiB,YAAY,CAAC,CAAC;AACzG,MAAI,CAAC,OAAO,UAAU,CAAC,OAAO,SAAU,QAAO,EAAE,cAAc,GAAG,aAAa,CAAC,GAAG,OAAO;AAE1F,QAAM,EAAE,aAAa,IAAI,eAAe,OAAO,OAAO,MAAM;AAC5D,MAAI,CAAC,UAAU,aAAa,SAAS,GAAG;AACtC,UAAM,YAAY,IAAI,IAAI,YAAY;AACtC,UAAM,SAAS,OAAO,YAAY,OAAO,QAAQ,OAAO,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,UAAU,IAAI,CAAC,CAAC,CAAC;AAClG,UAAM,UAA4B,EAAE,GAAG,OAAO,UAAU,cAAa,oBAAI,KAAK,GAAE,YAAY,GAAG,aAAa,OAAO,KAAK,MAAM,EAAE,QAAQ,OAAO;AAC/I,UAAM,iBAAAD,QAAG,UAAU,cAAc,GAAG,KAAK,UAAU,SAAS,MAAM,CAAC,CAAC;AAAA,GAAM,MAAM;AAAA,EAClF;AACA,SAAO,EAAE,cAAc,aAAa,QAAQ,aAAa,cAAc,OAAO;AAChF;AAEA,eAAsB,qBAAqB,WAAmB,cAA6C;AACzG,QAAM,CAAC,YAAY,MAAM,IAAI,MAAM,QAAQ,IAAI,CAAC,qBAAqB,YAAY,GAAG,iBAAiB,YAAY,CAAC,CAAC;AACnH,QAAM,SAAS,CAAC,GAAG,WAAW,MAAM;AACpC,QAAM,WAAW,CAAC,GAAG,WAAW,QAAQ;AACxC,MAAI,QAA4B,CAAC;AACjC,MAAI;AACF,YAAQ,MAAM,mBAAmB,SAAS;AAAA,EAC5C,SAAS,KAAK;AACZ,WAAO,KAAK,mBAAmB,kBAAAC,QAAK,QAAQ,SAAS,CAAC,MAAO,IAAc,OAAO,EAAE;AAAA,EACtF;AAEA,QAAM,EAAE,aAAa,aAAa,IAAI,eAAe,OAAO,OAAO,MAAM;AACzE,MAAI,YAAY,SAAS,EAAG,UAAS,KAAK,GAAG,YAAY,MAAM,mBAAmB;AAClF,MAAI,aAAa,SAAS,EAAG,UAAS,KAAK,GAAG,aAAa,MAAM,sBAAsB;AAEvF,QAAM,oBAAoB,MAAM;AAChC,QAAM,UAAU,KAAK,IAAI,GAAG,oBAAoB,YAAY,MAAM;AAClE,QAAM,kBACJ,sBAAsB,IAAK,WAAW,QAAQ,MAAM,IAAK,QAAS,UAAU,oBAAqB,KAAK,QAAQ,CAAC,CAAC;AAElH,SAAO;AAAA,IACL,eAAe,WAAW;AAAA,IAC1B;AAAA,IACA,sBAAsB,WAAW;AAAA,IACjC;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF;AACF;;;AXvMA,IAAM,cACmC,QAAgB,SAAS,IAC5D,UACA;AAEN,IAAM,qBAAqB;AAC3B,IAAM,wBAAwB;AAE9B,SAAS,kBACP,QACA,UACkD;AAClD,QAAM,aAAa;AAAA,IACjB;AAAA,IACA,mBAAmB,QAAQ,EAAE;AAAA,IAC7B,mBAAmB,UAAU,EAAE,eAAe,CAAC,EAAE,CAAC,EAAE;AAAA,EACtD;AACA,aAAW,aAAa,YAAY;AAClC,QAAI,OAAO,SAAS,EAAG,QAAO,EAAE,KAAK,WAAW,OAAO,OAAO,SAAS,EAAE;AACzE,UAAM,UAAU,UAAU,QAAQ,QAAQ,EAAE;AAC5C,QAAI,OAAO,OAAO,EAAG,QAAO,EAAE,KAAK,SAAS,OAAO,OAAO,OAAO,EAAE;AAAA,EACrE;AACA,SAAO;AACT;AAEO,SAAS,mBAA4B;AAC1C,QAAM,UAAU,IAAI,yBAAQ;AAE5B,UACG,KAAK,gBAAgB,EACrB,YAAY,yDAAyD,EACrE,QAAQ,WAAW;AAEtB,UACG,QAAQ,UAAU,EAClB,YAAY,kEAAkE,EAC9E,OAAO,mBAAmB,2BAA2B,kBAAkB,EACvE,OAAO,oBAAoB,6BAA6B,qBAAqB,EAC7E,OAAO,eAAe,+CAA+C,KAAK,EAC1E,OAAO,yBAAyB,mCAAmC,GAAG,EACtE,OAAO,eAAe,8CAA8C,EACpE;AAAA,IACC,OAAO,SAMD;AACJ,YAAM,cAAc,KAAK,IAAI,GAAG,OAAO,SAAS,KAAK,aAAa,EAAE,KAAK,CAAC;AAC1E,YAAM,SAAS,MAAM,qBAAqB,KAAK,KAAK,KAAK,KAAK;AAAA,QAC5D;AAAA,QACA,OAAO,QAAQ,KAAK,KAAK;AAAA,QACzB,eAAe,KAAK;AAAA,MACtB,CAAC;AACD,cAAQ;AAAA,QACN,aAAa,OAAO,UAAU,YAAY,OAAO,SAAS,WAAW,gBAAgB,OAAO,SAAS,aAAa,OAAO,MAAM,aAAa,OAAO,MAAM;AAAA,MAC3J;AAAA,IACF;AAAA,EACF;AAEF,UACG,QAAQ,UAAU,EAClB,YAAY,oCAAoC,EAChD,OAAO,qBAAqB,sBAAsB,qBAAqB,EACvE,OAAO,OAAO,SAA2B;AACxC,UAAM,SAAS,MAAM,qBAAqB,KAAK,IAAI;AACnD,eAAW,WAAW,OAAO,SAAU,SAAQ,KAAK,YAAY,OAAO,EAAE;AACzE,QAAI,CAAC,OAAO,OAAO;AACjB,iBAAW,SAAS,OAAO,OAAQ,SAAQ,MAAM,UAAU,KAAK,EAAE;AAClE,cAAQ,WAAW;AACnB;AAAA,IACF;AACA,YAAQ,IAAI,sBAAsB,OAAO,YAAY,YAAY;AAAA,EACnE,CAAC;AAEH,UACG,QAAQ,MAAM,EACd,YAAY,4DAA4D,EACxE,OAAO,mBAAmB,2BAA2B,kBAAkB,EACvE,OAAO,qBAAqB,sBAAsB,qBAAqB,EACvE,OAAO,SAAS,4EAA4E,KAAK,EACjG,OAAO,OAAO,SAAuD;AACpE,UAAM,OAAO,MAAM,cAAc,KAAK,KAAK,KAAK,IAAI;AACpD,QAAI,KAAK,QAAQ;AACf,cAAQ,IAAI,YAAY,KAAK,YAAY,YAAY;AACrD;AAAA,IACF;AACA,YAAQ;AAAA,MACN,gBAAgB,KAAK,YAAY,MAAM,aAAa,KAAK,aAAa,MAAM;AAAA,IAC9E;AACA,QAAI,KAAK,KAAK;AACZ,UAAI,KAAK,YAAY,SAAS,GAAG;AAC/B,cAAM,SAAS,MAAM,qBAAqB,KAAK,KAAK,KAAK,IAAI;AAC7D,gBAAQ;AAAA,UACN,+BAA+B,OAAO,SAAS,aAAa,OAAO,MAAM,sBAAsB,KAAK,aAAa,MAAM;AAAA,QACzH;AACA;AAAA,MACF;AACA,UAAI,KAAK,aAAa,SAAS,GAAG;AAChC,cAAM,UAAU,MAAM,sBAAsB,KAAK,KAAK,KAAK,IAAI;AAC/D,gBAAQ;AAAA,UACN,4BAA4B,QAAQ,YAAY;AAAA,QAClD;AACA;AAAA,MACF;AAAA,IACF;AACA,QAAI,CAAC,KAAK,IAAK,SAAQ,WAAW;AAAA,EACpC,CAAC;AAEH,UACG,QAAQ,SAAS,EACjB,YAAY,gDAAgD,EAC5D,OAAO,mBAAmB,2BAA2B,kBAAkB,EACvE,OAAO,qBAAqB,sBAAsB,qBAAqB,EACvE,OAAO,aAAa,iDAAiD,KAAK,EAC1E,OAAO,OAAO,SAA0D;AACvE,UAAM,SAAS,MAAM,sBAAsB,KAAK,KAAK,KAAK,MAAM;AAAA,MAC9D,QAAQ,QAAQ,KAAK,MAAM;AAAA,IAC7B,CAAC;AACD,UAAM,SAAS,OAAO,SAAS,2BAA2B;AAC1D,YAAQ,IAAI,GAAG,MAAM,IAAI,OAAO,YAAY,oBAAoB;AAAA,EAClE,CAAC;AAEH,UACG,QAAQ,QAAQ,EAChB,YAAY,8DAA8D,EAC1E,OAAO,mBAAmB,2BAA2B,kBAAkB,EACvE,OAAO,qBAAqB,sBAAsB,qBAAqB,EACvE,OAAO,OAAO,SAAwC;AACrD,UAAM,SAAS,MAAM,qBAAqB,KAAK,KAAK,KAAK,IAAI;AAC7D,YAAQ;AAAA,MACN,iBAAiB,OAAO,aAAa,cAAc,OAAO,eAAe,MAAM,OAAO,oBAAoB,cAAc,OAAO,iBAAiB;AAAA,IAClJ;AACA,eAAW,WAAW,OAAO,SAAU,SAAQ,KAAK,YAAY,OAAO,EAAE;AACzE,eAAW,SAAS,OAAO,OAAQ,SAAQ,MAAM,UAAU,KAAK,EAAE;AAClE,QAAI,CAAC,OAAO,iBAAiB,OAAO,OAAO,SAAS,GAAG;AACrD,cAAQ,WAAW;AAAA,IACrB;AAAA,EACF,CAAC;AAEH,UACG,QAAQ,oBAAoB,EAC5B,YAAY,uDAAuD,EACnE,OAAO,qBAAqB,sBAAsB,qBAAqB,EACvE,OAAO,OAAO,UAAkB,SAA2B;AAC1D,UAAM,MAAM,MAAM,iBAAAC,QAAG,SAAS,kBAAAC,QAAK,QAAQ,KAAK,IAAI,GAAG,MAAM;AAC7D,UAAM,SAAS,KAAK,MAAM,GAAG;AAC7B,UAAM,QAAQ,OAAO,SAAS,kBAAkB,OAAO,QAAQ,QAAQ,IAAI;AAC3E,QAAI,CAAC,OAAO;AACV,cAAQ,MAAM,4BAA4B,QAAQ,EAAE;AACpD,cAAQ,WAAW;AACnB;AAAA,IACF;AACA,YAAQ;AAAA,MACN,KAAK;AAAA,QACH;AAAA,UACE,KAAK,MAAM;AAAA,UACX,MAAM,MAAM,MAAM;AAAA,UAClB,OAAO,MAAM,MAAM;AAAA,UACnB,OAAO,MAAM,MAAM;AAAA,UACnB,QAAQ,MAAM,MAAM;AAAA,UACpB,UAAU;AAAA,YACR,YAAY,MAAM,MAAM,SAAS,MAAM;AAAA,YACvC,aAAa,MAAM,MAAM,SAAS,QAAQ,UAAU;AAAA,YACpD,WAAW,QAAQ,MAAM,MAAM,SAAS,MAAM;AAAA,UAChD;AAAA,UACA,UAAU,MAAM,MAAM;AAAA,QACxB;AAAA,QACA;AAAA,QACA;AAAA,MACF;AAAA,IACF;AAAA,EACF,CAAC;AAEH,SAAO;AACT;;;ADxLA,IAAM,kBAAkB,oBAAI,IAAI;AAAA,EAC9B;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,CAAC;AAED,eAAsB,OAAO,MAAgC;AAC3D,QAAM,UAAU,iBAAiB;AACjC,MAAI,CAAC,MAAM;AACT,UAAM,QAAQ,WAAW,QAAQ,IAAI;AACrC;AAAA,EACF;AACA,QAAM,aACJ,KAAK,WAAW,KAChB,gBAAgB,IAAI,KAAK,CAAC,CAAC,KAC3B,KAAK,CAAC,EAAE,WAAW,GAAG;AACxB,QAAM,QAAQ,WAAW,MAAM,aAAa,EAAE,MAAM,OAAO,IAAI,MAAS;AAC1E;","names":["import_promises","import_node_path","import_promises","import_node_path","r","g","b","encodeBlurHash","path","fs","fs","path","import_promises","import_node_path","fs","path","fs","path"]}