Initial Commit

This commit is contained in:
2026-05-27 23:29:53 +01:00
commit fbc70f96e6
10000 changed files with 1102152 additions and 0 deletions

View File

@@ -0,0 +1,49 @@
# `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<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.