Utilities

Framework-agnostic utility functions for common file operations and tasks.

File Utilities

generateId()

Generate a unique ID for files or queue items:

import { generateId } from '@dropper/core'

const id = generateId()
console.log(id) // "1699564800000-a1b2c3d4e"

Format: {timestamp}-{random}

  • Timestamp: Current time in milliseconds
  • Random: 9-character alphanumeric string

Use Cases:

  • Unique queue item IDs
  • Temporary file identifiers
  • Client-side tracking

formatFileSize()

Convert bytes to human-readable file size:

import { formatFileSize } from '@dropper/core'

formatFileSize(0)           // "0 Bytes"
formatFileSize(1024)        // "1 KB"
formatFileSize(1048576)     // "1 MB"
formatFileSize(1073741824)  // "1 GB"
formatFileSize(1536)        // "1.5 KB"

Parameters:

  • bytes (number): File size in bytes

Returns: Formatted string with appropriate unit

getFileExtension()

Extract file extension from filename:

import { getFileExtension } from '@dropper/core'

getFileExtension('photo.jpg')         // "jpg"
getFileExtension('document.pdf')      // "pdf"
getFileExtension('archive.tar.gz')    // "gz"
getFileExtension('README')            // ""

Parameters:

  • filename (string): File name

Returns: Lowercase extension without dot, or empty string

getMimeTypeCategory()

Get the category of a MIME type:

import { getMimeTypeCategory } from '@dropper/core'

getMimeTypeCategory('image/jpeg')           // "image"
getMimeTypeCategory('video/mp4')            // "video"
getMimeTypeCategory('audio/mpeg')           // "audio"
getMimeTypeCategory('application/pdf')      // "pdf"
getMimeTypeCategory('text/plain')           // "document"
getMimeTypeCategory('application/msword')   // "document"
getMimeTypeCategory('application/zip')      // "file"

Categories:

  • image - Image files
  • video - Video files
  • audio - Audio files
  • pdf - PDF documents
  • document - Text and document files
  • file - Other file types

Date Utilities

formatDate()

Format a date to ISO string:

import { formatDate } from '@dropper/core'

const date = new Date('2024-01-15T10:30:00')
formatDate(date)  // "2024-01-15T10:30:00.000Z"

// Also accepts string input
formatDate('2024-01-15')  // "2024-01-15T00:00:00.000Z"

Parameters:

  • date (Date | string): Date object or date string

Returns: ISO 8601 formatted string

Function Utilities

debounce()

Debounce a function to limit execution rate:

import { debounce } from '@dropper/core'

const searchFiles = debounce((query: string) => {
  console.log('Searching for:', query)
  // API call here
}, 300)

// Called multiple times rapidly
searchFiles('doc')
searchFiles('docu')
searchFiles('document')
// Only executes once after 300ms of inactivity

Parameters:

  • func (Function): Function to debounce
  • wait (number): Delay in milliseconds

Returns: Debounced function

Use Cases:

  • Search input handling
  • Window resize events
  • Scroll events
  • Form validation

throttle()

Throttle a function to execute at most once per interval:

import { throttle } from '@dropper/core'

const handleScroll = throttle(() => {
  console.log('Scroll position:', window.scrollY)
}, 100)

window.addEventListener('scroll', handleScroll)
// Executes at most once every 100ms

Parameters:

  • func (Function): Function to throttle
  • limit (number): Minimum interval in milliseconds

Returns: Throttled function

Use Cases:

  • Scroll events
  • Mouse move events
  • API rate limiting
  • Animation frames

sleep()

Pause execution for a specified duration:

import { sleep } from '@dropper/core'

async function delayedAction() {
  console.log('Starting...')
  await sleep(2000)  // Wait 2 seconds
  console.log('Done!')
}

Parameters:

  • ms (number): Duration in milliseconds

Returns: Promise that resolves after delay

Use Cases:

  • Artificial delays
  • Rate limiting
  • Animation timing
  • Testing

retry()

Retry a function with exponential backoff:

import { retry } from '@dropper/core'

const result = await retry(
  async () => {
    // Function that might fail
    return await fetchData()
  },
  {
    maxRetries: 3,
    initialDelay: 1000,
    maxDelay: 10000,
    backoffFactor: 2,
  }
)

Parameters:

  • fn (Function): Async function to retry
  • options (object):
    • maxRetries (number): Maximum retry attempts (default: 3)
    • initialDelay (number): Initial delay in ms (default: 1000)
    • maxDelay (number): Maximum delay in ms (default: 10000)
    • backoffFactor (number): Backoff multiplier (default: 2)

Returns: Promise with function result

Retry Schedule:

  • Attempt 1: Immediate
  • Attempt 2: After 1s (initialDelay)
  • Attempt 3: After 2s (initialDelay × backoffFactor)
  • Attempt 4: After 4s (initialDelay × backoffFactor²)

Complete Examples

File Upload with Formatting

import { 
  formatFileSize, 
  getFileExtension, 
  getMimeTypeCategory 
} from '@dropper/core'

function displayFileInfo(file: File) {
  console.log('File Information:')
  console.log(`Name: ${file.name}`)
  console.log(`Size: ${formatFileSize(file.size)}`)
  console.log(`Extension: ${getFileExtension(file.name)}`)
  console.log(`Category: ${getMimeTypeCategory(file.type)}`)
  console.log(`Type: ${file.type}`)
}

// Example output:
// File Information:
// Name: photo.jpg
// Size: 2.5 MB
// Extension: jpg
// Category: image
// Type: image/jpeg

Search with Debounce

import { debounce } from '@dropper/core'
import { DropperClient } from '@dropper/core'

const client = new DropperClient({
  publishableKey: 'pk_dropper_test_xxx',
})

const searchFiles = debounce(async (query: string) => {
  if (query.length < 2) return
  
  const results = await client.listFiles({
    search: query,
    limit: 10,
  })
  
  displayResults(results.data)
}, 300)

// Usage in input handler
function handleSearchInput(e: Event) {
  const query = (e.target as HTMLInputElement).value
  searchFiles(query)
}

Retry Failed Upload

import { retry, sleep } from '@dropper/core'
import { DropperClient } from '@dropper/core'

const client = new DropperClient({
  publishableKey: 'pk_dropper_test_xxx',
})

async function uploadWithRetry(file: File) {
  try {
    const result = await retry(
      async () => {
        console.log('Attempting upload...')
        return await client.uploadFile(file)
      },
      {
        maxRetries: 3,
        initialDelay: 1000,
        backoffFactor: 2,
      }
    )
    
    console.log('Upload successful:', result.url)
    return result
  } catch (error) {
    console.error('Upload failed after retries:', error)
    throw error
  }
}

Throttled Progress Updates

import { throttle } from '@dropper/core'

const updateUI = throttle((progress: number) => {
  // Update progress bar
  progressBar.style.width = `${progress}%`
  progressText.textContent = `${progress}%`
}, 100)

// Upload with throttled updates
await client.uploadFile(file, {
  onProgress: (progress) => {
    updateUI(progress)
  },
})

File List with Formatting

import { formatFileSize, formatDate, getMimeTypeCategory } from '@dropper/core'

async function displayFileList() {
  const response = await client.listFiles()
  
  response.data.forEach((file) => {
    const category = getMimeTypeCategory(file.mimeType)
    const size = formatFileSize(file.size)
    const date = new Date(file.createdAt).toLocaleDateString()
    
    console.log(`[${category}] ${file.originalFilename}`)
    console.log(`  Size: ${size}`)
    console.log(`  Uploaded: ${date}`)
    console.log()
  })
}

Generate Unique IDs

import { generateId } from '@dropper/core'

// Create upload queue items
const queueItems = files.map((file) => ({
  id: generateId(),
  file,
  status: 'pending',
  progress: 0,
}))

// Track uploads
const uploadTracking = new Map()
files.forEach((file) => {
  const trackingId = generateId()
  uploadTracking.set(trackingId, {
    file,
    startTime: Date.now(),
  })
})

React Integration Examples

Debounced Search Component

import { useState, useCallback } from 'react'
import { debounce } from '@dropper/core'

function FileSearch() {
  const [results, setResults] = useState([])
  
  const searchFiles = useCallback(
    debounce(async (query: string) => {
      const response = await client.listFiles({ search: query })
      setResults(response.data)
    }, 300),
    []
  )
  
  return (
    <input
      type="search"
      onChange={(e) => searchFiles(e.target.value)}
      placeholder="Search files..."
    />
  )
}

File Size Display

import { formatFileSize } from '@dropper/core'

function FileListItem({ file }) {
  return (
    <div>
      <span>{file.originalFilename}</span>
      <span>{formatFileSize(file.size)}</span>
    </div>
  )
}

Best Practices

1. Use Debounce for User Input

Reduce API calls for search and filter inputs:

const searchFiles = debounce(async (query) => {
  await client.listFiles({ search: query })
}, 300)

2. Use Throttle for Frequent Events

Limit execution rate for scroll, resize, and mouse events:

const handleScroll = throttle(() => {
  // Handle scroll
}, 100)

3. Format File Sizes for Display

Always format file sizes for user display:

// Good
console.log(`Size: ${formatFileSize(file.size)}`)

// Avoid
console.log(`Size: ${file.size} bytes`)

4. Use Retry for Network Operations

Add retry logic for unreliable network operations:

await retry(async () => {
  return await client.uploadFile(file)
}, { maxRetries: 3 })

5. Generate Unique IDs Client-Side

Use generateId() for client-side tracking:

const queueItem = {
  id: generateId(),
  file,
  status: 'pending',
}

TypeScript Support

All utilities are fully typed:

import type {
  generateId,
  formatFileSize,
  debounce,
  throttle,
  retry,
} from '@dropper/core'

// Type-safe usage
const id: string = generateId()
const size: string = formatFileSize(1024)
const debouncedFn = debounce((x: string) => console.log(x), 300)

Next Steps