Files
skills/docs/api-stack/api/api-plugin-file-upload/src/services/complete-chunked.md
2026-05-29 08:20:56 +01:00

2.5 KiB

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

import { runComplete } from "@suaveplan/api-plugin-file-upload";

const record = await runComplete(session, undefined, {
    storage,
    sessionStore,
    clock: () => Date.now(),
});

Usage: Advanced

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.