refactor: Centralize image and binary extension utilities#2363
refactor: Centralize image and binary extension utilities#2363charlesvien wants to merge 4 commits into
Conversation
53498dc to
e6f92b8
Compare
bc3bf9d to
5b0b845
Compare
5b0b845 to
cd7faa3
Compare
e6f92b8 to
e7cc5a9
Compare
What T-Rex did
ArtifactsVerbose targeted validation run
Standalone URI MIME validation output
Verbose vitest output for the targeted repro suite
Standalone Node script demonstrating image MIME behavior on mobile URIs
Reviews (1): Last reviewed commit: "harden image handling against HEIC/HEIF/..." | Re-trigger Greptile |
| base64: string; | ||
| } | ||
|
|
||
| export function extensionOf(filename: string): string { |
There was a problem hiding this comment.
extensionOf() slices everything after the last dot without removing ? or #. The mobile picker now calls getImageMimeType(uri) on raw picker URIs, so a real URI like ph://asset/IMG.png?width=1024 is parsed as extension png?width=1024. That returns application/octet-stream, and inferImageMime() falls back to image/jpeg, causing PNG/WebP attachments to be uploaded with the wrong MIME type.
Artifacts
Targeted repro suite covering URI query-string extension parsing
- Keeps the command output available without making the summary code-heavy.
Standalone mobile URI MIME repro output
- Keeps the command output available without making the summary code-heavy.
Standalone mobile URI MIME repro source
- Contains supporting evidence from the run (text/javascript; charset=utf-8).
| base64: string; | ||
| } | ||
|
|
||
| export function extensionOf(filename: string): string { |
There was a problem hiding this comment.
extensionOf() only checks whether the dot is past index 0 in the full path. For a path like /path/.heic, the dot is after the slash, so this returns heic and classifies the hidden file as an image. That can send hidden dotfiles through image, binary, or MIME-specific paths even though the basename has no real extension.
Artifacts
Targeted repro suite covering path dotfile extension parsing
- Keeps the command output available without making the summary code-heavy.
|
|
||
| export const DOCUMENT_BINARY_EXTENSIONS: ReadonlySet<string> = new Set(["pdf"]); | ||
|
|
||
| export const BINARY_EXTENSIONS: ReadonlySet<string> = new Set([ |
There was a problem hiding this comment.
BINARY_EXTENSIONS is built from every key in IMAGE_MIME_TYPES, which now includes svg. isBinaryFile('icon.svg') therefore returns true, so title generation skips reading the SVG XML and only emits [Attached: icon.svg]. SVG files are text resources elsewhere in this PR, so this shared binary set can suppress useful text content for SVG-only attachments.
Artifacts
Targeted repro suite covering SVG binary classification
- Keeps the command output available without making the summary code-heavy.
| type: "base64", | ||
| data: chunk.data, | ||
| media_type: chunk.mimeType as ImageMimeType, | ||
| media_type: chunk.mimeType as ClaudeImageMimeType, |
There was a problem hiding this comment.
This cast does not validate the incoming ACP image MIME type. If an image chunk arrives with image/svg+xml, image/heic, image/avif, image/bmp, or application/octet-stream, the converter still forwards that value as media_type even though ClaudeImageMimeType only covers JPEG, PNG, GIF, and WebP. Those unsupported image blocks can then fail at the Claude API boundary instead of being rejected or converted consistently.
Artifacts
Targeted repro suite covering unsupported Claude image MIME forwarding
- Keeps the command output available without making the summary code-heavy.

Problem
Image extension sets and MIME mappings were duplicated across 9+ files, causing inconsistent behavior across paste, drag/drop, attachments, thumbnails and Claude API conversion.
Changes
@posthog/shared/imageas canonical source:IMAGE_MIME_TYPES,ALLOWED_IMAGE_MIME_TYPES,CLAUDE_IMAGE_EXTENSIONS,ClaudeImageMimeType, plusisImageFile/isRasterImageFile/isClaudeImageFile/isGifFile/getImageMimeType/parseImageDataUrl@posthog/shared/binarywithBINARY_EXTENSIONSandisBinaryFile, image portion derived from the canonical image setapps/code/src/shared/constants/image.tsandapps/code/src/shared/utils/imageDataUrl.ts; migrate 7 importers to@posthog/shared@posthog/sharedinto mobile (package.json dep + Metro alias to TS source)COMMON_IMAGE_EXTENSIONSandImageMimeTypeunion in the Claude ACP adapter with shared exportsCodeEditorPaneltoisRasterImageFileso SVGs keep opening in CodeMirror (broadisImageFilenow includes svg/heic)How did you test this?
Manually
Publish to changelog?
no