# `handlers.ts` ## Purpose Per-route Hono handler implementations consumed by `buildUploadRoutes`. Each handler reads the auth principal from `c.get("user")`, optionally narrows to a tenant via `c.get("tenant")`, parses the request body / params with the per-route Zod schemas, delegates to `FileUploadService`, and projects the service's `UploadSession`/`UploadRecord` return value onto the wire response shape. ## Features - Single `readScope(c)` helper produces a `PrincipalScope` from the augmented Hono context. Missing principal raises `UPLOAD.INVALID_INPUT` (the sibling `requireAuth()` middleware should have already replied 401, so reaching this path indicates plugin misconfiguration). - `toStatus` projects either an `UploadSession` (in-progress) or an `UploadRecord` (complete) onto the uniform `UploadStatusResponse` shape so the `GET` route serves both phases through a single schema. - Multipart parsing for `POST /uploads` reads via `c.req.parseBody()`; chunk PUT reads via `c.req.raw.arrayBuffer()` so binary payloads are preserved byte-for-byte. ## Usage: Basic ```ts import { buildHandlers } from "@suaveplan/api-plugin-file-upload"; const handlers = buildHandlers(service); // `handlers.singleShot`, `handlers.initiate`, …, `handlers.status` ``` ## Usage: Advanced ```ts import { buildHandlers, FileUploadError } from "@suaveplan/api-plugin-file-upload"; const handlers = buildHandlers(service); app.post("/uploads", async (c) => { try { return await handlers.singleShot(c); } catch (err) { if (err instanceof FileUploadError && err.uploadCode === "UPLOAD.TOO_LARGE") { return c.json({ retry: false }, 413); } throw err; } }); ``` ## API Reference - `buildHandlers(service: FileUploadService): { singleShot, initiate, chunk, complete, abort, status }` — each value is a `(c: Context) => Promise`. ## Implementation Notes - The `readScope` helper checks for `null` and `undefined` so the same code runs whether or not the host installed `api-plugin-auth` (e.g. for integration tests that synthesise a principal directly). - `parseBody` may throw if the client did not send multipart — we translate that to `UPLOAD.INVALID_INPUT` (400) rather than letting a raw runtime error escape. - The chunk PUT handler does NOT cap chunk size at the handler layer because the assembler+service already enforce the session's `chunkSize` on `complete` time (a mismatched chunk would fail hash verification). - The abort handler returns a static `state: "complete"` response shape for idempotency — calling abort twice returns success both times.