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

@kilden-io/expo

v0.3.0

Published

Kilden Expo / React Native SDK — client-side events, identity and feature flags.

Readme

@kilden-io/expo

npm ci license

Kilden is an open customer data platform: analytics, campaigns and session replay on one event pipeline. This is the client SDK for Expo and React Native apps — events, identity and feature flags. For browsers use kilden; for your backend use one of the server SDKs.

Install

npx expo install @kilden-io/expo expo-crypto @react-native-async-storage/async-storage

Bare React Native works too: the SDK falls back from expo-crypto to any crypto.getRandomValues polyfill (e.g. react-native-get-random-values).

First event

import kilden from "@kilden-io/expo";

kilden.init("wk_your_public_key");

kilden.track("order_completed", { revenue: 99.9, currency: "CLP" });
kilden.screen("Checkout", { step: 2 });

Use your project's public write key (wk_...). Never put a secret key (sk_...) in an app: anything inside a bundle can be extracted, and a leaked secret key lets anyone write verified server events into your project. The constructor rejects secret keys outright.

Identity

Users start anonymous (a persisted anon_ id in AsyncStorage). When they log in:

kilden.identify("user_42", { plan: "pro", email: "[email protected]" });
// ... on logout:
kilden.reset();

identify links the anonymous history to the user; reset rotates to a fresh anonymous identity and drops the identity token and flag cache.

Identity verification

Client events are unverified by default — anyone can send events with any write key they can see. To get verified=true events (required by the messenger, respected by campaigns), your backend mints a short-lived JWT for the logged-in user and this SDK carries it. The Kilden server SDKs give you that endpoint for free (POST /kilden/identity in Laravel, or IdentitySigner anywhere else):

kilden.init("wk_your_public_key", {
  getIdentityToken: async () => {
    const response = await fetch("https://your-backend.example/kilden/identity", {
      headers: { Authorization: `Bearer ${yourSessionToken}` },
    });
    if (!response.ok) return null;
    const { token } = await response.json();
    return token;
  },
});

The SDK refreshes the token 60 seconds before it expires and once on a 401. Only mint tokens for users your backend has actually authenticated.

Sessions and screens

Every event carries a $session_id (UUID v7) that rotates after 30 minutes of inactivity — the same session semantics as the web SDK, persisted in AsyncStorage so a quick app restart stays in the same session. Nothing to configure. Screen views are their own event:

kilden.screen("Checkout", { step: 2 }); // emits $screen with $screen_name

With react-navigation or expo-router, let the SDK report screens for you — pass your navigation container ref and every distinct route becomes a $screen (the static route name, e.g. user/[id], never the resolved params):

import kilden, { attachNavigationTracking } from "@kilden-io/expo";

// react-navigation: <NavigationContainer ref={navigationRef} onReady={...}>
const detach = attachNavigationTracking(navigationRef, kilden);

The SDK also emits $app_opened (cold start and foreground return, with $from_background) and $app_backgrounded from AppState. Turn them off with trackAppLifecycle: false.

Exceptions (opt-in)

captureExceptions: true wraps React Native's global ErrorUtils handler and reports uncaught errors as $exception ($exception_type, $exception_message, $exception_fatal, $exception_stack). The message and stack are scrubbed before they enter the queue — email addresses and long digit runs are redacted, and the message is capped at 1000 characters — because exception messages routinely drag PII along. The previously installed handler (dev red screen, crash reporters) always still runs.

Session replay (opt-in, beta)

Visual replay records your app as periodic screenshots and synthesizes an rrweb stream Kilden's player understands — watch real sessions next to the event timeline. The pixels capture everything visible on screen, so it takes a double opt-in: sessionReplay: true here AND enabling mobile replay for the project in the panel (which asks you to accept exactly that risk).

npx expo install react-native-view-shot   # capture (bundled in Expo Go)
npm install jpeg-js                       # only if you use <KildenMask>

Adding react-native-view-shot changes your native fingerprint: apps using runtimeVersion: { policy: "fingerprint" } need a new native build — this cannot ship over OTA.

kilden.init("wk_your_public_key", {
  sessionReplay: true,
  replayDenylist: ["settings/payment"], // screens that never produce frames
});

Frames are captured on navigation, taps, foreground returns and a ≥10s heartbeat; identical frames are dropped, and a recording caps at 300 frames or 15 minutes. Sampling is per session against the rate you set in the panel. pauseSessionRecording() / resumeSessionRecording() gate capture manually.

Masking is opt-in: wrap sensitive views and their rects are blacked out before the frame leaves the device (an unmeasurable mask drops the whole frame — never an unmasked upload). Wrap your root in the provider if you want taps as markers in the player:

import { KildenMask, KildenReplayProvider } from "@kilden-io/expo/react";

<KildenReplayProvider>
  <App />
</KildenReplayProvider>

<KildenMask>
  <BalanceCard />
</KildenMask>

Feature flags

Remote evaluation against /decide, cached 30 seconds per identity:

if (await kilden.isFeatureEnabled("new_checkout")) { /* ... */ }

const variant = await kilden.getFeatureFlag("checkout_test", {
  personProperties: { plan: "pro" }, // this evaluation only, bypasses the cache
  default: "control",                // returned when Kilden can't answer
});

Flag reads never throw and never retry — a late flag answer is useless, so a failed read returns your default. The first read of each flag emits a $feature_flag_called exposure event.

Batching and shutdown

Events queue in memory and flush every 5 seconds, at 20 queued events, and when the app goes to background (AppState — the mobile stand-in for sendBeacon). Delivery retries with exponential backoff and honors Retry-After; the queue is bounded (10,000 events — at the cap the new event is dropped, never history).

By default the queue lives in memory: events still queued when the OS kills the app are lost (React Native exposes no "app will terminate" signal — the background flush is the last reliable hook). Set persistQueue: true to write the queue through to storage instead: undelivered events are restored and sent on the next launch. Delivery is at-least-once — Kilden dedups by event uuid, so a batch that was in flight during a kill never double-counts. Call close() if you have a natural shutdown point; kilden.flush() forces a flush any time.

Options

kilden.init("wk_your_public_key", {
  apiHost: "https://ingest.kilden.io",
  flushAt: 20,
  flushIntervalMs: 5000,
  maxQueueSize: 10000,
  requestTimeoutMs: 10000,
  debug: false,             // verbose logging + $-prefix warnings
  enabled: true,            // false = full no-op (e.g. in development)
  persistQueue: false,      // true = queue survives app kills (see above)
  trackAppLifecycle: true,  // $app_opened / $app_backgrounded from AppState
  captureExceptions: false, // uncaught errors as $exception (scrubbed)
  identityToken: undefined, // initial JWT, see identity verification
  getIdentityToken: undefined,
  context: undefined,       // () => extra $ properties for every event
  storage: undefined,       // KeyValueStorage override (defaults to AsyncStorage)
  transport: undefined,     // Transport override (defaults to fetch)
  getRandomValues: undefined,
  appState: undefined,
});

createClient(writeKey, options) returns an isolated client instance if you prefer not to use the singleton.

Spec

Wire format, retry policy and payload behavior are governed by kilden-sdk-spec — the same authority the five server SDKs follow. This repo's CI replays the frozen payload vectors against the spec's mock capture server.

Community

Questions → GitHub Discussions. Docs → kilden.io/docs.

License

MIT