@datalyr/api 1.0.3 → 1.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # @datalyr/api
2
2
 
3
- Official API SDK for Datalyr server-side tracking.
3
+ Official API SDK for Datalyr server-side tracking with identity resolution support.
4
4
 
5
5
  ## Installation
6
6
 
@@ -52,6 +52,57 @@ await datalyr.group('user_123', 'company_456', {
52
52
  await datalyr.close();
53
53
  ```
54
54
 
55
+ ## Identity Resolution (New in v1.1.0)
56
+
57
+ The SDK now supports anonymous IDs for complete user journey tracking:
58
+
59
+ ```javascript
60
+ // Option 1: Pass anonymous_id from browser/mobile for attribution preservation
61
+ await datalyr.track({
62
+ event: 'Purchase Completed',
63
+ userId: 'user_123',
64
+ anonymousId: req.body.anonymous_id, // From browser/mobile SDK
65
+ properties: {
66
+ amount: 99.99,
67
+ currency: 'USD'
68
+ }
69
+ });
70
+
71
+ // Option 2: Use legacy signature (SDK generates anonymous_id)
72
+ await datalyr.track('user_123', 'Purchase Completed', {
73
+ amount: 99.99
74
+ });
75
+
76
+ // Get the SDK's anonymous ID (useful for server-only tracking)
77
+ const anonymousId = datalyr.getAnonymousId();
78
+ ```
79
+
80
+ ### Express.js Example with Browser Attribution
81
+
82
+ ```javascript
83
+ app.post('/api/purchase', async (req, res) => {
84
+ const { items, anonymous_id } = req.body; // anonymous_id from browser
85
+
86
+ // Track with anonymous_id to preserve attribution (fbclid, gclid, etc.)
87
+ await datalyr.track({
88
+ event: 'Purchase Completed',
89
+ userId: req.user?.id,
90
+ anonymousId: anonymous_id, // Links to browser events!
91
+ properties: {
92
+ total: calculateTotal(items),
93
+ items: items.length
94
+ }
95
+ });
96
+
97
+ res.json({ success: true });
98
+ });
99
+ ```
100
+
101
+ ### Key Benefits:
102
+ - **Attribution Preservation**: Never lose fbclid, gclid, ttclid, or lyr tracking
103
+ - **Complete Journey**: Track users from web → server → mobile
104
+ - **Flexible API**: Support both legacy and new tracking methods
105
+
55
106
  ## Configuration
56
107
 
57
108
  ```javascript
package/dist/index.d.mts CHANGED
@@ -16,6 +16,12 @@ interface TrackEvent {
16
16
  context?: Record<string, any>;
17
17
  timestamp?: string;
18
18
  }
19
+ interface TrackOptions {
20
+ userId?: string;
21
+ anonymousId?: string;
22
+ event: string;
23
+ properties?: Record<string, any>;
24
+ }
19
25
  declare class Datalyr {
20
26
  private apiKey;
21
27
  private host;
@@ -29,7 +35,9 @@ declare class Datalyr {
29
35
  private timer?;
30
36
  private isFlushing;
31
37
  private isClosing;
38
+ private anonymousId?;
32
39
  constructor(config: DatalyrConfig | string);
40
+ track(options: TrackOptions): Promise<void>;
33
41
  track(userId: string | null, event: string, properties?: any): Promise<void>;
34
42
  identify(userId: string, traits?: any): Promise<void>;
35
43
  page(userId: string, name?: string, properties?: any): Promise<void>;
@@ -39,7 +47,9 @@ declare class Datalyr {
39
47
  private sendEvent;
40
48
  private startFlushTimer;
41
49
  private generateAnonymousId;
50
+ private getOrCreateAnonymousId;
51
+ getAnonymousId(): string;
42
52
  close(): Promise<void>;
43
53
  }
44
54
 
45
- export { Datalyr, type DatalyrConfig, type TrackEvent, Datalyr as default };
55
+ export { Datalyr, type DatalyrConfig, type TrackEvent, type TrackOptions, Datalyr as default };
package/dist/index.d.ts CHANGED
@@ -16,6 +16,12 @@ interface TrackEvent {
16
16
  context?: Record<string, any>;
17
17
  timestamp?: string;
18
18
  }
19
+ interface TrackOptions {
20
+ userId?: string;
21
+ anonymousId?: string;
22
+ event: string;
23
+ properties?: Record<string, any>;
24
+ }
19
25
  declare class Datalyr {
20
26
  private apiKey;
21
27
  private host;
@@ -29,7 +35,9 @@ declare class Datalyr {
29
35
  private timer?;
30
36
  private isFlushing;
31
37
  private isClosing;
38
+ private anonymousId?;
32
39
  constructor(config: DatalyrConfig | string);
40
+ track(options: TrackOptions): Promise<void>;
33
41
  track(userId: string | null, event: string, properties?: any): Promise<void>;
34
42
  identify(userId: string, traits?: any): Promise<void>;
35
43
  page(userId: string, name?: string, properties?: any): Promise<void>;
@@ -39,7 +47,9 @@ declare class Datalyr {
39
47
  private sendEvent;
40
48
  private startFlushTimer;
41
49
  private generateAnonymousId;
50
+ private getOrCreateAnonymousId;
51
+ getAnonymousId(): string;
42
52
  close(): Promise<void>;
43
53
  }
44
54
 
45
- export { Datalyr, type DatalyrConfig, type TrackEvent, Datalyr as default };
55
+ export { Datalyr, type DatalyrConfig, type TrackEvent, type TrackOptions, Datalyr as default };
package/dist/index.js CHANGED
@@ -25,6 +25,7 @@ __export(index_exports, {
25
25
  });
26
26
  module.exports = __toCommonJS(index_exports);
27
27
  var Datalyr = class {
28
+ // Persistent anonymous ID for identity resolution
28
29
  constructor(config) {
29
30
  this.queue = [];
30
31
  this.isFlushing = false;
@@ -62,24 +63,46 @@ var Datalyr = class {
62
63
  if (this.maxQueueSize > 1e4) this.maxQueueSize = 1e4;
63
64
  this.startFlushTimer();
64
65
  }
65
- async track(userId, event, properties) {
66
+ async track(userIdOrOptions, event, properties) {
66
67
  if (this.isClosing) {
67
68
  if (this.debug) {
68
- console.warn("[Datalyr] SDK is closing, event dropped:", event);
69
+ console.warn("[Datalyr] SDK is closing, event dropped");
69
70
  }
70
71
  return;
71
72
  }
72
- if (!event || typeof event !== "string") {
73
+ let userId;
74
+ let eventName;
75
+ let eventProperties;
76
+ let providedAnonymousId;
77
+ if (typeof userIdOrOptions === "object" && userIdOrOptions !== null) {
78
+ userId = userIdOrOptions.userId;
79
+ eventName = userIdOrOptions.event;
80
+ eventProperties = userIdOrOptions.properties || {};
81
+ providedAnonymousId = userIdOrOptions.anonymousId;
82
+ } else {
83
+ userId = userIdOrOptions || void 0;
84
+ eventName = event;
85
+ eventProperties = properties || {};
86
+ }
87
+ if (!eventName || typeof eventName !== "string") {
73
88
  throw new Error("Event name is required and must be a string");
74
89
  }
90
+ const anonymousId = providedAnonymousId || this.getOrCreateAnonymousId();
91
+ const enrichedProperties = {
92
+ ...eventProperties,
93
+ anonymous_id: anonymousId
94
+ };
75
95
  const trackEvent = {
76
96
  userId: userId || void 0,
77
- anonymousId: userId ? void 0 : this.generateAnonymousId(),
78
- event,
79
- properties: properties || {},
97
+ anonymousId,
98
+ // Always include for identity resolution
99
+ event: eventName,
100
+ properties: enrichedProperties,
80
101
  context: {
81
102
  library: "@datalyr/api",
82
- version: "1.0.3"
103
+ version: "1.0.4",
104
+ source: "api"
105
+ // Explicitly set source for server-side API
83
106
  },
84
107
  timestamp: (/* @__PURE__ */ new Date()).toISOString()
85
108
  };
@@ -89,7 +112,10 @@ var Datalyr = class {
89
112
  if (!userId) {
90
113
  throw new Error("userId is required for identify");
91
114
  }
92
- return this.track(userId, "$identify", { $set: traits });
115
+ return this.track(userId, "$identify", {
116
+ $set: traits,
117
+ anonymous_id: this.getOrCreateAnonymousId()
118
+ });
93
119
  }
94
120
  async page(userId, name, properties) {
95
121
  return this.track(userId, "$pageview", { name, ...properties });
@@ -217,6 +243,15 @@ var Datalyr = class {
217
243
  generateAnonymousId() {
218
244
  return "anon_" + Math.random().toString(36).substring(2) + Date.now().toString(36);
219
245
  }
246
+ getOrCreateAnonymousId() {
247
+ if (!this.anonymousId) {
248
+ this.anonymousId = this.generateAnonymousId();
249
+ }
250
+ return this.anonymousId;
251
+ }
252
+ getAnonymousId() {
253
+ return this.getOrCreateAnonymousId();
254
+ }
220
255
  // Cleanup
221
256
  async close() {
222
257
  this.isClosing = true;
package/dist/index.mjs CHANGED
@@ -1,5 +1,6 @@
1
1
  // src/index.ts
2
2
  var Datalyr = class {
3
+ // Persistent anonymous ID for identity resolution
3
4
  constructor(config) {
4
5
  this.queue = [];
5
6
  this.isFlushing = false;
@@ -37,24 +38,46 @@ var Datalyr = class {
37
38
  if (this.maxQueueSize > 1e4) this.maxQueueSize = 1e4;
38
39
  this.startFlushTimer();
39
40
  }
40
- async track(userId, event, properties) {
41
+ async track(userIdOrOptions, event, properties) {
41
42
  if (this.isClosing) {
42
43
  if (this.debug) {
43
- console.warn("[Datalyr] SDK is closing, event dropped:", event);
44
+ console.warn("[Datalyr] SDK is closing, event dropped");
44
45
  }
45
46
  return;
46
47
  }
47
- if (!event || typeof event !== "string") {
48
+ let userId;
49
+ let eventName;
50
+ let eventProperties;
51
+ let providedAnonymousId;
52
+ if (typeof userIdOrOptions === "object" && userIdOrOptions !== null) {
53
+ userId = userIdOrOptions.userId;
54
+ eventName = userIdOrOptions.event;
55
+ eventProperties = userIdOrOptions.properties || {};
56
+ providedAnonymousId = userIdOrOptions.anonymousId;
57
+ } else {
58
+ userId = userIdOrOptions || void 0;
59
+ eventName = event;
60
+ eventProperties = properties || {};
61
+ }
62
+ if (!eventName || typeof eventName !== "string") {
48
63
  throw new Error("Event name is required and must be a string");
49
64
  }
65
+ const anonymousId = providedAnonymousId || this.getOrCreateAnonymousId();
66
+ const enrichedProperties = {
67
+ ...eventProperties,
68
+ anonymous_id: anonymousId
69
+ };
50
70
  const trackEvent = {
51
71
  userId: userId || void 0,
52
- anonymousId: userId ? void 0 : this.generateAnonymousId(),
53
- event,
54
- properties: properties || {},
72
+ anonymousId,
73
+ // Always include for identity resolution
74
+ event: eventName,
75
+ properties: enrichedProperties,
55
76
  context: {
56
77
  library: "@datalyr/api",
57
- version: "1.0.3"
78
+ version: "1.0.4",
79
+ source: "api"
80
+ // Explicitly set source for server-side API
58
81
  },
59
82
  timestamp: (/* @__PURE__ */ new Date()).toISOString()
60
83
  };
@@ -64,7 +87,10 @@ var Datalyr = class {
64
87
  if (!userId) {
65
88
  throw new Error("userId is required for identify");
66
89
  }
67
- return this.track(userId, "$identify", { $set: traits });
90
+ return this.track(userId, "$identify", {
91
+ $set: traits,
92
+ anonymous_id: this.getOrCreateAnonymousId()
93
+ });
68
94
  }
69
95
  async page(userId, name, properties) {
70
96
  return this.track(userId, "$pageview", { name, ...properties });
@@ -192,6 +218,15 @@ var Datalyr = class {
192
218
  generateAnonymousId() {
193
219
  return "anon_" + Math.random().toString(36).substring(2) + Date.now().toString(36);
194
220
  }
221
+ getOrCreateAnonymousId() {
222
+ if (!this.anonymousId) {
223
+ this.anonymousId = this.generateAnonymousId();
224
+ }
225
+ return this.anonymousId;
226
+ }
227
+ getAnonymousId() {
228
+ return this.getOrCreateAnonymousId();
229
+ }
195
230
  // Cleanup
196
231
  async close() {
197
232
  this.isClosing = true;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@datalyr/api",
3
- "version": "1.0.3",
3
+ "version": "1.1.0",
4
4
  "description": "Datalyr API SDK for server-side tracking",
5
5
  "main": "dist/index.js",
6
6
  "module": "dist/index.mjs",