first commit

This commit is contained in:
2026-05-29 08:20:56 +01:00
commit 949299f58d
6368 changed files with 527176 additions and 0 deletions

View File

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