# Analytics Vllnt Generic, configurable analytics component for Bijection — any event, any dimension, any domain; rollup-on-write with funnels, retention, uniques - Package: @bijectionhq/analytics-vllnt - Category: Database & Backend - Status: Preview - License: MIT - Bijection source (repository access required): https://github.com/bijectionhq/analytics-vllnt/tree/d545d7c7238f2318b5d163cea8709645b90a6ef1/packages/convex-analytics - Upstream: https://github.com/vllnt/convex-analytics/tree/a5028f9f32a094da195e009244381e1c69b56a42/packages/convex-analytics - Documentation: https://docs.bijection.com/components/using - Directory: https://bijection.com/components/analytics-vllnt ## Get the source Bijection repository access is required. Request access at https://bijection.com/contact. ```sh git clone https://github.com/bijectionhq/analytics-vllnt.git cd analytics-vllnt git checkout d545d7c7238f2318b5d163cea8709645b90a6ef1 cd packages/convex-analytics ``` ## Before using this component Read the package README at the recorded revision for setup, dependencies, and API examples. Upstream documentation describes the original package and may differ from the Bijection adaptation. This is a source candidate for evaluation. Catalog inclusion does not establish npm availability or runtime qualification. Check compatibility with your SDK and deployment before adoption. External providers run their own compute. ## Upstream package documentation Recorded 2026-10-23. This describes @vllnt/convex-analytics; check the Bijection adaptation separately. @vllnt/convex-analytics stores events and exposes analytical queries inside your Convex backend. Event names, subject references, properties, and dimensions are supplied by your application rather than imposed by a web-only schema. Track typed event properties and choose the dimensions to aggregate. Query metrics, breakdowns, timeseries, funnels, retention, or raw events. Rollups support aggregate reads; an optional web preset adds web-specific helpers and an optional React entry supports reactive displays. The host defines event meaning, identity, consent, and access rules. Sampling, retention, and selected dimensions affect interpretation and coverage. This component does not automatically instrument every client or establish causation from a funnel; validate the events your product sends. Maintained by [bntvllnt](https://bntvllnt.com) at [VLLNT](https://vllnt.com). Find the author on [GitHub](https://github.com/bntvllnt) and [X](https://x.com/bntvllnt). ### Use cases - SaaS onboarding: follow the steps between signup and first useful action. - Mobile products: compare activity and return behavior across features. - Games and learning: analyze session events, completion steps, and repeat usage. - AI applications: measure feature adoption or host-reported task outcomes by model or workflow dimension. ### How it works Track typed event properties and choose the dimensions to aggregate. Query metrics, breakdowns, timeseries, funnels, retention, or raw events. Rollups support aggregate reads; an optional web preset adds web-specific helpers and an optional React entry supports reactive displays. The host defines event meaning, identity, consent, and access rules. Sampling, retention, and selected dimensions affect interpretation and coverage. This component does not automatically instrument every client or establish causation from a funnel; validate the events your product sends. ### Upstream README # @vllnt/convex-analytics A generic, configurable, embedded analytics component for Convex — any event, any dimension, any domain — with rollup-on-write so reads stay O(1). ```ts import { AnalyticsClient } from "@vllnt/convex-analytics"; import { components } from "./_generated/api"; const analytics = new AnalyticsClient(components.analytics, { dimensions: ["plan", "country"], // prop keys to roll up on }); await analytics.track(ctx, "signup", { subjectRef: userId, props: { plan: "pro", country: "FR" } }); const byPlan = await analytics.top(ctx, "signup", "plan"); // [{ value: "pro", count: 1 }] ``` ## Features - **Generic events** — free-string event name, opaque `subjectRef` / `sessionRef`, host-typed `props`. - **Host-declared dimensions** — you pass the prop keys to roll up on; nothing is hardcoded. - **Rollup-on-write** — counts are incremented as events land, so `metric` / `top` / `timeseries` read in O(1) (backed by `@convex-dev/aggregate` + `@convex-dev/sharded-counter`). - **Rich verb set** — `metric`, `top`, `timeseries`, `uniques`, `funnel`, `retention`, `distribution`, plus paginated raw `list`. - **Configurable** — `scope`, `dimensions`, `granularities`, `retentionDays`, `sampleRate`, `propsValidator`; sensible defaults, zero config required. - **Per-session rate limiting + dedupe + sampling** built into `track`. - **Opt-in web preset** — `@vllnt/convex-analytics/web` adds web dimensions + UA/geo helpers when you want them. - **Optional React hooks** — `@vllnt/convex-analytics/react` for reactive aggregate reads. - **Zero hardcoded domain** — no `v.any()`, no baked-in web fields; the host owns meaning and auth. ## Generic core vs web preset The core is domain-neutral: it knows nothing about web, mobile, or any vertical. Web analytics — pageview dimensions (`path`, `referrer`, `device`, `browser`, `os`, `country`, UTM) plus `parseUserAgent` / `geoFromHeaders` / `trackPageview` helpers — lives in the opt-in `@vllnt/convex-analytics/web` preset. You turn it on by passing `webDimensions` to the client's `dimensions` config; otherwise the core ships no web fields. See [docs/web-preset.md](docs/web-preset.md). ## Installation ```bash npm install @vllnt/convex-analytics ``` ## Usage ```ts // convex/convex.config.ts import { defineApp } from "convex/server"; import analytics from "@vllnt/convex-analytics/convex.config"; const app = defineApp(); app.use(analytics); export default app; ``` ```ts // convex/analytics.ts — track + read, server-side import { AnalyticsClient } from "@vllnt/convex-analytics"; import { components } from "./_generated/api"; type MyProps = { plan?: string; country?: string }; const analytics = new AnalyticsClient(components.analytics, { dimensions: ["plan", "country"], granularities: ["day"], }); await analytics.track(ctx, "signup", { subjectRef: userId, props: { plan: "pro" } }); const total = await analytics.metric(ctx, "signup"); ``` ## Config Passed to the `AnalyticsClient` constructor. All optional — the defaults work with zero config. | Option | Type | Default | Purpose | |--------|------|---------|---------| | `scope` | `string` | `"default"` | Multi-tenant partition; isolates one tenant's data. | | `dimensions` | `string[]` | `[]` | Prop keys to roll up on (drives rollup-on-write). Empty = count by event name only. | | `granularities` | `("minute" \| "hour" \| "day")[]` | `["day"]` | Rollup bucket sizes (`minute` for short live windows). | | `retentionDays` | `number` | `90` | Raw-event TTL in days (rollups are kept forever). Applied via `configure`. | | `sampleRate` | `number` | `1` | Fraction `0..1` of events to keep at ingest. | | `sessionIdleMs` | `number` | `1800000` | Idle timeout before a session is closed. Applied via `configure`. | | `propsValidator` | Convex validator | typed scalar record | Optional host validator narrowing `props` at the boundary. | `scope`, `dimensions`, `granularities`, and `sampleRate` apply per call from the client. `retentionDays`, `sampleRate`, and `sessionIdleMs` are persisted for the prune/close-sessions crons by calling `analytics.configure(ctx, { ... })` once. ## API | Verb | Kind | Purpose | |------|------|---------| | `track(ctx, name, opts)` | mutation | Ingest an event; rollup-on-write + raw event + counter. Returns `"tracked" \| "dropped" \| "duplicate"`. | | `metric(ctx, name, opts)` | query | Total count over a range, optionally filtered by a dimension value. | | `top(ctx, name, dimension, opts)` | query | Top values of a dimension (breakdown). | | `timeseries(ctx, name, opts)` | query | Bucketed counts over a range. | | `uniques(ctx, opts)` | query | DAU / WAU / MAU from subjects. | | `funnel(ctx, steps, opts)` | query | Ordered step conversion, keyed by `subjectRef`. | | `retention(ctx, opts)` | query | Cohort return rates by first-seen period. | | `distribution(ctx, name, measure, opts)` | query | Histogram of a numeric measure over declared buckets + overflow (with `count` / `sum`). | | `list(ctx, name, paginationOpts, opts)` | query | Paginated raw events, newest first. | | `configure(ctx, opts)` | mutation | Persist cron-relevant config (retention / sampling / idle). | Full reference: [docs/client-sdk.md](docs/client-sdk.md). REST surface: [docs/api-reference.md](docs/api-reference.md). ## What it serves The Convex-native, authoritative in-app aggregate layer — predefined-dimension rollups served **reactively** in O(1). It complements a product-analytics warehouse; it doesn't replace one. Declare the dimensions and measures you'll query and the rollups answer instantly; ad-hoc or retroactive questions over **non-declared** dimensions, or beyond the raw-event retention window, are an export concern. `metric` / `top` / `timeseries` read pre-aggregated rollups (kept forever); `funnel` / `retention` / `distribution` read raw events in range (index-backed, bounded, TTL-pruned). ## Web preset ```ts import { AnalyticsClient } from "@vllnt/convex-analytics"; import { webDimensions, trackPageview } from "@vllnt/convex-analytics/web"; const analytics = new AnalyticsClient(components.analytics, { dimensions: webDimensions }); await trackPageview(analytics, ctx, { path: "/pricing", ua: request.headers.get("user-agent") ?? undefined, headers: request.headers, }); ``` `webDimensions` opts the rollups into `path` / `referrer` / `device` / `browser` / `os` / `country` / UTM; `parseUserAgent` and `geoFromHeaders` build those props from a request. See [docs/web-preset.md](docs/web-preset.md). ## React ```tsx import { useMetric, useTop } from "@vllnt/convex-analytics/react"; import { api } from "../convex/_generated/api"; const signups = useMetric(api.analytics.metric, { name: "signup" }); const byPlan = useTop(api.analytics.top, { name: "signup", dimension: "plan" }); ``` Thin reactive wrappers over the host's re-exported query refs (`metric`, `top`, `timeseries`, `uniques`). `react` + `convex` are optional peer deps; backend-only consumers pull no React. ## MCP (Claude Code) ```bash claude mcp add convex-analytics-mcp \ --env CONVEX_URL=https://your-deployment.convex.cloud \ --env ANALYTICS_API_KEY=your-key ``` 7 tools: `track`, `get_metric`, `get_top`, `get_timeseries`, `get_uniques`, `detect_anomalies`, `query_analytics` (NL router). See [docs/mcp-tools.md](docs/mcp-tools.md). ## Security - Auth-agnostic mount — the host gates access and passes opaque `subjectRef` / `sessionRef` in. - Tables are sandboxed; the host reaches them only through the exported functions. - REST endpoints require an `x-api-key` header, compared timing-safe. See [docs/architecture.md](docs/architecture.md). ## Testing ```bash pnpm test ``` ```ts import analyticsTest from "@vllnt/convex-analytics/test"; import { convexTest } from "convex-test"; const t = convexTest(hostSchema, hostModules); analyticsTest.register(t); ``` ## Documentation [Schema](docs/schema.md) · [Client SDK](docs/client-sdk.md) · [REST API](docs/api-reference.md) · [Web Preset](docs/web-preset.md) · [Multi-Product](docs/multi-product.md) · [Architecture](docs/architecture.md) · [MCP Tools](docs/mcp-tools.md). For AI agents: [`llms.txt`](llms.txt) · [`llms-full.txt`](llms-full.txt). ## Contributing See [CONTRIBUTING.md](CONTRIBUTING.md). ## Author Built by [bntvllnt](https://github.com/bntvllnt) · [bntvllnt.com](https://bntvllnt.com) · [X @bntvllnt](https://x.com/bntvllnt) Part of the [@vllnt](https://github.com/vllnt) Convex component fleet — [vllnt.com](https://vllnt.com) If this is useful, [sponsor the work](https://github.com/sponsors/bntvllnt). ## License MIT — see [LICENSE](LICENSE).