@awarevue/ui-analytics
v0.1.0
Published
Tiny browser client for emitting append-only telemetry events to the ui-analytics service.
Readme
@awarevue/ui-analytics
Tiny, framework-free browser client for emitting append-only telemetry events to the ui-analytics service. Optional React helpers under @awarevue/ui-analytics/react.
yarn add @awarevue/ui-analyticsBasic use
import { createTelemetryClient } from "@awarevue/ui-analytics";
const telemetry = createTelemetryClient({
endpoint: "https://telemetry.example.com", // events go to `${endpoint}/api/events`
app: "marine-map",
version: APP_VERSION,
});
telemetry.track("vessel.selected", {
subject: { type: "vessel", id: vesselId },
context: { view: "marine_map", surface: "vessel_popup" },
data: { selectionSizeAfter: 3, interaction: "click" },
});
telemetry.setActor({ userId: user.id, accountId: user.accountId });
telemetry.enableVisibilityTracking(); // emits app.visibility_changed on every change
ws.onopen = () => telemetry.connectionOpened({ transport: "websocket" });
ws.onclose = (e) => telemetry.connectionClosed({ code: e.code, clean: e.wasClean });The client takes care of event ids (UUIDv7), the session id and per-session sequence, timestamps, source metadata, batching (every 2 s or 50 events), retries with backoff, an outbox persisted in localStorage so events survive a suspended or killed tab, and a keepalive flush when the page is hidden or unloading. track never throws; delivery problems are reported through onError.
Event names are <object>.<fact> in lowercase past tense (favorite.added, annotation.deleted). Keep data small and free of user content — record descriptionLength, not the description.
React
import {
TelemetryProvider,
TelemetryScope,
useTelemetry,
useTelemetryActor,
useTrackOnMount,
} from "@awarevue/ui-analytics/react";
<TelemetryProvider client={telemetry} visibilityTracking>
<TelemetryScope context={{ view: "marine_map" }}>
<MapView />
</TelemetryScope>
</TelemetryProvider>;
function VesselPopup({ vessel }) {
const telemetry = useTelemetry(); // track() carries { view: "marine_map" } automatically
useTrackOnMount("vessel_popup.opened", { subject: { type: "vessel", id: vessel.id } });
return (
<TelemetryScope context={{ surface: "vessel_popup" }}>
<button onClick={() => telemetry.track("favorite.added", { subject: { type: "vessel", id: vessel.id } })}>
★
</button>
</TelemetryScope>
);
}
function Root() {
const user = useCurrentUser();
useTelemetryActor(user && { userId: user.id }); // keeps the actor in sync with auth
// ...
}TelemetryScope layers context down the tree and nested scopes shallow-merge, so view/surface never have to be threaded by hand into every track call.
Options
| Option | Default | Purpose |
| ------------------ | ------------ | --------------------------------------------------------------- |
| endpoint | — | base URL of the telemetry service |
| app, version | — | recorded as source |
| actor, context | — | initial actor / default context merged under every event |
| flushIntervalMs | 2000 | send cadence |
| maxBatchSize | 50 | events per request; reaching it flushes immediately |
| maxQueueSize | 1000 | queue cap; oldest events are dropped beyond it |
| sessionTimeoutMs | 1800000 | inactivity after which a new session starts |
| persistence | "browser" | "browser" (sessionStorage + localStorage) or "memory" |
| fetch | global fetch | injectable for tests / non-browser runtimes |
| onError | console.warn | delivery problem reporter |
Events emitted by the library itself: session.started, app.visibility_changed { state }, connection.opened, connection.closed.
