@klime/node 1.0.3

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 ADDED
@@ -0,0 +1,229 @@
1
+ # @klime/node
2
+
3
+ Klime SDK for Node.js.
4
+
5
+ ## Installation
6
+
7
+ ```bash
8
+ npm install @klime/node
9
+ ```
10
+
11
+ ## Quick Start
12
+
13
+ ```javascript
14
+ const { KlimeClient } = require("@klime/node");
15
+
16
+ const client = new KlimeClient({
17
+ writeKey: "your-write-key",
18
+ });
19
+
20
+ // Identify a user
21
+ client.identify("user_123", {
22
+ email: "user@example.com",
23
+ name: "Stefan",
24
+ });
25
+
26
+ // Track an event
27
+ client.track(
28
+ "Button Clicked",
29
+ {
30
+ buttonName: "Sign up",
31
+ plan: "pro",
32
+ },
33
+ { userId: "user_123" }
34
+ );
35
+
36
+ // Associate user with a group and set group traits
37
+ client.group(
38
+ "org_456",
39
+ { name: "Acme Inc", plan: "enterprise" },
40
+ { userId: "user_123" }
41
+ );
42
+
43
+ // Or just link the user to a group (if traits are already set)
44
+ client.group("org_456", null, { userId: "user_123" });
45
+
46
+ // Shutdown gracefully
47
+ process.on("SIGTERM", async () => {
48
+ await client.shutdown();
49
+ process.exit(0);
50
+ });
51
+ ```
52
+
53
+ ## API Reference
54
+
55
+ ### Constructor
56
+
57
+ ```typescript
58
+ new KlimeClient(config: {
59
+ writeKey: string; // Required: Your Klime write key
60
+ endpoint?: string; // Optional: API endpoint (default: https://i.klime.com)
61
+ flushInterval?: number; // Optional: Milliseconds between flushes (default: 2000)
62
+ maxBatchSize?: number; // Optional: Max events per batch (default: 20, max: 100)
63
+ maxQueueSize?: number; // Optional: Max queued events (default: 1000)
64
+ retryMaxAttempts?: number; // Optional: Max retry attempts (default: 5)
65
+ retryInitialDelay?: number; // Optional: Initial retry delay in ms (default: 1000)
66
+ flushOnShutdown?: boolean; // Optional: Auto-flush on SIGTERM/SIGINT (default: true)
67
+ })
68
+ ```
69
+
70
+ ### Methods
71
+
72
+ #### `track(event: string, properties?: object, options?: { userId?, groupId?, ip? })`
73
+
74
+ Track a user event. A `userId` is required for events to be useful in Klime.
75
+
76
+ ```javascript
77
+ client.track(
78
+ "Button Clicked",
79
+ {
80
+ buttonName: "Sign up",
81
+ plan: "pro",
82
+ },
83
+ { userId: "user_123" }
84
+ );
85
+
86
+ // With IP address (for geolocation)
87
+ client.track(
88
+ "Button Clicked",
89
+ {
90
+ buttonName: "Sign up",
91
+ plan: "pro",
92
+ },
93
+ {
94
+ userId: "user_123",
95
+ ip: "192.168.1.1",
96
+ }
97
+ );
98
+ ```
99
+
100
+ > **Advanced**: The `groupId` option is available for multi-tenant scenarios where a user belongs to multiple organizations and you need to specify which organization context the event occurred in.
101
+
102
+ #### `identify(userId: string, traits?: object, options?: { ip? })`
103
+
104
+ Identify a user with traits.
105
+
106
+ ```javascript
107
+ client.identify(
108
+ "user_123",
109
+ {
110
+ email: "user@example.com",
111
+ name: "Stefan",
112
+ },
113
+ {
114
+ ip: "192.168.1.1",
115
+ }
116
+ );
117
+ ```
118
+
119
+ #### `group(groupId: string, traits?: object, options?: { userId?, ip? })`
120
+
121
+ Associate a user with a group and/or set group traits.
122
+
123
+ ```javascript
124
+ // Associate user with a group and set group traits (most common)
125
+ client.group(
126
+ "org_456",
127
+ { name: "Acme Inc", plan: "enterprise" },
128
+ { userId: "user_123" }
129
+ );
130
+
131
+ // Just link a user to a group (traits already set or not needed)
132
+ client.group("org_456", null, { userId: "user_123" });
133
+
134
+ // Just update group traits (e.g., from a webhook or background job)
135
+ client.group("org_456", { plan: "enterprise", employeeCount: 50 });
136
+ ```
137
+
138
+ #### `flush(): Promise<void>`
139
+
140
+ Manually flush queued events immediately.
141
+
142
+ ```javascript
143
+ await client.flush();
144
+ ```
145
+
146
+ #### `shutdown(): Promise<void>`
147
+
148
+ Gracefully shutdown the client, flushing remaining events.
149
+
150
+ ```javascript
151
+ await client.shutdown();
152
+ ```
153
+
154
+ ## Features
155
+
156
+ - **Automatic Batching**: Events are automatically batched and sent every 2 seconds or when the batch size reaches 20 events
157
+ - **Automatic Retries**: Failed requests are automatically retried with exponential backoff
158
+ - **Process Exit Handling**: Automatically flushes events on SIGTERM/SIGINT
159
+ - **Zero Dependencies**: Uses only Node.js standard library (fetch for Node 18+, https/http for older versions)
160
+
161
+ ## Configuration
162
+
163
+ ### Default Values
164
+
165
+ - `flushInterval`: 2000ms
166
+ - `maxBatchSize`: 20 events
167
+ - `maxQueueSize`: 1000 events
168
+ - `retryMaxAttempts`: 5 attempts
169
+ - `retryInitialDelay`: 1000ms
170
+ - `flushOnShutdown`: true
171
+
172
+ ## Error Handling
173
+
174
+ The SDK automatically handles:
175
+
176
+ - **Transient errors** (429, 503, network failures): Retries with exponential backoff
177
+ - **Permanent errors** (400, 401): Logs error and drops event
178
+ - **Rate limiting**: Respects `Retry-After` header
179
+
180
+ ## Size Limits
181
+
182
+ - Maximum event size: 200KB
183
+ - Maximum batch size: 10MB
184
+ - Maximum events per batch: 100
185
+
186
+ Events exceeding these limits are rejected and logged.
187
+
188
+ ## Express.js Example
189
+
190
+ ```javascript
191
+ const express = require("express");
192
+ const { KlimeClient } = require("@klime/node");
193
+
194
+ const app = express();
195
+ const client = new KlimeClient({
196
+ writeKey: process.env.KLIME_WRITE_KEY,
197
+ });
198
+
199
+ app.post("/api/button-clicked", (req, res) => {
200
+ client.track(
201
+ "Button Clicked",
202
+ {
203
+ buttonName: req.body.buttonName,
204
+ },
205
+ {
206
+ userId: req.user.id,
207
+ ip: req.ip,
208
+ }
209
+ );
210
+
211
+ res.json({ success: true });
212
+ });
213
+
214
+ // Graceful shutdown
215
+ process.on("SIGTERM", async () => {
216
+ await client.shutdown();
217
+ process.exit(0);
218
+ });
219
+ ```
220
+
221
+ ## Requirements
222
+
223
+ - Node.js 14.0.0 or higher
224
+ - For Node.js 18+, native `fetch` is used
225
+ - For Node.js 14-17, `https`/`http` modules are used
226
+
227
+ ## License
228
+
229
+ MIT
@@ -0,0 +1,28 @@
1
+ import { KlimeConfig, TrackOptions, IdentifyOptions, GroupOptions } from "./types";
2
+ export declare class KlimeClient {
3
+ private config;
4
+ private queue;
5
+ private flushTimer;
6
+ private isShutdown;
7
+ private flushPromise;
8
+ private shutdownHandlers;
9
+ constructor(config: KlimeConfig);
10
+ track(event: string, properties?: Record<string, any>, options?: TrackOptions): void;
11
+ identify(userId: string, traits?: Record<string, any>, options?: IdentifyOptions): void;
12
+ group(groupId: string, traits?: Record<string, any>, options?: GroupOptions): void;
13
+ flush(): Promise<void>;
14
+ shutdown(): Promise<void>;
15
+ private enqueue;
16
+ private doFlush;
17
+ private extractBatch;
18
+ private sendBatch;
19
+ private sendBatchWithFetch;
20
+ private sendBatchWithHttps;
21
+ private makeRequest;
22
+ private scheduleFlush;
23
+ private generateUUID;
24
+ private generateTimestamp;
25
+ private getContext;
26
+ private estimateEventSize;
27
+ private sleep;
28
+ }
package/dist/index.js ADDED
@@ -0,0 +1,419 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.KlimeClient = void 0;
37
+ const https = __importStar(require("https"));
38
+ const http = __importStar(require("http"));
39
+ const url = __importStar(require("url"));
40
+ const DEFAULT_ENDPOINT = "https://i.klime.com";
41
+ const DEFAULT_FLUSH_INTERVAL = 2000;
42
+ const DEFAULT_MAX_BATCH_SIZE = 20;
43
+ const DEFAULT_MAX_QUEUE_SIZE = 1000;
44
+ const DEFAULT_RETRY_MAX_ATTEMPTS = 5;
45
+ const DEFAULT_RETRY_INITIAL_DELAY = 1000;
46
+ const MAX_BATCH_SIZE = 100;
47
+ const MAX_EVENT_SIZE_BYTES = 200 * 1024; // 200KB
48
+ const MAX_BATCH_SIZE_BYTES = 10 * 1024 * 1024; // 10MB
49
+ const SDK_VERSION = "1.0.1";
50
+ // Check if fetch is available (Node 18+)
51
+ const hasNativeFetch = typeof fetch !== "undefined";
52
+ class KlimeClient {
53
+ constructor(config) {
54
+ this.queue = [];
55
+ this.flushTimer = null;
56
+ this.isShutdown = false;
57
+ this.flushPromise = null;
58
+ this.shutdownHandlers = [];
59
+ if (!config.writeKey) {
60
+ throw new Error("writeKey is required");
61
+ }
62
+ this.config = {
63
+ writeKey: config.writeKey,
64
+ endpoint: config.endpoint || DEFAULT_ENDPOINT,
65
+ flushInterval: config.flushInterval ?? DEFAULT_FLUSH_INTERVAL,
66
+ maxBatchSize: Math.min(config.maxBatchSize ?? DEFAULT_MAX_BATCH_SIZE, MAX_BATCH_SIZE),
67
+ maxQueueSize: config.maxQueueSize ?? DEFAULT_MAX_QUEUE_SIZE,
68
+ retryMaxAttempts: config.retryMaxAttempts ?? DEFAULT_RETRY_MAX_ATTEMPTS,
69
+ retryInitialDelay: config.retryInitialDelay ?? DEFAULT_RETRY_INITIAL_DELAY,
70
+ flushOnShutdown: config.flushOnShutdown ?? true,
71
+ };
72
+ if (this.config.flushOnShutdown) {
73
+ const shutdownHandler = async () => {
74
+ await this.shutdown();
75
+ };
76
+ process.on("SIGTERM", shutdownHandler);
77
+ process.on("SIGINT", shutdownHandler);
78
+ this.shutdownHandlers.push(() => {
79
+ process.removeListener("SIGTERM", shutdownHandler);
80
+ process.removeListener("SIGINT", shutdownHandler);
81
+ });
82
+ }
83
+ this.scheduleFlush();
84
+ }
85
+ track(event, properties, options) {
86
+ if (this.isShutdown) {
87
+ return;
88
+ }
89
+ const eventObj = {
90
+ type: "track",
91
+ messageId: this.generateUUID(),
92
+ event,
93
+ timestamp: this.generateTimestamp(),
94
+ properties: properties || {},
95
+ context: this.getContext(options?.ip),
96
+ };
97
+ if (options?.userId) {
98
+ eventObj.userId = options.userId;
99
+ }
100
+ if (options?.groupId) {
101
+ eventObj.groupId = options.groupId;
102
+ }
103
+ this.enqueue(eventObj);
104
+ }
105
+ identify(userId, traits, options) {
106
+ if (this.isShutdown) {
107
+ return;
108
+ }
109
+ const eventObj = {
110
+ type: "identify",
111
+ messageId: this.generateUUID(),
112
+ userId,
113
+ timestamp: this.generateTimestamp(),
114
+ traits: traits || {},
115
+ context: this.getContext(options?.ip),
116
+ };
117
+ this.enqueue(eventObj);
118
+ }
119
+ group(groupId, traits, options) {
120
+ if (this.isShutdown) {
121
+ return;
122
+ }
123
+ const eventObj = {
124
+ type: "group",
125
+ messageId: this.generateUUID(),
126
+ groupId,
127
+ timestamp: this.generateTimestamp(),
128
+ traits: traits || {},
129
+ context: this.getContext(options?.ip),
130
+ };
131
+ if (options?.userId) {
132
+ eventObj.userId = options.userId;
133
+ }
134
+ this.enqueue(eventObj);
135
+ }
136
+ async flush() {
137
+ if (this.flushPromise) {
138
+ return this.flushPromise;
139
+ }
140
+ this.flushPromise = this.doFlush();
141
+ try {
142
+ await this.flushPromise;
143
+ }
144
+ finally {
145
+ this.flushPromise = null;
146
+ }
147
+ }
148
+ async shutdown() {
149
+ if (this.isShutdown) {
150
+ return;
151
+ }
152
+ this.isShutdown = true;
153
+ // Remove shutdown handlers
154
+ this.shutdownHandlers.forEach((handler) => handler());
155
+ this.shutdownHandlers = [];
156
+ if (this.flushTimer) {
157
+ clearTimeout(this.flushTimer);
158
+ this.flushTimer = null;
159
+ }
160
+ await this.flush();
161
+ }
162
+ enqueue(event) {
163
+ // Check event size
164
+ const eventSize = this.estimateEventSize(event);
165
+ if (eventSize > MAX_EVENT_SIZE_BYTES) {
166
+ console.error(`Klime: Event size (${eventSize} bytes) exceeds ${MAX_EVENT_SIZE_BYTES} bytes limit`);
167
+ return;
168
+ }
169
+ // Drop oldest if queue is full
170
+ if (this.queue.length >= this.config.maxQueueSize) {
171
+ this.queue.shift();
172
+ }
173
+ this.queue.push(event);
174
+ // Check if we should flush immediately
175
+ if (this.queue.length >= this.config.maxBatchSize) {
176
+ this.flush();
177
+ }
178
+ }
179
+ async doFlush() {
180
+ if (this.queue.length === 0) {
181
+ return;
182
+ }
183
+ // Clear the flush timer
184
+ if (this.flushTimer) {
185
+ clearTimeout(this.flushTimer);
186
+ this.flushTimer = null;
187
+ }
188
+ // Process batches
189
+ while (this.queue.length > 0) {
190
+ const batch = this.extractBatch();
191
+ if (batch.length === 0) {
192
+ break;
193
+ }
194
+ await this.sendBatch(batch);
195
+ }
196
+ // Schedule next flush
197
+ this.scheduleFlush();
198
+ }
199
+ extractBatch() {
200
+ const batch = [];
201
+ let batchSize = 0;
202
+ while (this.queue.length > 0 && batch.length < MAX_BATCH_SIZE) {
203
+ const event = this.queue[0];
204
+ const eventSize = this.estimateEventSize(event);
205
+ // Check if adding this event would exceed batch size limit
206
+ if (batchSize + eventSize > MAX_BATCH_SIZE_BYTES) {
207
+ break;
208
+ }
209
+ batch.push(this.queue.shift());
210
+ batchSize += eventSize;
211
+ }
212
+ return batch;
213
+ }
214
+ async sendBatch(batch) {
215
+ if (batch.length === 0) {
216
+ return;
217
+ }
218
+ const request = { batch };
219
+ const requestBody = JSON.stringify(request);
220
+ if (hasNativeFetch) {
221
+ await this.sendBatchWithFetch(batch, requestBody);
222
+ }
223
+ else {
224
+ await this.sendBatchWithHttps(batch, requestBody);
225
+ }
226
+ }
227
+ async sendBatchWithFetch(batch, requestBody) {
228
+ const requestUrl = `${this.config.endpoint}/v1/batch`;
229
+ let attempt = 0;
230
+ let delay = this.config.retryInitialDelay;
231
+ while (attempt < this.config.retryMaxAttempts) {
232
+ try {
233
+ const response = await fetch(requestUrl, {
234
+ method: "POST",
235
+ headers: {
236
+ "Content-Type": "application/json",
237
+ Authorization: `Bearer ${this.config.writeKey}`,
238
+ },
239
+ body: requestBody,
240
+ });
241
+ const data = (await response.json());
242
+ if (response.ok) {
243
+ if (data.failed > 0 && data.errors) {
244
+ console.warn(`Klime: Batch partially failed. Accepted: ${data.accepted}, Failed: ${data.failed}`, data.errors);
245
+ }
246
+ return;
247
+ }
248
+ if (response.status === 400 || response.status === 401) {
249
+ console.error(`Klime: Permanent error (${response.status}):`, data);
250
+ return;
251
+ }
252
+ if (response.status === 429 || response.status === 503) {
253
+ const retryAfter = response.headers.get("Retry-After");
254
+ if (retryAfter) {
255
+ delay = parseInt(retryAfter, 10) * 1000;
256
+ }
257
+ attempt++;
258
+ if (attempt < this.config.retryMaxAttempts) {
259
+ await this.sleep(delay);
260
+ delay = Math.min(delay * 2, 16000);
261
+ continue;
262
+ }
263
+ }
264
+ attempt++;
265
+ if (attempt < this.config.retryMaxAttempts) {
266
+ await this.sleep(delay);
267
+ delay = Math.min(delay * 2, 16000);
268
+ }
269
+ }
270
+ catch (error) {
271
+ attempt++;
272
+ if (attempt < this.config.retryMaxAttempts) {
273
+ await this.sleep(delay);
274
+ delay = Math.min(delay * 2, 16000);
275
+ }
276
+ else {
277
+ console.error("Klime: Failed to send batch after retries:", error);
278
+ }
279
+ }
280
+ }
281
+ }
282
+ async sendBatchWithHttps(batch, requestBody) {
283
+ const parsedUrl = url.parse(this.config.endpoint);
284
+ const isHttps = parsedUrl.protocol === "https:";
285
+ const client = isHttps ? https : http;
286
+ const port = parsedUrl.port
287
+ ? parseInt(parsedUrl.port, 10)
288
+ : isHttps
289
+ ? 443
290
+ : 80;
291
+ const hostname = parsedUrl.hostname || "";
292
+ let attempt = 0;
293
+ let delay = this.config.retryInitialDelay;
294
+ while (attempt < this.config.retryMaxAttempts) {
295
+ try {
296
+ const result = await this.makeRequest(client, hostname, port, "/v1/batch", requestBody);
297
+ if (result.statusCode === 200) {
298
+ const data = JSON.parse(result.body);
299
+ if (data.failed > 0 && data.errors) {
300
+ console.warn(`Klime: Batch partially failed. Accepted: ${data.accepted}, Failed: ${data.failed}`, data.errors);
301
+ }
302
+ return;
303
+ }
304
+ if (result.statusCode === 400 || result.statusCode === 401) {
305
+ const data = JSON.parse(result.body);
306
+ console.error(`Klime: Permanent error (${result.statusCode}):`, data);
307
+ return;
308
+ }
309
+ if (result.statusCode === 429 || result.statusCode === 503) {
310
+ const retryAfter = result.headers["retry-after"];
311
+ if (retryAfter) {
312
+ delay =
313
+ parseInt(Array.isArray(retryAfter) ? retryAfter[0] : retryAfter, 10) * 1000;
314
+ }
315
+ attempt++;
316
+ if (attempt < this.config.retryMaxAttempts) {
317
+ await this.sleep(delay);
318
+ delay = Math.min(delay * 2, 16000);
319
+ continue;
320
+ }
321
+ }
322
+ attempt++;
323
+ if (attempt < this.config.retryMaxAttempts) {
324
+ await this.sleep(delay);
325
+ delay = Math.min(delay * 2, 16000);
326
+ }
327
+ }
328
+ catch (error) {
329
+ attempt++;
330
+ if (attempt < this.config.retryMaxAttempts) {
331
+ await this.sleep(delay);
332
+ delay = Math.min(delay * 2, 16000);
333
+ }
334
+ else {
335
+ console.error("Klime: Failed to send batch after retries:", error);
336
+ }
337
+ }
338
+ }
339
+ }
340
+ makeRequest(client, hostname, port, path, body) {
341
+ return new Promise((resolve, reject) => {
342
+ const options = {
343
+ hostname,
344
+ port,
345
+ path,
346
+ method: "POST",
347
+ headers: {
348
+ "Content-Type": "application/json",
349
+ Authorization: `Bearer ${this.config.writeKey}`,
350
+ "Content-Length": Buffer.byteLength(body),
351
+ },
352
+ };
353
+ const req = client.request(options, (res) => {
354
+ let data = "";
355
+ res.on("data", (chunk) => {
356
+ data += chunk;
357
+ });
358
+ res.on("end", () => {
359
+ resolve({
360
+ statusCode: res.statusCode || 500,
361
+ body: data,
362
+ headers: res.headers,
363
+ });
364
+ });
365
+ });
366
+ req.on("error", (error) => {
367
+ reject(error);
368
+ });
369
+ req.write(body);
370
+ req.end();
371
+ });
372
+ }
373
+ scheduleFlush() {
374
+ if (this.isShutdown || this.flushTimer) {
375
+ return;
376
+ }
377
+ this.flushTimer = setTimeout(() => {
378
+ this.flush();
379
+ }, this.config.flushInterval);
380
+ }
381
+ generateUUID() {
382
+ if (typeof crypto !== "undefined" && crypto.randomUUID) {
383
+ return crypto.randomUUID();
384
+ }
385
+ // Fallback for Node < 15
386
+ return "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g, (c) => {
387
+ const r = (Math.random() * 16) | 0;
388
+ const v = c === "x" ? r : (r & 0x3) | 0x8;
389
+ return v.toString(16);
390
+ });
391
+ }
392
+ generateTimestamp() {
393
+ return new Date().toISOString();
394
+ }
395
+ getContext(ip) {
396
+ const context = {
397
+ library: {
398
+ name: "node-sdk",
399
+ version: SDK_VERSION,
400
+ },
401
+ };
402
+ if (ip) {
403
+ context.ip = ip;
404
+ }
405
+ return context;
406
+ }
407
+ estimateEventSize(event) {
408
+ try {
409
+ return JSON.stringify(event).length;
410
+ }
411
+ catch {
412
+ return 500;
413
+ }
414
+ }
415
+ sleep(ms) {
416
+ return new Promise((resolve) => setTimeout(resolve, ms));
417
+ }
418
+ }
419
+ exports.KlimeClient = KlimeClient;
@@ -0,0 +1,54 @@
1
+ export interface KlimeConfig {
2
+ writeKey: string;
3
+ endpoint?: string;
4
+ flushInterval?: number;
5
+ maxBatchSize?: number;
6
+ maxQueueSize?: number;
7
+ retryMaxAttempts?: number;
8
+ retryInitialDelay?: number;
9
+ flushOnShutdown?: boolean;
10
+ }
11
+ export interface TrackOptions {
12
+ userId?: string;
13
+ groupId?: string;
14
+ ip?: string;
15
+ }
16
+ export interface IdentifyOptions {
17
+ ip?: string;
18
+ }
19
+ export interface GroupOptions {
20
+ userId?: string;
21
+ ip?: string;
22
+ }
23
+ export interface Event {
24
+ type: 'track' | 'identify' | 'group';
25
+ messageId: string;
26
+ event?: string;
27
+ userId?: string;
28
+ groupId?: string;
29
+ timestamp: string;
30
+ properties?: Record<string, any>;
31
+ traits?: Record<string, any>;
32
+ context?: EventContext;
33
+ }
34
+ export interface EventContext {
35
+ library?: {
36
+ name: string;
37
+ version: string;
38
+ };
39
+ ip?: string;
40
+ }
41
+ export interface BatchRequest {
42
+ batch: Event[];
43
+ }
44
+ export interface BatchResponse {
45
+ status: string;
46
+ accepted: number;
47
+ failed: number;
48
+ errors?: ValidationError[];
49
+ }
50
+ export interface ValidationError {
51
+ index: number;
52
+ message: string;
53
+ code: string;
54
+ }
package/dist/types.js ADDED
@@ -0,0 +1,2 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
package/package.json ADDED
@@ -0,0 +1,40 @@
1
+ {
2
+ "name": "@klime/node",
3
+ "version": "1.0.3",
4
+ "description": "Klime SDK for Node.js",
5
+ "main": "dist/index.js",
6
+ "types": "dist/index.d.ts",
7
+ "scripts": {
8
+ "build": "tsc",
9
+ "clean": "rm -rf dist",
10
+ "test": "vitest run",
11
+ "test:watch": "vitest",
12
+ "prepublishOnly": "npm run build"
13
+ },
14
+ "keywords": [
15
+ "analytics",
16
+ "klime",
17
+ "tracking",
18
+ "events",
19
+ "node"
20
+ ],
21
+ "author": "Klime",
22
+ "license": "MIT",
23
+ "devDependencies": {
24
+ "@types/node": "^20.0.0",
25
+ "typescript": "^5.0.0",
26
+ "vitest": "^2.0.0"
27
+ },
28
+ "files": [
29
+ "dist"
30
+ ],
31
+ "engines": {
32
+ "node": ">=14.0.0"
33
+ },
34
+ "repository": {
35
+ "type": "git",
36
+ "url": "https://github.com/klimeapp/klime-js.git",
37
+ "directory": "packages/node"
38
+ },
39
+ "homepage": "https://github.com/klimeapp/klime-js/tree/main/packages/node#readme"
40
+ }