# `single-shot.ts` ## Purpose Internal back-end implementing the `singleShot` operation of `FileUploadService`. Extracted from the main service file to keep each implementation file under the suaveplan 300-line budget while still keeping the responsibility cohesive: size limit → MIME validation (with sniff cross-check) → storage persistence → record save → OTel emission. ## Features - Encapsulates the full single-shot validation pipeline so the public `FileUploadService.singleShot` is a single function call. - Reads the first 4 KiB into the MIME sniffer so spoofed Content-Types are rejected without buffering the whole payload twice. - Counts outcomes (`success` / `rejected` / `error`) onto `upload.singleshot_total` and bytes onto `upload.bytes_total` so dashboards reflect every code path. - Generic in the id generator and clock so `createFileUploadService` can plug in its own deterministic values for tests. ## Usage: Basic ```ts import { runSingleShot } from "@suaveplan/api-plugin-file-upload"; const record = await runSingleShot( { principalId: "alice", fileName: "hello.txt", bytes, declaredMimeType: "text/plain" }, { config, storage, sessionStore, idGenerator: () => "u1", clock: () => Date.now() } ); ``` ## Usage: Advanced ```ts import { runSingleShot, FileUploadError } from "@suaveplan/api-plugin-file-upload"; try { await runSingleShot(input, opts); } catch (err) { if (err instanceof FileUploadError && err.uploadCode === "UPLOAD.MIME_REJECTED") { // spoof or denied — render UI-friendly 415 page } throw err; } ``` ## API Reference - `SingleShotInput` — `{ principalId, tenantId?, fileName, bytes, declaredMimeType }`. - `RunSingleShotOptions` — `{ config, storage, sessionStore, idGenerator, clock }`. - `runSingleShot(input, opts): Promise` — see Purpose for pipeline order; throws `UPLOAD.TOO_LARGE`, `UPLOAD.MIME_REJECTED`, or `UPLOAD.STORAGE_FAILURE` depending on which guard fires. ## Implementation Notes - The sniff sample is capped at 4 KiB because every magic-bytes signature the `@suaveplan/mime` registry ships fits well inside that window; sniffing more wastes CPU on tiny files and offers no additional confidence. - Outcomes distinguish `rejected` (validation-side failure, `FileUploadError`) from `error` (storage/store-side failure, any other Error) so on-call rotations can alert on `error` without being paged for legitimate user rejections.