userpath-js 0.0.0 → 0.0.2
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 +609 -0
- package/dist/index.js +1 -0
- package/dist/server/index.js +1 -0
- package/dist/types/client/src/base/api.d.ts +12 -0
- package/dist/types/client/src/base/api.d.ts.map +1 -0
- package/dist/types/client/src/base/events.d.ts +184 -0
- package/dist/types/client/src/base/events.d.ts.map +1 -0
- package/dist/types/client/src/base/session.d.ts +68 -0
- package/dist/types/client/src/base/session.d.ts.map +1 -0
- package/dist/types/client/src/index.d.ts +57 -0
- package/dist/types/client/src/index.d.ts.map +1 -0
- package/dist/types/client/src/pixel.d.ts +2 -0
- package/dist/types/client/src/pixel.d.ts.map +1 -0
- package/dist/types/client/src/schemas/config.d.ts +32 -0
- package/dist/types/client/src/schemas/config.d.ts.map +1 -0
- package/dist/types/client/src/schemas/events.d.ts +58 -0
- package/dist/types/client/src/schemas/events.d.ts.map +1 -0
- package/dist/types/client/src/schemas/identity.d.ts +7 -0
- package/dist/types/client/src/schemas/identity.d.ts.map +1 -0
- package/dist/types/client/src/server/index.d.ts +42 -0
- package/dist/types/client/src/server/index.d.ts.map +1 -0
- package/dist/types/core/src/browser/index.d.ts +4 -0
- package/dist/types/core/src/browser/index.d.ts.map +1 -0
- package/dist/types/core/src/browser/libs/LocalStorage.d.ts +11 -0
- package/dist/types/core/src/browser/libs/LocalStorage.d.ts.map +1 -0
- package/dist/types/core/src/browser/libs/cookie.d.ts +27 -0
- package/dist/types/core/src/browser/libs/cookie.d.ts.map +1 -0
- package/dist/types/core/src/browser/libs/memory.d.ts +4 -0
- package/dist/types/core/src/browser/libs/memory.d.ts.map +1 -0
- package/dist/types/src/base/api.d.ts +12 -0
- package/dist/types/src/base/api.d.ts.map +1 -0
- package/dist/types/src/base/events.d.ts +184 -0
- package/dist/types/src/base/events.d.ts.map +1 -0
- package/dist/types/src/base/session.d.ts +64 -0
- package/dist/types/src/base/session.d.ts.map +1 -0
- package/dist/types/src/index.d.ts +57 -0
- package/dist/types/src/index.d.ts.map +1 -0
- package/dist/types/src/pixel.d.ts +2 -0
- package/dist/types/src/pixel.d.ts.map +1 -0
- package/dist/types/src/schemas/config.d.ts +32 -0
- package/dist/types/src/schemas/config.d.ts.map +1 -0
- package/dist/types/src/schemas/events.d.ts +58 -0
- package/dist/types/src/schemas/events.d.ts.map +1 -0
- package/dist/types/src/schemas/identity.d.ts +7 -0
- package/dist/types/src/schemas/identity.d.ts.map +1 -0
- package/dist/types/src/server/index.d.ts +42 -0
- package/dist/types/src/server/index.d.ts.map +1 -0
- package/package.json +25 -4
- package/dist/pixel.js +0 -1
- package/examples/usage.ts +0 -83
- package/src/clients/api.ts +0 -92
- package/src/clients/events.ts +0 -55
- package/src/clients/identity.ts +0 -54
- package/src/clients/session.ts +0 -47
- package/src/index.ts +0 -72
- package/src/pixel.ts +0 -28
- package/src/schemas/config.ts +0 -4
- package/src/schemas/events.ts +0 -31
- package/src/schemas/identity.ts +0 -6
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
import { Config } from '../schemas/config';
|
|
2
|
+
import { SessionClient } from './session';
|
|
3
|
+
/**
|
|
4
|
+
* Session client for managing user sessions and automatic tracking
|
|
5
|
+
*/
|
|
6
|
+
export declare class EventsClient {
|
|
7
|
+
#private;
|
|
8
|
+
constructor(session: SessionClient, config?: Config);
|
|
9
|
+
destroy(): void;
|
|
10
|
+
/**
|
|
11
|
+
* Set up automatic click event tracking
|
|
12
|
+
*/
|
|
13
|
+
private setupClickTracking;
|
|
14
|
+
/**
|
|
15
|
+
* Handle click events
|
|
16
|
+
*/
|
|
17
|
+
private handleClick;
|
|
18
|
+
/**
|
|
19
|
+
* Handle scroll and resize events
|
|
20
|
+
*/
|
|
21
|
+
private handleScroll;
|
|
22
|
+
/**
|
|
23
|
+
* Set up automatic scroll event tracking
|
|
24
|
+
*/
|
|
25
|
+
private setupScrollTracking;
|
|
26
|
+
/**
|
|
27
|
+
* Calculate the current scroll depth as a percentage
|
|
28
|
+
*/
|
|
29
|
+
private calculateScrollDepth;
|
|
30
|
+
/**
|
|
31
|
+
* Check and track scroll depth milestones
|
|
32
|
+
*/
|
|
33
|
+
private checkAndTrackScrollDepth;
|
|
34
|
+
/**
|
|
35
|
+
* Extract a meaningful label from a clicked element
|
|
36
|
+
*/
|
|
37
|
+
private extractClickLabel;
|
|
38
|
+
/**
|
|
39
|
+
* Find the label text for an input element
|
|
40
|
+
*/
|
|
41
|
+
private findInputLabel;
|
|
42
|
+
/**
|
|
43
|
+
* Track a page view event
|
|
44
|
+
*/
|
|
45
|
+
trackPageView(): void;
|
|
46
|
+
/**
|
|
47
|
+
* Track a custom event
|
|
48
|
+
*/
|
|
49
|
+
trackCustomEvent(name: string, properties?: Record<string, any>, options?: {
|
|
50
|
+
immediate: boolean;
|
|
51
|
+
}): void;
|
|
52
|
+
/**
|
|
53
|
+
* Track a purchase event
|
|
54
|
+
*/
|
|
55
|
+
trackPurchase(params: {
|
|
56
|
+
productId: string;
|
|
57
|
+
price: number;
|
|
58
|
+
currency: string;
|
|
59
|
+
properties?: Record<string, any>;
|
|
60
|
+
}): void;
|
|
61
|
+
/**
|
|
62
|
+
* Set up automatic form input tracking
|
|
63
|
+
*/
|
|
64
|
+
private setupFormTracking;
|
|
65
|
+
/**
|
|
66
|
+
* Handle input focus events
|
|
67
|
+
*/
|
|
68
|
+
private handleInputFocus;
|
|
69
|
+
/**
|
|
70
|
+
* Get the current value of an input element
|
|
71
|
+
*/
|
|
72
|
+
private getElementValue;
|
|
73
|
+
/**
|
|
74
|
+
* Handle input blur events
|
|
75
|
+
*/
|
|
76
|
+
private handleInputBlur;
|
|
77
|
+
/**
|
|
78
|
+
* Get a meaningful identifier for the form if available
|
|
79
|
+
*/
|
|
80
|
+
private getFormName;
|
|
81
|
+
/**
|
|
82
|
+
* Get an identifier for an input when no label is found
|
|
83
|
+
*/
|
|
84
|
+
private getInputIdentifier;
|
|
85
|
+
/**
|
|
86
|
+
* Find the label text for a form element
|
|
87
|
+
*/
|
|
88
|
+
private findFormElementLabel;
|
|
89
|
+
/**
|
|
90
|
+
* Handle form change events (select, radio, checkbox)
|
|
91
|
+
*/
|
|
92
|
+
private handleFormChange;
|
|
93
|
+
/**
|
|
94
|
+
* Track select element changes
|
|
95
|
+
*/
|
|
96
|
+
private trackSelectChange;
|
|
97
|
+
/**
|
|
98
|
+
* Track radio button changes
|
|
99
|
+
*/
|
|
100
|
+
private trackRadioChange;
|
|
101
|
+
/**
|
|
102
|
+
* Track checkbox changes
|
|
103
|
+
*/
|
|
104
|
+
private trackCheckboxChange;
|
|
105
|
+
/**
|
|
106
|
+
* Track range input changes
|
|
107
|
+
*/
|
|
108
|
+
private trackRangeChange;
|
|
109
|
+
/**
|
|
110
|
+
* Handle form submission events
|
|
111
|
+
*/
|
|
112
|
+
private handleFormSubmit;
|
|
113
|
+
/**
|
|
114
|
+
* Set up automatic video tracking
|
|
115
|
+
*/
|
|
116
|
+
private setupVideoTracking;
|
|
117
|
+
/**
|
|
118
|
+
* Get video metadata for tracking
|
|
119
|
+
*/
|
|
120
|
+
private getVideoMetadata;
|
|
121
|
+
/**
|
|
122
|
+
* Handle video play events
|
|
123
|
+
*/
|
|
124
|
+
private handleVideoPlay;
|
|
125
|
+
/**
|
|
126
|
+
* Handle video pause events
|
|
127
|
+
*/
|
|
128
|
+
private handleVideoPause;
|
|
129
|
+
/**
|
|
130
|
+
* Handle video end events
|
|
131
|
+
*/
|
|
132
|
+
private handleVideoEnd;
|
|
133
|
+
/**
|
|
134
|
+
* Set up page time tracking
|
|
135
|
+
*/
|
|
136
|
+
private setupPageTimeTracking;
|
|
137
|
+
/**
|
|
138
|
+
* Handle visibility change events (tab switch, minimize)
|
|
139
|
+
*/
|
|
140
|
+
private handleVisibilityChange;
|
|
141
|
+
/**
|
|
142
|
+
* Handle page unload events
|
|
143
|
+
*/
|
|
144
|
+
private handleBeforeUnload;
|
|
145
|
+
/**
|
|
146
|
+
* Set up inactivity tracking
|
|
147
|
+
*/
|
|
148
|
+
private setupInactivityTracking;
|
|
149
|
+
/**
|
|
150
|
+
* Handle user activity events
|
|
151
|
+
*/
|
|
152
|
+
private handleUserActivity;
|
|
153
|
+
/**
|
|
154
|
+
* Check for user inactivity
|
|
155
|
+
*/
|
|
156
|
+
private checkInactivity;
|
|
157
|
+
/**
|
|
158
|
+
* Install global error handlers
|
|
159
|
+
*/
|
|
160
|
+
installErrorHandler(): void;
|
|
161
|
+
/**
|
|
162
|
+
* Remove global error handlers
|
|
163
|
+
*/
|
|
164
|
+
uninstallErrorHandler(): void;
|
|
165
|
+
/**
|
|
166
|
+
* Get error handler installation state
|
|
167
|
+
*/
|
|
168
|
+
isErrorHandlerInstalled(): boolean;
|
|
169
|
+
/**
|
|
170
|
+
* Handle uncaught errors
|
|
171
|
+
*/
|
|
172
|
+
private handleError;
|
|
173
|
+
/**
|
|
174
|
+
* Handle unhandled promise rejections
|
|
175
|
+
*/
|
|
176
|
+
private handleRejection;
|
|
177
|
+
/**
|
|
178
|
+
* Track an error event
|
|
179
|
+
*/
|
|
180
|
+
trackError(error: Error, properties?: Record<string, any>, options?: {
|
|
181
|
+
immediate: boolean;
|
|
182
|
+
}): void;
|
|
183
|
+
}
|
|
184
|
+
//# sourceMappingURL=events.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"events.d.ts","sourceRoot":"","sources":["../../../../src/base/events.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAkB,MAAM,mBAAmB,CAAC;AAO3D,OAAO,EAAE,aAAa,EAAE,MAAM,WAAW,CAAC;AA6D1C;;GAEG;AAEH,qBAAa,YAAY;;gBAoCZ,OAAO,EAAE,aAAa,EAAE,MAAM,GAAE,MAAuB;IAkE5D,OAAO,IAAI,IAAI;IAiCtB;;OAEG;IACH,OAAO,CAAC,kBAAkB;IAI1B;;OAEG;IACH,OAAO,CAAC,WAAW;IAiBnB;;OAEG;IACH,OAAO,CAAC,YAAY;IAqBpB;;OAEG;IACH,OAAO,CAAC,mBAAmB;IAK3B;;OAEG;IACH,OAAO,CAAC,oBAAoB;IAY5B;;OAEG;IACH,OAAO,CAAC,wBAAwB;IAUhC;;OAEG;IACH,OAAO,CAAC,iBAAiB;IAqCzB;;OAEG;IACH,OAAO,CAAC,cAAc;IAsBtB;;OAEG;IACI,aAAa,IAAI,IAAI;IAY5B;;OAEG;IACI,gBAAgB,CACtB,IAAI,EAAE,MAAM,EACZ,UAAU,GAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAM,EACpC,OAAO,GAAE;QACR,SAAS,EAAE,OAAO,CAAC;KAGnB,GACC,IAAI;IAiBP;;OAEG;IACI,aAAa,CAAC,MAAM,EAAE;QAC5B,SAAS,EAAE,MAAM,CAAC;QAClB,KAAK,EAAE,MAAM,CAAC;QACd,QAAQ,EAAE,MAAM,CAAC;QACjB,UAAU,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;KACjC,GAAG,IAAI;IAkBR;;OAEG;IACH,OAAO,CAAC,iBAAiB;IAQzB;;OAEG;IACH,OAAO,CAAC,gBAAgB;IA6BxB;;OAEG;IACH,OAAO,CAAC,eAAe;IAavB;;OAEG;IACH,OAAO,CAAC,eAAe;IA0CvB;;OAEG;IACH,OAAO,CAAC,WAAW;IAYnB;;OAEG;IACH,OAAO,CAAC,kBAAkB;IAS1B;;OAEG;IACH,OAAO,CAAC,oBAAoB;IA4B5B;;OAEG;IACH,OAAO,CAAC,gBAAgB;IA2BxB;;OAEG;IACH,OAAO,CAAC,iBAAiB;IAczB;;OAEG;IACH,OAAO,CAAC,gBAAgB;IAgBxB;;OAEG;IACH,OAAO,CAAC,mBAAmB;IAa3B;;OAEG;IACH,OAAO,CAAC,gBAAgB;IAYxB;;OAEG;IACH,OAAO,CAAC,gBAAgB;IAqBxB;;OAEG;IACH,OAAO,CAAC,kBAAkB;IAO1B;;OAEG;IACH,OAAO,CAAC,gBAAgB;IAUxB;;OAEG;IACH,OAAO,CAAC,eAAe;IAUvB;;OAEG;IACH,OAAO,CAAC,gBAAgB;IAexB;;OAEG;IACH,OAAO,CAAC,cAAc;IAetB;;OAEG;IACH,OAAO,CAAC,qBAAqB;IAQ7B;;OAEG;IACH,OAAO,CAAC,sBAAsB;IAoB9B;;OAEG;IACH,OAAO,CAAC,kBAAkB;IAqB1B;;OAEG;IACH,OAAO,CAAC,uBAAuB;IAY/B;;OAEG;IACH,OAAO,CAAC,kBAAkB;IAoB1B;;OAEG;IACH,OAAO,CAAC,eAAe;IAoBvB;;OAEG;IACI,mBAAmB,IAAI,IAAI;IAclC;;OAEG;IACI,qBAAqB,IAAI,IAAI;IAWpC;;OAEG;IACI,uBAAuB,IAAI,OAAO;IAIzC;;OAEG;IACH,OAAO,CAAC,WAAW;IAWnB;;OAEG;IACH,OAAO,CAAC,eAAe;IASvB;;OAEG;IACI,UAAU,CAChB,KAAK,EAAE,KAAK,EACZ,UAAU,GAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAM,EACpC,OAAO,GAAE;QACR,SAAS,EAAE,OAAO,CAAC;KAGnB,GACC,IAAI;CAoCP"}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import { Config } from '../schemas/config';
|
|
2
|
+
import { Event } from '../schemas/events';
|
|
3
|
+
import { UserIdentity } from '../schemas/identity';
|
|
4
|
+
import { ApiClient } from './api';
|
|
5
|
+
import { EventsClient } from './events';
|
|
6
|
+
/**
|
|
7
|
+
* Session client for managing user sessions and automatic tracking
|
|
8
|
+
*/
|
|
9
|
+
export declare class SessionClient {
|
|
10
|
+
#private;
|
|
11
|
+
constructor(api: ApiClient, config?: Config);
|
|
12
|
+
init(events: EventsClient): void;
|
|
13
|
+
destroy(): void;
|
|
14
|
+
/**
|
|
15
|
+
* Setup session tracking
|
|
16
|
+
*/
|
|
17
|
+
private setup;
|
|
18
|
+
/**
|
|
19
|
+
* Set up tracking for history/navigation changes
|
|
20
|
+
*/
|
|
21
|
+
private setupHistoryChange;
|
|
22
|
+
/**
|
|
23
|
+
* Track page view only if URL has changed
|
|
24
|
+
*/
|
|
25
|
+
private trackPageViewIfUrlChanged;
|
|
26
|
+
/**
|
|
27
|
+
* Get the current user ID if available
|
|
28
|
+
*/
|
|
29
|
+
getUserId(): string | undefined;
|
|
30
|
+
/**
|
|
31
|
+
* Set the user ID
|
|
32
|
+
*/
|
|
33
|
+
setUserId(userId: string): void;
|
|
34
|
+
/**
|
|
35
|
+
* Get the current app ID
|
|
36
|
+
*/
|
|
37
|
+
getAppId(): string;
|
|
38
|
+
/**
|
|
39
|
+
* Set the app ID
|
|
40
|
+
*/
|
|
41
|
+
setAppId(appId: string): void;
|
|
42
|
+
/**
|
|
43
|
+
* Add an event to the queue and flush if needed
|
|
44
|
+
*/
|
|
45
|
+
track(event: Event): void;
|
|
46
|
+
/**
|
|
47
|
+
* Identify a user so that we can show in the User Journey
|
|
48
|
+
*/
|
|
49
|
+
identify(identity: UserIdentity): Promise<void>;
|
|
50
|
+
/**
|
|
51
|
+
* Flush queued events to the server
|
|
52
|
+
*/
|
|
53
|
+
protected flush(): void;
|
|
54
|
+
publish(events: any[]): Promise<Response>;
|
|
55
|
+
/**
|
|
56
|
+
* Get or generate a session ID
|
|
57
|
+
*/
|
|
58
|
+
getSessionId(): string;
|
|
59
|
+
/**
|
|
60
|
+
* Generate a UUID v4
|
|
61
|
+
*/
|
|
62
|
+
generateId(): string;
|
|
63
|
+
}
|
|
64
|
+
//# sourceMappingURL=session.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"session.d.ts","sourceRoot":"","sources":["../../../../src/base/session.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,MAAM,EAAE,MAAM,mBAAmB,CAAC;AAC3C,OAAO,EAAE,KAAK,EAAE,MAAM,mBAAmB,CAAC;AAC1C,OAAO,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AACnD,OAAO,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAClC,OAAO,EAAE,YAAY,EAAE,MAAM,UAAU,CAAC;AAExC;;GAEG;AACH,qBAAa,aAAa;;gBASb,GAAG,EAAE,SAAS,EAAE,MAAM,CAAC,EAAE,MAAM;IAMpC,IAAI,CAAC,MAAM,EAAE,YAAY,GAAG,IAAI;IAKhC,OAAO,IAAI,IAAI;IAStB;;OAEG;IACH,OAAO,CAAC,KAAK;IAiBb;;OAEG;IACH,OAAO,CAAC,kBAAkB;IAqB1B;;OAEG;IACH,OAAO,CAAC,yBAAyB;IAQjC;;OAEG;IACI,SAAS,IAAI,MAAM,GAAG,SAAS;IAItC;;OAEG;IACI,SAAS,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI;IAItC;;OAEG;IACI,QAAQ,IAAI,MAAM;IAIzB;;OAEG;IACI,QAAQ,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI;IAIpC;;OAEG;IACI,KAAK,CAAC,KAAK,EAAE,KAAK,GAAG,IAAI;IAQhC;;OAEG;IACU,QAAQ,CAAC,QAAQ,EAAE,YAAY,GAAG,OAAO,CAAC,IAAI,CAAC;IAiB5D;;OAEG;IACH,SAAS,CAAC,KAAK,IAAI,IAAI;IAaV,OAAO,CAAC,MAAM,EAAE,GAAG,EAAE;IAgClC;;OAEG;IACI,YAAY,IAAI,MAAM;IAS7B;;OAEG;IACI,UAAU,IAAI,MAAM;CAU3B"}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { Config } from './schemas/config';
|
|
2
|
+
import { UserIdentity } from './schemas/identity';
|
|
3
|
+
declare class UserPath {
|
|
4
|
+
#private;
|
|
5
|
+
constructor(config: Config);
|
|
6
|
+
/**
|
|
7
|
+
* Get the current app ID
|
|
8
|
+
*/
|
|
9
|
+
getAppId(): string;
|
|
10
|
+
/**
|
|
11
|
+
* Set the app ID directly
|
|
12
|
+
*/
|
|
13
|
+
setAppId(appId: string): void;
|
|
14
|
+
/**
|
|
15
|
+
* Install global error handlers
|
|
16
|
+
*/
|
|
17
|
+
installErrorHandler(): void;
|
|
18
|
+
/**
|
|
19
|
+
* Remove global error handlers
|
|
20
|
+
*/
|
|
21
|
+
uninstallErrorHandler(): void;
|
|
22
|
+
/**
|
|
23
|
+
* Track an error
|
|
24
|
+
*/
|
|
25
|
+
trackError(error: Error, properties?: Record<string, any>): void;
|
|
26
|
+
/**
|
|
27
|
+
* Set the user ID directly
|
|
28
|
+
*/
|
|
29
|
+
setUserId(userId: string): void;
|
|
30
|
+
/**
|
|
31
|
+
* Get the current user ID
|
|
32
|
+
*/
|
|
33
|
+
getUserId(): string | undefined;
|
|
34
|
+
/**
|
|
35
|
+
* Identify a user and associate them with their anonymous ID
|
|
36
|
+
*/
|
|
37
|
+
identify(identity: UserIdentity): Promise<void>;
|
|
38
|
+
/**
|
|
39
|
+
* Track an event
|
|
40
|
+
*/
|
|
41
|
+
track(eventType: 'page_view'): void;
|
|
42
|
+
track(eventType: 'event', params: {
|
|
43
|
+
name: string;
|
|
44
|
+
properties?: Record<string, any>;
|
|
45
|
+
}): void;
|
|
46
|
+
track(eventType: 'purchase', params: {
|
|
47
|
+
price: number;
|
|
48
|
+
amount?: number;
|
|
49
|
+
currency: string;
|
|
50
|
+
properties?: Record<string, any>;
|
|
51
|
+
}): void;
|
|
52
|
+
}
|
|
53
|
+
export * from './schemas/events';
|
|
54
|
+
export * from './schemas/config';
|
|
55
|
+
export * from './schemas/identity';
|
|
56
|
+
export { UserPath };
|
|
57
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/index.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,MAAM,EAAkB,MAAM,kBAAkB,CAAC;AAC1D,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAElD,cAAM,QAAQ;;gBAMD,MAAM,EAAE,MAAM;IAyB1B;;OAEG;IACI,QAAQ,IAAI,MAAM;IAIzB;;OAEG;IACI,QAAQ,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI;IAIpC;;OAEG;IACI,mBAAmB,IAAI,IAAI;IAIlC;;OAEG;IACI,qBAAqB,IAAI,IAAI;IAIpC;;OAEG;IACI,UAAU,CAAC,KAAK,EAAE,KAAK,EAAE,UAAU,GAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAM,GAAG,IAAI;IAY3E;;OAEG;IACI,SAAS,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI;IAItC;;OAEG;IACI,SAAS,IAAI,MAAM,GAAG,SAAS;IAItC;;OAEG;IACU,QAAQ,CAAC,QAAQ,EAAE,YAAY,GAAG,OAAO,CAAC,IAAI,CAAC;IAI5D;;OAEG;IACI,KAAK,CAAC,SAAS,EAAE,WAAW,GAAG,IAAI;IACnC,KAAK,CACX,SAAS,EAAE,OAAO,EAClB,MAAM,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,UAAU,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAA;KAAE,GACxD,IAAI;IACA,KAAK,CACX,SAAS,EAAE,UAAU,EACrB,MAAM,EAAE;QACP,KAAK,EAAE,MAAM,CAAC;QACd,MAAM,CAAC,EAAE,MAAM,CAAC;QAChB,QAAQ,EAAE,MAAM,CAAC;QACjB,UAAU,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;KACjC,GACC,IAAI;CAkBP;AAGD,cAAc,kBAAkB,CAAC;AACjC,cAAc,kBAAkB,CAAC;AACjC,cAAc,oBAAoB,CAAC;AAEnC,OAAO,EAAE,QAAQ,EAAE,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pixel.d.ts","sourceRoot":"","sources":["../../../src/pixel.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
export interface Config {
|
|
2
|
+
appId: string;
|
|
3
|
+
serverUrl?: string;
|
|
4
|
+
version?: number;
|
|
5
|
+
userId?: string;
|
|
6
|
+
autoTrackErrors?: boolean;
|
|
7
|
+
autoTrack?: {
|
|
8
|
+
errors?: boolean;
|
|
9
|
+
clicks?: boolean;
|
|
10
|
+
scrolling?: boolean;
|
|
11
|
+
forms?: boolean;
|
|
12
|
+
videos?: boolean;
|
|
13
|
+
pageVisibility?: boolean;
|
|
14
|
+
inactivity?: boolean;
|
|
15
|
+
};
|
|
16
|
+
}
|
|
17
|
+
export declare const DEFAULT_CONFIG: {
|
|
18
|
+
readonly appId: "";
|
|
19
|
+
readonly serverUrl: "https://api.userpath.co";
|
|
20
|
+
readonly version: 1;
|
|
21
|
+
readonly autoTrackErrors: true;
|
|
22
|
+
readonly autoTrack: {
|
|
23
|
+
readonly errors: true;
|
|
24
|
+
readonly clicks: true;
|
|
25
|
+
readonly scrolling: true;
|
|
26
|
+
readonly forms: true;
|
|
27
|
+
readonly videos: true;
|
|
28
|
+
readonly pageVisibility: true;
|
|
29
|
+
readonly inactivity: true;
|
|
30
|
+
};
|
|
31
|
+
};
|
|
32
|
+
//# sourceMappingURL=config.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../../../src/schemas/config.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,MAAM;IACtB,KAAK,EAAE,MAAM,CAAC;IACd,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B,SAAS,CAAC,EAAE;QACX,MAAM,CAAC,EAAE,OAAO,CAAC;QACjB,MAAM,CAAC,EAAE,OAAO,CAAC;QACjB,SAAS,CAAC,EAAE,OAAO,CAAC;QACpB,KAAK,CAAC,EAAE,OAAO,CAAC;QAChB,MAAM,CAAC,EAAE,OAAO,CAAC;QACjB,cAAc,CAAC,EAAE,OAAO,CAAC;QACzB,UAAU,CAAC,EAAE,OAAO,CAAC;KACrB,CAAC;CACF;AAED,eAAO,MAAM,cAAc;;;;;;;;;;;;;;CAcjB,CAAC"}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
export interface PageViewEvent {
|
|
2
|
+
name: 'page_view';
|
|
3
|
+
url: string;
|
|
4
|
+
referrer: string;
|
|
5
|
+
title: string;
|
|
6
|
+
user_id?: string;
|
|
7
|
+
timestamp: string;
|
|
8
|
+
}
|
|
9
|
+
export interface CustomEvent {
|
|
10
|
+
name: string;
|
|
11
|
+
url: string;
|
|
12
|
+
referrer: string;
|
|
13
|
+
title: string;
|
|
14
|
+
properties: Record<string, any>;
|
|
15
|
+
user_id?: string;
|
|
16
|
+
timestamp: string;
|
|
17
|
+
}
|
|
18
|
+
export interface PurchaseEvent {
|
|
19
|
+
name: 'purchase';
|
|
20
|
+
productId: string;
|
|
21
|
+
price: number;
|
|
22
|
+
currency: string;
|
|
23
|
+
properties?: Record<string, any>;
|
|
24
|
+
user_id?: string;
|
|
25
|
+
timestamp: string;
|
|
26
|
+
url: string;
|
|
27
|
+
referrer: string;
|
|
28
|
+
title: string;
|
|
29
|
+
}
|
|
30
|
+
export interface IdentifyEvent {
|
|
31
|
+
name: 'identify';
|
|
32
|
+
anonymousId: string;
|
|
33
|
+
userId: string;
|
|
34
|
+
email?: string;
|
|
35
|
+
properties?: Record<string, any>;
|
|
36
|
+
user_id?: string;
|
|
37
|
+
timestamp: string;
|
|
38
|
+
url?: string;
|
|
39
|
+
referrer?: string;
|
|
40
|
+
title?: string;
|
|
41
|
+
}
|
|
42
|
+
export interface ErrorEvent {
|
|
43
|
+
name: 'error';
|
|
44
|
+
message: string;
|
|
45
|
+
stack?: string;
|
|
46
|
+
source?: string;
|
|
47
|
+
lineno?: number;
|
|
48
|
+
colno?: number;
|
|
49
|
+
filename?: string;
|
|
50
|
+
properties?: Record<string, any>;
|
|
51
|
+
user_id?: string;
|
|
52
|
+
timestamp: string;
|
|
53
|
+
url?: string;
|
|
54
|
+
referrer?: string;
|
|
55
|
+
title?: string;
|
|
56
|
+
}
|
|
57
|
+
export type Event = PageViewEvent | CustomEvent | PurchaseEvent | IdentifyEvent | ErrorEvent;
|
|
58
|
+
//# sourceMappingURL=events.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"events.d.ts","sourceRoot":"","sources":["../../../../src/schemas/events.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,aAAa;IAC7B,IAAI,EAAE,WAAW,CAAC;IAClB,GAAG,EAAE,MAAM,CAAC;IACZ,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,WAAW;IAC3B,IAAI,EAAE,MAAM,CAAC;IACb,GAAG,EAAE,MAAM,CAAC;IACZ,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,EAAE,MAAM,CAAC;IACd,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;IAChC,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,aAAa;IAC7B,IAAI,EAAE,UAAU,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;IACjC,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;IAClB,GAAG,EAAE,MAAM,CAAC;IACZ,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,EAAE,MAAM,CAAC;CACd;AAED,MAAM,WAAW,aAAa;IAC7B,IAAI,EAAE,UAAU,CAAC;IACjB,WAAW,EAAE,MAAM,CAAC;IACpB,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,UAAU,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;IACjC,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;IAClB,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,KAAK,CAAC,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,UAAU;IAC1B,IAAI,EAAE,OAAO,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,UAAU,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;IACjC,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;IAClB,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,KAAK,CAAC,EAAE,MAAM,CAAC;CACf;AAED,MAAM,MAAM,KAAK,GACd,aAAa,GACb,WAAW,GACX,aAAa,GACb,aAAa,GACb,UAAU,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"identity.d.ts","sourceRoot":"","sources":["../../../../src/schemas/identity.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,YAAY;IAC5B,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,UAAU,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;CACjC"}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* First party server integration for UserPath
|
|
3
|
+
* UserPath is compliant with WinterCG
|
|
4
|
+
* meaning you can extend it with any framework that supports the native Fetch API
|
|
5
|
+
* including cloudflare workers.
|
|
6
|
+
*/
|
|
7
|
+
export declare const userpath: {
|
|
8
|
+
/**
|
|
9
|
+
* Fetch Handler for First Party Server Integration
|
|
10
|
+
* @param request - The request object
|
|
11
|
+
* @returns The response object
|
|
12
|
+
* @example
|
|
13
|
+
* ```ts
|
|
14
|
+
* import { userpath } from 'userpath-js/server';
|
|
15
|
+
*
|
|
16
|
+
* // Using Node.js
|
|
17
|
+
* http
|
|
18
|
+
* .createServer(function (req, res) {
|
|
19
|
+
* return userpath.fetch(req);
|
|
20
|
+
* })
|
|
21
|
+
* .listen(3000);
|
|
22
|
+
*
|
|
23
|
+
* // Using Bun
|
|
24
|
+
* Bun.serve({
|
|
25
|
+
* fetch(req) {
|
|
26
|
+
* return userpath.fetch(req);
|
|
27
|
+
* },
|
|
28
|
+
* });
|
|
29
|
+
*
|
|
30
|
+
* // Using Elysia
|
|
31
|
+
* const app = new Elysia({ name: 'my-userpath' });
|
|
32
|
+
*
|
|
33
|
+
* app.get('/', () => 'Hello World from Elysia');
|
|
34
|
+
*
|
|
35
|
+
* app.mount('/events', userpath.fetch); // Mount the UserPath server
|
|
36
|
+
*
|
|
37
|
+
* app.listen(3000);
|
|
38
|
+
* ```
|
|
39
|
+
*/
|
|
40
|
+
fetch(request: Request): Promise<any>;
|
|
41
|
+
};
|
|
42
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/server/index.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,eAAO,MAAM,QAAQ;IACpB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;OA+BG;mBACkB,OAAO;CAqD5B,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,14 +1,35 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "userpath-js",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.2",
|
|
4
4
|
"private": false,
|
|
5
|
-
"main": "dist/
|
|
5
|
+
"main": "dist/index.js",
|
|
6
|
+
"types": "dist/types/src/index.d.ts",
|
|
7
|
+
"exports": {
|
|
8
|
+
".": {
|
|
9
|
+
"types": "./dist/types/src/index.d.ts",
|
|
10
|
+
"default": "./dist/index.js"
|
|
11
|
+
},
|
|
12
|
+
"./server": {
|
|
13
|
+
"types": "./dist/types/src/server/index.d.ts",
|
|
14
|
+
"default": "./dist/server/index.js"
|
|
15
|
+
}
|
|
16
|
+
},
|
|
17
|
+
"files": [
|
|
18
|
+
"dist/index.js",
|
|
19
|
+
"dist/types",
|
|
20
|
+
"dist/server"
|
|
21
|
+
],
|
|
6
22
|
"scripts": {
|
|
7
23
|
"dev": "bun build src/index.ts --outdir ./dist --watch",
|
|
8
|
-
"
|
|
24
|
+
"types": "tsc",
|
|
25
|
+
"build": "bun run build.client && bun run build.pixel && bun run build.server",
|
|
26
|
+
"build.prod": "bun run build.client.prod && bun run build.pixel.prod && bun run build.server.prod",
|
|
27
|
+
"build.client": "bun run types && bun build src/index.ts --outdir ./dist --no-cache",
|
|
28
|
+
"build.client.prod": "bun run types && bun build src/index.ts --outdir ./dist --minify",
|
|
9
29
|
"build.pixel": "bun build src/pixel.ts --outdir ./dist",
|
|
10
30
|
"build.pixel.prod": "bun build src/pixel.ts --outdir ./dist --minify",
|
|
11
|
-
"
|
|
31
|
+
"build.server": "bun run types && bun build src/server/index.ts --outdir ./dist/server",
|
|
32
|
+
"build.server.prod": "bun run types && bun build src/server/index.ts --outdir ./dist/server --minify"
|
|
12
33
|
},
|
|
13
34
|
"devDependencies": {
|
|
14
35
|
"bun-types": "latest",
|
package/dist/pixel.js
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
class n{namespace;constructor({namespace:r}){this.namespace=r}getItem(r){let t=localStorage.getItem(`${this.namespace}.${r}`)||"";try{return JSON.parse(t)}catch(e){return null}}setItem(r,t){localStorage.setItem(`${this.namespace}.${r}`,JSON.stringify(t))}removeItem(r){localStorage.removeItem(`${this.namespace}.${r}`)}}var u=new n({namespace:"userpath"});class c{baseUrl;siteId;queue=[];flushInterval=null;constructor(r){this.baseUrl=r.baseUrl||"",this.siteId=r.siteId,this.flushInterval=window.setInterval(()=>this.flush(),5000)}track(r){if(this.queue.push(r),this.queue.length>=10)this.flush()}flush(){if(this.queue.length===0)return;let r=[...this.queue];this.queue=[],fetch(`${this.baseUrl}/events`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({siteId:this.siteId,sessionId:this.getSessionId(),timestamp:new Date().toISOString(),userAgent:navigator.userAgent,screenWidth:window.screen.width,screenHeight:window.screen.height,language:navigator.language,timezone:Intl.DateTimeFormat().resolvedOptions().timeZone,events:r}),keepalive:!0}).catch((t)=>{this.queue=[...r,...this.queue].slice(0,100),console.error("Error sending analytics data:",t)})}getSessionId(){let r=u.getItem("session_id");if(!r)r=this.generateId(),u.setItem("session_id",r);return r}generateId(){return"xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g,function(r){let t=Math.random()*16|0;return(r==="x"?t:t&3|8).toString(16)})}}class o{api;constructor(r){this.api=r}trackPageView(){let r={type:"pageview",url:window.location.href,referrer:document.referrer,title:document.title};this.api.track(r)}trackCustomEvent(r,t={}){let e={type:"event",name:r,properties:t};this.api.track(e)}trackPurchase(r){let t={type:"purchase",...r};this.api.track(t)}}class g{api;anonymousId=null;currentUser=null;constructor(r){this.api=r;this.anonymousId=this.getStoredAnonymousId()}getAnonymousId(){if(!this.anonymousId)this.anonymousId=this.api.generateId(),localStorage.setItem("up_anonymous_id",this.anonymousId);return this.anonymousId}getUserId(){return this.currentUser?.userId||null}identify(r){this.currentUser=r,this.api.track({type:"identify",anonymousId:this.getAnonymousId(),...r})}getStoredAnonymousId(){return localStorage.getItem("up_anonymous_id")}}class p{api;events;constructor(r){this.api=r;this.events=new o(r),this.initialize()}initialize(){this.events.trackPageView(),this.setupHistoryChange()}setupHistoryChange(){let{pushState:r,replaceState:t}=history;history.pushState=(...e)=>{r.apply(history,e),this.events.trackPageView()},history.replaceState=(...e)=>{t.apply(history,e),this.events.trackPageView()},window.addEventListener("popstate",()=>{this.events.trackPageView()})}}class m{events;session;identity;constructor(r){let t=new c(r);this.events=new o(t),this.session=new p(t),this.identity=new g(t)}getAnonymousId(){return this.identity.getAnonymousId()}getUserId(){return this.identity.getUserId()}identify(r){this.identity.identify(r)}trackPageView(){this.events.trackPageView()}trackEvent(r,t={}){this.events.trackCustomEvent(r,t)}trackPurchase(r){this.events.trackPurchase(r)}}var w=typeof window!=="undefined";document.addEventListener("DOMContentLoaded",()=>{if(!w)return;try{let r=document.querySelector("[data-up]").getAttribute("data-up")||"",t=document.querySelector("[data-up]").getAttribute("src")||"",a=new URL(t).origin+"/v1",d=new m({siteId:r,baseUrl:a});window.userpath=d,setTimeout(()=>{console.log(r,a,d)},1000)}catch(r){console.error(r)}});
|
package/examples/usage.ts
DELETED
|
@@ -1,83 +0,0 @@
|
|
|
1
|
-
// Example code showing how the client SDK should handle user identity resolution
|
|
2
|
-
|
|
3
|
-
import { UserPath } from '../src';
|
|
4
|
-
|
|
5
|
-
// Initialize the tracking SDK
|
|
6
|
-
const userpath = new UserPath({
|
|
7
|
-
siteId: 'site_123', // Required
|
|
8
|
-
baseUrl: 'https://api.userpath.to/v1', // Optional
|
|
9
|
-
});
|
|
10
|
-
|
|
11
|
-
// When a user first visits the site, they get an anonymous ID
|
|
12
|
-
// This is stored in local storage
|
|
13
|
-
const anonymousId = userpath.getAnonymousId();
|
|
14
|
-
console.log('Anonymous visitor ID:', anonymousId);
|
|
15
|
-
|
|
16
|
-
// Example: Track a page view (this is done automatically on initialization)
|
|
17
|
-
// But you can also track it manually if needed
|
|
18
|
-
userpath.trackPageView();
|
|
19
|
-
|
|
20
|
-
// Example: Track a custom event (like clicking the purchase button)
|
|
21
|
-
function onPurchaseButtonClick() {
|
|
22
|
-
userpath.trackEvent('click_purchase', {
|
|
23
|
-
buttonId: 'purchase-btn',
|
|
24
|
-
plan: 'premium',
|
|
25
|
-
});
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
// Example: When user identifies themselves (e.g., signs up or logs in)
|
|
29
|
-
function onUserIdentified(user: { id: string; email: string; name: string }) {
|
|
30
|
-
// Identify the user and connect them to their anonymous activity
|
|
31
|
-
userpath.identify({
|
|
32
|
-
userId: user.id,
|
|
33
|
-
email: user.email,
|
|
34
|
-
name: user.name,
|
|
35
|
-
});
|
|
36
|
-
|
|
37
|
-
console.log('User identified:', userpath.getUserId());
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
// Example: Track a purchase event
|
|
41
|
-
function trackPurchase(purchaseDetails: {
|
|
42
|
-
productId: string;
|
|
43
|
-
price: number;
|
|
44
|
-
currency: string;
|
|
45
|
-
plan: string;
|
|
46
|
-
couponApplied?: string;
|
|
47
|
-
}) {
|
|
48
|
-
userpath.trackPurchase({
|
|
49
|
-
productId: purchaseDetails.productId,
|
|
50
|
-
price: purchaseDetails.price,
|
|
51
|
-
currency: purchaseDetails.currency,
|
|
52
|
-
properties: {
|
|
53
|
-
plan: purchaseDetails.plan,
|
|
54
|
-
couponApplied: purchaseDetails.couponApplied,
|
|
55
|
-
// Include user identification if available
|
|
56
|
-
userId: userpath.getUserId(),
|
|
57
|
-
anonymousId: userpath.getAnonymousId(),
|
|
58
|
-
},
|
|
59
|
-
});
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
// Example usage flow:
|
|
63
|
-
|
|
64
|
-
// 1. SDK is initialized and first pageview is tracked automatically
|
|
65
|
-
|
|
66
|
-
// 2. User clicks purchase button while still anonymous
|
|
67
|
-
onPurchaseButtonClick();
|
|
68
|
-
|
|
69
|
-
// 3. User signs up or logs in
|
|
70
|
-
onUserIdentified({
|
|
71
|
-
id: 'user_123',
|
|
72
|
-
email: 'john@example.com',
|
|
73
|
-
name: 'John Doe',
|
|
74
|
-
});
|
|
75
|
-
|
|
76
|
-
// 4. User completes a purchase (now as an identified user)
|
|
77
|
-
trackPurchase({
|
|
78
|
-
productId: 'plan_premium',
|
|
79
|
-
price: 1337,
|
|
80
|
-
currency: 'USD',
|
|
81
|
-
plan: 'Premium Annual',
|
|
82
|
-
couponApplied: 'WELCOME20',
|
|
83
|
-
});
|