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

@intempt-technologies/react-native

v0.2.0

Published

Intempt SDK for React Native — event tracking, identity, consent and personalization

Readme

@intempt-technologies/react-native

Intempt SDK for React Native. Wraps intempt-swift on iOS and intempt-android on Android.

Not yet published to npm. Both native prerequisites are satisfied — see Status. The JavaScript layer, the bridge contract and the test suite are complete and reviewable now; publishing this package is the one remaining step.

Status

| Piece | State | |---|---| | JavaScript / TypeScript layer | complete | | TurboModule spec + codegen config | complete | | Contract fixture corpus | complete, 32 fixtures over 29 methods | | iOS native module | complete, typechecked against Intempt 0.2.0 | | Android native module | complete against intempt-android 3.1; 3 push methods reject | | iOS distribution | publishedIntempt 0.2.0 on CocoaPods trunk | | Android distribution | publishedintempt-android 3.1.0 on Maven Central | | npm distribution | not yet published — release workflow ready, no tag cut yet |

Install

npm install @intempt-technologies/react-native
cd ios && pod install

No JavaScript dependencies. Everything this package needs is generated natively.

Requirements: React Native 0.76+, iOS 15.1+, Android API 24+.

Where this SDK runs — and where it does not

This SDK is mobile-only: Android and iOS devices, simulators and emulators. It is a wrapper over the native Intempt SDKs, so it works exactly where they do and nowhere else.

  • No web. react-native-web has no native module. On any platform without one, importing the SDK is safe, but init() (and every other call) rejects with an IntemptError whose isUnsupported is true — catch it and fall back to intemptjs. A cross-platform app should split at bundle time (analytics.native.ts / analytics.web.ts); a runtime Platform.OS check is too late for web bundlers that cannot resolve the native import.
  • No desktop. react-native-windows / react-native-macos are not supported — same isUnsupported rejection as web.
  • No servers. Backend tracking belongs to the Node.js SDK (or PHP/Python), with server credentials — never this package.
  • No Expo Go. Native modules require a dev build: npx expo prebuild, then expo run:android / expo run:ios.
  • Production only. The delivery endpoint (https://api.intempt.com) is compiled into the native SDKs and is not configurable. Credentials from a non-production environment will queue events locally and deliver nothing.
  • Platform gaps are errors, not crashes. A method the current platform's native SDK does not implement (for example push methods on Android before intempt-android covers them) rejects with isUnsupported — the same shape as the wrong-platform case, so one branch handles both.

Quick start

import { init } from '@intempt-technologies/react-native';

const intempt = await init({
  apiKey: 'yourPrefix.yourSecret',
  orgId: 'your-org',
  projectId: 'your-project',
  sourceId: 'your-source',
});

await intempt.track('Signed up', { plan: 'pro', seats: 3, trial: false });

init() resolves to an instance. Every method on it returns a Promise.

Verifying it worked

track() resolves to whether the event was accepted into the queue — not whether it was delivered.

const queued = await intempt.track('Signed up');
if (!queued) {
  // opted out, invalid property, encoding failure, or storage unavailable
}

const delivered = await intempt.flush();
console.log(`${delivered} events delivered`);

API

Identity

await intempt.identify('user-123', { userAttributes: { email: '[email protected]' } });
await intempt.group('acct-9', { accountAttributes: { tier: 'enterprise' } });

await intempt.getProfileId();
await intempt.getSessionId();

await intempt.logOut();  // rotate identity, keep the queue
await intempt.reset();   // rotate identity AND empty the queue

logOut() exists so the next person using a shared device does not inherit the previous identity. reset() additionally discards events not yet delivered. They are not interchangeable.

Events

await intempt.track('Viewed pricing', { source: 'nav' });

await intempt.record('Renewed', {
  userId: 'user-123',
  accountId: 'acct-9',
  data: { mrr: 120 },
});

Property values may be strings, numbers, booleans, null, Date, arrays or nested objects. Date crosses the bridge as ISO 8601 and is re-typed natively.

Commerce

await intempt.productView('sku-1');
await intempt.productAdd('sku-1', 2);
await intempt.productOrdered([
  { productId: 'sku-1', quantity: 2 },
  { productId: 'sku-2', quantity: 1 },
]);

Consent

import { ConsentAction } from 'intempt-react-native';

await intempt.consent(ConsentAction.Accept, 1798761600, { email: '[email protected]' });

Three behaviours to know:

  • Consent transmits even when the user is opted out — a withdrawal has to reach the server.
  • It goes to its own endpoint, unbatched.
  • Reject opts out; Accept opts in. You do not need to call optOut() yourself.

Opt in / out

await intempt.optOut();          // stops collection AND discards the queue
await intempt.optIn();
await intempt.hasOptedOut();
await intempt.isOptedIn();

optOut() discards events already collected. Setting a flag alone would leave events gathered before the objection to be uploaded after it. Queued consent records are preserved — they are the evidence of the decision.

Feature flags

// Ask for a KEY, never a mode. Whether the key names an experiment, a personalization
// or a flag is the platform's business — its serving query filters on channel and
// status and never on mode, so this call does not change when that does.
const on = await intempt.boolVariation('new_checkout', { userId: 'user-123' }, false);

const copy = await intempt.stringVariation('checkout_copy', { userId: 'user-123' }, 'Buy now');
const limit = await intempt.numberVariation('free_shipping', { userId: 'user-123' }, 50);

// A payload is arbitrary JSON. You branch on it; there is no visual editor for a
// native surface, so the value is authored as a payload in the studio.
const theme = await intempt.variation<{ accent: string }>(
  'checkout_theme',
  { userId: 'user-123' },
  { accent: '#000' }
);

const all = await intempt.allFlags({ userId: 'user-123' });

defaultValue is required, and it is a real decision. It is what renders when Intempt cannot be reached — a 5xx, a timeout, an unknown key. Choose the behaviour you already have. A flag lookup never throws for a service failure on either platform.

A wrong-typed value falls back; it is never coerced. A flag configured as a string and read with boolVariation returns your default, not true. Boolean('false') is true, and a silent coercion is indistinguishable from a deliberate value.

A programming error does throw. A blank key, a key outside ^[a-zA-Z0-9_-]+$ (the server's own pattern), or an undefined default is something you can fix, so it fails at the call site rather than quietly returning a default that looks like a flag being off.

FlagContext takes userId and profileId, both optional. Omit profileId and the native SDK supplies the device identifier it already holds — the one that survives sign-in, and therefore the one that keeps a person's assignment stable across it.

variationDetail is deliberately absent. See docs/CONVENTIONS.md for why, and for the requirement that is still open at the platform.

Both native pins resolve to a release that carries the flag surface, as of 2026-08-31: Intempt 0.2.0 on CocoaPods trunk and com.intempt.sdk:intempt-android:3.1.0 on Maven Central. Before those releases existed the pins selected 0.1.0 and 3.0.4, and a consumer's first build failed with cannot find 'FlagContext' in scope. npm run check:native-pins re-measures both registries on every CI run — it downloads what each pin selects and looks for the symbol, so a version number is never taken as evidence.

Recommendations

const products = await intempt.products({ feedId: 'feed-1', count: 10 });

Recommendation feeds are a different thing from flags and from assignment, and are here.

products() defaults fields to productId, title, price, imageUrl, url.

Do not widen it by omission. An unfielded request returns every catalog column including raw ML embedding vectors — measured at 443x the payload for the same ten products, 503 bytes against 222,919. Ask for the columns your screen renders.

Automatic events

await intempt.setAutomaticEvents({
  sessions: true,
  versionChanges: false,
  appStateChanges: false,
});

Only sessions is on by default. An SDK that silently emits events you never asked for is how an event-volume bill surprises someone.

Autocapture

Different from automatic events, and easy to confuse with them. Automatic events are lifecycle facts the SDK already knows. Autocapture hooks the view layer — on iOS it swizzles UIKit — so it installs nothing until you start it.

await intempt.autocapture.configure({
  screenViews: true,
  controlInteractions: true,
});
await intempt.autocapture.start();

await intempt.autocapture.isRunning();
await intempt.autocapture.stop();

configure() alone changes nothing. start() is the point at which instrumentation is installed.

The two options map onto finer native ones. On iOS, screenViews covers screen appearances and exits; controlInteractions covers button presses and value changes. iOS's rawTouches is deliberately not exposed here — a tap on a control already emits its own event, so enabling raw touches alongside it double-counts every button press.

Push

await intempt.setPushToken(hexToken);
await intempt.trackPushOpen(notification.data);
await intempt.trackPushReceived(notification.data);

iOS: setPushToken takes the APNs token as a hex string, since Data has no bridge representation.

Android: registration needs Google Play Services. An emulator running the default system image has none — use a google_apis image, or token registration fails in a way that is hard to read.

Multiple instances

const eu = await init({ ...config, instanceName: 'eu' });
const us = await init({ ...config, instanceName: 'us' });

Each instance has its own credentials, queue and identity.

Errors

Every rejection is an IntemptError with a code.

import { IntemptError, IntemptErrorCode } from 'intempt-react-native';

try {
  await intempt.track('e');
} catch (error) {
  if (error instanceof IntemptError) {
    if (error.isUnsupported) {
      // contract method not on this platform yet
    } else if (error.isRetryable) {
      // transport or 5xx; error.retryAfter may be set
    }
  }
}

A 401 is terminal, not retryable — a bad credential cannot succeed on retry. The queued events are kept, because the data is valid and the integration is what is broken.

Platform gaps

A contract method missing on one platform rejects with unsupported_on_android or unsupported_on_ios plus the method name. It never resolves silently.

Currently unsupported on Android, pending intempt-android 3.0: reset, getProfileId, getSessionId, flush, getFlushInterval, setFlushInterval, experiments, products, getAutomaticEvents, setAutomaticEvents, the whole autocapture object, setPushToken, trackPushOpen, trackPushReceived.

Android also ignores the credentials passed to init() until 3.0 — it reads android/app/src/main/assets/intempt-config.json. init() fails loudly when that file is absent rather than reporting success and sending events nowhere.

The contract

This package implements intempt-swift/docs/SDK-API-CONTRACT.md, the surface every Intempt client SDK conforms to. docs/CONTRACT.md beside it defines the wire.

Conformance is enforced by a fixture corpus, not by review:

node scripts/check-corpus.mjs   # no dependencies; runs before npm install
npm run typecheck               # tsc
npm test                        # 57 tests
./scripts/typecheck-ios.sh      # the Swift bridge against the real SDK

typecheck-ios.sh resolves every Intempt symbol the bridge uses against an actual intempt-swift build. It does not verify React Native itself — the promise blocks are stubbed, so @objc export shape, the RCT_EXTERN_METHOD declarations, autolinking and codegen still need a real pod install and an Xcode build.

Adding a method to the TurboModule spec without a fixture fails the build.

Contributing

npm install
npm run typecheck
npm test               # 131 tests
npm run mutation       # Stryker; gate is 95, currently 99.03
./scripts/typecheck-ios.sh   # Swift bridge against the real SDK

The package runs on Node 18+. The dev toolchain needs Node 22+ — Stryker refuses anything older, which is how CI failed while it passed locally on 23.

Design and open questions: docs/superpowers/specs/2026-08-15-intempt-reactnative-design.md.

License

Apache 2.0. See LICENSE and NOTICE — the package structure is adapted from mixpanel-react-native, also Apache 2.0.