@klime/node 1.0.3 → 1.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +233 -25
- package/dist/index.cjs +545 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +110 -0
- package/dist/index.d.ts +86 -4
- package/dist/index.js +483 -393
- package/dist/index.js.map +1 -0
- package/package.json +18 -3
- package/dist/types.d.ts +0 -54
- package/dist/types.js +0 -2
package/README.md
CHANGED
|
@@ -11,7 +11,11 @@ npm install @klime/node
|
|
|
11
11
|
## Quick Start
|
|
12
12
|
|
|
13
13
|
```javascript
|
|
14
|
-
|
|
14
|
+
// ESM
|
|
15
|
+
import { KlimeClient } from "@klime/node";
|
|
16
|
+
|
|
17
|
+
// CommonJS
|
|
18
|
+
// const { KlimeClient } = require("@klime/node");
|
|
15
19
|
|
|
16
20
|
const client = new KlimeClient({
|
|
17
21
|
writeKey: "your-write-key",
|
|
@@ -50,6 +54,87 @@ process.on("SIGTERM", async () => {
|
|
|
50
54
|
});
|
|
51
55
|
```
|
|
52
56
|
|
|
57
|
+
## Installation Prompt
|
|
58
|
+
|
|
59
|
+
Copy and paste this prompt into Cursor, Copilot, or your favorite AI editor to integrate Klime:
|
|
60
|
+
|
|
61
|
+
```
|
|
62
|
+
Integrate Klime for customer analytics. Klime tracks user activity to identify which customers are healthy vs at risk of churning.
|
|
63
|
+
|
|
64
|
+
ANALYTICS MODES (determine which applies):
|
|
65
|
+
- Companies & Teams: Your customers are companies with multiple team members (SaaS, enterprise tools)
|
|
66
|
+
→ Use identify() + group() + track()
|
|
67
|
+
- Individual Customers: Your customers are individuals with private accounts (consumer apps, creator tools)
|
|
68
|
+
→ Use identify() + track() only (no group() needed)
|
|
69
|
+
|
|
70
|
+
KEY CONCEPTS:
|
|
71
|
+
- Every track() call requires either userId OR groupId (no anonymous events)
|
|
72
|
+
- Use groupId alone for org-level events (webhooks, cron jobs, system metrics)
|
|
73
|
+
- group() links a user to a company AND sets company traits (only for Companies & Teams mode)
|
|
74
|
+
- Order doesn't matter - events before identify/group still get attributed correctly
|
|
75
|
+
|
|
76
|
+
BEST PRACTICES:
|
|
77
|
+
- Initialize client ONCE at app startup (singleton pattern)
|
|
78
|
+
- Store write key in KLIME_WRITE_KEY environment variable
|
|
79
|
+
- Call shutdown() on SIGTERM/SIGINT to flush remaining events
|
|
80
|
+
|
|
81
|
+
Install: npm install @klime/node
|
|
82
|
+
|
|
83
|
+
const { KlimeClient } = require("@klime/node");
|
|
84
|
+
|
|
85
|
+
// Initialize with your KLIME_WRITE_KEY environment variable
|
|
86
|
+
const client = new KlimeClient({ writeKey: "YOUR_WRITE_KEY" });
|
|
87
|
+
|
|
88
|
+
// Identify users at signup/login:
|
|
89
|
+
client.identify("usr_abc123", { email: "jane@acme.com", name: "Jane Smith" });
|
|
90
|
+
|
|
91
|
+
// Track key activities:
|
|
92
|
+
client.track("Report Generated", { report_type: "revenue" }, { userId: "usr_abc123" });
|
|
93
|
+
client.track("Feature Used", { feature: "export", format: "csv" }, { userId: "usr_abc123" });
|
|
94
|
+
client.track("Teammate Invited", { role: "member" }, { userId: "usr_abc123" });
|
|
95
|
+
|
|
96
|
+
// If Companies & Teams mode: link user to their company and set company traits
|
|
97
|
+
client.group("org_456", { name: "Acme Inc", plan: "enterprise" }, { userId: "usr_abc123" });
|
|
98
|
+
|
|
99
|
+
// Graceful shutdown:
|
|
100
|
+
process.on("SIGTERM", async () => { await client.shutdown(); process.exit(0); });
|
|
101
|
+
|
|
102
|
+
INTEGRATION WORKFLOW:
|
|
103
|
+
|
|
104
|
+
Phase 1: Discover
|
|
105
|
+
Explore the codebase to understand:
|
|
106
|
+
1. What framework is used? (Express, Hono, Fastify, Koa, Next.js, NestJS, etc.)
|
|
107
|
+
2. Where is user identity available? (e.g., req.user.id, ctx.state.user, session.userId, JWT payload)
|
|
108
|
+
3. Is this Companies & Teams or Individual Customers?
|
|
109
|
+
- Look for: organization, workspace, tenant, team, account models → Companies & Teams (use group())
|
|
110
|
+
- No company/org concept, just individual users → Individual Customers (skip group())
|
|
111
|
+
4. Where do core user actions happen? (route handlers, services, controllers)
|
|
112
|
+
5. Is there existing analytics? (search: segment, posthog, mixpanel, amplitude, .track)
|
|
113
|
+
Match your integration style to the framework's conventions.
|
|
114
|
+
|
|
115
|
+
Phase 2: Instrument
|
|
116
|
+
Add these calls using idiomatic patterns for the framework:
|
|
117
|
+
- Initialize client once at startup (Express: app.js, Next.js: instrumentation.ts, NestJS: module provider)
|
|
118
|
+
- identify() in auth/login success handler
|
|
119
|
+
- group() when user-org association is established (Companies & Teams mode only)
|
|
120
|
+
- track() for key user actions (see below)
|
|
121
|
+
|
|
122
|
+
WHAT TO TRACK:
|
|
123
|
+
Active engagement (primary): feature usage, resource creation, collaboration, completing flows
|
|
124
|
+
Session signals (secondary): login/session start, dashboard access - distinguishes "low usage" from "churned"
|
|
125
|
+
Do NOT track: every page view, every API request, health checks, background jobs
|
|
126
|
+
|
|
127
|
+
Phase 3: Verify
|
|
128
|
+
Confirm: client initialized, shutdown handled, identify/group/track calls added
|
|
129
|
+
|
|
130
|
+
Phase 4: Summarize
|
|
131
|
+
Report what you added:
|
|
132
|
+
- Files modified and what was added to each
|
|
133
|
+
- Events being tracked (list event names and what triggers them)
|
|
134
|
+
- How userId is obtained (and groupId if Companies & Teams mode)
|
|
135
|
+
- Any assumptions made or questions
|
|
136
|
+
```
|
|
137
|
+
|
|
53
138
|
## API Reference
|
|
54
139
|
|
|
55
140
|
### Constructor
|
|
@@ -64,16 +149,23 @@ new KlimeClient(config: {
|
|
|
64
149
|
retryMaxAttempts?: number; // Optional: Max retry attempts (default: 5)
|
|
65
150
|
retryInitialDelay?: number; // Optional: Initial retry delay in ms (default: 1000)
|
|
66
151
|
flushOnShutdown?: boolean; // Optional: Auto-flush on SIGTERM/SIGINT (default: true)
|
|
152
|
+
logger?: Logger; // Optional: Custom logger (default: console with [Klime] prefix)
|
|
153
|
+
onError?: (error, events) => void; // Optional: Callback for batch failures
|
|
154
|
+
onSuccess?: (response) => void; // Optional: Callback for successful sends
|
|
67
155
|
})
|
|
68
156
|
```
|
|
69
157
|
|
|
70
158
|
### Methods
|
|
71
159
|
|
|
72
|
-
#### `track(event: string, properties?: object, options?: { userId?, groupId
|
|
160
|
+
#### `track(event: string, properties?: object, options?: { userId?, groupId? })`
|
|
161
|
+
|
|
162
|
+
Track an event. Events can be attributed in two ways:
|
|
73
163
|
|
|
74
|
-
|
|
164
|
+
- **User events**: Provide `userId` to track user activity (most common)
|
|
165
|
+
- **Group events**: Provide `groupId` without `userId` for organization-level events
|
|
75
166
|
|
|
76
167
|
```javascript
|
|
168
|
+
// User event (most common)
|
|
77
169
|
client.track(
|
|
78
170
|
"Button Clicked",
|
|
79
171
|
{
|
|
@@ -83,40 +175,31 @@ client.track(
|
|
|
83
175
|
{ userId: "user_123" }
|
|
84
176
|
);
|
|
85
177
|
|
|
86
|
-
//
|
|
178
|
+
// Group event (for webhooks, cron jobs, system events)
|
|
87
179
|
client.track(
|
|
88
|
-
"
|
|
180
|
+
"Events Received",
|
|
89
181
|
{
|
|
90
|
-
|
|
91
|
-
|
|
182
|
+
count: 100,
|
|
183
|
+
source: "webhook",
|
|
92
184
|
},
|
|
93
|
-
{
|
|
94
|
-
userId: "user_123",
|
|
95
|
-
ip: "192.168.1.1",
|
|
96
|
-
}
|
|
185
|
+
{ groupId: "org_456" }
|
|
97
186
|
);
|
|
98
187
|
```
|
|
99
188
|
|
|
100
|
-
> **
|
|
189
|
+
> **Note**: The `groupId` option can also be combined with `userId` for multi-tenant scenarios where you need to specify which organization context a user event occurred in.
|
|
101
190
|
|
|
102
|
-
#### `identify(userId: string, traits?: object
|
|
191
|
+
#### `identify(userId: string, traits?: object)`
|
|
103
192
|
|
|
104
193
|
Identify a user with traits.
|
|
105
194
|
|
|
106
195
|
```javascript
|
|
107
|
-
client.identify(
|
|
108
|
-
"
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
name: "Stefan",
|
|
112
|
-
},
|
|
113
|
-
{
|
|
114
|
-
ip: "192.168.1.1",
|
|
115
|
-
}
|
|
116
|
-
);
|
|
196
|
+
client.identify("user_123", {
|
|
197
|
+
email: "user@example.com",
|
|
198
|
+
name: "Stefan",
|
|
199
|
+
});
|
|
117
200
|
```
|
|
118
201
|
|
|
119
|
-
#### `group(groupId: string, traits?: object, options?: { userId
|
|
202
|
+
#### `group(groupId: string, traits?: object, options?: { userId? })`
|
|
120
203
|
|
|
121
204
|
Associate a user with a group and/or set group traits.
|
|
122
205
|
|
|
@@ -151,12 +234,92 @@ Gracefully shutdown the client, flushing remaining events.
|
|
|
151
234
|
await client.shutdown();
|
|
152
235
|
```
|
|
153
236
|
|
|
237
|
+
#### `getQueueSize(): number`
|
|
238
|
+
|
|
239
|
+
Return the number of events currently in the queue.
|
|
240
|
+
|
|
241
|
+
```javascript
|
|
242
|
+
const pending = client.getQueueSize();
|
|
243
|
+
console.log(`${pending} events waiting to be sent`);
|
|
244
|
+
```
|
|
245
|
+
|
|
246
|
+
### Synchronous Methods
|
|
247
|
+
|
|
248
|
+
For cases where you need confirmation that events were sent (e.g., before process exit, in tests), use the synchronous variants:
|
|
249
|
+
|
|
250
|
+
#### `trackSync(event, properties, options): Promise<BatchResponse>`
|
|
251
|
+
|
|
252
|
+
Track an event synchronously. Returns `BatchResponse` or throws `SendError`.
|
|
253
|
+
|
|
254
|
+
```javascript
|
|
255
|
+
const { SendError } = require("@klime/node");
|
|
256
|
+
|
|
257
|
+
try {
|
|
258
|
+
const response = await client.trackSync(
|
|
259
|
+
"Critical Action",
|
|
260
|
+
{ key: "value" },
|
|
261
|
+
{ userId: "user_123" }
|
|
262
|
+
);
|
|
263
|
+
console.log(`Sent! Accepted: ${response.accepted}`);
|
|
264
|
+
} catch (error) {
|
|
265
|
+
if (error instanceof SendError) {
|
|
266
|
+
console.error(`Failed to send: ${error.message}`);
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
```
|
|
270
|
+
|
|
271
|
+
#### `identifySync(userId, traits): Promise<BatchResponse>`
|
|
272
|
+
|
|
273
|
+
Identify a user synchronously. Returns `BatchResponse` or throws `SendError`.
|
|
274
|
+
|
|
275
|
+
#### `groupSync(groupId, traits, options): Promise<BatchResponse>`
|
|
276
|
+
|
|
277
|
+
Associate a user with a group synchronously. Returns `BatchResponse` or throws `SendError`.
|
|
278
|
+
|
|
154
279
|
## Features
|
|
155
280
|
|
|
156
281
|
- **Automatic Batching**: Events are automatically batched and sent every 2 seconds or when the batch size reaches 20 events
|
|
157
282
|
- **Automatic Retries**: Failed requests are automatically retried with exponential backoff
|
|
158
283
|
- **Process Exit Handling**: Automatically flushes events on SIGTERM/SIGINT
|
|
159
284
|
- **Zero Dependencies**: Uses only Node.js standard library (fetch for Node 18+, https/http for older versions)
|
|
285
|
+
- **Universal Module Support**: Works with ESM and CommonJS out of the box
|
|
286
|
+
|
|
287
|
+
## Module Compatibility
|
|
288
|
+
|
|
289
|
+
This package ships dual ESM/CommonJS builds:
|
|
290
|
+
|
|
291
|
+
```javascript
|
|
292
|
+
// ESM (recommended for modern projects)
|
|
293
|
+
import { KlimeClient } from "@klime/node";
|
|
294
|
+
|
|
295
|
+
// CommonJS
|
|
296
|
+
const { KlimeClient } = require("@klime/node");
|
|
297
|
+
```
|
|
298
|
+
|
|
299
|
+
Works with all major Node.js frameworks including Express, Fastify, Hono, Next.js, and NestJS.
|
|
300
|
+
|
|
301
|
+
## Performance
|
|
302
|
+
|
|
303
|
+
When you call `track()`, `identify()`, or `group()`, the SDK:
|
|
304
|
+
|
|
305
|
+
1. Adds the event to an in-memory queue (microseconds)
|
|
306
|
+
2. Returns immediately without waiting for network I/O
|
|
307
|
+
|
|
308
|
+
Events are sent to Klime's servers asynchronously via Node.js's event loop. This means:
|
|
309
|
+
|
|
310
|
+
- **No network blocking**: HTTP requests happen asynchronously without blocking the event loop
|
|
311
|
+
- **No latency impact**: Tracking calls add < 1ms to your request handling time
|
|
312
|
+
- **Automatic batching**: Events are queued and sent in batches (default: every 2 seconds or 20 events)
|
|
313
|
+
|
|
314
|
+
```javascript
|
|
315
|
+
// This returns immediately - no HTTP request is made here
|
|
316
|
+
client.track("Button Clicked", { button: "signup" }, { userId: "user_123" });
|
|
317
|
+
|
|
318
|
+
// Your code continues without waiting
|
|
319
|
+
res.json({ success: true });
|
|
320
|
+
```
|
|
321
|
+
|
|
322
|
+
The only blocking operation is `await flush()`, which waits for all queued events to be sent. This is typically only called during graceful shutdown.
|
|
160
323
|
|
|
161
324
|
## Configuration
|
|
162
325
|
|
|
@@ -169,6 +332,38 @@ await client.shutdown();
|
|
|
169
332
|
- `retryInitialDelay`: 1000ms
|
|
170
333
|
- `flushOnShutdown`: true
|
|
171
334
|
|
|
335
|
+
### Logging
|
|
336
|
+
|
|
337
|
+
The SDK uses a console wrapper with `[Klime]` prefix by default. You can provide a custom logger:
|
|
338
|
+
|
|
339
|
+
```javascript
|
|
340
|
+
const client = new KlimeClient({
|
|
341
|
+
writeKey: "your-write-key",
|
|
342
|
+
logger: {
|
|
343
|
+
debug: (msg, ...args) => myLogger.debug(msg, ...args),
|
|
344
|
+
info: (msg, ...args) => myLogger.info(msg, ...args),
|
|
345
|
+
warn: (msg, ...args) => myLogger.warn(msg, ...args),
|
|
346
|
+
error: (msg, ...args) => myLogger.error(msg, ...args),
|
|
347
|
+
},
|
|
348
|
+
});
|
|
349
|
+
```
|
|
350
|
+
|
|
351
|
+
### Callbacks
|
|
352
|
+
|
|
353
|
+
```javascript
|
|
354
|
+
const client = new KlimeClient({
|
|
355
|
+
writeKey: "your-write-key",
|
|
356
|
+
onError: (error, events) => {
|
|
357
|
+
// Report to your error tracking service
|
|
358
|
+
Sentry.captureException(error);
|
|
359
|
+
console.error(`Failed to send ${events.length} events: ${error.message}`);
|
|
360
|
+
},
|
|
361
|
+
onSuccess: (response) => {
|
|
362
|
+
console.log(`Sent ${response.accepted} events`);
|
|
363
|
+
},
|
|
364
|
+
});
|
|
365
|
+
```
|
|
366
|
+
|
|
172
367
|
## Error Handling
|
|
173
368
|
|
|
174
369
|
The SDK automatically handles:
|
|
@@ -177,6 +372,20 @@ The SDK automatically handles:
|
|
|
177
372
|
- **Permanent errors** (400, 401): Logs error and drops event
|
|
178
373
|
- **Rate limiting**: Respects `Retry-After` header
|
|
179
374
|
|
|
375
|
+
For synchronous operations, use `*Sync()` methods which throw `SendError` on failure:
|
|
376
|
+
|
|
377
|
+
```javascript
|
|
378
|
+
const { KlimeClient, SendError } = require("@klime/node");
|
|
379
|
+
|
|
380
|
+
try {
|
|
381
|
+
const response = await client.trackSync("Event", {}, { userId: "user_123" });
|
|
382
|
+
} catch (error) {
|
|
383
|
+
if (error instanceof SendError) {
|
|
384
|
+
console.error(`Failed: ${error.message}, events: ${error.events.length}`);
|
|
385
|
+
}
|
|
386
|
+
}
|
|
387
|
+
```
|
|
388
|
+
|
|
180
389
|
## Size Limits
|
|
181
390
|
|
|
182
391
|
- Maximum event size: 200KB
|
|
@@ -204,7 +413,6 @@ app.post("/api/button-clicked", (req, res) => {
|
|
|
204
413
|
},
|
|
205
414
|
{
|
|
206
415
|
userId: req.user.id,
|
|
207
|
-
ip: req.ip,
|
|
208
416
|
}
|
|
209
417
|
);
|
|
210
418
|
|