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

@mrvltechnologies/attribr-react-native

v0.2.3

Published

Tier 1 JS-only Attribr client for React Native / Expo apps. No native module, no CocoaPods/Gradle dependency.

Readme

@mrvltechnologies/attribr-react-native

Tier 1 — JS-only React Native / Expo client for Attribr. No native module, no CocoaPods dependency, no Gradle dependency, no Web SDK equivalent (see "What this is (and isn't)" below).

What this is (and isn't)

This package standardizes the REST-client pattern that four MRVL RN/Expo apps (ARK-by-MRVL, KonnectBusiness, KonnectbyMRVL, campus-fellowship-rn) were each independently hand-rolling — with real, divergent bugs as a result: campus-fellowship-rn posted to a non-existent endpoint, KonnectBusiness omitted a required auth header, KonnectbyMRVL's revenue path called a native module that was never implemented. See CHANGELOG.md for the full history this package is meant to prevent from recurring.

This is Tier 1 — it does not yet cover:

  • SKAdNetwork conversion values (registerSKAN/updateConversionValue on the Swift SDK) — no Android equivalent exists either; this is inherently a native-platform concern.
  • Native push-token registration for uninstall detection.
  • Native uninstall detection.
  • Automatic StoreKit / Google Play purchase hooks — you call trackRevenue() yourself, from your own IAP success handler, the same way every existing hand-rolled client in this portfolio already does.
  • A native module or Expo config plugin.
  • A Web/browser SDK. This package is React Native / Expo only — it does not run in, and is not tested against, a browser or Node web server environment.

Those are Tier 2 (or out of scope entirely), and not implemented in this repo. Do not claim otherwise in any integration doc.

Status

Public on npm. Originally built and pilot-tested internally across four MRVL portfolio apps before public release — see CHANGELOG.md for the full pilot history and version log.

Install

npm install @mrvltechnologies/attribr-react-native

Peer dependencies (both optional, but required for the default device-hash strategy — see "Device hash" below):

npx expo install expo-application expo-crypto

Quick start

import * as Attribr from '@mrvltechnologies/attribr-react-native';

// Once, at app start:
Attribr.initialize({
  // Use a Client key (sdk_ingest scope) from Settings → API Keys, never a
  // Full key — this string ships inside your JS bundle and is extractable
  // by anyone who unpacks the app. A Client key can only submit events; it
  // cannot read your Attribr dashboard data even if extracted. See SECURITY.md.
  apiKey: 'attr_live_yourkey…',       // from your Attribr dashboard
  appId: 'com.yourcompany.yourapp',   // omit if expo-application is installed — it's read automatically
  supabaseAnonKey: 'eyJ...',          // required only for trackRevenue() — see below
  debug: __DEV__,
});

// After your GDPR/consent banner resolves:
Attribr.setConsent('granted');

// On every cold start / foreground:
await Attribr.trackLaunch();

No network request is made before setConsent('granted'). Every call before that resolves immediately with { ok: false, skipped: true, reason: 'consent_not_granted' } — never a crash, never a pending network request firing later.

API

initialize(config: AttribrConfig): void;
isConfigured(): boolean;
resetForTesting(): void;

setConsent(state: 'unknown' | 'granted' | 'revoked'): void;

trackLaunch(options?: TrackLaunchOptions): Promise<TrackResult>;
trackEvent(event: TrackEventInput): Promise<TrackResult>;
trackRevenue(event: TrackRevenueInput): Promise<TrackResult>;
attributeInstall(input: AttributeInstallInput): Promise<TrackResult>;

Every tracking call returns a TrackResult and never throws — matching the "revenue tracking is non-critical, never block the purchase flow" convention already used everywhere else in this portfolio:

interface TrackResult {
  ok: boolean;
  skipped?: boolean;   // true = no network request was attempted at all
  reason?: string;      // why it was skipped, e.g. 'consent_not_granted'
  status?: number;      // HTTP status, when a request was actually made
  error?: string;       // network-level error message, when applicable
}

Full type definitions are in src/types.ts. See examples/basic-usage.ts for a worked example of every method.

trackEvent() — backend-supported as of 2026-08-29

attribr-track now persists name/value/currency/metadata for trackEvent() calls as a distinct row (attribr_raw_events.event_type = 'custom'), separate from install/launch records — confirmed against production: the resulting row carries the exact event_name/value/ currency/metadata sent, and creates zero attribr_installs side effect (a custom event does not require a prior trackLaunch() call and does not consume install quota). Fixed at the backend in Attribr-by-MRVL/supabase/functions/attribr-track — no change was needed in this package, since the client was already sending the correct payload; the backend simply wasn't reading it. See that repo's session notes for the fix if you need the exact detail.

trackRevenue() needs supabaseAnonKey — this is not optional

attribr-revenue (unlike attribr-track/attribr-attribute) has Supabase's platform-level JWT gate enabled. A call with only X-Attribr-Key 401s before Attribr's own validation code ever runs — this exact bug was independently found and fixed in both KonnectBusiness and KonnectbyMRVL during this portfolio's hygiene work (see CHANGELOG.md). This package requires Authorization: Bearer <supabaseAnonKey> on every trackRevenue() call. If supabaseAnonKey isn't configured, trackRevenue() skips safely (reason: 'missing_supabase_anon_key') rather than making a call that's guaranteed to fail.

supabaseAnonKey is Supabase's public/publishable anon key for Attribr's project — safe to embed in client code by design, not a secret. Ask in the Attribr dashboard or check an existing integration if you don't have it.

Device hash

Attribr's backend requires a 64-character lowercase-hex SHA-256 string as device_hash on every call — never a raw device identifier, and never IDFA. The default strategy:

  1. iOS: Application.getIosIdForVendorAsync() (IDFV) → SHA-256.
  2. Android: Application.getAndroidId() → SHA-256.
  3. Hashing via expo-crypto.

Both expo-application and expo-crypto are optional peer dependencies. If they aren't installed (e.g. a bare React Native app with no Expo modules at all), the default strategy throws DeviceHashUnavailableError internally and every tracking call skips safely (reason: 'device_hash_unavailable') — it never falls back to sending something else in device_hash's place.

Known limitation: there is no dependency-free way to get a stable, privacy-safe device identifier in bare React Native without either Expo modules or a real native module (Tier 2, not this package). If your app can't install expo-application/expo-crypto, provide your own strategy:

Attribr.initialize({
  apiKey: '...',
  getDeviceHash: async () => {
    // Must resolve to a 64-char lowercase-hex string, e.g. SHA-256 of a
    // stable, install-scoped identifier your app already manages. Never
    // return a raw device ID, and never IDFA.
    return mySha256Hash;
  },
});

Security

See SECURITY.md.

License

MIT. Copyright 2026 MRVL Technologies.