Skip to content

designbycode/uploader-core

Repository files navigation

@designbycode/uploader-core

A framework-agnostic JavaScript file uploader library with queue management, progress tracking, abort support, and file validation.

npm version license

Features

  • Framework-agnostic — Works in browsers and Node.js
  • Dual module support — ESM and CommonJS
  • Queue management — Add multiple files and upload them in sequence or parallel
  • Progress tracking — Real-time upload progress via events
  • Abort support — Cancel individual uploads or all at once
  • File validation — Built-in size and MIME type validation with glob patterns
  • Custom validators — Support for async validation functions
  • Concurrency control — Limit simultaneous uploads with maxConcurrent
  • Retry logic — Automatic retry for failed uploads
  • Event-driven — Subscribe to add, progress, success, error, remove, retry events
  • TypeScript — Full TypeScript support with type definitions

Installation

# Using bun
bun add @designbycode/uploader-core

# Using npm
npm install @designbycode/uploader-core

# Using pnpm
pnpm add @designbycode/uploader-core

Quick Start

import { Uploader } from "@designbycode/uploader-core";

const uploader = new Uploader({
  process: async (file, { signal, onProgress }) => {
    const formData = new FormData();
    formData.append("file", file);

    const response = await fetch("/api/upload", {
      method: "POST",
      body: formData,
      signal,
    });

    return { serverId: response.json().id };
  },
});

// Add files and upload
const input = document.querySelector('input[type="file"]');
input.addEventListener("change", async () => {
  const added = await uploader.addFiles(Array.from(input.files));
  uploader.uploadAll();
});

// Listen for events
uploader.on("progress", (file) => {
  console.log(`${file.file.name}: ${file.progress}%`);
});

uploader.on("success", (file) => {
  console.log(`${file.file.name} uploaded successfully`);
});

API

new Uploader(options?)

Creates a new uploader instance.

const uploader = new Uploader({
  autoUpload: false, // Automatically upload files when added
  maxConcurrent: 2, // Limit simultaneous uploads
  validation: {
    // File validation rules
    maxSize: 10 * 1024 * 1024, // 10MB
    acceptedMimeTypes: ["image/png", "image/jpeg"],
  },
  process: async (file, { signal, onProgress }) => {
    // Your upload logic here
    return { serverId: "file-id" };
  },
});

Options

Option Type Description
autoUpload boolean Automatically upload files when added
maxConcurrent number Maximum simultaneous uploads (default: unlimited)
validation ValidationRule | FileValidator File validation rules or custom validator
maxRetries number Maximum retry attempts for failed uploads
retryDelay number Delay between retries in ms (default: 1000)
process ProcessFn Function to handle the actual upload
revert RevertFn Function to undo upload (e.g., delete from server)
onAdd Callback Called when a file is added
onProgress Callback Called when upload progress updates
onSuccess Callback Called when upload succeeds
onError Callback Called when upload fails
onRemove Callback Called when a file is removed
onRetry Callback Called when upload is retried
onValidationError Callback Called when validation fails

Methods

addFiles(files: File[]): Promise<UploadFile[]>

Add files to the upload queue. Returns the added UploadFile objects.

const added = await uploader.addFiles([file1, file2, file3]);
console.log(`Added ${added.length} files`);

uploadFile(id: string): Promise<void>

Upload a single file by its ID.

await uploader.uploadFile("file-uuid");

uploadAll(): void

Upload all queued files.

uploader.uploadAll();

cancelFile(id: string): void

Cancel an in-progress upload.

uploader.cancelFile("file-uuid");

cancelAll(): void

Cancel all in-progress uploads.

uploader.cancelAll();

removeFile(id: string): Promise<void>

Remove a file from the queue. Calls revert() if the file was uploaded.

await uploader.removeFile("file-uuid");

getFiles(): ReadonlyArray<UploadFile>

Get all files in the queue.

const files = uploader.getFiles();
files.forEach((f) => console.log(f.file.name, f.status));

getFile(id: string): UploadFile | undefined

Get a single file by ID.

const file = uploader.getFile("file-uuid");

getFileByServerId(serverId: string): UploadFile | undefined

Get a single file by its server ID (after upload).

const file = uploader.getFileByServerId("server-123");

clear(): void

Clear all files from the queue.

uploader.clear();

Events

on(event, callback): () => void

Subscribe to an event. Returns an unsubscribe function.

const unsubscribe = uploader.on("success", (file) => {
  console.log(`${file.file.name} uploaded!`);
});

// Later, unsubscribe
unsubscribe();

once(event, callback): () => void

Subscribe to an event for a single execution.

uploader.once("success", (file) => {
  console.log("First upload completed!");
});

off(event, callback): void

Unsubscribe from an event.

const handler = (file) => console.log(file.file.name);
uploader.on("success", handler);
uploader.off("success", handler);

Event Types

Event Payload Description
add UploadFile File added to queue
progress UploadFile Upload progress updated
success UploadFile Upload completed
error UploadFile Upload failed
remove UploadFile File removed from queue
retry { file: UploadFile, attempt: number } Upload being retried

Validation

Built-in Rules

const uploader = new Uploader({
  validation: {
    maxSize: 5 * 1024 * 1024, // 5MB max
    minSize: 1000, // 1KB min
    acceptedMimeTypes: ["image/png", "image/jpeg", "image/gif"],
    rejectedMimeTypes: ["application/exe"],
  },
  process: async (file, { signal, onProgress }) => {
    // ...
  },
});

uploader.on("validationError", (file, error) => {
  console.error(`${file.name} rejected: ${error}`);
});

Custom Validator

const uploader = new Uploader({
  validation: async (file) => {
    // Check file name
    if (!file.name.match(/^[a-zA-Z0-9-_]+\.[a-z]+$/)) {
      return { valid: false, error: "Invalid file name format" };
    }

    // Async validation (e.g., check against API)
    const isAllowed = await checkFileAllowed(file);
    if (!isAllowed) {
      return { valid: false, error: "File not allowed" };
    }

    return { valid: true };
  },
});

Concurrency Control

Limit simultaneous uploads:

const uploader = new Uploader({
  maxConcurrent: 2, // Only 2 uploads at a time
  process: async (file, { signal, onProgress }) => {
    // ...
  },
});

// Add 10 files, only 2 will upload at once
await uploader.addFiles(files);
uploader.uploadAll();

Retry Logic

Automatically retry failed uploads:

const uploader = new Uploader({
  maxRetries: 3, // Retry up to 3 times
  retryDelay: 1000, // Wait 1 second between retries
  process: async (file, { signal, onProgress }) => {
    // ...
  },
});

uploader.on("retry", ({ file, attempt }) => {
  console.log(`Retrying ${file.file.name} (attempt ${attempt})`);
});

Types

UploadFile

interface UploadFile {
  id: string;
  file: File;
  status: "idle" | "queued" | "uploading" | "success" | "error" | "cancelled";
  progress: number;
  serverId?: string;
  error?: string;
}

UploadStatus

type UploadStatus =
  | "idle"
  | "queued"
  | "uploading"
  | "success"
  | "error"
  | "cancelled";

ValidationRule

interface ValidationRule {
  maxSize?: number;
  minSize?: number;
  acceptedMimeTypes?: string[];
  rejectedMimeTypes?: string[];
}

License

MIT

About

No description, website, or topics provided.

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors