# `complete-chunked.ts` ## Purpose Internal back-end implementing the `complete` operation of `FileUploadService`. Extracted from the main service file so each module stays under the suaveplan 300-line budget. The pipeline is: assemble every chunk in `[0, totalChunks)` order → verify SHA-256 against the declared expected hash (or an explicit override) → write the assembled bytes to the canonical record key → save the `UploadRecord` → garbage-collect per-chunk storage entries → clear the in-flight session. ## Features - Single-pass assembly via the `assembler` helper so chunk ordering and missing-chunk detection live in one place. - Override-on-complete lets clients adjust the declared hash if they recomputed mid-flight without rebuilding the entire session. - Emits both the outcome counter (`upload.completes_total{outcome}`) and the duration histogram (`upload.complete_duration_ms{outcome}`) on every code path so dashboards can correlate failure rate with latency. - Cleanup runs even on success (chunk-side storage entries are deleted) so the storage adapter never accumulates orphaned chunk keys. ## Usage: Basic ```ts import { runComplete } from "@suaveplan/api-plugin-file-upload"; const record = await runComplete(session, undefined, { storage, sessionStore, clock: () => Date.now(), }); ``` ## Usage: Advanced ```ts import { runComplete, FileUploadError } from "@suaveplan/api-plugin-file-upload"; try { await runComplete(session, clientRecomputedHash, opts); } catch (err) { if (err instanceof FileUploadError) { switch (err.uploadCode) { case "UPLOAD.INCOMPLETE": return c.json({ retry: true, reason: "missing-chunks" }, 409); case "UPLOAD.HASH_MISMATCH": return c.json({ retry: true, reason: "hash-mismatch" }, 422); } } throw err; } ``` ## API Reference - `RunCompleteOptions` — `{ storage, sessionStore, clock }`. - `runComplete(session, overrideSha256?, opts): Promise` — see Purpose for pipeline order; throws `UPLOAD.INCOMPLETE`, `UPLOAD.HASH_MISMATCH`, or `UPLOAD.STORAGE_FAILURE`. ## Implementation Notes - The chunk deletion loop runs sequentially. A parallel `Promise.all` is tempting but most storage adapters (`FileSystemUploadStorage` in particular) prefer FIFO syscalls to avoid file-descriptor exhaustion on very large uploads. - The histogram is recorded INSIDE the `try` so even early failures (e.g. an assemble that throws on the second chunk) still contribute to the observed-failure-latency picture.