@klime/browser 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 +166 -0
- package/dist/index.d.ts +25 -0
- package/dist/index.js +296 -0
- package/dist/types.d.ts +49 -0
- package/dist/types.js +1 -0
- package/package.json +38 -0
package/README.md
ADDED
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
# @klime/browser
|
|
2
|
+
|
|
3
|
+
Klime SDK for browsers.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install @klime/browser
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Quick Start
|
|
12
|
+
|
|
13
|
+
```javascript
|
|
14
|
+
import { KlimeClient } from "@klime/browser";
|
|
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
|
+
|
|
47
|
+
## API Reference
|
|
48
|
+
|
|
49
|
+
### Constructor
|
|
50
|
+
|
|
51
|
+
```typescript
|
|
52
|
+
new KlimeClient(config: {
|
|
53
|
+
writeKey: string; // Required: Your Klime write key
|
|
54
|
+
endpoint?: string; // Optional: API endpoint (default: https://i.klime.com)
|
|
55
|
+
flushInterval?: number; // Optional: Milliseconds between flushes (default: 2000)
|
|
56
|
+
maxBatchSize?: number; // Optional: Max events per batch (default: 20, max: 100)
|
|
57
|
+
maxQueueSize?: number; // Optional: Max queued events (default: 1000)
|
|
58
|
+
retryMaxAttempts?: number; // Optional: Max retry attempts (default: 5)
|
|
59
|
+
retryInitialDelay?: number; // Optional: Initial retry delay in ms (default: 1000)
|
|
60
|
+
autoFlushOnUnload?: boolean; // Optional: Auto-flush on page unload (default: true)
|
|
61
|
+
})
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
### Methods
|
|
65
|
+
|
|
66
|
+
#### `track(event: string, properties?: object, options?: { userId?, groupId? })`
|
|
67
|
+
|
|
68
|
+
Track a user event. A `userId` is required for events to be useful in Klime.
|
|
69
|
+
|
|
70
|
+
```javascript
|
|
71
|
+
client.track(
|
|
72
|
+
"Button Clicked",
|
|
73
|
+
{
|
|
74
|
+
buttonName: "Sign up",
|
|
75
|
+
plan: "pro",
|
|
76
|
+
},
|
|
77
|
+
{ userId: "user_123" }
|
|
78
|
+
);
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
> **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.
|
|
82
|
+
|
|
83
|
+
#### `identify(userId: string, traits?: object)`
|
|
84
|
+
|
|
85
|
+
Identify a user with traits.
|
|
86
|
+
|
|
87
|
+
```javascript
|
|
88
|
+
client.identify("user_123", {
|
|
89
|
+
email: "user@example.com",
|
|
90
|
+
name: "Stefan",
|
|
91
|
+
});
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
#### `group(groupId: string, traits?: object, options?: { userId? })`
|
|
95
|
+
|
|
96
|
+
Associate a user with a group and/or set group traits.
|
|
97
|
+
|
|
98
|
+
```javascript
|
|
99
|
+
// Associate user with a group and set group traits (most common)
|
|
100
|
+
client.group(
|
|
101
|
+
"org_456",
|
|
102
|
+
{ name: "Acme Inc", plan: "enterprise" },
|
|
103
|
+
{ userId: "user_123" }
|
|
104
|
+
);
|
|
105
|
+
|
|
106
|
+
// Just link a user to a group (traits already set or not needed)
|
|
107
|
+
client.group("org_456", null, { userId: "user_123" });
|
|
108
|
+
|
|
109
|
+
// Just update group traits (e.g., from a webhook or background job)
|
|
110
|
+
client.group("org_456", { plan: "enterprise", employeeCount: 50 });
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
#### `flush(): Promise<void>`
|
|
114
|
+
|
|
115
|
+
Manually flush queued events immediately.
|
|
116
|
+
|
|
117
|
+
```javascript
|
|
118
|
+
await client.flush();
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
#### `shutdown(): Promise<void>`
|
|
122
|
+
|
|
123
|
+
Gracefully shutdown the client, flushing remaining events.
|
|
124
|
+
|
|
125
|
+
```javascript
|
|
126
|
+
await client.shutdown();
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
## Features
|
|
130
|
+
|
|
131
|
+
- **Automatic Batching**: Events are automatically batched and sent every 2 seconds or when the batch size reaches 20 events
|
|
132
|
+
- **Automatic Retries**: Failed requests are automatically retried with exponential backoff
|
|
133
|
+
- **Browser Context**: Automatically captures userAgent, locale, and timezone
|
|
134
|
+
- **Page Unload Handling**: Automatically flushes events when the page is about to unload
|
|
135
|
+
- **Zero Dependencies**: Uses only native browser APIs
|
|
136
|
+
|
|
137
|
+
## Configuration
|
|
138
|
+
|
|
139
|
+
### Default Values
|
|
140
|
+
|
|
141
|
+
- `flushInterval`: 2000ms
|
|
142
|
+
- `maxBatchSize`: 20 events
|
|
143
|
+
- `maxQueueSize`: 1000 events
|
|
144
|
+
- `retryMaxAttempts`: 5 attempts
|
|
145
|
+
- `retryInitialDelay`: 1000ms
|
|
146
|
+
- `autoFlushOnUnload`: true
|
|
147
|
+
|
|
148
|
+
## Error Handling
|
|
149
|
+
|
|
150
|
+
The SDK automatically handles:
|
|
151
|
+
|
|
152
|
+
- **Transient errors** (429, 503, network failures): Retries with exponential backoff
|
|
153
|
+
- **Permanent errors** (400, 401): Logs error and drops event
|
|
154
|
+
- **Rate limiting**: Respects `Retry-After` header
|
|
155
|
+
|
|
156
|
+
## Size Limits
|
|
157
|
+
|
|
158
|
+
- Maximum event size: 200KB
|
|
159
|
+
- Maximum batch size: 10MB
|
|
160
|
+
- Maximum events per batch: 100
|
|
161
|
+
|
|
162
|
+
Events exceeding these limits are rejected and logged.
|
|
163
|
+
|
|
164
|
+
## License
|
|
165
|
+
|
|
166
|
+
MIT
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { KlimeConfig, TrackOptions } from "./types";
|
|
2
|
+
export declare class KlimeClient {
|
|
3
|
+
private config;
|
|
4
|
+
private queue;
|
|
5
|
+
private flushTimer;
|
|
6
|
+
private isShutdown;
|
|
7
|
+
private flushPromise;
|
|
8
|
+
private unloadHandler;
|
|
9
|
+
constructor(config: KlimeConfig);
|
|
10
|
+
track(event: string, properties?: Record<string, any>, options?: TrackOptions): void;
|
|
11
|
+
identify(userId: string, traits?: Record<string, any>): void;
|
|
12
|
+
group(groupId: string, traits?: Record<string, any>, options?: TrackOptions): void;
|
|
13
|
+
flush(): Promise<void>;
|
|
14
|
+
shutdown(): Promise<void>;
|
|
15
|
+
private enqueue;
|
|
16
|
+
private doFlush;
|
|
17
|
+
private extractBatch;
|
|
18
|
+
private sendBatch;
|
|
19
|
+
private scheduleFlush;
|
|
20
|
+
private generateUUID;
|
|
21
|
+
private generateTimestamp;
|
|
22
|
+
private getContext;
|
|
23
|
+
private estimateEventSize;
|
|
24
|
+
private sleep;
|
|
25
|
+
}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,296 @@
|
|
|
1
|
+
const DEFAULT_ENDPOINT = "https://i.klime.com";
|
|
2
|
+
const DEFAULT_FLUSH_INTERVAL = 2000;
|
|
3
|
+
const DEFAULT_MAX_BATCH_SIZE = 20;
|
|
4
|
+
const DEFAULT_MAX_QUEUE_SIZE = 1000;
|
|
5
|
+
const DEFAULT_RETRY_MAX_ATTEMPTS = 5;
|
|
6
|
+
const DEFAULT_RETRY_INITIAL_DELAY = 1000;
|
|
7
|
+
const MAX_BATCH_SIZE = 100;
|
|
8
|
+
const MAX_EVENT_SIZE_BYTES = 200 * 1024; // 200KB
|
|
9
|
+
const MAX_BATCH_SIZE_BYTES = 10 * 1024 * 1024; // 10MB
|
|
10
|
+
const SDK_VERSION = "1.0.1";
|
|
11
|
+
export class KlimeClient {
|
|
12
|
+
constructor(config) {
|
|
13
|
+
this.queue = [];
|
|
14
|
+
this.flushTimer = null;
|
|
15
|
+
this.isShutdown = false;
|
|
16
|
+
this.flushPromise = null;
|
|
17
|
+
this.unloadHandler = null;
|
|
18
|
+
if (!config.writeKey) {
|
|
19
|
+
throw new Error("writeKey is required");
|
|
20
|
+
}
|
|
21
|
+
this.config = {
|
|
22
|
+
writeKey: config.writeKey,
|
|
23
|
+
endpoint: config.endpoint || DEFAULT_ENDPOINT,
|
|
24
|
+
flushInterval: config.flushInterval ?? DEFAULT_FLUSH_INTERVAL,
|
|
25
|
+
maxBatchSize: Math.min(config.maxBatchSize ?? DEFAULT_MAX_BATCH_SIZE, MAX_BATCH_SIZE),
|
|
26
|
+
maxQueueSize: config.maxQueueSize ?? DEFAULT_MAX_QUEUE_SIZE,
|
|
27
|
+
retryMaxAttempts: config.retryMaxAttempts ?? DEFAULT_RETRY_MAX_ATTEMPTS,
|
|
28
|
+
retryInitialDelay: config.retryInitialDelay ?? DEFAULT_RETRY_INITIAL_DELAY,
|
|
29
|
+
autoFlushOnUnload: config.autoFlushOnUnload ?? true,
|
|
30
|
+
};
|
|
31
|
+
if (this.config.autoFlushOnUnload && typeof window !== "undefined") {
|
|
32
|
+
this.unloadHandler = () => {
|
|
33
|
+
this.flush();
|
|
34
|
+
};
|
|
35
|
+
window.addEventListener("beforeunload", this.unloadHandler);
|
|
36
|
+
}
|
|
37
|
+
this.scheduleFlush();
|
|
38
|
+
}
|
|
39
|
+
track(event, properties, options) {
|
|
40
|
+
if (this.isShutdown) {
|
|
41
|
+
return;
|
|
42
|
+
}
|
|
43
|
+
const eventObj = {
|
|
44
|
+
type: "track",
|
|
45
|
+
messageId: this.generateUUID(),
|
|
46
|
+
event,
|
|
47
|
+
timestamp: this.generateTimestamp(),
|
|
48
|
+
properties: properties || {},
|
|
49
|
+
context: this.getContext(),
|
|
50
|
+
};
|
|
51
|
+
if (options?.userId) {
|
|
52
|
+
eventObj.userId = options.userId;
|
|
53
|
+
}
|
|
54
|
+
if (options?.groupId) {
|
|
55
|
+
eventObj.groupId = options.groupId;
|
|
56
|
+
}
|
|
57
|
+
this.enqueue(eventObj);
|
|
58
|
+
}
|
|
59
|
+
identify(userId, traits) {
|
|
60
|
+
if (this.isShutdown) {
|
|
61
|
+
return;
|
|
62
|
+
}
|
|
63
|
+
const eventObj = {
|
|
64
|
+
type: "identify",
|
|
65
|
+
messageId: this.generateUUID(),
|
|
66
|
+
userId,
|
|
67
|
+
timestamp: this.generateTimestamp(),
|
|
68
|
+
traits: traits || {},
|
|
69
|
+
context: this.getContext(),
|
|
70
|
+
};
|
|
71
|
+
this.enqueue(eventObj);
|
|
72
|
+
}
|
|
73
|
+
group(groupId, traits, options) {
|
|
74
|
+
if (this.isShutdown) {
|
|
75
|
+
return;
|
|
76
|
+
}
|
|
77
|
+
const eventObj = {
|
|
78
|
+
type: "group",
|
|
79
|
+
messageId: this.generateUUID(),
|
|
80
|
+
groupId,
|
|
81
|
+
timestamp: this.generateTimestamp(),
|
|
82
|
+
traits: traits || {},
|
|
83
|
+
context: this.getContext(),
|
|
84
|
+
};
|
|
85
|
+
if (options?.userId) {
|
|
86
|
+
eventObj.userId = options.userId;
|
|
87
|
+
}
|
|
88
|
+
this.enqueue(eventObj);
|
|
89
|
+
}
|
|
90
|
+
async flush() {
|
|
91
|
+
if (this.flushPromise) {
|
|
92
|
+
return this.flushPromise;
|
|
93
|
+
}
|
|
94
|
+
this.flushPromise = this.doFlush();
|
|
95
|
+
try {
|
|
96
|
+
await this.flushPromise;
|
|
97
|
+
}
|
|
98
|
+
finally {
|
|
99
|
+
this.flushPromise = null;
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
async shutdown() {
|
|
103
|
+
if (this.isShutdown) {
|
|
104
|
+
return;
|
|
105
|
+
}
|
|
106
|
+
this.isShutdown = true;
|
|
107
|
+
if (this.unloadHandler && typeof window !== "undefined") {
|
|
108
|
+
window.removeEventListener("beforeunload", this.unloadHandler);
|
|
109
|
+
}
|
|
110
|
+
if (this.flushTimer) {
|
|
111
|
+
clearTimeout(this.flushTimer);
|
|
112
|
+
this.flushTimer = null;
|
|
113
|
+
}
|
|
114
|
+
await this.flush();
|
|
115
|
+
}
|
|
116
|
+
enqueue(event) {
|
|
117
|
+
// Check event size
|
|
118
|
+
const eventSize = this.estimateEventSize(event);
|
|
119
|
+
if (eventSize > MAX_EVENT_SIZE_BYTES) {
|
|
120
|
+
console.error(`Klime: Event size (${eventSize} bytes) exceeds ${MAX_EVENT_SIZE_BYTES} bytes limit`);
|
|
121
|
+
return;
|
|
122
|
+
}
|
|
123
|
+
// Drop oldest if queue is full
|
|
124
|
+
if (this.queue.length >= this.config.maxQueueSize) {
|
|
125
|
+
this.queue.shift();
|
|
126
|
+
}
|
|
127
|
+
this.queue.push(event);
|
|
128
|
+
// Check if we should flush immediately
|
|
129
|
+
if (this.queue.length >= this.config.maxBatchSize) {
|
|
130
|
+
this.flush();
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
async doFlush() {
|
|
134
|
+
if (this.queue.length === 0) {
|
|
135
|
+
return;
|
|
136
|
+
}
|
|
137
|
+
// Clear the flush timer
|
|
138
|
+
if (this.flushTimer) {
|
|
139
|
+
clearTimeout(this.flushTimer);
|
|
140
|
+
this.flushTimer = null;
|
|
141
|
+
}
|
|
142
|
+
// Process batches
|
|
143
|
+
while (this.queue.length > 0) {
|
|
144
|
+
const batch = this.extractBatch();
|
|
145
|
+
if (batch.length === 0) {
|
|
146
|
+
break;
|
|
147
|
+
}
|
|
148
|
+
await this.sendBatch(batch);
|
|
149
|
+
}
|
|
150
|
+
// Schedule next flush
|
|
151
|
+
this.scheduleFlush();
|
|
152
|
+
}
|
|
153
|
+
extractBatch() {
|
|
154
|
+
const batch = [];
|
|
155
|
+
let batchSize = 0;
|
|
156
|
+
while (this.queue.length > 0 && batch.length < MAX_BATCH_SIZE) {
|
|
157
|
+
const event = this.queue[0];
|
|
158
|
+
const eventSize = this.estimateEventSize(event);
|
|
159
|
+
// Check if adding this event would exceed batch size limit
|
|
160
|
+
if (batchSize + eventSize > MAX_BATCH_SIZE_BYTES) {
|
|
161
|
+
break;
|
|
162
|
+
}
|
|
163
|
+
batch.push(this.queue.shift());
|
|
164
|
+
batchSize += eventSize;
|
|
165
|
+
}
|
|
166
|
+
return batch;
|
|
167
|
+
}
|
|
168
|
+
async sendBatch(batch) {
|
|
169
|
+
if (batch.length === 0) {
|
|
170
|
+
return;
|
|
171
|
+
}
|
|
172
|
+
const request = { batch };
|
|
173
|
+
const url = `${this.config.endpoint}/v1/batch`;
|
|
174
|
+
let attempt = 0;
|
|
175
|
+
let delay = this.config.retryInitialDelay;
|
|
176
|
+
while (attempt < this.config.retryMaxAttempts) {
|
|
177
|
+
try {
|
|
178
|
+
const response = await fetch(url, {
|
|
179
|
+
method: "POST",
|
|
180
|
+
headers: {
|
|
181
|
+
"Content-Type": "application/json",
|
|
182
|
+
Authorization: `Bearer ${this.config.writeKey}`,
|
|
183
|
+
},
|
|
184
|
+
body: JSON.stringify(request),
|
|
185
|
+
});
|
|
186
|
+
const data = await response.json();
|
|
187
|
+
if (response.ok) {
|
|
188
|
+
// Success - check for partial failures
|
|
189
|
+
if (data.failed > 0 && data.errors) {
|
|
190
|
+
console.warn(`Klime: Batch partially failed. Accepted: ${data.accepted}, Failed: ${data.failed}`, data.errors);
|
|
191
|
+
}
|
|
192
|
+
return;
|
|
193
|
+
}
|
|
194
|
+
// Handle error responses
|
|
195
|
+
if (response.status === 400 || response.status === 401) {
|
|
196
|
+
// Permanent errors - don't retry
|
|
197
|
+
console.error(`Klime: Permanent error (${response.status}):`, data);
|
|
198
|
+
return;
|
|
199
|
+
}
|
|
200
|
+
// Transient errors - retry with backoff
|
|
201
|
+
if (response.status === 429 || response.status === 503) {
|
|
202
|
+
const retryAfter = response.headers.get("Retry-After");
|
|
203
|
+
if (retryAfter) {
|
|
204
|
+
delay = parseInt(retryAfter, 10) * 1000;
|
|
205
|
+
}
|
|
206
|
+
attempt++;
|
|
207
|
+
if (attempt < this.config.retryMaxAttempts) {
|
|
208
|
+
await this.sleep(delay);
|
|
209
|
+
delay = Math.min(delay * 2, 16000); // Cap at 16s
|
|
210
|
+
continue;
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
// Other errors - retry
|
|
214
|
+
attempt++;
|
|
215
|
+
if (attempt < this.config.retryMaxAttempts) {
|
|
216
|
+
await this.sleep(delay);
|
|
217
|
+
delay = Math.min(delay * 2, 16000);
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
catch (error) {
|
|
221
|
+
// Network errors - retry
|
|
222
|
+
attempt++;
|
|
223
|
+
if (attempt < this.config.retryMaxAttempts) {
|
|
224
|
+
await this.sleep(delay);
|
|
225
|
+
delay = Math.min(delay * 2, 16000);
|
|
226
|
+
}
|
|
227
|
+
else {
|
|
228
|
+
console.error("Klime: Failed to send batch after retries:", error);
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
scheduleFlush() {
|
|
234
|
+
if (this.isShutdown || this.flushTimer) {
|
|
235
|
+
return;
|
|
236
|
+
}
|
|
237
|
+
this.flushTimer = setTimeout(() => {
|
|
238
|
+
this.flush();
|
|
239
|
+
}, this.config.flushInterval);
|
|
240
|
+
}
|
|
241
|
+
generateUUID() {
|
|
242
|
+
if (typeof crypto !== "undefined" && crypto.randomUUID) {
|
|
243
|
+
return crypto.randomUUID();
|
|
244
|
+
}
|
|
245
|
+
// Fallback for older browsers
|
|
246
|
+
return "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g, (c) => {
|
|
247
|
+
const r = (Math.random() * 16) | 0;
|
|
248
|
+
const v = c === "x" ? r : (r & 0x3) | 0x8;
|
|
249
|
+
return v.toString(16);
|
|
250
|
+
});
|
|
251
|
+
}
|
|
252
|
+
generateTimestamp() {
|
|
253
|
+
return new Date().toISOString();
|
|
254
|
+
}
|
|
255
|
+
getContext() {
|
|
256
|
+
const context = {
|
|
257
|
+
library: {
|
|
258
|
+
name: "js-sdk",
|
|
259
|
+
version: SDK_VERSION,
|
|
260
|
+
},
|
|
261
|
+
};
|
|
262
|
+
if (typeof navigator !== "undefined") {
|
|
263
|
+
if (navigator.userAgent) {
|
|
264
|
+
context.userAgent = navigator.userAgent;
|
|
265
|
+
}
|
|
266
|
+
if (navigator.language) {
|
|
267
|
+
context.locale = navigator.language;
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
if (typeof Intl !== "undefined" && Intl.DateTimeFormat) {
|
|
271
|
+
try {
|
|
272
|
+
const timezone = Intl.DateTimeFormat().resolvedOptions().timeZone;
|
|
273
|
+
if (timezone) {
|
|
274
|
+
context.timezone = timezone;
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
catch (e) {
|
|
278
|
+
// Ignore timezone errors
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
return context;
|
|
282
|
+
}
|
|
283
|
+
estimateEventSize(event) {
|
|
284
|
+
// Rough estimate: JSON stringified size
|
|
285
|
+
try {
|
|
286
|
+
return JSON.stringify(event).length;
|
|
287
|
+
}
|
|
288
|
+
catch {
|
|
289
|
+
// Fallback: rough estimate based on structure
|
|
290
|
+
return 500; // Conservative estimate
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
sleep(ms) {
|
|
294
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
295
|
+
}
|
|
296
|
+
}
|
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,49 @@
|
|
|
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
|
+
autoFlushOnUnload?: boolean;
|
|
10
|
+
}
|
|
11
|
+
export interface TrackOptions {
|
|
12
|
+
userId?: string;
|
|
13
|
+
groupId?: string;
|
|
14
|
+
}
|
|
15
|
+
export interface Event {
|
|
16
|
+
type: 'track' | 'identify' | 'group';
|
|
17
|
+
messageId: string;
|
|
18
|
+
event?: string;
|
|
19
|
+
userId?: string;
|
|
20
|
+
groupId?: string;
|
|
21
|
+
timestamp: string;
|
|
22
|
+
properties?: Record<string, any>;
|
|
23
|
+
traits?: Record<string, any>;
|
|
24
|
+
context?: EventContext;
|
|
25
|
+
}
|
|
26
|
+
export interface EventContext {
|
|
27
|
+
library?: {
|
|
28
|
+
name: string;
|
|
29
|
+
version: string;
|
|
30
|
+
};
|
|
31
|
+
userAgent?: string;
|
|
32
|
+
locale?: string;
|
|
33
|
+
timezone?: string;
|
|
34
|
+
ip?: string;
|
|
35
|
+
}
|
|
36
|
+
export interface BatchRequest {
|
|
37
|
+
batch: Event[];
|
|
38
|
+
}
|
|
39
|
+
export interface BatchResponse {
|
|
40
|
+
status: string;
|
|
41
|
+
accepted: number;
|
|
42
|
+
failed: number;
|
|
43
|
+
errors?: ValidationError[];
|
|
44
|
+
}
|
|
45
|
+
export interface ValidationError {
|
|
46
|
+
index: number;
|
|
47
|
+
message: string;
|
|
48
|
+
code: string;
|
|
49
|
+
}
|
package/dist/types.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
package/package.json
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@klime/browser",
|
|
3
|
+
"version": "1.0.3",
|
|
4
|
+
"description": "Klime SDK for browsers",
|
|
5
|
+
"main": "dist/index.js",
|
|
6
|
+
"module": "dist/index.js",
|
|
7
|
+
"types": "dist/index.d.ts",
|
|
8
|
+
"scripts": {
|
|
9
|
+
"build": "tsc",
|
|
10
|
+
"clean": "rm -rf dist",
|
|
11
|
+
"test": "vitest run",
|
|
12
|
+
"test:watch": "vitest",
|
|
13
|
+
"prepublishOnly": "npm run build"
|
|
14
|
+
},
|
|
15
|
+
"keywords": [
|
|
16
|
+
"analytics",
|
|
17
|
+
"klime",
|
|
18
|
+
"tracking",
|
|
19
|
+
"events",
|
|
20
|
+
"browser"
|
|
21
|
+
],
|
|
22
|
+
"author": "Klime",
|
|
23
|
+
"license": "MIT",
|
|
24
|
+
"devDependencies": {
|
|
25
|
+
"jsdom": "^25.0.0",
|
|
26
|
+
"typescript": "^5.0.0",
|
|
27
|
+
"vitest": "^2.0.0"
|
|
28
|
+
},
|
|
29
|
+
"files": [
|
|
30
|
+
"dist"
|
|
31
|
+
],
|
|
32
|
+
"repository": {
|
|
33
|
+
"type": "git",
|
|
34
|
+
"url": "https://github.com/klimeapp/klime-js.git",
|
|
35
|
+
"directory": "packages/browser"
|
|
36
|
+
},
|
|
37
|
+
"homepage": "https://github.com/klimeapp/klime-js/tree/main/packages/browser#readme"
|
|
38
|
+
}
|