file-ingest-contracts

Installation
SKILL.md

File ingest contracts

Getting a file into the page is a chain of browser contracts: cancel the right drag events, read from the right DataTransfer surface, distrust the file's self-reported type, and revoke every preview URL you mint. Each link passes a happy-path demo and fails only with nested drop zones, dropped folders, spoofed types, clipboard pastes, or long sessions.

Checklist (lead with the trap; test shapes and evidence framing in references/)

file-ingest-contracts

  1. Cancel dragover or the drop never fires — and guard the window. Cancel dragover to make a custom element a drop target, then cancel drop to suppress the browser's default handling. Browsers generally do not require canceling dragenter, even though the drag-and-drop specification describes that step; do not use it as the rationale for whether drop can fire. Miss the required cancellation and a file dropped on (or near) the zone can make the browser navigate to / open the file and throw away the page. Add a window-level dragover+drop preventDefault (or scope-check the target) so a near-miss cannot blow away unsaved state.
  2. Toggle the highlight on a dragenter/dragleave counter, not raw events. dragenter fires on every child element and dragleave fires when the pointer crosses from the parent into a child, so a naive add/remove-class flickers over nested content. Keep an integer: ++ on dragenter, -- on dragleave, add the highlight when it reaches 1, clear when it returns to 0. Toggle on dragenter, not dragover (dragover refires every few hundred ms and forces needless repaints).
  3. Re-set dropEffect on every dragover. dropEffect describes the desired effect for that one dispatch and reverts next tick; set dataTransfer.dropEffect = 'copy' inside the dragover handler each time or the cursor won't reflect the operation. Use dropEffect = 'none' to visibly reject a spot.
  4. Read folders through items + webkitGetAsEntry, not files. DataTransfer.files is a flat FileList with no directory support; only DataTransfer.itemsDataTransferItem.webkitGetAsEntry() (or getAsFileSystemHandle()) can recurse into a dropped directory, and both are non-standard — feature-detect ('getAsFileSystemHandle' in DataTransferItem.prototype) and fall back. items is only live inside the drop/dragstart handler, so capture entries synchronously before any await. For click-to-select folders use <input webkitdirectory multiple>; it can't also pick loose files and shows a browser trust prompt.
  5. Treat accept and file.type as UX hints, never a type gate. accept only filters the OS picker (MDN is explicit: it does not validate) and drag-drop/paste bypass it entirely. file.type is guessed from the extension, is spoofable, and is often an empty string. For a real check, sniff magic bytes client-side (Blob.slice(0, N)arrayBuffer/FileReader → compare the signature) as a fast pre-filter, and re-validate on the server — that is the trust boundary. Header-only sniffing is still defeated by polyglots. Route active SVG/HTML rendering risk to frontend-security-baseline; route frontend-owned server upload limits, multipart reconstruction, and relay policy to bff-proxy-security-contracts.
  6. Extract pasted images from clipboardData.items and bound count/size. On the paste event, iterate event.clipboardData.items, take entries where kind === 'file' (or type starts with image/), and call getAsFile(). A single paste can carry both text and an image, so decide precedence rather than assuming one. Enforce files.length and file.size limits before you read — accept/multiple do not bound how many or how large.
  7. One object URL per preview, and revoke it. URL.createObjectURL mints a new blob URL every call and pins the blob in memory until URL.revokeObjectURL; re-creating one per render leaks. Revoke when the preview is replaced and on unmount (React: useEffect cleanup) — but not inside the img onload handler, which breaks right-click / open-in-new-tab. The browser only frees these on document unload, so long single-page sessions accumulate them.

Quick probes

Use as leads; confirm the source-to-sink path before filing.

Installs
1
GitHub Stars
1
First Seen
3 days ago
file-ingest-contracts — voidmatcha/frontend-niche-skills