{"_id":"@abmeter/react-native","name":"@abmeter/react-native","dist-tags":{"latest":"0.1.0"},"versions":{"0.1.0":{"name":"@abmeter/react-native","version":"0.1.0","description":"ABMeter React Native SDK — feature flags and A/B experiments over the abmeter JS core, with AsyncStorage + AppState platform adapters","license":"MIT","homepage":"https://abmeter.ai","repository":{"type":"git","url":"git+https://github.com/abmeter/abmeter-react-native.git"},"keywords":["ab-testing","feature-flags","experiments","abmeter","react-native","expo"],"type":"module","sideEffects":false,"main":"./dist/index.cjs","module":"./dist/index.mjs","types":"./dist/index.d.ts","exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.mjs","require":"./dist/index.cjs"},"./package.json":"./package.json"},"engines":{"node":">=18"},"scripts":{"build":"tsup","test":"vitest run","lint":"eslint . && tsc --noEmit"},"peerDependencies":{"abmeter":">=0.3 <1","@react-native-async-storage/async-storage":">=1.19","react-native":">=0.72"},"devDependencies":{"@types/node":"^22.10.0","eslint":"^9.17.0","tsup":"^8.3.5","typescript":"^5.7.2","typescript-eslint":"^8.18.0","vitest":"^2.1.8"},"_id":"@abmeter/react-native@0.1.0","gitHead":"ba238da7bcc70e360df3cb4344fa3c4f86bfad16","bugs":{"url":"https://github.com/abmeter/abmeter-react-native/issues"},"_nodeVersion":"22.22.3","_npmVersion":"10.9.8","dist":{"integrity":"sha512-kQz/pjKclBNOSTEQ/CIenJYsuemHjTxty8iYVwH3z+b+KzGLoOqwBy7WtoTIA3zfX8ymXYE/LetDFYNU2zXuJQ==","shasum":"475bee24acd3a0a7be8b39934c167401f68625ba","tarball":"https://registry.npmjs.org/@abmeter/react-native/-/react-native-0.1.0.tgz","fileCount":12,"unpackedSize":26706,"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEQCIHBVpNvsd+S10gjEPnGWnDG+0wipZpvoEW0KxRkHAIlAAiBASE7wglH9EgBe+xDVRDobDOEOcItM/TjY1C2+3fprMg=="}]},"_npmUser":{"name":"abmeter","email":"info@abmeter.com"},"directories":{},"maintainers":[{"name":"abmeter","email":"info@abmeter.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/react-native_0.1.0_1786307648233_0.8952946160650102"},"_hasShrinkwrap":false}},"time":{"created":"2026-08-09T20:34:07.936Z","0.1.0":"2026-08-09T20:34:08.383Z","modified":"2026-08-09T20:34:08.628Z"},"maintainers":[{"name":"abmeter","email":"info@abmeter.com"}],"description":"ABMeter React Native SDK — feature flags and A/B experiments over the abmeter JS core, with AsyncStorage + AppState platform adapters","homepage":"https://abmeter.ai","keywords":["ab-testing","feature-flags","experiments","abmeter","react-native","expo"],"repository":{"type":"git","url":"git+https://github.com/abmeter/abmeter-react-native.git"},"bugs":{"url":"https://github.com/abmeter/abmeter-react-native/issues"},"license":"MIT","readme":"# ABMeter React Native SDK\n\n[ABMeter](https://abmeter.ai) is a feature-flag and A/B-testing platform. You define parameters, experiments, and feature flags in the ABMeter Lab; this package reads the value assigned to each user in a React Native or Expo app and reports exposures and events back.\n\n`@abmeter/react-native` is a thin platform adapter over the [`abmeter`](https://www.npmjs.com/package/abmeter) JS core: AsyncStorage instead of `localStorage`, `AppState` instead of tab-visibility events, no cookies, no DOM. The SDK fetches the **values already assigned** to the current user (`POST /api/v1/user-assignments`) and caches them on the device — your experiment setup never leaves the server, so nothing in your app bundle reveals what you are testing or how. Reading a value is a synchronous lookup; an exposure is reported only when a value is actually read.\n\n## Install\n\n```bash\nnpm install @abmeter/react-native abmeter @react-native-async-storage/async-storage\n```\n\n`react-native` itself is a peer dependency your app already has. In an Expo app, install AsyncStorage with `npx expo install @react-native-async-storage/async-storage` so the version matches your Expo SDK.\n\nPin exact versions before production — a range still lets a release you did not deploy reach your app.\n\n## Quick start\n\n```js\nimport * as abmeter from '@abmeter/react-native';\n\nabmeter.configure({\n  apiKey: 'pk_your_publishable_key',\n  // Optional: identify a logged-in user. When omitted, the SDK generates a\n  // stable anonymous track id (a random UUID persisted in AsyncStorage).\n  user: { userId: 'user_123', email: 'user@example.com' },\n});\n\nawait abmeter.ready();\n\nconst buttonColor = abmeter.resolveParameter('button-color');\n\nabmeter.trackEvent('purchase', { price: 4.99 });\n```\n\n`configure` is synchronous, same as the browser core. Identity (the anonymous track id in AsyncStorage), the cached assignment map, and the first assignments fetch all settle behind `ready()` — await it before reading parameters.\n\n`apiKey` must be a **publishable key** (`pk_...`, minted on the Lab API Keys page) — it is safe to embed in client code and is limited server-side to the three endpoints this SDK uses. `configure` refuses any other key: secret keys (`api-...`) are never client-safe.\n\n## Identity\n\n- **Anonymous users** get a generated `track id` — a random UUID persisted in AsyncStorage — sent as the `user_id`. It survives app restarts; it does not survive an app uninstall.\n- **Logged-in users**: pass `user: { userId }` yourself. Keep one randomization unit per experiment — do not switch a user's id across the login boundary mid-experiment.\n- `email` is optional and used only by email-predicate audiences.\n\n### Decide the identity before the first `configure`\n\nCalling `configure` again with a different `userId` is **not** a supported way to\nupgrade an anonymous user to a logged-in one. Two things change that you cannot\nundo:\n\n- **The user may flip variant.** Assignments are fetched per user id, so the second\n  `configure` gets a different map. Whatever the app already rendered was for the old\n  identity.\n- **Attribution splits.** Exposures already recorded carry the old id, everything after\n  carries the new one, and results match events to a user by the id their exposure was\n  recorded under. The two halves never meet — no error, just a metric quietly missing\n  conversions.\n\nQueued telemetry itself is safe: `configure` drains the previous configuration in the\nbackground rather than discarding it. Use `await reset()` first if you need certainty\nthat the drain completed.\n\nIf the user is unknown until an auth request returns, either **stay anonymous** for the\nlife of the experiment and let the generated track id be the randomization unit, or\n**configure once auth resolves** and render defaults until then.\n\n## Event submission\n\nExposures and events are queued and submitted in small batches in the background. The queue also drains when the app leaves the foreground (`AppState` → `inactive`/`background`) with fire-and-forget requests — background JS keeps running briefly on both platforms, and the browser tab-death tricks (`keepalive`, `sendBeacon`) are harmless no-ops here. Call `abmeter.flush()` at moments you want an eager drain.\n\nIf the OS kills the app before a background flush completes, that tail of the queue is lost. The SDK treats network loss as expected and never throws.\n\n## API\n\n| Function | Description |\n| --- | --- |\n| `configure(options)` | Initialize the SDK. Options: `apiKey` (required), `baseUrl`, `user: { userId?, email? }`, `flushInterval` (ms, default 1000), `logger`, `errorCallback`, `platform` (override the RN adapter). |\n| `ready()` | Resolves once the first assignment fetch has settled. |\n| `resolveParameter(slug)` | Resolved value for this user, or `undefined` if unknown. Queues an exposure lazily (deduplicated over a 10-minute window). |\n| `getExposure(slug)` | The exposure metadata for a parameter (`null` for feature-flag/default resolutions), without queueing anything. |\n| `trackEvent(eventSlug, customFields?)` | Queue an event for the configured user. |\n| `flush()` | Drain the queue now (returns a promise). |\n| `reset(options?)` | Drain fully and tear down timers/listeners. `configure` again to restart. |\n\nAll read/track functions are error-safe: failures are logged (and passed to `errorCallback` when configured) and return a safe default instead of throwing.\n","readmeFilename":"README.md","_rev":"1-3f30334c6c41e474d968a666ad758e23"}