@teamanalyst/sdk
v0.0.1
Published
Typed, runtime-agnostic client SDK for the Analyst analytics platform. Batched, non-blocking event tracking for browsers, Node, and Cloudflare Workers.
Maintainers
Readme
@teamanalyst/sdk
Typed, runtime-agnostic client for the Analyst analytics platform. Works in browsers, Node ≥ 18, and Cloudflare Workers. Zero dependencies, tree-shakeable, dual ESM/CJS.
npm install @teamanalyst/sdkUsage
import { createAnalyst } from "@teamanalyst/sdk";
const analyst = createAnalyst({
endpoint: "https://ingest.your-analyst.example.com",
apiKey: "ak_...",
tenantId: "tenant_123",
});
// Synchronous, non-blocking, never throws.
analyst.track("credits.consumed", { amount: 42, model: "tebby-pro" });
// Per-call context overrides client defaults.
analyst.track("page.viewed", { path: "/setup" }, { userId: "user_9" });
// Serverless / process exit: deliver everything before the runtime freezes.
await analyst.shutdown();In a Cloudflare Worker
export default {
async fetch(request: Request, env: Env, ctx: ExecutionContext): Promise<Response> {
const analyst = createAnalyst({ endpoint: env.ANALYST_URL, apiKey: env.ANALYST_KEY, tenantId: "t1" });
analyst.track("api.request", { path: new URL(request.url).pathname });
ctx.waitUntil(analyst.shutdown()); // don't block the response
return new Response("ok");
},
};In the browser
Events are batched and flushed with keepalive, so they survive page navigations. Call analyst.identify({ userId }) after login — the session id stays stable, which is what powers anonymous → identified stitching server-side.
How it behaves
- Batching — events queue locally and ship when
flushAt(default 20) is reached or everyflushIntervalMs(default 5 s), whichever comes first. Requests never exceed 500 events (the ingest limit). - Idempotency — every event gets a client-generated UUID
idempotency_key, so at-least-once delivery (SDK retries, queue redelivery) never duplicates data. - Retries — network errors and 408/429/5xx retry with exponential backoff (
maxRetries, default 3). 4xx responses don't retry. - Never breaks your app —
track()is synchronous and exception-free. Failures (queue overflow, missing tenant, exhausted retries) are reported toonErrorwith the affected events, so you can persist and replay them. - Timers don't hold Node open — flush timers are
unref'd.
API
| Member | Description |
| --- | --- |
| createAnalyst(options) | Create a client. endpoint is required; see AnalystOptions for the rest. |
| analyst.track(event, properties?, context?) | Queue one event. Returns the built event, or null if dropped. |
| analyst.trackBatch(events) | Queue several {event, properties?, context?} entries. |
| analyst.identify(context) | Update default context (e.g. userId after login). |
| analyst.flush() | Force-send everything queued. Resolves when delivery settles. |
| analyst.shutdown() | Flush, then stop accepting events. Await before exit. |
| generateId() | The UUID v4 helper the SDK uses (exported for convenience). |
Event shape on the wire (see @teamanalyst/schema for the validating schema):
{
tenant_id: string;
user_id: string | null;
session_id: string;
instance_id: string | null;
event_name: string; // dot-namespaced: "credits.consumed"
timestamp: string; // ISO 8601, stamped at track() time
properties: Record<string, JsonValue>;
idempotency_key: string; // UUID v4
}License
MIT
