Skip to content

TypeScript Client

View as markdown

Zero runtime dependencies. Uses native fetch and Web Crypto. Full TypeScript types. ESM and CJS exports. Works in Node 18+ and all modern browsers.

Install

Terminal window
npm install wirelog

Quick start (singleton)

The recommended pattern: import the wl singleton, init once, use everywhere.

import { wl } from "wirelog";
wl.init({ apiKey: "pk_your_public_key" });
wl.track({ event_type: "signup", user_id: "u_123", event_properties: { plan: "free" } });
wl.identify({ user_id: "alice@acme.org", user_properties: { plan: "pro" } });
// All subsequent track() calls include user_id automatically
wl.track({ event_type: "checkout", event_properties: { amount: 42 } });

If you call track(), identify(), or query() before init(), the client logs a console.warn and no-ops.


Browser identity

In browsers, the client automatically manages identity using the same localStorage keys as the Script Tag:

StorageKeyPersistence
device_idwl_did in localStoragePermanent until reset()
session_idIn-memoryRegenerated after 30 minutes of inactivity
user_idwl_uid in localStorageSet via identify(), cleared via reset()

Every track() call in the browser auto-injects device_id, session_id, and user_id, plus event context (url, language, timezone) and clientOriginated: true.

Browser track() calls are buffered by default:

  • flush on 10 events or every 2 seconds
  • retries transient failures with backoff (up to 3 retries)
  • flush on visibilitychange (hidden) and pagehide

Using with the Script Tag

Both the TypeScript client and the Script Tag read and write the same wl_did and wl_uid localStorage keys. This means:

  • The Script Tag handles automatic page_view tracking and delivery
  • The TypeScript client handles typed event tracking in your React/Vue/Svelte app (with buffered browser track() delivery)
  • Identity is shared. Calling identify() from either SDK persists the user_id for both
// Your React app -- the script tag already set up device_id via localStorage
import { wl } from "wirelog";
wl.init({ apiKey: "pk_your_public_key" });
// device_id and session_id are auto-injected, user_id too if identify() was called
wl.track({ event_type: "feature_used", event_properties: { feature: "export" } });

In Node.js, device_id, session_id, and browser identity are not applicable. Pass user_id explicitly on each track() call.


Explicit instances

For server-side Node.js, multiple projects, or test isolation, create instances directly:

import { WireLog } from "wirelog";
const client = new WireLog({ apiKey: "sk_your_secret_key" });
await client.track({ event_type: "invoice.paid", user_id: "u_123", event_properties: { amount: 99 } });
const result = await client.query("invoice.paid | last 7d | count by day");
console.log(result);

Types

interface WireLogConfig {
apiKey?: string;
host?: string; // Defaults to https://api.wirelog.ai
}
interface TrackEvent {
event_type: string; // Required
user_id?: string;
device_id?: string;
session_id?: string;
clientOriginated?: boolean;
time?: string; // RFC 3339. Auto-generated if omitted
event_properties?: Record<string, unknown>;
user_properties?: Record<string, unknown>;
insert_id?: string; // Auto-generated if omitted
}
interface TrackResult {
accepted: number;
buffered?: boolean;
}
interface IdentifyParams {
user_id: string; // Required
device_id?: string;
user_properties?: Record<string, unknown>;
user_property_ops?: {
$set?: Record<string, unknown>;
$set_once?: Record<string, unknown>;
$add?: Record<string, number>;
$unset?: string[];
};
}
interface IdentifyResult {
ok: boolean;
}
interface QueryOptions {
format?: "llm" | "json" | "csv";
limit?: number; // Default 100, max 10,000
offset?: number;
}

Methods

init()

wl.init(config: WireLogConfig): void

Initialize the singleton. Call once at app startup.

wl.init({ apiKey: "pk_your_public_key" });

track()

wl.track(event: TrackEvent): Promise<TrackResult>

Track a single event. Auto-generates insert_id and time if not provided. In browsers, auto-injects device_id, session_id, user_id, event context, and clientOriginated: true.

const result = await wl.track({
event_type: "purchase",
user_id: "u_123",
event_properties: { plan: "pro", amount: 49 },
});
// Browser: { accepted: 1, buffered: true } (queued locally)
// Node: { accepted: 1 } (API response)

trackBatch()

wl.trackBatch(events: TrackEvent[]): Promise<TrackResult>

Track up to 2000 events in one request. In browsers, auto-injects identity/context per event, marks the request/events as client-originated, and sends immediately as one API request.

await wl.trackBatch([
{ event_type: "page_view", user_id: "u_1", event_properties: { page: "/" } },
{ event_type: "click", user_id: "u_1", event_properties: { button: "cta" } },
]);
// { accepted: 2 }

flush()

wl.flush(): Promise<TrackResult>

Flush queued browser track() events immediately.

await wl.flush();
// Browser: { accepted: N }
// Node: { accepted: 0 }

query()

wl.query(q: string, opts?: QueryOptions): Promise<unknown>

Run a pipe DSL query. Returns a Markdown string (llm), parsed JSON (json), or CSV string (csv).

// Markdown table (default)
const md = await wl.query("* | last 30d | count by event_type | top 10");
// JSON
const data = await wl.query("page_view | last 7d | count", { format: "json" });
// CSV with pagination
const csv = await wl.query("* | last 90d | count by event_type", {
format: "csv",
limit: 10000,
});

identify()

wl.identify(params: IdentifyParams): Promise<IdentifyResult>

Bind device_id to user_id and upsert user profile properties. In browsers, persists user_id to localStorage so it’s shared with the Script Tag.

await wl.identify({
user_id: "alice@acme.org",
user_properties: { email: "alice@acme.org" },
user_property_ops: {
$set: { plan: "enterprise" },
$set_once: { signup_source: "ads" },
$add: { login_count: 1 },
$unset: ["legacy_flag"],
},
});
// { ok: true }

reset()

Clear identity state. In browsers, generates a new device_id and clears the stored user_id. Use on logout.

wl.reset();

deviceId / userId

Read-only accessors for the current browser identity. Returns null in Node.

console.log(wl.deviceId); // "a1b2c3d4e5f6..."
console.log(wl.userId); // "alice@acme.org" or null

Error handling

trackBatch(), identify(), and query() throw WireLogError on non-2xx responses.

In browsers, track() is buffered by default and does not throw transport errors at call time. Use flush() when you want to force immediate delivery.

import { wl, WireLogError } from "wirelog";
try {
await wl.trackBatch([{ event_type: "test" }]);
} catch (e) {
if (e instanceof WireLogError) {
console.error(e.status); // HTTP status code
console.error(e.message); // "WireLog API 401: ..."
}
}

Source

github.com/wirelogai/wirelog-typescript