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

@dynlabs/react-native-immutable-file-cache

v1.1.0

Published

Cross-platform immutable file cache with pluggable storage adapters for React Native and Web

Downloads

790

Readme


Features

  • Immutable entries — Once cached, entries cannot be overwritten
  • Cross-platform — iOS, Android, and Web support
  • Pluggable adapters — Swap storage backends without code changes
  • TTL & LRU pruning — Automatic cache management
  • Atomic writes — Crash-safe file operations
  • TypeScript first — Full type safety

Installation

npm install @dynlabs/react-native-immutable-file-cache

React Native

npm install react-native-fs
cd ios && pod install

Web

No additional dependencies required — uses Cache Storage and IndexedDB.

Quick Start

import { createImmutableFileCache } from "@dynlabs/react-native-immutable-file-cache";

const cache = await createImmutableFileCache({
  namespace: "images",
  defaultTtlMs: 7 * 24 * 60 * 60 * 1000, // 7 days
  maxSizeBytes: 100 * 1024 * 1024, // 100 MB
});

// Cache from URL
const result = await cache.putFromUrl("avatar-123", "https://example.com/avatar.jpg");

if (result.status === "created") {
  console.log("Cached:", result.entry.sizeBytes, "bytes");
}

// Retrieve
const entry = await cache.get("avatar-123");
if (entry) {
  <Image source={{ uri: entry.uri }} />
}

API

Configuration

createImmutableFileCache({
  namespace: "default",        // Cache isolation namespace
  defaultTtlMs: undefined,     // Default TTL (ms), undefined = no expiry
  maxSizeBytes: undefined,     // Max size, triggers LRU when exceeded
  autoPruneExpired: true,      // Auto-remove expired entries
  adapter: undefined,          // Custom storage adapter
  hashFn: undefined,           // Custom hash function (default: SHA-256)
  onEvent: undefined,          // Event handler for observability
});

Put Operations

All put operations are immutable — returns { status: "exists" } if the key already exists.

// From URL
await cache.putFromUrl(key, url, { ttlMs, ext, metadata, headers, onProgress });

// From file (Native only)
await cache.putFromFile(key, filePath, options);

// From Blob (Web only)
await cache.putFromBlob(key, blob, options);

// From bytes
await cache.putFromBytes(key, new Uint8Array([...]), options);

Get Operations

// Get with URI (updates lastAccessedAt)
const result = await cache.get(key);
// => { entry, uri } | null

// Check existence
const exists = await cache.has(key);

// Peek without updating access time
const entry = await cache.peek(key);

// Get URI only
const uri = await cache.getUri(key);

// Read-through cache
const result = await cache.getOrPut(key, async (k) => ({
  bytes: new Uint8Array([...]),
  ttlMs: 86400000,
  ext: ".json",
}));

List & Stats

// List entries
const entries = await cache.list({
  sortBy: "createdAt",  // "createdAt" | "lastAccessedAt" | "sizeBytes" | "key"
  order: "desc",        // "asc" | "desc"
  limit: 10,
  offset: 0,
  filter: (e) => e.sizeBytes > 1000,
});

// Get all keys
const keys = await cache.keys();

// Count entries
const count = await cache.count();

// Statistics
const stats = await cache.stats();
// => { entryCount, totalSizeBytes, oldestEntry?, newestEntry? }

Cache Management

// Remove single entry
await cache.remove(key);

// Remove expired entries
await cache.removeExpired();
// => { removedCount, freedBytes, removedKeys }

// LRU prune to size limit
await cache.pruneLru(50 * 1024 * 1024);

// Clear all
await cache.clear();

// Validate & repair index
await cache.validateAndRepair();

// TTL management
await cache.touch(key);              // Refresh access time
await cache.setTtl(key, 86400000);   // Set new TTL
await cache.extendTtl(key, 3600000); // Extend TTL

Lifecycle

// Flush pending writes (when using debounce)
await cache.flush();

// Destroy and release resources
await cache.destroy();

Adapters

The library uses a pluggable adapter architecture:

| Adapter | Platform | Storage | |---------|----------|---------| | RNFS | iOS/Android | react-native-fs | | Web | Browser | Cache Storage + IndexedDB | | Memory | All | In-memory (testing) |

Default Adapter (Auto-selected)

// React Native — uses RNFS adapter
import { createImmutableFileCache } from "@dynlabs/react-native-immutable-file-cache";

// Web — uses Web adapter
import { createImmutableFileCache } from "@dynlabs/react-native-immutable-file-cache/web";

Custom Adapter

import { createImmutableFileCache, createRnfsAdapter } from "@dynlabs/react-native-immutable-file-cache";

const cache = await createImmutableFileCache({
  adapter: createRnfsAdapter({ baseDir: "/custom/path", namespace: "my-app" }),
});

Supported Sources

| Source | RNFS | Web | Memory | |--------|------|-----|--------| | url | ✓ | ✓ | ✓ | | file | ✓ | — | ✓ | | blob | — | ✓ | ✓ | | bytes | ✓ | ✓ | ✓ |

Implementing Custom Adapters

import type { IStorageAdapter } from "@dynlabs/react-native-immutable-file-cache";

const myAdapter: IStorageAdapter = {
  kind: "my-storage",
  rootId: "my-root",
  supportedSources: new Set(["url", "bytes"]),

  async ensureDir(path) { /* ... */ },
  async exists(path) { /* ... */ },
  async remove(path) { /* ... */ },
  async removeDir(path) { /* ... */ },
  async listDir(path) { /* ... */ },
  async readText(path, encoding) { /* ... */ },
  async writeTextAtomic(path, content, encoding) { /* ... */ },
  async stat(path) { /* ... */ },
  async writeBinaryAtomic(path, source, options) { /* ... */ },
  async getPublicUri(path) { /* ... */ },
};

Error Handling

import {
  CacheError,
  UnsupportedSourceError,
  AdapterIOError,
  CorruptIndexError,
  ImmutableConflictError,
  EntryNotFoundError,
} from "@dynlabs/react-native-immutable-file-cache";

try {
  await cache.putFromBlob("key", blob);
} catch (error) {
  if (error instanceof UnsupportedSourceError) {
    console.log(`${error.sourceType} not supported on ${error.adapterKind}`);
  }
}

Observability

const cache = await createImmutableFileCache({
  onEvent: (event) => {
    switch (event.type) {
      case "cache_hit":
        console.log(`HIT: ${event.key} (${event.ageMs}ms old)`);
        break;
      case "cache_miss":
        console.log(`MISS: ${event.key} (${event.reason})`);
        break;
      case "cache_write":
        console.log(`WRITE: ${event.key} (${event.sizeBytes} bytes)`);
        break;
      case "cache_prune":
        console.log(`PRUNE: removed ${event.removedCount} entries`);
        break;
    }
  },
});

Testing

Use the memory adapter for unit tests:

import { CacheEngine, createMemoryAdapter } from "@dynlabs/react-native-immutable-file-cache";

const adapter = createMemoryAdapter("test");
const cache = new CacheEngine({ namespace: "test" }, adapter);
await cache.init();

// Run tests...

adapter._reset(); // Clean up

Contributing

git clone https://github.com/dienp/react-native-immutable-file-cache.git
cd react-native-immutable-file-cache
npm install

npm run typecheck  # Type check
npm run lint       # Lint
npm test           # Run tests
npm run build      # Build

See CONTRIBUTING.md for details.

License

MIT