@frank/analytics (0.2.0)
Installation
@frank:registry=npm install @frank/analytics@0.2.0"@frank/analytics": "0.2.0"About this package
@frank/analytics
The Meta monitoring read model, ported from Blockwise src/lib/meta-monitor:
it builds the Results/Home reporting payload from Meta Graph reads only,
caches it as a snapshot with etag/TTL, and provides the chart, pacing, suburb
and budget math. Nothing here writes to Meta; publish and mutation stay in the
product.
Public API
Everything is exported from the package root:
getMetaMonitorData/getResultsPayload: build the live payload (or the labelled sample when nothing is connected).loadReportingSnapshot/refreshReportingSnapshot/reportingRangeKey/isMatchingEtag: the cached read-through layer.queueReportingRefresh(es): durable refresh requests (dedupe buckets kept).buildSampleMetaMonitorPayload: the demo fixture.fetchMetaInsightRowsand friends: raw Meta Graph reads (15s deadline, pagination, no-store).- Math:
CHART_METRICS,buildResultsHierarchy,safeRate,safeCpl,calculateBudgetPacing,detectCreativeFatigue, budget alerts, owned performance persistence, suburb resolution.
Browser entry (@frank/analytics/browser)
Browser hosts (the Blockwise monitor components, Frank Window) import the
browser subpath, which re-exports only the pure math and types - date ranges,
calculations, chart metrics, budget, results hierarchy, manageability,
payload state, the demo fixture - and never the Node-only half of the package
(db, provider reads, the node:crypto snapshot cache, queues). Every symbol is
the same implementation the root exports; nothing is copied. The server API at
the package root is unchanged, so server callers keep importing
@frank/analytics.
import { formatCurrency, safeRate, CHART_METRICS } from "@frank/analytics/browser";
Config
configFromEnv() (the only env reader) yields AnalyticsConfig: sample flag,
budget, site URL, graph version, dev warnings, and the Blockwise table names.
Pass DEFAULT_CONFIG in tests. DB access goes through the ReadDb/ServiceDb
seam (a Supabase client matches); email and queue access go through ports.
Usage
import { configFromEnv, loadReportingSnapshot } from "@frank/analytics";
const { snapshot, needsRefresh } = await loadReportingSnapshot({
supabase, workspaceId, range: "last_7",
ports: { config: configFromEnv(process.env), provider: "meta", loadProviderTokens, budgetAlert },
});
Dependencies
Dependencies
| ID | Version |
|---|---|
| @sentry/core | ^10.69.0 |
Development dependencies
| ID | Version |
|---|---|
| @types/node | ^22.0.0 |
| esbuild | ^0.28.2 |