@sperax/analytics
v0.2.2
Published
SperaxOS analytics library — type-safe event tracking with PostHog and GA4
Maintainers
Readme
@sperax/analytics
Install
npm install @sperax/analyticsOne typed track() call, fanned out to every analytics provider you have
configured. PostHog (browser and Node), GA4, and Umami today; adding a provider
does not change a single call site.
import { createAnalytics } from '@sperax/analytics';
const analytics = createAnalytics({
business: 'speraxos',
providers: {
ga4: { enabled: true, measurementId: 'G-XXXXXXX' },
posthog: { enabled: true, host: 'https://eu.i.posthog.com', key: 'phc_…' },
},
});
await analytics.initialize();
await analytics.trackEvent('button_click', { button_name: 'connect_wallet' });Install
npm install @sperax/analyticsReact is an optional peer — only needed for the /react entry point.
Three entry points, because the browser and the server need different provider implementations:
| Import | Use in |
| --- | --- |
| @sperax/analytics | Browser and shared code — PostHog (posthog-js), GA4, Umami |
| @sperax/analytics/server | Node — PostHog (posthog-node), no DOM access |
| @sperax/analytics/react | React — provider + hooks |
Why a manager at all
Every provider fails differently and none of them should ever break a render or
a request. AnalyticsManager guarantees three things:
- Failures are swallowed per provider. One misconfigured key cannot take
down tracking for the others, and no
track()call ever rejects into your code. - Calls before
initialize()are dropped, not queued forever.track()returns immediately when the manager is not ready. - Every event is enriched once, centrally, rather than at each call site.
Configuration
interface AnalyticsConfig {
/** Product/business identifier attached to every event. */
business: string;
debug?: boolean;
providers: {
ga4?: { enabled: boolean; measurementId: string; gtagConfig?: Record<string, any>; debug?: boolean };
posthog?: { enabled: boolean; key: string; host?: string; debug?: boolean };
posthogNode?: { enabled: boolean; key: string; host?: string; debug?: boolean };
umami?: { enabled: boolean; websiteId: string; scriptUrl?: string; debug?: boolean };
};
}Every provider carries its own enabled flag, so a provider can be wired up in
config and switched off per environment without a code change:
providers: {
posthog: { enabled: process.env.NODE_ENV === 'production', key: process.env.POSTHOG_KEY! },
}Manager API
await analytics.initialize(); // load provider SDKs
await analytics.track({ name: 'swap_executed', properties: { chain: 42_161 } });
await analytics.trackEvent('page_view', { page: '/portfolio' }); // typed, see below
await analytics.trackPageView('/portfolio', { referrer });
await analytics.identify(userId, { plan: 'pro' });
await analytics.reset(); // on sign-out
analytics.getStatus(); // { initialized, providersCount }trackEvent is the typed path. Its event names and property shapes come from
PredefinedEvents, so a typo or a missing required property is a compile error:
interface PredefinedEvents {
button_click: { button_name: string; spm?: string; [key: string]: any };
page_view: { page: string; spm?: string; [key: string]: any };
user_login: { spm?: string; [key: string]: any };
user_signup: { spm?: string; [key: string]: any };
}Extend that interface to cover your own events. Anything ad-hoc still goes
through track({ name, properties }).
React
import { AnalyticsProvider, createAnalytics } from '@sperax/analytics/react';
const client = createAnalytics({ business: 'speraxos', providers: { … } });
export function Root({ children }) {
return (
<AnalyticsProvider client={client} onInitializeError={(e) => reportError(e)}>
{children}
</AnalyticsProvider>
);
}AnalyticsProvider calls initialize() once on mount (guarded against Strict
Mode double-invocation) and registers the client globally so non-React code can
reach it.
| Prop | Default | Description |
| --- | --- | --- |
| client | — | Required. An AnalyticsManager. |
| autoInitialize | true | Initialize on mount. Set false to gate on consent. |
| registerGlobal | true | Also register via setGlobalAnalytics. |
| globalName | '__default__' | Key for the global registration. |
| onInitializeSuccess | — | Called once initialization resolves. |
| onInitializeError | — | Called with the error if it rejects. |
Four hooks, differing only in how they handle "not ready yet":
const { analytics, isReady, isInitializing, error } = useAnalytics(); // nullable + state
const analytics = useAnalyticsStrict(); // throws if no provider above it
const analytics = useAnalyticsOptional(); // null outside a provider
const { isInitialized, isInitializing, error } = useAnalyticsState(); // state onlyuseAnalytics is the default. Reach for useAnalyticsStrict in components that
are always inside the provider, and useAnalyticsOptional in shared components
that must also render in apps with no analytics at all.
Gating on consent is autoInitialize={false} plus an explicit call:
const { analytics } = useAnalytics();
const onAccept = () => analytics?.initialize();Server
import { createServerAnalytics } from '@sperax/analytics/server';
const analytics = createServerAnalytics({
business: 'speraxos',
providers: { posthogNode: { enabled: true, key: process.env.POSTHOG_KEY! } },
});
await analytics.initialize();
await analytics.track({ name: 'agent_run_completed', properties: { durationMs, userId } });The server entry point never touches window, so it is safe in route handlers,
cron jobs, and workers. Use posthogNode there — the browser posthog provider
expects a DOM.
Singleton
Most apps want one instance for the whole process:
import { createSingletonAnalytics, getGlobalAnalytics } from '@sperax/analytics';
createSingletonAnalytics(config); // once, at startup
getGlobalAnalytics().track({ name: 'x' }); // anywherecreateAnalytics returns a fresh, unregistered manager — use it for tests and
for anywhere you need two independent configurations.
Exports
import {
AnalyticsManager, BaseAnalytics,
PostHogAnalyticsProvider, GoogleAnalyticsProvider,
createAnalytics, createSingletonAnalytics,
getGlobalAnalytics, setGlobalAnalytics,
} from '@sperax/analytics';
import type {
AnalyticsConfig, AnalyticsEvent, EventContext, Platform,
PredefinedEvents, ProviderConfig, ProviderTypeMap,
GoogleAnalyticsProviderConfig, PostHogProviderAnalyticsConfig,
PostHogNodeProviderAnalyticsConfig, UmamiProviderAnalyticsConfig,
} from '@sperax/analytics';
import { PostHogNodeAnalyticsProvider, createServerAnalytics } from '@sperax/analytics/server';
import {
AnalyticsProvider, useAnalytics, useAnalyticsOptional,
useAnalyticsState, useAnalyticsStrict, useEventTracking,
} from '@sperax/analytics/react';Apache-2.0
License
analytics is released under the Apache-2.0 license.
