@rabtstore/rabt-analytics
v0.6.0
Published
Shared analytics contract and tracking API for Rabt applications.
Readme
@rabtstore/rabt-analytics
Shared analytics API for Rabt applications. It owns the common
track() interface and event-name rules, while each application connects the SDKs appropriate to its runtime. Browser
applications send the same events to Firebase and Meta Pixel; native apps use Firebase.
Runtime routing
| Surface | Transport |
| --- | --- |
| Expo iOS and Android | @react-native-firebase/analytics |
| Expo web | Firebase Web Analytics SDK + Meta Pixel |
| Marketing site and Creator Pages | Firebase Web Analytics SDK + Meta Pixel |
Google Tag Manager is not part of this package or routing model.
Usage
Configure the transport once at application startup:
import { configureAnalytics } from '@rabtstore/rabt-analytics'
configureAnalytics({
sendEvent(eventName, parameters) {
return firebaseTransport.logEvent(eventName, parameters)
},
sendView(view) {
return firebaseTransport.logView(view)
},
})Then every feature uses the same function:
import { track } from '@rabtstore/rabt-analytics'
void track('sign_up_completed', { method: 'email' })Events that the Rabt API also sends through Meta's Conversions API carry an
event_id parameter so Meta counts them once. The Meta Pixel transport forwards
it as eventID and strips it from the payload; other transports receive it as
an ordinary parameter.
void track('sign_up', { method: 'email', surface: 'app', event_id: `registration:${store.uuid}` })Framework-specific routers report navigation through the shared view API:
import { trackView } from '@rabtstore/rabt-analytics'
void trackView({
name: 'app_dashboard_home',
path: '/dashboard',
title: 'Dashboard',
location: 'https://app.rabt.store/dashboard',
surface: 'app',
parameters: {
creator_username: 'john',
block_id: 'abc123',
},
})The web transport converts this to page_view; the native transport converts
it to screen_view. The package removes query strings and fragments before
passing a view to either transport. Optional parameters carry route context
without putting dynamic identifiers into the normalized view name or path.
Every view uses one of five surfaces: marketing, creator, app, blog, or
help. Its normalized name must begin with the same surface followed by an
underscore. Rabt view names follow <surface>_<area>_<view>[_<state>], such as
marketing_site_home, creator_service_product, or
app_dashboard_settings.
The package intentionally does not depend on either Firebase SDK. Importing a native Firebase module into a browser bundle, or the Web SDK into a native bundle, would make the shared package platform-dependent. The application-level transport is the thin boundary that prevents that coupling.
Distribution
The source repository is private, while the package is published publicly on
npm as @rabtstore/rabt-analytics. Applications can install it without registry
credentials.
Releasing a new version
Pushing main does not publish a package. Normal releases are published by the
GitHub Actions workflow when a matching version tag is pushed.
One-time npm setup
On the npm package settings page, configure a Trusted Publisher with:
- Provider: GitHub Actions
- GitHub organization:
rabtstore - Repository:
analytics - Workflow filename:
publish.yml - Allowed action:
npm publish
This lets GitHub publish through short-lived OIDC credentials. No npm token is stored in GitHub.
Normal automated release
Always publish a new version number; npm does not allow replacing an existing
version. For example, to release 0.2.0:
npm version minor --no-git-tag-version
npm test
git add package.json
git commit -m "release analytics 0.2.0"
git push origin main
git tag rabt-analytics-v0.2.0
git push origin rabt-analytics-v0.2.0The tag starts .github/workflows/publish.yml, which tests and publishes the
version in package.json. Confirm that workflow succeeds before updating a
consumer:
pnpm add @rabtstore/[email protected]Manual fallback
If Trusted Publishing or GitHub Actions is unavailable, authenticate to npm and publish the new version directly:
npm login --registry=https://registry.npmjs.org
npm test
npm publish --access publicDo not run the workflow or push a release tag for the same version afterward; npm will reject a duplicate publication.
Firebase Web configuration must be supplied separately to Expo web and the
landing site. Native GoogleService-Info.plist and google-services.json files
cannot configure a browser application.
Multiple destinations (0.5.0)
configureAnalytics() accepts one transport or an array. track() and
trackView() fan out to all transports with independent error handling. A
transport can return false when unavailable. Tracking returns true if at
least one destination accepted the event, not proof of delivery to a vendor.
There are no automatic retries, so a failed destination does not cause duplicate
purchases at a successful one. Calls before configuration return false.
import { configureAnalytics } from '@rabtstore/rabt-analytics'
import { createMetaPixelTransport } from '@rabtstore/rabt-analytics/meta-pixel'
configureAnalytics([
firebaseTransport,
createMetaPixelTransport({
pixelId: '2141315989757961',
hostnames: ['rabt.store', 'www.rabt.store'],
pageViewSurfaces: ['marketing'],
}),
])The browser adapter owns script loading, initialization, and a strict acquisition
allowlist. Only events with surface: 'app' qualify:
| Rabt event | Meta event |
| --- | --- |
| sign_up | CompleteRegistration |
| trial_started | StartTrial |
| subscription_started | Subscribe |
Customer purchases, checkout starts, product interactions, login, all other
custom events, and creator-store/app page views remain Firebase-only. Marketing PageViews can
be explicitly enabled with pageViewSurfaces: ['marketing'] to initialize the
pixel on advertising entry pages. Ignored events do not load the Meta script. Signup forwards only method; subscription forwards
only plan, value, and currency when supplied.
Applications supply the pixel ID and exact allowed hosts. The adapter safely
skips SSR and other hosts. The separate subpath keeps browser integration out
of native bundles. Subscription completion must be verified by the application
server before calling track('subscription_started', { surface: 'app', plan }).
A checkout click or success URL alone is not a completed subscription.
Consumer packaging
Version 0.5.0 is published on npm. Consumers depend on the registry version
(pnpm add @rabtstore/[email protected]); no checked-in tarball is needed.
Publish a new version before changing a consumer to depend on new package
behavior.
Deduplication
track(name, parameters, { deduplicationKey }) records acceptance independently
for each named transport. Firebase succeeding never marks a declined Meta event
as accepted. Configure storage with getItem/setItem for persistence across
reloads. Storage failures fall back to in-memory deduplication. Concurrent calls
share a pending send. Acceptance means queued/handed to an SDK, not confirmed
network delivery (browser blockers can still prevent delivery).
Rabt web emits trial_started after a successful Stripe checkout return is
verified by /billing/verify and refreshed billing confirms the same trialing
Stripe subscription. Its key is per user/subscription and distinct from the
paid conversion key. An ordinary visit to an existing trial does not emit it.
It sends StartTrial to Meta and trial_started (custom event) to GA4, with plan
but no paid value. A user who never returns from checkout is not reported by
this browser-only trial tracking; server-side delivery would be needed for that.
For Rabt subscriptions the API records trial_converted_at once when a stored
Stripe subscription transitions from trialing to active. Existing active
subscriptions are not backfilled. The app checks server billing state on every
billing-page visit and emits only for active Stripe subscriptions with this flag,
using a user/subscription key. The flag works across browsers; delivery still waits
for a billing-page visit, and deduplication remains local to each browser.
Deploy the API migration before the app. Older API responses without the flag
safely suppress Subscribe. This policy requires trials: if
STRIPE_SUBSCRIPTION_TRIAL_DAYS becomes 0, change the conversion policy too.
Server-side CAPI is a separate future step for delivery without a return visit.
Registration is emitted after authenticated onboarding is saved, covering email
and OAuth without counting an unconfirmed email signup. Its key is per user.
Invalid pixel IDs disable only Meta; Firebase continues. Expo web accepts
EXPO_PUBLIC_META_PIXEL_ID with the configured ID as default (build-time setting);
landing uses Nuxt public runtime config.
