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

despia-native

v1.0.28

Published

Web-native iOS and Android SDK. Biometrics, GPS, in-app purchases, push notifications, and 50+ native APIs - no Swift, no Kotlin.

Readme

Despia Native

Add 50+ native iOS and Android capabilities to any web app (React, Vue, Angular, Svelte, Next.js, vanilla JS) through a single despia() function. Plus native offline support, a local SQLite database, and an on-device streaming CDN, solving 15 years of hybrid-app problems with modern runtime architecture.

npm

Full documentation


Installation

npm install despia-native
# pnpm add despia-native
# yarn add despia-native
import despia from 'despia-native';

Or via CDN:

<!-- UMD -->
<script src="https://cdn.jsdelivr.net/npm/despia-native/index.min.js"></script>
<!-- ESM -->
<script type="module">
  import despia from 'https://cdn.jsdelivr.net/npm/despia-native/+esm';
</script>

Quick Start

import despia from 'despia-native';

// Launch a RevenueCat paywall
despia(`revenuecat://launchPaywall?external_id=${userId}&offering=default`);

// Link the device to your user for OneSignal push
despia(`setonesignalplayerid://?user_id=${userId}`);

// Store a session token behind Face ID / Touch ID (survives reinstall)
await despia('setvault://?key=sessionToken&value=abc123&locked=true');

// Read it back later (prompts biometric)
const { sessionToken } = await despia('readvault://?key=sessionToken', ['sessionToken']);

No initialization. Open your app in the Despia runtime and it works.


Why Despia

Despia is a hybrid app runtime built on the web platform. The web handles what it's good at (logic, UI, routing, state, storage); everything else (biometrics, push, in-app purchases, background GPS, system pickers) is handed to a native engine via a single despia() call.

Works with any web codebase. Bring an existing app or start fresh with any framework, any build tool, any host. No framework lock-in, no proprietary hosting, nothing to migrate.

Full native offline support. @despia/local serves your web build from http://localhost, @despia/powersync adds a native SQLite database that syncs with your backend, and the Local CDN streams cached media from device storage. File transfers stream between native storage and the web layer without loading into the JS heap.

Push, monetization, ads, and attribution are built in: OneSignal, RevenueCat, AdMob, AppsFlyer. Pre-wired into the runtime, configured from the Despia dashboard, called from web code. Builds, signing, and store submission run from the browser. No Xcode, no Android Studio.


Contents


Environment Detection

The SDK only activates inside the Despia runtime. Gate native calls:

const isDespia = navigator.userAgent.toLowerCase().includes('despia');

const isDespiaIOS = isDespia && (
  navigator.userAgent.toLowerCase().includes('iphone') ||
  navigator.userAgent.toLowerCase().includes('ipad')
);

const isDespiaAndroid = isDespia && navigator.userAgent.toLowerCase().includes('android');

if (isDespia) despia('lighthaptic://');

For AI Coding

Full API context: llms.txt.

  • Import as default: import despia from 'despia-native'
  • Never write window.despia directly; the SDK uses it internally
  • There is no .init(), .ready(), or .setup(); the SDK exports a single function with no initialization
  • Do not mock or reimplement the SDK; mock implementations will not trigger the native runtime on real devices
  • Gate native calls with the environment check above
  • For Local Server apps, install via a package manager so the SDK ships with the build
  • Do not await localcdn://write with a watch key; use window.contentServerChange
  • For the upload API, read window.location.host; don't hardcode ports

API Reference

despia(command, watch?)

| Parameter | Type | Description | | --------- | ---------- | ---------------------------------------------- | | command | string | A Despia protocol URL, e.g. 'lighthaptic://' | | watch | string[] | Variable names to wait for |

Returns a Promise that resolves when all watched variables are set, with a 30-second timeout. Long-running work (downloads, purchases, biometric prompts) reports back via global callbacks like window.onRevenueCatPurchase or window.contentServerChange instead. despia.variableName is equivalent to window.variableName.

Protocol format: feature://action?param1=value1&param2=value2


Deployment Models

Remote hydration (default). Binary ships without web assets; Despia fetches the current build on launch. Web updates do not require store resubmission.

Local Server. Build is cached on-device and served from http://localhost. See Local Server.

Version gating. Use despia-version-guard to gate features by minimum runtime version:

import { VersionGuard } from 'despia-version-guard';

<VersionGuard min_version="21.0.3">
  <NewFeature />
</VersionGuard>

Features

RevenueCat In-App Purchases

// Paywall
despia(`revenuecat://launchPaywall?external_id=${userId}&offering=default`);

// Direct purchase
despia(`revenuecat://purchase?external_id=${userId}&product=monthly_premium_ios`);

window.onRevenueCatPurchase() fires when the store confirms a transaction. Verify entitlement before unlocking (via your backend webhook, or by reading the store directly):

window.onRevenueCatPurchase = async () => {
  const { restoredData } = await despia('getpurchasehistory://', ['restoredData']);
  if (restoredData.some(p => p.isActive && p.entitlementId === 'premium')) unlockPremium();
};

In the browser, fall back to a RevenueCat Web Purchase Link.

Intro · Reference


Push Notifications

Permission and OneSignal registration happen automatically at launch. Link the device to your user on every authenticated load:

despia(`setonesignalplayerid://?user_id=${userId}`);

// Check permission
const { nativePushEnabled } = await despia('checkNativePushPermissions://', ['nativePushEnabled']);
if (!nativePushEnabled) despia('settingsapp://');

// Local scheduled notification
despia('sendlocalpushmsg://push.send?s=60&msg=Hello&!#Title&!#https://myapp.com');

Configure OneSignal with Native iOS and Native Android as platforms, then send remote pushes from your backend with the OneSignal REST API.

Intro · Reference


Identity Vault

Encrypted key-value storage backed by iCloud KV (iOS) and Android App Backup. Values survive uninstall and sync across a user's own devices. Set locked=true to require Face ID, Touch ID, or fingerprint on read.

// Write
await despia('setvault://?key=sessionToken&value=abc123&locked=true');

// Read (prompts biometric if locked)
const { sessionToken } = await despia('readvault://?key=sessionToken', ['sessionToken']);

readvault:// throws if the key doesn't exist. Wrap in try/catch.

Docs


OAuth Authentication

Opens a secure browser session (ASWebAuthenticationSession / Chrome Custom Tabs). A deeplink with the oauth/ prefix closes the session and returns to your WebView.

const redirectUri = isDespia
  ? 'https://yourapp.com/native-callback.html'
  : 'https://yourapp.com/auth/callback';

const authUrl = `https://provider.com/oauth/authorize?client_id=xxx&redirect_uri=${encodeURIComponent(redirectUri)}`;

if (isDespia) {
  despia(`oauth://?url=${encodeURIComponent(authUrl)}`);
} else {
  window.location.href = authUrl;
}

In public/native-callback.html (plain HTML, not a framework route, so the hash isn't stripped on navigation):

<script>
  var hash  = new URLSearchParams(window.location.hash.substring(1));
  var token = hash.get('access_token');
  if (token) window.location.href = '{yourscheme}://oauth/auth?access_token=' + encodeURIComponent(token);
</script>

Deeplink format: {yourscheme}://oauth/{path}?params. Without the oauth/ prefix the browser session stays open.

Docs


AppsFlyer Attribution

iOS today, Android soon. Enable in Despia > App > Settings > Integrations > AppsFlyer with your dev key.

// Attribution available on every launch
despia.appsFlyerAttribution
despia.appsFlyerReferrer
despia.appsFlyerUID

// After login
despia('appsflyer://set_user_id?customer_user_id=' + encodeURIComponent(userId));

// Log an event (af_-prefixed events map to Meta and TikTok automatically)
const values = { af_revenue: 9.99, af_currency: 'USD' };
despia('appsflyer://log_event?event_name=af_purchase&event_values=' + encodeURIComponent(JSON.stringify(values)));

Intro · Attribution


GPS Location

window.onLocationChange = (data) => {
  if (data.active) console.log(data.latitude, data.longitude);
};

// buffer in seconds, movement threshold in centimetres
despia('location://?buffer=60&movement=100');

const { locationSession } = await despia('stoplocation://', ['locationSession']);

Add &server=https://api.example.com/track to POST each point as it's recorded.

Docs


Apple Health (HealthKit)

iOS only. Gate behind isDespiaIOS. Permissions are requested on first call per identifier.

// Read
const { healthkitResponse } = await despia(
  'readhealthkit://HKQuantityTypeIdentifierStepCount?days=7',
  ['healthkitResponse']
);

// Write
despia('writehealthkit://HKQuantityTypeIdentifierBodyMass//74.5');

// Observe and POST to your backend on change
despia('healthkit://observe?types=HKQuantityTypeIdentifierStepCount&frequency=hourly&server=https://api.example.com/webhook');

Any HKQuantityTypeIdentifier, HKCategoryTypeIdentifier, HKWorkoutTypeIdentifier, or HKCharacteristicTypeIdentifier works.

Docs


File and Media Operations

Standard HTML file inputs route to native UI with events delivered back to the input:

<input type="file">                                       <!-- Action sheet -->
<input type="file" accept="image/*">                      <!-- Image gallery -->
<input type="file" accept="video/*">                      <!-- Video gallery -->
<input type="file" accept="image/*" capture="environment"><!-- Camera -->
despia('takescreenshot://');
despia('savethisimage://?url=https://example.com/image.jpg');
despia('file://https://example.com/document.pdf');
despia('shareapp://message?=Check%20this%20out&url=https://myapp.com');

File sharing · Camera roll


AdMob Inline Ads

Ads render as real DOM elements via Google's WebView API for Ads. Enable in Despia > App > Settings > AdMob, then use standard AdSense, Google Publisher Tag, or IMA tags. No SDK calls from web.

Inline ads · Rewarded


Web Payment Request API

Apple Pay and Google Pay work through the standard Payment Request API with no Despia-specific calls.

const request = new PaymentRequest(
  [{ supportedMethods: 'https://apple.com/apple-pay' }],
  { total: { label: 'Total', amount: { currency: 'USD', value: '9.99' } } }
);
await (await request.show()).complete('success');

Docs


Local Server

Caches the web build on-device and serves it from http://localhost for offline launch. Updates are detected and swapped atomically on the next launch.

npm install --save-dev @despia/local
// vite.config.js (also Webpack, Rollup, Nuxt, SvelteKit, Astro, Remix, esbuild)
import { despiaLocalPlugin } from '@despia/local/vite';

export default {
  plugins: [despiaLocalPlugin({ outDir: 'dist', entryHtml: 'index.html' })],
};

Or run npx despia-local dist after any build. Deploy as normal.

Intro · Reference


Local CDN

Cache remote files on-device for offline playback and background downloads. Transfers use NSURLSession / WorkManager and continue when the app is closed, with Live Activity (iOS) or system tray (Android) progress.

// Called on download complete
window.contentServerChange = (item) => {
  // item.local_cdn, item.cdn, item.index, item.size, item.status, item.local_path
};

// Start a download. Do NOT await with a watch key.
despia(`localcdn://write?url=${remoteUrl}&filename=videos/clip.mp4&index=clip_1`);

// Query cache
const { cdnItems } = await despia('localcdn://query', ['cdnItems']);

Play cached media from the local_cdn URL. For uploads, POST to http://${window.location.host}/api/upload (Local Server only).

Intro · Reference


Web Storage APIs

The app runs on a real origin, so localStorage, IndexedDB, and Web Crypto work normally. For data that must survive uninstall or be locked behind biometrics, use Identity Vault.


Haptic Feedback

despia('lighthaptic://');   // Subtle
despia('heavyhaptic://');   // Strong
despia('successhaptic://'); // Success
despia('warninghaptic://'); // Warning
despia('errorhaptic://');   // Error

Docs


UI Controls and Styling

despia('spinneron://');
despia('spinneroff://');
despia('hidebars://on');                     // Hide status bar
despia('hidebars://off');
despia('statusbarcolor://{255, 255, 255}');  // RGB
despia('statusbartextcolor://{black}');      // black | white
despia('settingsapp://');                    // Open native app settings
despia('reset://');

Safe areas · App settings


App Information and Device Data

const { versionNumber, bundleNumber } = await despia('getappversion://', ['versionNumber', 'bundleNumber']);
const { uuid }             = await despia('get-uuid://',              ['uuid']);
const { storeLocation }    = await despia('getstorelocation://',      ['storeLocation']);
const { trackingDisabled } = await despia('user-disable-tracking://', ['trackingDisabled']);

Device indexing · Store location · App privacy


Clipboard

const { clipboarddata } = await despia('getclipboard://', ['clipboarddata']);

Docs


Contacts

const { contacts } = await despia('readcontacts://', ['contacts']);

Docs


Capability Reference

Core. Identity Vault, Face ID / Touch ID / fingerprint, background GPS, contacts, clipboard, haptics, native file system, image saving, background audio, local push, status bar controls, safe area CSS variables, device orientation, zoom lock, sleep lock, fullscreen, splash screen, iOS Home Widgets, Siri Shortcuts, native share, AirPrint, screen shield, PkPass for mobile wallets.

SDK bridges. RevenueCat, OneSignal, AppsFlyer, AdMob, HealthKit.

Infrastructure. ATT compliance, vendor ID, store location, jailbreak detection, App Clips, Share Extensions, Home Widget configuration.

Web interception. <input type="file"> routes to the native action sheet; capture opens the camera; accept filters to media gallery. Deeplinks and HTTPS deeplinks are handled natively.


Safe Area

Top and bottom safe area insets are exposed as CSS custom properties:

.header { padding-top: var(--safe-area-top); }
.footer { padding-bottom: var(--safe-area-bottom); }

Left and right insets are not exposed.

Docs


Extending the Runtime

Intercept any custom scheme in WebViewController.swift (iOS) or MainActivity.java (Android), run native code, write the result back as a window variable. The SDK resolves it the same way as built-in schemes.

// iOS
if requestURL.absoluteString.hasPrefix("mycustom://") {
    webView.evaluateJavaScript("window.myResult = '\(runMyNativeCode())';")
    decisionHandler(.cancel)
    return
}
// Android
if (url.startsWith("mycustom://")) {
    webView.evaluateJavascript("window.myResult = '" + runMyNativeCode() + "';", null);
    return true;
}
// Web
const { myResult } = await despia('mycustom://', ['myResult']);

Full Xcode and Android Studio project export is available for custom native code.


Web Native vs React Native

This SDK is for web-native apps (React, Vue, Angular, Svelte, Next.js, Vite, Nuxt, vanilla JS) running inside the Despia runtime. It is not for React Native, Expo, or native mobile development.


Publishing

iOS and Android store deployment runs from the web editor. Cloud Mac Mini infrastructure handles code signing, provisioning, and submission.

iOS · Android


MCP Server

Point your AI assistant at the Despia MCP for the full despia-native API.

Install in Cursor Install in VS Code

For web builders (Lovable, Bolt, v0), paste: https://setup.despia.com/mcp.

Setup guide


How It Works

The SDK is a thin wrapper over a setter-based protocol bridge. Calling despia() assigns the scheme string to window.despia, which the native layer intercepts:

// When you call:
despia('lighthaptic://');

// the SDK does:
window.despia = 'lighthaptic://';

iOS intercepts the assignment in WebViewController.swift; Android intercepts it in MainActivity.java. Results come back as named window variables, which the SDK resolves as a promise via the optional watch array. For long-running work (downloads, purchases, biometric prompts), the native layer calls back via global functions like window.onRevenueCatPurchase or window.contentServerChange instead.

No dependencies, no initialization, no lifecycle to manage.


Support

[email protected]