@datalyr/api 1.2.1 → 1.2.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 +174 -203
- package/dist/index.d.mts +2 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +25 -7
- package/dist/index.mjs +25 -7
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -2,183 +2,139 @@
|
|
|
2
2
|
|
|
3
3
|
Server-side analytics and attribution SDK for Node.js. Track events, identify users, and preserve attribution data from your backend.
|
|
4
4
|
|
|
5
|
-
## Table of Contents
|
|
6
|
-
|
|
7
|
-
- [Installation](#installation)
|
|
8
|
-
- [Quick Start](#quick-start)
|
|
9
|
-
- [How It Works](#how-it-works)
|
|
10
|
-
- [Configuration](#configuration)
|
|
11
|
-
- [Event Tracking](#event-tracking)
|
|
12
|
-
- [Custom Events](#custom-events)
|
|
13
|
-
- [Page Views](#page-views)
|
|
14
|
-
- [User Identity](#user-identity)
|
|
15
|
-
- [Anonymous ID](#anonymous-id)
|
|
16
|
-
- [Identifying Users](#identifying-users)
|
|
17
|
-
- [Groups](#groups)
|
|
18
|
-
- [Attribution Preservation](#attribution-preservation)
|
|
19
|
-
- [Event Queue](#event-queue)
|
|
20
|
-
- [Framework Examples](#framework-examples)
|
|
21
|
-
- [Express.js](#expressjs)
|
|
22
|
-
- [Stripe Webhooks](#stripe-webhooks)
|
|
23
|
-
- [TypeScript](#typescript)
|
|
24
|
-
- [Troubleshooting](#troubleshooting)
|
|
25
|
-
- [License](#license)
|
|
26
|
-
|
|
27
|
-
---
|
|
28
|
-
|
|
29
5
|
## Installation
|
|
30
6
|
|
|
31
7
|
```bash
|
|
32
8
|
npm install @datalyr/api
|
|
33
9
|
```
|
|
34
10
|
|
|
35
|
-
---
|
|
36
|
-
|
|
37
11
|
## Quick Start
|
|
38
12
|
|
|
39
13
|
```javascript
|
|
40
14
|
import { Datalyr } from '@datalyr/api';
|
|
41
15
|
|
|
42
|
-
//
|
|
16
|
+
// String shorthand
|
|
43
17
|
const datalyr = new Datalyr('dk_your_api_key');
|
|
44
18
|
|
|
45
|
-
//
|
|
46
|
-
|
|
19
|
+
// Or with config object
|
|
20
|
+
const datalyr = new Datalyr({
|
|
21
|
+
apiKey: 'dk_your_api_key',
|
|
22
|
+
debug: true,
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
// Track an event
|
|
26
|
+
await datalyr.track('user_123', 'signup_completed', { plan: 'pro' });
|
|
47
27
|
|
|
48
|
-
// Identify
|
|
28
|
+
// Identify a user
|
|
49
29
|
await datalyr.identify('user_123', { email: 'user@example.com' });
|
|
50
30
|
|
|
51
|
-
//
|
|
31
|
+
// Flush and shut down
|
|
52
32
|
await datalyr.close();
|
|
53
33
|
```
|
|
54
34
|
|
|
55
|
-
|
|
35
|
+
## Configuration
|
|
56
36
|
|
|
57
|
-
|
|
37
|
+
The constructor accepts a `DatalyrConfig` object or an API key string.
|
|
58
38
|
|
|
59
|
-
|
|
39
|
+
```javascript
|
|
40
|
+
// Config object
|
|
41
|
+
const datalyr = new Datalyr({
|
|
42
|
+
apiKey: 'dk_...', // Required. Must start with "dk_".
|
|
43
|
+
host: 'https://ingest.datalyr.com/track', // API endpoint (default: 'https://ingest.datalyr.com/track')
|
|
44
|
+
flushAt: 20, // Flush when queue reaches this size (default: 20, range: 1-100)
|
|
45
|
+
flushInterval: 10000, // Flush timer interval in ms (default: 10000)
|
|
46
|
+
debug: false, // Log events and errors to console (default: false)
|
|
47
|
+
timeout: 10000, // HTTP request timeout in ms (default: 10000, range: 1000-60000)
|
|
48
|
+
retryLimit: 3, // Max retries for failed requests (default: 3)
|
|
49
|
+
maxQueueSize: 1000, // Max queued events before dropping oldest (default: 1000, range: 100-10000)
|
|
50
|
+
});
|
|
60
51
|
|
|
61
|
-
|
|
52
|
+
// String shorthand — uses all defaults
|
|
53
|
+
const datalyr = new Datalyr('dk_your_api_key');
|
|
54
|
+
```
|
|
62
55
|
|
|
63
|
-
|
|
64
|
-
2. Events are queued locally and sent in batches
|
|
65
|
-
3. Batches are sent when queue reaches 20 events or every 10 seconds
|
|
66
|
-
4. Failed requests are retried with exponential backoff
|
|
67
|
-
5. Events are processed server-side for analytics and attribution reporting
|
|
56
|
+
## Methods
|
|
68
57
|
|
|
69
|
-
###
|
|
58
|
+
### track()
|
|
70
59
|
|
|
71
|
-
|
|
60
|
+
Two call signatures:
|
|
72
61
|
|
|
73
62
|
```javascript
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
63
|
+
// Object form (TrackOptions)
|
|
64
|
+
await datalyr.track({
|
|
65
|
+
event: 'Purchase Completed', // Required
|
|
66
|
+
userId: 'user_123', // Optional
|
|
67
|
+
anonymousId: 'anon_from_browser', // Optional — override the auto-generated anonymous ID
|
|
68
|
+
properties: { // Optional
|
|
69
|
+
amount: 99.99,
|
|
70
|
+
currency: 'USD',
|
|
71
|
+
},
|
|
72
|
+
});
|
|
77
73
|
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
74
|
+
// Legacy form
|
|
75
|
+
await datalyr.track('user_123', 'Purchase Completed', {
|
|
76
|
+
amount: 99.99,
|
|
77
|
+
currency: 'USD',
|
|
78
|
+
});
|
|
81
79
|
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
}
|
|
80
|
+
// Pass null as userId for anonymous events
|
|
81
|
+
await datalyr.track(null, 'page_loaded', { url: '/pricing' });
|
|
85
82
|
```
|
|
86
83
|
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
## Configuration
|
|
84
|
+
### identify()
|
|
90
85
|
|
|
91
86
|
```javascript
|
|
92
|
-
|
|
93
|
-
// Required
|
|
94
|
-
apiKey: string,
|
|
95
|
-
|
|
96
|
-
// Optional
|
|
97
|
-
host?: string, // Custom endpoint (default: https://ingest.datalyr.com)
|
|
98
|
-
flushAt?: number, // Batch size (default: 20)
|
|
99
|
-
flushInterval?: number, // Send interval ms (default: 10000)
|
|
100
|
-
timeout?: number, // Request timeout ms (default: 10000)
|
|
101
|
-
retryLimit?: number, // Max retries (default: 3)
|
|
102
|
-
maxQueueSize?: number, // Max queued events (default: 1000)
|
|
103
|
-
debug?: boolean, // Console logging (default: false)
|
|
104
|
-
});
|
|
87
|
+
await datalyr.identify(userId: string, traits?: any);
|
|
105
88
|
```
|
|
106
89
|
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
## Event Tracking
|
|
110
|
-
|
|
111
|
-
### Custom Events
|
|
112
|
-
|
|
113
|
-
Track any action in your application:
|
|
90
|
+
Links a user ID to traits. Internally sends a `$identify` event.
|
|
114
91
|
|
|
115
92
|
```javascript
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
await datalyr.track('user_123', 'product_viewed', {
|
|
121
|
-
product_id: 'SKU123',
|
|
122
|
-
product_name: 'Blue Shirt',
|
|
123
|
-
price: 29.99,
|
|
124
|
-
currency: 'USD',
|
|
125
|
-
});
|
|
126
|
-
|
|
127
|
-
// Purchase event
|
|
128
|
-
await datalyr.track('user_123', 'Purchase Completed', {
|
|
129
|
-
order_id: 'ORD-456',
|
|
130
|
-
total: 99.99,
|
|
131
|
-
currency: 'USD',
|
|
132
|
-
items: ['SKU123', 'SKU456'],
|
|
93
|
+
await datalyr.identify('user_123', {
|
|
94
|
+
email: 'user@example.com',
|
|
95
|
+
name: 'Jane Doe',
|
|
96
|
+
plan: 'premium',
|
|
133
97
|
});
|
|
134
98
|
```
|
|
135
99
|
|
|
136
|
-
###
|
|
100
|
+
### page()
|
|
101
|
+
|
|
102
|
+
```javascript
|
|
103
|
+
await datalyr.page(userId: string, name?: string, properties?: any);
|
|
104
|
+
```
|
|
137
105
|
|
|
138
|
-
Track
|
|
106
|
+
Track a page view. Internally sends a `$pageview` event.
|
|
139
107
|
|
|
140
108
|
```javascript
|
|
141
|
-
await datalyr.page('user_123', '
|
|
142
|
-
url: 'https://example.com',
|
|
109
|
+
await datalyr.page('user_123', 'Pricing', {
|
|
110
|
+
url: 'https://example.com/pricing',
|
|
143
111
|
referrer: 'https://google.com',
|
|
144
112
|
});
|
|
145
|
-
|
|
146
|
-
await datalyr.page('user_123', 'Product Details', {
|
|
147
|
-
url: 'https://example.com/products/123',
|
|
148
|
-
product_id: 'SKU123',
|
|
149
|
-
});
|
|
150
113
|
```
|
|
151
114
|
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
## User Identity
|
|
155
|
-
|
|
156
|
-
### Anonymous ID
|
|
157
|
-
|
|
158
|
-
The SDK generates a persistent anonymous ID:
|
|
115
|
+
### alias()
|
|
159
116
|
|
|
160
117
|
```javascript
|
|
161
|
-
|
|
162
|
-
// 'a1b2c3d4-e5f6-7890-abcd-ef1234567890'
|
|
118
|
+
await datalyr.alias(newUserId: string, previousId?: string);
|
|
163
119
|
```
|
|
164
120
|
|
|
165
|
-
|
|
121
|
+
Link a new user ID to a previous one (e.g., after account merge). If `previousId` is omitted, the current anonymous ID is used. Internally sends a `$alias` event.
|
|
166
122
|
|
|
167
|
-
|
|
123
|
+
```javascript
|
|
124
|
+
// Link new ID to the current anonymous user
|
|
125
|
+
await datalyr.alias('new_user_456');
|
|
168
126
|
|
|
169
|
-
|
|
127
|
+
// Or specify the previous ID explicitly
|
|
128
|
+
await datalyr.alias('new_user_456', 'old_user_123');
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
### group()
|
|
170
132
|
|
|
171
133
|
```javascript
|
|
172
|
-
await datalyr.
|
|
173
|
-
email: 'user@example.com',
|
|
174
|
-
name: 'John Doe',
|
|
175
|
-
plan: 'premium',
|
|
176
|
-
});
|
|
134
|
+
await datalyr.group(userId: string, groupId: string, traits?: any);
|
|
177
135
|
```
|
|
178
136
|
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
Associate users with companies or teams:
|
|
137
|
+
Associate a user with a group (company, team, etc.). Internally sends a `$group` event.
|
|
182
138
|
|
|
183
139
|
```javascript
|
|
184
140
|
await datalyr.group('user_123', 'company_456', {
|
|
@@ -188,71 +144,90 @@ await datalyr.group('user_123', 'company_456', {
|
|
|
188
144
|
});
|
|
189
145
|
```
|
|
190
146
|
|
|
191
|
-
|
|
147
|
+
### flush()
|
|
192
148
|
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
Pass the anonymous ID from browser/mobile SDKs to preserve attribution data:
|
|
149
|
+
Send all queued events immediately.
|
|
196
150
|
|
|
197
151
|
```javascript
|
|
198
|
-
|
|
199
|
-
await datalyr.track({
|
|
200
|
-
event: 'Purchase Completed',
|
|
201
|
-
userId: 'user_123',
|
|
202
|
-
anonymousId: req.body.anonymous_id, // From browser SDK
|
|
203
|
-
properties: {
|
|
204
|
-
amount: 99.99,
|
|
205
|
-
currency: 'USD',
|
|
206
|
-
},
|
|
207
|
-
});
|
|
152
|
+
await datalyr.flush();
|
|
208
153
|
```
|
|
209
154
|
|
|
210
|
-
|
|
211
|
-
- UTM parameters (utm_source, utm_medium, utm_campaign)
|
|
212
|
-
- Click IDs (fbclid, gclid, ttclid)
|
|
213
|
-
- Referrer and landing page
|
|
214
|
-
- Customer journey touchpoints
|
|
155
|
+
### close()
|
|
215
156
|
|
|
216
|
-
|
|
157
|
+
Stops the flush timer, then attempts a final flush with a **5-second timeout**. Any events still queued after the timeout are dropped. New events tracked after `close()` is called are silently ignored.
|
|
217
158
|
|
|
218
|
-
|
|
159
|
+
```javascript
|
|
160
|
+
process.on('SIGTERM', async () => {
|
|
161
|
+
await datalyr.close();
|
|
162
|
+
process.exit(0);
|
|
163
|
+
});
|
|
164
|
+
```
|
|
219
165
|
|
|
220
|
-
|
|
166
|
+
### getAnonymousId()
|
|
221
167
|
|
|
222
|
-
|
|
168
|
+
Returns the SDK instance's persistent anonymous ID. The ID is generated lazily on first use and has the format `anon_<random><timestamp>` (e.g., `anon_k7x2m9f1lxyzabc`).
|
|
223
169
|
|
|
224
170
|
```javascript
|
|
225
|
-
const
|
|
226
|
-
apiKey: 'dk_your_api_key',
|
|
227
|
-
flushAt: 20, // Send when 20 events queued
|
|
228
|
-
flushInterval: 10000, // Or every 10 seconds
|
|
229
|
-
});
|
|
171
|
+
const anonId = datalyr.getAnonymousId();
|
|
230
172
|
```
|
|
231
173
|
|
|
232
|
-
|
|
174
|
+
## Event Payload
|
|
233
175
|
|
|
234
|
-
|
|
176
|
+
Every event sent to the API has this structure:
|
|
235
177
|
|
|
236
178
|
```javascript
|
|
237
|
-
|
|
179
|
+
{
|
|
180
|
+
event: 'Purchase Completed',
|
|
181
|
+
userId: 'user_123', // undefined if not provided
|
|
182
|
+
anonymousId: 'anon_k7x2m9f...', // Always present
|
|
183
|
+
properties: {
|
|
184
|
+
amount: 99.99,
|
|
185
|
+
anonymous_id: 'anon_k7x2m9f...', // Automatically added
|
|
186
|
+
},
|
|
187
|
+
context: {
|
|
188
|
+
library: '@datalyr/api',
|
|
189
|
+
version: '1.2.1',
|
|
190
|
+
source: 'api',
|
|
191
|
+
},
|
|
192
|
+
timestamp: '2025-01-15T10:30:00.000Z',
|
|
193
|
+
}
|
|
238
194
|
```
|
|
239
195
|
|
|
240
|
-
|
|
196
|
+
Notes:
|
|
197
|
+
- `anonymous_id` is automatically added to `properties` on every event for attribution.
|
|
198
|
+
- The `context` object identifies the SDK and version.
|
|
199
|
+
- `timestamp` is set to the ISO 8601 time when the event was created.
|
|
200
|
+
|
|
201
|
+
## Batching and Retry Behavior
|
|
202
|
+
|
|
203
|
+
Events are queued locally and sent in batches, not one at a time.
|
|
204
|
+
|
|
205
|
+
- **Auto-flush triggers:** when the queue reaches `flushAt` events, or every `flushInterval` ms.
|
|
206
|
+
- **Batch size:** events are sent in parallel batches of 10 within a single flush.
|
|
207
|
+
- **Queue overflow:** when the queue reaches `maxQueueSize`, the oldest event is dropped to make room.
|
|
208
|
+
- **Retry:** 5xx (server) errors are retried up to `retryLimit` times with exponential backoff (1s, 2s, 4s, ... capped at 10s). 4xx (client) errors are permanent failures and are not retried.
|
|
209
|
+
- **Failed events:** events that fail after all retries are re-queued at the front.
|
|
210
|
+
|
|
211
|
+
## Attribution Preservation
|
|
241
212
|
|
|
242
|
-
|
|
213
|
+
Pass the anonymous ID from your browser or mobile SDK to link server-side events to a client-side session:
|
|
243
214
|
|
|
244
215
|
```javascript
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
216
|
+
await datalyr.track({
|
|
217
|
+
event: 'Purchase Completed',
|
|
218
|
+
userId: 'user_123',
|
|
219
|
+
anonymousId: req.body.anonymous_id, // From browser SDK
|
|
220
|
+
properties: {
|
|
221
|
+
amount: 99.99,
|
|
222
|
+
},
|
|
248
223
|
});
|
|
249
224
|
```
|
|
250
225
|
|
|
251
|
-
|
|
226
|
+
This preserves UTM parameters, click IDs (gclid, fbclid, ttclid), referrer, landing page, and the full customer journey.
|
|
252
227
|
|
|
253
228
|
## Framework Examples
|
|
254
229
|
|
|
255
|
-
### Express.js
|
|
230
|
+
### Express.js Middleware
|
|
256
231
|
|
|
257
232
|
```javascript
|
|
258
233
|
import express from 'express';
|
|
@@ -264,7 +239,6 @@ const datalyr = new Datalyr('dk_your_api_key');
|
|
|
264
239
|
app.post('/api/purchase', async (req, res) => {
|
|
265
240
|
const { items, anonymous_id } = req.body;
|
|
266
241
|
|
|
267
|
-
// Track with anonymous_id to preserve attribution
|
|
268
242
|
await datalyr.track({
|
|
269
243
|
event: 'Purchase Completed',
|
|
270
244
|
userId: req.user?.id,
|
|
@@ -278,7 +252,6 @@ app.post('/api/purchase', async (req, res) => {
|
|
|
278
252
|
res.json({ success: true });
|
|
279
253
|
});
|
|
280
254
|
|
|
281
|
-
// Graceful shutdown
|
|
282
255
|
process.on('SIGTERM', async () => {
|
|
283
256
|
await datalyr.close();
|
|
284
257
|
process.exit(0);
|
|
@@ -299,93 +272,91 @@ app.post('/webhooks/stripe', async (req, res) => {
|
|
|
299
272
|
const event = stripe.webhooks.constructEvent(req.body, sig, endpointSecret);
|
|
300
273
|
|
|
301
274
|
switch (event.type) {
|
|
302
|
-
case 'checkout.session.completed':
|
|
275
|
+
case 'checkout.session.completed': {
|
|
303
276
|
const session = event.data.object;
|
|
304
|
-
await datalyr.track(
|
|
305
|
-
session.
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
currency: session.currency,
|
|
310
|
-
stripe_session_id: session.id,
|
|
311
|
-
}
|
|
312
|
-
);
|
|
277
|
+
await datalyr.track(session.client_reference_id, 'Purchase Completed', {
|
|
278
|
+
amount: session.amount_total / 100,
|
|
279
|
+
currency: session.currency,
|
|
280
|
+
stripe_session_id: session.id,
|
|
281
|
+
});
|
|
313
282
|
break;
|
|
283
|
+
}
|
|
314
284
|
|
|
315
|
-
case 'customer.subscription.created':
|
|
285
|
+
case 'customer.subscription.created': {
|
|
316
286
|
const subscription = event.data.object;
|
|
317
|
-
await datalyr.track(
|
|
318
|
-
subscription.
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
mrr: subscription.items.data[0].price.unit_amount / 100,
|
|
323
|
-
interval: subscription.items.data[0].price.recurring.interval,
|
|
324
|
-
}
|
|
325
|
-
);
|
|
287
|
+
await datalyr.track(subscription.metadata.userId, 'Subscription Started', {
|
|
288
|
+
plan: subscription.items.data[0].price.nickname,
|
|
289
|
+
mrr: subscription.items.data[0].price.unit_amount / 100,
|
|
290
|
+
interval: subscription.items.data[0].price.recurring.interval,
|
|
291
|
+
});
|
|
326
292
|
break;
|
|
293
|
+
}
|
|
327
294
|
}
|
|
328
295
|
|
|
329
296
|
res.json({ received: true });
|
|
330
297
|
});
|
|
331
298
|
```
|
|
332
299
|
|
|
333
|
-
---
|
|
334
|
-
|
|
335
300
|
## TypeScript
|
|
336
301
|
|
|
302
|
+
Full type definitions are included. Exported types:
|
|
303
|
+
|
|
337
304
|
```typescript
|
|
338
|
-
import { Datalyr, TrackOptions,
|
|
305
|
+
import { Datalyr, DatalyrConfig, TrackOptions, TrackEvent } from '@datalyr/api';
|
|
339
306
|
|
|
340
|
-
const
|
|
307
|
+
const config: DatalyrConfig = {
|
|
308
|
+
apiKey: 'dk_your_api_key',
|
|
309
|
+
debug: true,
|
|
310
|
+
};
|
|
311
|
+
|
|
312
|
+
const datalyr = new Datalyr(config);
|
|
341
313
|
|
|
342
|
-
|
|
343
|
-
const trackOptions: TrackOptions = {
|
|
314
|
+
const options: TrackOptions = {
|
|
344
315
|
event: 'Purchase Completed',
|
|
345
316
|
userId: 'user_123',
|
|
346
|
-
anonymousId: '
|
|
317
|
+
anonymousId: 'anon_from_browser',
|
|
347
318
|
properties: {
|
|
348
319
|
amount: 99.99,
|
|
349
320
|
currency: 'USD',
|
|
350
321
|
},
|
|
351
322
|
};
|
|
352
323
|
|
|
353
|
-
await datalyr.track(
|
|
324
|
+
await datalyr.track(options);
|
|
354
325
|
```
|
|
355
326
|
|
|
356
|
-
---
|
|
357
|
-
|
|
358
327
|
## Troubleshooting
|
|
359
328
|
|
|
360
|
-
|
|
329
|
+
**Events not appearing**
|
|
361
330
|
|
|
362
|
-
1.
|
|
363
|
-
2. Enable `debug: true`
|
|
364
|
-
3. Call `flush()` to force
|
|
365
|
-
4. Check
|
|
331
|
+
1. Verify your API key starts with `dk_`.
|
|
332
|
+
2. Enable `debug: true` to see console output.
|
|
333
|
+
3. Call `await datalyr.flush()` to force-send queued events.
|
|
334
|
+
4. Check for 4xx errors in debug output -- these indicate a client-side issue (bad API key, malformed payload).
|
|
366
335
|
|
|
367
|
-
|
|
336
|
+
**Request timeouts**
|
|
337
|
+
|
|
338
|
+
Increase `timeout` and `retryLimit`:
|
|
368
339
|
|
|
369
340
|
```javascript
|
|
370
341
|
const datalyr = new Datalyr({
|
|
371
342
|
apiKey: 'dk_your_api_key',
|
|
372
|
-
timeout: 30000,
|
|
373
|
-
retryLimit: 5,
|
|
343
|
+
timeout: 30000,
|
|
344
|
+
retryLimit: 5,
|
|
374
345
|
});
|
|
375
346
|
```
|
|
376
347
|
|
|
377
|
-
|
|
348
|
+
**Queue full (oldest events dropped)**
|
|
349
|
+
|
|
350
|
+
Increase `maxQueueSize` or flush more aggressively:
|
|
378
351
|
|
|
379
352
|
```javascript
|
|
380
353
|
const datalyr = new Datalyr({
|
|
381
354
|
apiKey: 'dk_your_api_key',
|
|
382
|
-
maxQueueSize: 5000,
|
|
383
|
-
flushAt: 50,
|
|
355
|
+
maxQueueSize: 5000,
|
|
356
|
+
flushAt: 50,
|
|
384
357
|
});
|
|
385
358
|
```
|
|
386
359
|
|
|
387
|
-
---
|
|
388
|
-
|
|
389
360
|
## License
|
|
390
361
|
|
|
391
362
|
MIT
|
package/dist/index.d.mts
CHANGED
|
@@ -11,6 +11,7 @@ interface DatalyrConfig {
|
|
|
11
11
|
interface TrackEvent {
|
|
12
12
|
userId?: string;
|
|
13
13
|
anonymousId?: string;
|
|
14
|
+
eventId?: string;
|
|
14
15
|
event: string;
|
|
15
16
|
properties?: Record<string, any>;
|
|
16
17
|
context?: Record<string, any>;
|
|
@@ -40,6 +41,7 @@ declare class Datalyr {
|
|
|
40
41
|
track(options: TrackOptions): Promise<void>;
|
|
41
42
|
track(userId: string | null, event: string, properties?: any): Promise<void>;
|
|
42
43
|
identify(userId: string, traits?: any): Promise<void>;
|
|
44
|
+
alias(newUserId: string, previousId?: string): Promise<void>;
|
|
43
45
|
page(userId: string, name?: string, properties?: any): Promise<void>;
|
|
44
46
|
group(userId: string, groupId: string, traits?: any): Promise<void>;
|
|
45
47
|
private enqueue;
|
package/dist/index.d.ts
CHANGED
|
@@ -11,6 +11,7 @@ interface DatalyrConfig {
|
|
|
11
11
|
interface TrackEvent {
|
|
12
12
|
userId?: string;
|
|
13
13
|
anonymousId?: string;
|
|
14
|
+
eventId?: string;
|
|
14
15
|
event: string;
|
|
15
16
|
properties?: Record<string, any>;
|
|
16
17
|
context?: Record<string, any>;
|
|
@@ -40,6 +41,7 @@ declare class Datalyr {
|
|
|
40
41
|
track(options: TrackOptions): Promise<void>;
|
|
41
42
|
track(userId: string | null, event: string, properties?: any): Promise<void>;
|
|
42
43
|
identify(userId: string, traits?: any): Promise<void>;
|
|
44
|
+
alias(newUserId: string, previousId?: string): Promise<void>;
|
|
43
45
|
page(userId: string, name?: string, properties?: any): Promise<void>;
|
|
44
46
|
group(userId: string, groupId: string, traits?: any): Promise<void>;
|
|
45
47
|
private enqueue;
|
package/dist/index.js
CHANGED
|
@@ -24,6 +24,13 @@ __export(index_exports, {
|
|
|
24
24
|
default: () => index_default
|
|
25
25
|
});
|
|
26
26
|
module.exports = __toCommonJS(index_exports);
|
|
27
|
+
function generateEventId() {
|
|
28
|
+
if (typeof crypto !== "undefined" && crypto.randomUUID) {
|
|
29
|
+
return crypto.randomUUID();
|
|
30
|
+
}
|
|
31
|
+
return "evt_" + Math.random().toString(36).substring(2) + Date.now().toString(36);
|
|
32
|
+
}
|
|
33
|
+
var SDK_VERSION = "1.2.3";
|
|
27
34
|
var Datalyr = class {
|
|
28
35
|
// Persistent anonymous ID for identity resolution
|
|
29
36
|
constructor(config) {
|
|
@@ -32,7 +39,7 @@ var Datalyr = class {
|
|
|
32
39
|
this.isClosing = false;
|
|
33
40
|
if (typeof config === "string") {
|
|
34
41
|
this.apiKey = config;
|
|
35
|
-
this.host = "https://
|
|
42
|
+
this.host = "https://ingest.datalyr.com/track";
|
|
36
43
|
this.debug = false;
|
|
37
44
|
this.flushAt = 20;
|
|
38
45
|
this.flushInterval = 1e4;
|
|
@@ -41,7 +48,7 @@ var Datalyr = class {
|
|
|
41
48
|
this.maxQueueSize = 1e3;
|
|
42
49
|
} else {
|
|
43
50
|
this.apiKey = config.apiKey;
|
|
44
|
-
this.host = config.host || "https://
|
|
51
|
+
this.host = config.host || "https://ingest.datalyr.com/track";
|
|
45
52
|
this.debug = config.debug || false;
|
|
46
53
|
this.flushAt = config.flushAt || 20;
|
|
47
54
|
this.flushInterval = config.flushInterval || 1e4;
|
|
@@ -96,13 +103,13 @@ var Datalyr = class {
|
|
|
96
103
|
userId: userId || void 0,
|
|
97
104
|
anonymousId,
|
|
98
105
|
// Always include for identity resolution
|
|
106
|
+
eventId: generateEventId(),
|
|
99
107
|
event: eventName,
|
|
100
108
|
properties: enrichedProperties,
|
|
101
109
|
context: {
|
|
102
110
|
library: "@datalyr/api",
|
|
103
|
-
version:
|
|
111
|
+
version: SDK_VERSION,
|
|
104
112
|
source: "api"
|
|
105
|
-
// Explicitly set source for server-side API
|
|
106
113
|
},
|
|
107
114
|
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
108
115
|
};
|
|
@@ -113,7 +120,17 @@ var Datalyr = class {
|
|
|
113
120
|
throw new Error("userId is required for identify");
|
|
114
121
|
}
|
|
115
122
|
return this.track(userId, "$identify", {
|
|
116
|
-
|
|
123
|
+
...traits,
|
|
124
|
+
anonymous_id: this.getOrCreateAnonymousId()
|
|
125
|
+
});
|
|
126
|
+
}
|
|
127
|
+
async alias(newUserId, previousId) {
|
|
128
|
+
if (!newUserId) {
|
|
129
|
+
throw new Error("newUserId is required for alias");
|
|
130
|
+
}
|
|
131
|
+
return this.track(newUserId, "$alias", {
|
|
132
|
+
new_user_id: newUserId,
|
|
133
|
+
previous_id: previousId || this.getOrCreateAnonymousId(),
|
|
117
134
|
anonymous_id: this.getOrCreateAnonymousId()
|
|
118
135
|
});
|
|
119
136
|
}
|
|
@@ -124,7 +141,7 @@ var Datalyr = class {
|
|
|
124
141
|
if (!groupId) {
|
|
125
142
|
throw new Error("groupId is required for group");
|
|
126
143
|
}
|
|
127
|
-
return this.track(userId, "$group", { groupId, traits });
|
|
144
|
+
return this.track(userId, "$group", { groupId, ...traits });
|
|
128
145
|
}
|
|
129
146
|
enqueue(event) {
|
|
130
147
|
if (this.queue.length >= this.maxQueueSize) {
|
|
@@ -183,7 +200,8 @@ var Datalyr = class {
|
|
|
183
200
|
method: "POST",
|
|
184
201
|
headers: {
|
|
185
202
|
"X-API-Key": this.apiKey,
|
|
186
|
-
"Content-Type": "application/json"
|
|
203
|
+
"Content-Type": "application/json",
|
|
204
|
+
"User-Agent": `@datalyr/api/${SDK_VERSION}`
|
|
187
205
|
},
|
|
188
206
|
body: JSON.stringify(event),
|
|
189
207
|
signal: controller.signal
|
package/dist/index.mjs
CHANGED
|
@@ -1,4 +1,11 @@
|
|
|
1
1
|
// src/index.ts
|
|
2
|
+
function generateEventId() {
|
|
3
|
+
if (typeof crypto !== "undefined" && crypto.randomUUID) {
|
|
4
|
+
return crypto.randomUUID();
|
|
5
|
+
}
|
|
6
|
+
return "evt_" + Math.random().toString(36).substring(2) + Date.now().toString(36);
|
|
7
|
+
}
|
|
8
|
+
var SDK_VERSION = "1.2.3";
|
|
2
9
|
var Datalyr = class {
|
|
3
10
|
// Persistent anonymous ID for identity resolution
|
|
4
11
|
constructor(config) {
|
|
@@ -7,7 +14,7 @@ var Datalyr = class {
|
|
|
7
14
|
this.isClosing = false;
|
|
8
15
|
if (typeof config === "string") {
|
|
9
16
|
this.apiKey = config;
|
|
10
|
-
this.host = "https://
|
|
17
|
+
this.host = "https://ingest.datalyr.com/track";
|
|
11
18
|
this.debug = false;
|
|
12
19
|
this.flushAt = 20;
|
|
13
20
|
this.flushInterval = 1e4;
|
|
@@ -16,7 +23,7 @@ var Datalyr = class {
|
|
|
16
23
|
this.maxQueueSize = 1e3;
|
|
17
24
|
} else {
|
|
18
25
|
this.apiKey = config.apiKey;
|
|
19
|
-
this.host = config.host || "https://
|
|
26
|
+
this.host = config.host || "https://ingest.datalyr.com/track";
|
|
20
27
|
this.debug = config.debug || false;
|
|
21
28
|
this.flushAt = config.flushAt || 20;
|
|
22
29
|
this.flushInterval = config.flushInterval || 1e4;
|
|
@@ -71,13 +78,13 @@ var Datalyr = class {
|
|
|
71
78
|
userId: userId || void 0,
|
|
72
79
|
anonymousId,
|
|
73
80
|
// Always include for identity resolution
|
|
81
|
+
eventId: generateEventId(),
|
|
74
82
|
event: eventName,
|
|
75
83
|
properties: enrichedProperties,
|
|
76
84
|
context: {
|
|
77
85
|
library: "@datalyr/api",
|
|
78
|
-
version:
|
|
86
|
+
version: SDK_VERSION,
|
|
79
87
|
source: "api"
|
|
80
|
-
// Explicitly set source for server-side API
|
|
81
88
|
},
|
|
82
89
|
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
83
90
|
};
|
|
@@ -88,7 +95,17 @@ var Datalyr = class {
|
|
|
88
95
|
throw new Error("userId is required for identify");
|
|
89
96
|
}
|
|
90
97
|
return this.track(userId, "$identify", {
|
|
91
|
-
|
|
98
|
+
...traits,
|
|
99
|
+
anonymous_id: this.getOrCreateAnonymousId()
|
|
100
|
+
});
|
|
101
|
+
}
|
|
102
|
+
async alias(newUserId, previousId) {
|
|
103
|
+
if (!newUserId) {
|
|
104
|
+
throw new Error("newUserId is required for alias");
|
|
105
|
+
}
|
|
106
|
+
return this.track(newUserId, "$alias", {
|
|
107
|
+
new_user_id: newUserId,
|
|
108
|
+
previous_id: previousId || this.getOrCreateAnonymousId(),
|
|
92
109
|
anonymous_id: this.getOrCreateAnonymousId()
|
|
93
110
|
});
|
|
94
111
|
}
|
|
@@ -99,7 +116,7 @@ var Datalyr = class {
|
|
|
99
116
|
if (!groupId) {
|
|
100
117
|
throw new Error("groupId is required for group");
|
|
101
118
|
}
|
|
102
|
-
return this.track(userId, "$group", { groupId, traits });
|
|
119
|
+
return this.track(userId, "$group", { groupId, ...traits });
|
|
103
120
|
}
|
|
104
121
|
enqueue(event) {
|
|
105
122
|
if (this.queue.length >= this.maxQueueSize) {
|
|
@@ -158,7 +175,8 @@ var Datalyr = class {
|
|
|
158
175
|
method: "POST",
|
|
159
176
|
headers: {
|
|
160
177
|
"X-API-Key": this.apiKey,
|
|
161
|
-
"Content-Type": "application/json"
|
|
178
|
+
"Content-Type": "application/json",
|
|
179
|
+
"User-Agent": `@datalyr/api/${SDK_VERSION}`
|
|
162
180
|
},
|
|
163
181
|
body: JSON.stringify(event),
|
|
164
182
|
signal: controller.signal
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@datalyr/api",
|
|
3
|
-
"version": "1.2.
|
|
3
|
+
"version": "1.2.3",
|
|
4
4
|
"description": "Datalyr API SDK for server-side tracking",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"module": "dist/index.mjs",
|
|
@@ -43,4 +43,4 @@
|
|
|
43
43
|
"engines": {
|
|
44
44
|
"node": ">=14.0.0"
|
|
45
45
|
}
|
|
46
|
-
}
|
|
46
|
+
}
|