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

cap-update

v8.0.3

Published

Capacitor native plugin for over-the-air (OTA) live bundle updates using Bridge.setServerBasePath — no local HTTP server needed.

Readme

A lightweight, secure, and high-performance Over-The-Air (OTA) live update solution for Capacitor apps. Download, manage, and apply web bundle updates at runtime without rebuilding or resubmitting your app.

Cap Update leverages Capacitor's native engine to serve web assets directly. This ensures maximum performance, zero port conflicts, and full compatibility with all your existing plugins.

npm Capacitor


Key Features & Benefits

  • 🚀 Native Performance: Uses Capacitor's built-in Bridge.setServerBasePath() API. No local HTTP server, no extra overhead.
  • 🔒 Secure by Design: Maintains the https://localhost origin. No cleartext traffic issues or CORS headaches.
  • 🛠️ Seamless Integration: Fully compatible with all Capacitor plugins since it uses the standard platform engine.
  • 📦 Atomic Updates: Bundles are downloaded and extracted in the background, ready to be applied instantly or on the next restart.
  • 🛡️ App Store Friendly: Uses standard APIs that don't trigger "embedded server" flags during review.
  • 🪶 Zero Dependencies: Lightweight footprint on both iOS and Android.

Compatibility

| Plugin Version | Capacitor Version | Status | | -------------- | ----------------- | ---------- | | 8.0.x | >=8.x.x | Supported | | 7.0.x | 7.x.x | Supported |


Install

npm install cap-update
npx cap sync

iOS

No additional configuration required. The plugin uses SSZipArchive for ZIP extraction (included automatically).

Android

No additional configuration required. Uses Java's built-in java.util.zip — zero extra dependencies.


Quick Start

import { CapUpdate } from 'cap-update';

// Full automated update cycle
const result = await CapUpdate.sync({
  url: 'https://your-server.com/api/updates/check',
  channel: 'production',
});

if (result.updated) {
  console.log('App updated to latest bundle!');
}

API Reference

downloadBundle(options)

Download a ZIP bundle from a URL and extract it locally.

await CapUpdate.downloadBundle({
  url: 'https://your-server.com/bundles/v1.2.0.zip',
  bundleId: 'v1.2.0',
  checksum: 'sha256-abc123...',  // optional integrity check
});

| Param | Type | Description | |---|---|---| | url | string | (required) URL of the ZIP bundle | | bundleId | string | Identifier for this bundle. Auto-generated from URL if omitted | | checksum | string | SHA-256 checksum for integrity verification |

Returns: Promise<BundleInfo>


setBundle(options)

Set a downloaded bundle as the active bundle. By default, it takes effect on the next app restart. Set immediate: true to reload the WebView immediately.

// Apply on next restart
await CapUpdate.setBundle({ bundleId: 'v1.2.0' });

// Apply immediately (reloads WebView)
await CapUpdate.setBundle({ bundleId: 'v1.2.0', immediate: true });

| Param | Type | Default | Description | |---|---|---|---| | bundleId | string | — | (required) ID of the downloaded bundle | | immediate | boolean | false | Reload WebView immediately |


getBundle()

Get info about the currently active bundle.

const info = await CapUpdate.getBundle();
// { bundleId: 'v1.2.0', path: '/data/.../bundles/v1.2.0', status: 'active' }
// { bundleId: 'built-in', status: 'active' }  // when using default

Returns: Promise<BundleInfo>


getBundles()

List all downloaded bundles on the device.

const { bundles } = await CapUpdate.getBundles();
// [{ bundleId: 'v1.1.0', status: 'downloaded' }, { bundleId: 'v1.2.0', status: 'active' }]

Returns: Promise<{ bundles: BundleInfo[] }>


deleteBundle(options)

Delete a specific downloaded bundle. Cannot delete the currently active bundle.

await CapUpdate.deleteBundle({ bundleId: 'v1.1.0' });

reset(options?)

Reset to the built-in bundle (the one shipped with the app binary).

// Reset and reload immediately
await CapUpdate.reset({ immediate: true });

// Reset on next restart
await CapUpdate.reset();

reload()

Reload the WebView. Useful after calling setBundle() without immediate: true.

await CapUpdate.reload();

sync(options)

Automated update cycle: check for updates → download → apply → reload.

const result = await CapUpdate.sync({
  url: 'https://your-server.com/api/updates/check',
  channel: 'production',
});

console.log(result.updated);       // boolean
console.log(result.latestBundle);   // bundle metadata from server

| Param | Type | Default | Description | |---|---|---|---| | url | string | — | (required) Update check endpoint URL | | channel | string | 'production' | Deployment channel |

Returns: Promise<SyncResult>


checkForUpdate(options)

Check if an update is available without downloading it.

const result = await CapUpdate.checkForUpdate({
  url: 'https://your-server.com/api/updates/check',
  channel: 'staging',
});

if (result.isUpdateAvailable) {
  console.log('New version:', result.latestBundle);
  console.log('Download from:', result.downloadUrl);
}

Returns: Promise<CheckUpdateResult>


Update Server Protocol

The sync() and checkForUpdate() methods send a GET request to your endpoint with these headers:

| Header | Description | |---|---| | X-Device-Identifier | Unique device ID | | X-Platform | android or ios | | X-Bundle-Id | Currently active bundle ID | | X-Channel | Deployment channel |

Expected JSON Response

{
  "is_update_available": true,
  "latest_bundle": {
    "id": "v1.2.0",
    "version": "1.2.0"
  },
  "current_bundle": {
    "id": "v1.1.0"
  },
  "download_url": "https://your-server.com/bundles/v1.2.0.zip"
}

Types

interface BundleInfo {
  bundleId: string;
  status: 'active' | 'downloaded' | 'built-in';
}

interface DownloadBundleOptions {
  url: string;
  bundleId?: string;
  checksum?: string;
}

interface SetBundleOptions {
  bundleId: string;
  immediate?: boolean;
}

interface SyncOptions {
  url: string;
  channel?: string;
}

interface SyncResult {
  updated: boolean;
  latestBundle?: any;
}

interface CheckUpdateResult {
  isUpdateAvailable: boolean;
  latestBundle?: any;
  currentBundle?: any;
  downloadUrl?: string;
}

How It Works

┌─────────────────────────────────────────────────┐
│  Your App (WebView)                             │
│  Served via Capacitor's built-in local server   │
│  URL: https://localhost (unchanged)             │
└──────────────────┬──────────────────────────────┘
                   │
          bridge.setServerBasePath(path)
                   │
     ┌─────────────┴─────────────┐
     │  Default: assets/public   │  ← Built-in bundle (APK/IPA)
     │  Custom:  files/bundles/  │  ← Downloaded OTA bundles
     └───────────────────────────┘
  1. Download — ZIP bundle is downloaded and extracted to {app_files}/cap_update_bundles/{bundleId}/
  2. Setbridge.setServerBasePath(path) tells Capacitor's server to serve from the new directory
  3. Persist — The path is saved so it survives app restarts
  4. Reload — WebView reloads, now serving the updated assets

No secondary HTTP server. No port management. Just Capacitor doing what it already does — serving files — from a different folder.


License

MIT