@happyvertical/smrt-analytics
Analytics integration models for the s-m-r-t framework. Manages analytics properties (GA4, Plausible, Matomo), data streams, server-side event tracking with retry support, and AI-powered report generation with scheduling.
Installation
pnpm add @happyvertical/smrt-analytics
Usage
import {
AnalyticsProperty, AnalyticsPropertyCollection,
AnalyticsDataStream, AnalyticsDataStreamCollection,
AnalyticsEvent, AnalyticsEventCollection,
AnalyticsReport, AnalyticsReportCollection,
AnalyticsProvider, DataStreamType, ReportFrequency
} from '@happyvertical/smrt-analytics';
// Create a GA4 property
const properties = await AnalyticsPropertyCollection.create({ db });
const property = await properties.create({
name: 'main-site',
displayName: 'Main Site Analytics',
provider: AnalyticsProvider.GA4,
externalId: 'properties/123456789',
measurementId: 'G-XXXXXXXXXX',
apiSecret: 'server-side-secret',
status: 'active',
});
// Add a web data stream
const streams = await AnalyticsDataStreamCollection.create({ db });
await streams.create({
propertyId: property.id,
displayName: 'Web Traffic',
streamType: DataStreamType.WEB,
measurementId: 'G-XXXXXXXXXX',
defaultUri: 'https://example.com',
status: 'active',
});
// Track a server-side event with retry support
const events = await AnalyticsEventCollection.create({ db });
const event = await events.create({
propertyId: property.id,
eventName: 'purchase',
clientId: 'client-uuid',
params: JSON.stringify({ value: 99.99, currency: 'USD' }),
status: 'pending',
});
// After sending: event.markSent() or event.markFailed('timeout')
// Retry logic: event.shouldRetry(3) checks retryCount < maxRetries
// Create a scheduled report with dimensions and metrics
const reports = await AnalyticsReportCollection.create({ db });
const report = await reports.create({
propertyId: property.id,
name: 'Weekly Traffic Report',
dimensions: JSON.stringify([{ name: 'country' }, { name: 'deviceCategory' }]),
metrics: JSON.stringify([{ name: 'activeUsers' }, { name: 'sessions' }]),
dateRangeStart: '7daysAgo',
dateRangeEnd: 'today',
frequency: ReportFrequency.WEEKLY,
});
// AI-powered operations (uses do() and is() from SmrtObject)
const analysis = await property.analyzePerformance({ period: '30 days' });
const isHealthy = await property.isPerformingWell();
const insights = await report.analyzeResults();
const trending = await report.hasPositiveTrends();
hasPositiveTrends() keeps that boolean API. When decisions: { type: 'typesafe' } is configured and no AI tools are registered, it sends the
resolved report prompt plus only the selected metrics and persisted aggregate
rows to the typed-decision route. Its threshold defaults to 0.5; a configured
uncertainty band uses the same resolved prompt on the generative route, matching
the framework's decision fallback behavior. The route treats higher
user-growth, engagement, and conversion values as favorable, and higher bounce
or error rates as adverse. Unknown metric directions, incomplete comparison
periods, and insufficient or ambiguous data are not inferred as positive.
Configured decision failures propagate. Without a decision provider, the
existing prompt-driven yes/no behavior remains in use. resultData retains
the documented caller-managed PII limitation in either route.
The framework website's analytics guide should add a worked decision-provider configuration example; this package README documents the runtime contract until that guide is updated.
Server-Side Event Lifecycle
Events track their delivery status with built-in retry support:
- PENDING -- created, not yet sent to provider
- SENT -- successfully delivered (
markSent()setssentAttimestamp) - FAILED -- delivery failed (
markFailed(error)incrementsretryCount)
Use shouldRetry(maxRetries) to check if a failed event should be retried, and resetForRetry() to reset it to PENDING.
Report Scheduling
Reports support automatic scheduling via ReportFrequency:
ONCE-- single run, no reschedulingDAILY/WEEKLY/MONTHLY--calculateNextRun()setsnextRunAtafter each run
Use isDue() to check if a scheduled report needs to run.
API
Models
| Export | Description |
|---|---|
AnalyticsProperty | GA4 property, Plausible site, or Matomo site. AI methods: analyzePerformance(), isPerformingWell() |
AnalyticsDataStream | Data stream within a property (web/iOS/Android). getPlatformId() returns measurementId or firebaseAppId |
AnalyticsEvent | Server-side tracking event with retry queue. toTrackEvent() builds SDK payload |
AnalyticsReport | Saved report config with dimensions/metrics, scheduling, result caching. AI methods: analyzeResults(), hasPositiveTrends() |
Collections
AnalyticsPropertyCollection, AnalyticsDataStreamCollection, AnalyticsEventCollection, AnalyticsReportCollection
Enums
| Export | Description |
|---|---|
AnalyticsProvider | ga4, plausible, matomo |
AnalyticsPropertyStatus | active, inactive, pending |
DataStreamType | WEB_DATA_STREAM, ANDROID_APP_DATA_STREAM, IOS_APP_DATA_STREAM |
DataStreamStatus | active, inactive |
TrackingEventStatus | pending, sent, failed |
ReportStatus | draft, scheduled, running, completed, failed |
ReportFrequency | once, daily, weekly, monthly |
CountingMethod | ONCE_PER_EVENT, ONCE_PER_SESSION |
CustomDimensionScope | EVENT, USER, ITEM |
SDK Types
Re-exported from ./types for SDK compatibility: AnalyticsInterface, AnalyticsCapabilities, SDKProperty, SDKDataStream, SDKTrackEvent, SDKPageviewEvent, SDKReportOptions, SDKReportResult, SDKCustomDimension, SDKCustomMetric, SDKKeyEvent, PropertyStatsWithTrend.
Dependencies
@happyvertical/smrt-core-- ORM and code generation- Peer:
svelte(optional, for Svelte components via@happyvertical/smrt-analytics/svelte)
Contributor guide
See AGENTS.md for package architecture, invariants, validation,
and contributor guidance.
Browser tracking
Use the isolated @happyvertical/smrt-analytics/browser entry for opt-in browser/SPA tracking and the Matomo adapter. Shared components emit static standard events through the optional @happyvertical/smrt-ui/analytics context. See browser integration and privacy contract.