{"release_id":"01a1c502-4ad1-0000-ec62-adc54d3c69aa","chunk_id":"01a1c502-4bf5-7430-bcea-6fe7e8d8b944","file":"CVs4g0kn.mjs","ignoreList":[],"mappings":"wVAkBA,MAAMA,MAAkB,IAAI,CAC3B,OACA,OACA,OACA,OACA,OACA,OACA,OACA,OACA,OACA,MACD,CAAC,EAED,eAAeC,EAAUC,EAA6B,CACrD,MAAMC,EAAQD,EAAK,MAAM,EAAG,EAAE,EACxBE,EAAM,IAAI,WAAW,MAAMD,EAAM,aAAa,EAIpD,OAHIC,EAAI,OAAS,IAEJ,OAAO,aAAaA,EAAI,CAAC,EAAGA,EAAI,CAAC,EAAGA,EAAI,CAAC,EAAGA,EAAI,CAAC,CAAC,IAClD,OAAe,GACrB,OAAO,aAAaA,EAAI,CAAC,EAAGA,EAAI,CAAC,EAAGA,EAAI,EAAE,EAAGA,EAAI,EAAE,CAAC,CAC5D,CAEA,eAAsBC,EAAOH,EAA8B,CAC1D,GAAIA,EAAK,OAAS,cAAgBA,EAAK,OAAS,aAAc,MAAO,GACrE,MAAMI,EAAQ,MAAML,EAAUC,CAAI,EAClC,OAAOF,EAAY,IAAIM,CAAK,CAC7B,CAQA,eAAsBC,EAAWL,EAAYM,EAAkB,IAAqB,CAEnF,GADI,OAAO,OAAW,KAClB,CAAE,MAAMH,EAAOH,CAAI,EAAI,OAAOA,EAElC,GAAI,CAEH,MAAMO,GADM,YAAM,OAAO,gBAAU,OAAAC,KAAA,QACsC,QACnEC,EAAM,MAAMF,EAAS,CAAE,KAAMP,EAAM,OAAQ,aAAc,QAAAM,EAAS,EAClEI,EAAO,MAAM,QAAQD,CAAG,EAAIA,EAAI,CAAC,EAAIA,EACrCE,EAAOX,EAAK,KAAK,QAAQ,WAAY,EAAE,GAAK,QAClD,OAAO,IAAI,KAAK,CAACU,CAAI,EAAG,GAAGC,CAAI,OAAQ,CAAE,KAAM,aAAc,aAAc,KAAK,MAAO,CACxF,MAAQ,CACP,OAAOX,CACR,CACD","names":["HEIC_BRANDS","readMagic","file","slice","buf","isHeic","brand","heicToJpeg","quality","heic2any","n","out","blob","base"],"sources":["../../../../../../src/lib/utils/heic-to-jpeg.ts"],"sourcesContent":["/**\n * Detect HEIC/HEIF content and convert to JPEG client-side before upload.\n *\n * iOS shoots HEIC by default. Safari usually transcodes to JPEG on file-input\n * upload, but not always — direct-API paths, renamed `.jpg` files, and some\n * picker flows let raw HEIC through. Neither our canvas pipeline (browsers\n * can't decode HEIC into a bitmap) nor our sharp build (no libheif) can strip\n * EXIF on a HEIC, so we convert client-side here. After conversion the file\n * is a normal JPEG and the existing strip/compress paths take over.\n *\n * heic2any is dynamic-imported so the ~1.5MB wasm bundle only ships to the\n * 1% of sessions that actually need it.\n */\n\n// Bytes 4..12 of a HEIC/HEIF container are \"ftyp<brand>\", with brands like\n// \"heic\", \"heix\", \"mif1\", \"msf1\" (and HEVC variants \"hevc\", \"hevx\"). The\n// extension/mime are unreliable (iPhone photos renamed to .jpg keep the\n// HEIC payload), so we sniff the magic bytes from the buffer itself.\nconst HEIC_BRANDS = new Set([\n\t'heic',\n\t'heix',\n\t'heim',\n\t'heis',\n\t'hevc',\n\t'hevx',\n\t'hevm',\n\t'hevs',\n\t'mif1',\n\t'msf1'\n]);\n\nasync function readMagic(file: File): Promise<string> {\n\tconst slice = file.slice(0, 12);\n\tconst buf = new Uint8Array(await slice.arrayBuffer());\n\tif (buf.length < 12) return '';\n\t// bytes 4-7 = \"ftyp\", bytes 8-11 = brand\n\tconst ftyp = String.fromCharCode(buf[4], buf[5], buf[6], buf[7]);\n\tif (ftyp !== 'ftyp') return '';\n\treturn String.fromCharCode(buf[8], buf[9], buf[10], buf[11]);\n}\n\nexport async function isHeic(file: File): Promise<boolean> {\n\tif (file.type === 'image/heic' || file.type === 'image/heif') return true;\n\tconst brand = await readMagic(file);\n\treturn HEIC_BRANDS.has(brand);\n}\n\n/**\n * If the file is HEIC, convert it to JPEG. Otherwise return as-is. Falls back\n * to the original file on any conversion error — the server still has its own\n * (best-effort) skip path, and a passthrough is preferable to blocking the\n * upload.\n */\nexport async function heicToJpeg(file: File, quality: number = 0.92): Promise<File> {\n\tif (typeof window === 'undefined') return file; // SSR-safe\n\tif (!(await isHeic(file))) return file;\n\n\ttry {\n\t\tconst mod = await import('heic2any');\n\t\tconst heic2any = (mod as { default: typeof import('heic2any').default }).default;\n\t\tconst out = await heic2any({ blob: file, toType: 'image/jpeg', quality });\n\t\tconst blob = Array.isArray(out) ? out[0] : out;\n\t\tconst base = file.name.replace(/\\.[^.]+$/, '') || 'photo';\n\t\treturn new File([blob], `${base}.jpg`, { type: 'image/jpeg', lastModified: Date.now() });\n\t} catch {\n\t\treturn file;\n\t}\n}\n"],"version":3}