npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@xiboplayer/cache

v0.7.19

Published

Offline caching and download management with parallel chunk downloads

Downloads

4,078

Readme

@xiboplayer/cache

Offline caching and download management with parallel chunk downloads, resume support, and storage health monitoring.

Overview

Manages the complete lifecycle of media files from CMS to local storage:

  • REST-based file store -- StoreClient provides CRUD operations (has, get, put, remove, list) via proxy endpoints
  • Flat-queue download orchestration -- DownloadManager with per-file and global concurrency limits
  • Intelligent chunking -- files > 100MB split into 50MB chunks with prioritized headers and trailers for fast video start
  • Download resume -- partial downloads resume automatically; expired URLs defer to next collection cycle
  • Stale media detection -- CacheAnalyzer identifies orphaned files and evicts when storage exceeds threshold
  • Widget HTML preprocessing -- base tag injection, dependency rewriting for iframe sandboxing

No Cache API is used. All content is stored on the filesystem via the proxy's ContentStore.

Architecture

CMS (RequiredFiles: size, URL, MD5)
        |
        v
+-------------------------------------+
|      DownloadManager (Facade)       |
|  +- enqueue(fileInfo)               |
|  +- prioritizeLayoutFiles(mediaIds) |
|  +- urgentChunk(type, id, idx)      |
+-------------------------------------+
             |
             v
      DownloadQueue (Flat)
    +-----------------------------+
    | [Task, Task, BARRIER,       |
    |  Task, Task, Task]          |
    |                             |
    | Global concurrency: 6       |
    | Per-file chunks: 2-3        |
    +-----------------------------+
       |
       +-> DownloadTask (chunk-0, high priority)
       +-> DownloadTask (chunk-last, high priority)
       +-> BARRIER (gate: waits for above to finish)
       +-> DownloadTask (bulk chunks, normal priority)
               |
               v
         HTTP Range GET
               |
               v
       ContentStore (proxy)
         /store/media/{id}
         /store/layout/{id}
         /store/widget/{L}/{R}/{M}

Installation

npm install @xiboplayer/cache

Usage

StoreClient -- direct REST access to ContentStore

import { StoreClient } from '@xiboplayer/cache';

const store = new StoreClient();

// Check existence
const exists = await store.has('media', '123');

// Retrieve file
const blob = await store.get('media', '456');

// Store widget HTML
const html = new Blob(['<h1>Widget</h1>'], { type: 'text/html' });
await store.put('widget', 'layout/1/region/2/media/3', html, 'text/html');

// List all cached files
const files = await store.list();

// Delete orphaned files
await store.remove([
  { type: 'media', id: '999' },
  { type: 'media', id: '1000' },
]);

DownloadManager -- orchestrated downloads

import { DownloadManager } from '@xiboplayer/cache';

const dm = new DownloadManager({
  concurrency: 6,
  chunkSize: 50 * 1024 * 1024,
  chunksPerFile: 2,
});

// Enqueue a file
const media = dm.enqueue({
  id: '123',
  type: 'media',
  path: 'https://cdn.example.com/video.mp4',
  size: 250 * 1024 * 1024,
  md5: 'abc123def456',
});

// Wait for completion
const blob = await media.wait();

// Get progress
const progress = dm.getProgress();
// { 'media/123': { downloaded: 50M, total: 250M, percent: '20.0', state: 'downloading' } }

Prioritization and emergency chunks

// Boost files needed by current layout
dm.prioritizeLayoutFiles(['123', '456']);

// Emergency: chunk needed for video playback is stalled
// Moves to front of queue, reduces global concurrency to 2
dm.urgentChunk('media', '789', 3);

CacheAnalyzer -- storage health

import { CacheAnalyzer } from '@xiboplayer/cache';

const analyzer = new CacheAnalyzer(store, { threshold: 80 });

const report = await analyzer.analyze(requiredFiles);
console.log(`Storage: ${report.storage.percent}%`);
console.log(`Orphaned: ${report.files.orphaned} files`);
console.log(`Evicted: ${report.evicted.length} files`);

Download Pipeline

Files flow through stages:

  1. Enqueueing -- deduplication via type/id key, URL refresh if new URL has longer expiry
  2. Preparation -- HEAD request determines chunking (> 100MB = chunked)
  3. Task creation -- chunk-0 and chunk-last get high priority (video headers/trailers); BARRIER separates from bulk chunks
  4. Processing -- concurrency-aware: 6 global connections, 2-3 per file
  5. Execution -- HTTP Range GET with retries and exponential backoff
  6. Storage -- proxy ContentStore saves chunks to filesystem
  7. Assembly -- chunks concatenated; progressive callback enables playback before full download

Chunk strategy

  • Default chunk size: 50MB
  • Threshold: files > 100MB get chunked
  • Header+trailer first: MP4 moov atom in chunk-0 and chunk-last allows instant playback start
  • Barriers: critical chunks download before bulk chunks begin
  • Resume: cached chunks tracked in skipChunks Set; only missing chunks downloaded
  • Expired URLs: deferred (not failed) -- next collection provides fresh URL

Retry strategy by type

| Type | Max retries | Backoff | Notes | |------|------------|---------|-------| | media | 3 | 500ms | Large, cacheable files | | layout | 3 | 500ms | Layout XML, stable URL | | dataset | 4 | 15s, 30s, 60s, 120s | Re-enqueues 5x for "cache not ready" | | static | 3 | 500ms | Widget dependencies (CSS, fonts) |

API Reference

StoreClient

| Method | Signature | Returns | Description | |--------|-----------|---------|-------------| | has() | has(type, id) | Promise<boolean> | Check if file exists | | get() | get(type, id) | Promise<Blob \| null> | Retrieve file as Blob | | put() | put(type, id, body, contentType?) | Promise<boolean> | Store file | | remove() | remove(files) | Promise<{deleted, total}> | Delete files | | list() | list() | Promise<Array> | List all cached files |

DownloadManager

new DownloadManager({ concurrency?, chunkSize?, chunksPerFile? })

| Method | Signature | Returns | Description | |--------|-----------|---------|-------------| | enqueue() | enqueue(fileInfo) | FileDownload | Add file to download queue | | getTask() | getTask(key) | FileDownload \| null | Get task by "type/id" key | | getProgress() | getProgress() | Record<string, Progress> | All in-progress downloads | | prioritizeLayoutFiles() | prioritizeLayoutFiles(mediaIds) | void | Boost priority for layout files | | urgentChunk() | urgentChunk(type, id, chunkIndex) | boolean | Move chunk to front, reduce concurrency | | clear() | clear() | void | Cancel all, clear queue |

CacheAnalyzer

new CacheAnalyzer(storeClient, { threshold: 80 })

| Method | Signature | Returns | Description | |--------|-----------|---------|-------------| | analyze() | analyze(requiredFiles) | Promise<Report> | Compare cache vs required, evict if needed |

Report:

{
  storage: { usage, quota, percent },
  files: { required, orphaned, total },
  orphaned: [{ id, type, size, cachedAt }],
  evicted: [{ id, type, size }],
  threshold: 80
}

CacheManager

| Method | Signature | Returns | Description | |--------|-----------|---------|-------------| | getCacheKey() | getCacheKey(type, id) | string | Get cache key path | | addDependant() | addDependant(mediaId, layoutId) | void | Track layout -> media reference | | removeLayoutDependants() | removeLayoutDependants(layoutId) | string[] | Remove layout, return orphaned media IDs | | isMediaReferenced() | isMediaReferenced(mediaId) | boolean | Check if media is still used |

Dependencies

  • @xiboplayer/utils -- logger
  • spark-md5 -- MD5 hashing for file verification

xiboplayer.org · Part of the XiboPlayer SDK