@capuchoo/updater
v0.9.0
Published
App-side runtime for Capucho OTA and native updates in Capacitor apps
Maintainers
Readme
@capuchoo/updater
The app-side runtime for Capuchoo: it asks your update server
what to do, downloads OTA bundles or native binaries, and drives the install. Built on
@capgo/capacitor-updater.
npx @capuchoo/cli setupThat installs this package, the @capgo/capacitor-updater plugin it drives and @capacitor/app
into your app - they must be the app's own dependencies for cap sync to wire up their native
halves - and then runs cap sync. Add --native if the app installs APKs itself.
Required peers, if you would rather add them yourself: @capacitor/core, @capacitor/app,
@capgo/capacitor-updater. Optional: @capacitor/device (reports OS version and emulator flag),
vue (only for the /vue entry point), and @capacitor/filesystem, @capacitor/network,
@capacitor/file-transfer, @capawesome-team/capacitor-file-opener - loaded on demand, and only by
the native-APK path.
Three things, in order
1. Call notifyAppReady() first
// src/main.ts
import { notifyAppReady } from "@capuchoo/updater";
void notifyAppReady();Early, and unconditionally. It confirms that the bundle currently running booted. If the plugin
does not hear it within appReadyTimeout (10 s), it concludes the bundle crashed and rolls back to
the previous one — so gating this call behind a condition, or awaiting a network request before it,
reverts working updates. It is not a gate on auto-update.
2. Configure the plugin through capuchooUpdaterConfig()
// capacitor.config.ts
import { capuchooUpdaterConfig } from "@capuchoo/updater/capacitor";
plugins: {
CapacitorUpdater: capuchooUpdaterConfig({
apiUrl: process.env.VITE_UPDATE_API_URL,
channel: process.env.VITE_UPDATE_CHANNEL,
}),
}This returns autoUpdate: "onlyDownload", because the app drives the install itself — with
autoUpdate: true the plugin and your UI both apply bundles, and a device can download the same
bundle twice or reload mid-prompt. It also throws on an empty apiUrl rather than accepting
one: an empty update URL does not fail at runtime, it silently disables updates, which ships a build
that never checks.
3. Drive it from your UI
import { useUpdater } from "@capuchoo/updater/vue";
const updater = useUpdater();
await updater.init();UpdaterState exposes checking, downloading, installing, updateAvailable, currentUpdate,
progress, cachedPath, error, statusMessage and lastCheckMessage.
Without Vue, use the services directly: checkForUpdate(), downloadNativeUpdate(),
openNativeInstaller(), applyOtaUpdate(), getCurrentBundle(), discardBundle().
Errors are not "up to date"
checkForUpdate() throws UpdateCheckBlockedError when the server reports a configuration problem
— an unknown channel, or an environment mismatch between the build and the channel. Show it.
Treating every non-update response as "nothing to do" is how a broken channel goes unnoticed for
weeks.
UpdaterConfigError means the runtime was never configured — usually a missing apiUrl.
One request decides everything
The runtime asks POST /api/update and nothing else. It is the only endpoint that consults the
channel's assigned native version and an OTA bundle's min_update_version gate, and it sends the
real current bundle version rather than a constant. Native updates outrank OTA, because the server
can legitimately return both.
Stability
Pre-1.0: the surface may change between minor versions.
