Files
2026-05-27 23:29:53 +01:00

2.4 KiB

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

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

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<UploadRecord> — 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.