@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 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
- // Initialize
16
+ // String shorthand
43
17
  const datalyr = new Datalyr('dk_your_api_key');
44
18
 
45
- // Track events
46
- await datalyr.track('user_123', 'button_clicked', { button: 'signup' });
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 users
28
+ // Identify a user
49
29
  await datalyr.identify('user_123', { email: 'user@example.com' });
50
30
 
51
- // Clean up on shutdown
31
+ // Flush and shut down
52
32
  await datalyr.close();
53
33
  ```
54
34
 
55
- ---
35
+ ## Configuration
56
36
 
57
- ## How It Works
37
+ The constructor accepts a `DatalyrConfig` object or an API key string.
58
38
 
59
- The SDK collects events and sends them to the Datalyr backend for analytics and attribution.
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
- ### Data Flow
52
+ // String shorthand — uses all defaults
53
+ const datalyr = new Datalyr('dk_your_api_key');
54
+ ```
62
55
 
63
- 1. Events are created with `track()`, `identify()`, `page()`, or `group()`
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
- ### Event Payload
58
+ ### track()
70
59
 
71
- Every event includes:
60
+ Two call signatures:
72
61
 
73
62
  ```javascript
74
- {
75
- event: 'purchase', // Event name
76
- properties: { ... }, // Custom properties
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
- // Identity
79
- anonymous_id: 'uuid', // Persistent ID
80
- user_id: 'user_123', // Set after identify()
74
+ // Legacy form
75
+ await datalyr.track('user_123', 'Purchase Completed', {
76
+ amount: 99.99,
77
+ currency: 'USD',
78
+ });
81
79
 
82
- // Timestamps
83
- timestamp: '2024-01-15T10:30:00Z',
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
- const datalyr = new Datalyr({
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
- // Simple event
117
- await datalyr.track('user_123', 'signup_started');
118
-
119
- // Event with properties
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
- ### Page Views
100
+ ### page()
101
+
102
+ ```javascript
103
+ await datalyr.page(userId: string, name?: string, properties?: any);
104
+ ```
137
105
 
138
- Track server-rendered page views:
106
+ Track a page view. Internally sends a `$pageview` event.
139
107
 
140
108
  ```javascript
141
- await datalyr.page('user_123', 'Homepage', {
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
- const anonymousId = datalyr.getAnonymousId();
162
- // 'a1b2c3d4-e5f6-7890-abcd-ef1234567890'
118
+ await datalyr.alias(newUserId: string, previousId?: string);
163
119
  ```
164
120
 
165
- For attribution preservation, pass the anonymous ID from your browser/mobile SDK instead.
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
- ### Identifying Users
123
+ ```javascript
124
+ // Link new ID to the current anonymous user
125
+ await datalyr.alias('new_user_456');
168
126
 
169
- Link events to a known user:
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.identify('user_123', {
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
- ### Groups
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
- ## Attribution Preservation
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
- // Object signature with anonymousId
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
- This links server-side events to the user's browser session, preserving:
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
- ## Event Queue
159
+ ```javascript
160
+ process.on('SIGTERM', async () => {
161
+ await datalyr.close();
162
+ process.exit(0);
163
+ });
164
+ ```
219
165
 
220
- Events are batched for efficiency.
166
+ ### getAnonymousId()
221
167
 
222
- ### Configuration
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 datalyr = new Datalyr({
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
- ### Manual Flush
174
+ ## Event Payload
233
175
 
234
- Send all queued events immediately:
176
+ Every event sent to the API has this structure:
235
177
 
236
178
  ```javascript
237
- await datalyr.flush();
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
- ### Graceful Shutdown
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
- Always close the client on application shutdown:
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
- process.on('SIGTERM', async () => {
246
- await datalyr.close(); // Flushes remaining events
247
- process.exit(0);
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.client_reference_id,
306
- 'Purchase Completed',
307
- {
308
- amount: session.amount_total / 100,
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.metadata.userId,
319
- 'Subscription Started',
320
- {
321
- plan: subscription.items.data[0].price.nickname,
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, IdentifyOptions } from '@datalyr/api';
305
+ import { Datalyr, DatalyrConfig, TrackOptions, TrackEvent } from '@datalyr/api';
339
306
 
340
- const datalyr = new Datalyr('dk_your_api_key');
307
+ const config: DatalyrConfig = {
308
+ apiKey: 'dk_your_api_key',
309
+ debug: true,
310
+ };
311
+
312
+ const datalyr = new Datalyr(config);
341
313
 
342
- // Type-safe tracking
343
- const trackOptions: TrackOptions = {
314
+ const options: TrackOptions = {
344
315
  event: 'Purchase Completed',
345
316
  userId: 'user_123',
346
- anonymousId: 'anon_456',
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(trackOptions);
324
+ await datalyr.track(options);
354
325
  ```
355
326
 
356
- ---
357
-
358
327
  ## Troubleshooting
359
328
 
360
- ### Events not appearing
329
+ **Events not appearing**
361
330
 
362
- 1. Check API key starts with `dk_`
363
- 2. Enable `debug: true`
364
- 3. Call `flush()` to force send
365
- 4. Check server logs for errors
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
- ### Request timeouts
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, // Increase timeout
373
- retryLimit: 5, // More retries
343
+ timeout: 30000,
344
+ retryLimit: 5,
374
345
  });
375
346
  ```
376
347
 
377
- ### Queue full
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, // Increase queue size
383
- flushAt: 50, // Larger batches
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://api.datalyr.com";
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://api.datalyr.com";
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: "1.1.0",
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
- $set: traits,
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://api.datalyr.com";
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://api.datalyr.com";
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: "1.1.0",
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
- $set: traits,
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.1",
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
+ }