@dotcms/analytics 0.0.1-beta.43 → 0.0.1-beta.45
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 +167 -100
- package/lib/dotAnalytics/plugin/dot-analytics.plugin.js +48 -23
- package/lib/dotAnalytics/plugin/enricher/dot-analytics.enricher.plugin.d.ts +6 -7
- package/lib/dotAnalytics/plugin/enricher/dot-analytics.enricher.plugin.js +11 -10
- package/lib/dotAnalytics/shared/dot-content-analytics.constants.d.ts +13 -2
- package/lib/dotAnalytics/shared/dot-content-analytics.constants.js +11 -7
- package/lib/dotAnalytics/shared/dot-content-analytics.http.d.ts +5 -5
- package/lib/dotAnalytics/shared/dot-content-analytics.http.js +19 -6
- package/lib/dotAnalytics/shared/dot-content-analytics.model.d.ts +70 -29
- package/lib/dotAnalytics/shared/dot-content-analytics.utils.d.ts +7 -13
- package/lib/dotAnalytics/shared/dot-content-analytics.utils.js +38 -46
- package/lib/react/hook/useContentAnalytics.js +18 -20
- package/lib/react/hook/useRouterTracker.js +8 -7
- package/package.json +3 -2
- package/types/src/lib/editor/public.js +5 -0
- package/types/src/lib/events/internal.js +4 -0
- package/uve/src/internal/constants.js +3 -0
- package/uve/src/internal/events.js +108 -0
- package/uve/src/lib/core/core.utils.js +21 -0
- package/uve/src/lib/dom/dom.utils.js +81 -0
package/README.md
CHANGED
|
@@ -1,159 +1,226 @@
|
|
|
1
|
-
# @dotcms/analytics
|
|
1
|
+
# dotCMS Content Analytics SDK (@dotcms/analytics)
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Lightweight JavaScript SDK for tracking content-aware events in dotCMS. Works in vanilla JS and React apps. Angular & Vue support coming soon.
|
|
4
4
|
|
|
5
|
-
##
|
|
5
|
+
## 🚀 Quick Start
|
|
6
6
|
|
|
7
|
-
|
|
8
|
-
- **React Support**: Built-in React components and hooks for seamless integration
|
|
9
|
-
- **Event Tracking**: Simple API to track custom events with additional properties
|
|
10
|
-
- **Automatic PageView**: Option to automatically track page views
|
|
11
|
-
- **Debug Mode**: Optional debug logging for development
|
|
7
|
+
### Vanilla JavaScript
|
|
12
8
|
|
|
13
|
-
|
|
9
|
+
**CDN (Script Tag - Auto Page View)**
|
|
10
|
+
|
|
11
|
+
```html
|
|
12
|
+
<script
|
|
13
|
+
src="ca.min.js"
|
|
14
|
+
data-analytics-server="https://demo.dotcms.com"
|
|
15
|
+
data-analytics-key="SITE_KEY"
|
|
16
|
+
data-analytics-auto-page-view></script>
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
**npm (ES Module)**
|
|
14
20
|
|
|
15
21
|
```bash
|
|
16
22
|
npm install @dotcms/analytics
|
|
17
23
|
```
|
|
18
24
|
|
|
19
|
-
|
|
25
|
+
```javascript
|
|
26
|
+
import { initializeContentAnalytics } from '@dotcms/analytics';
|
|
20
27
|
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
28
|
+
const analytics = initializeContentAnalytics({
|
|
29
|
+
siteKey: 'SITE_KEY',
|
|
30
|
+
server: 'https://demo.dotcms.com'
|
|
31
|
+
});
|
|
24
32
|
|
|
25
|
-
|
|
33
|
+
analytics.track('page-loaded');
|
|
34
|
+
```
|
|
26
35
|
|
|
27
|
-
###
|
|
36
|
+
### React
|
|
28
37
|
|
|
29
|
-
|
|
38
|
+
```bash
|
|
39
|
+
npm install @dotcms/analytics
|
|
40
|
+
```
|
|
30
41
|
|
|
31
42
|
```tsx
|
|
32
43
|
import { DotContentAnalyticsProvider } from '@dotcms/analytics/react';
|
|
33
|
-
```
|
|
34
44
|
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
//
|
|
39
|
-
const analyticsConfig = {
|
|
40
|
-
apiKey: 'your-api-key-from-dotcms-analytics-app',
|
|
41
|
-
server: 'https://your-dotcms-instance.com'
|
|
45
|
+
const config = {
|
|
46
|
+
siteKey: 'SITE_KEY',
|
|
47
|
+
server: 'https://demo.dotcms.com',
|
|
48
|
+
autoPageView: true // Optional, default is true
|
|
42
49
|
};
|
|
43
50
|
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
<YourApp />
|
|
48
|
-
</DotContentAnalyticsProvider>
|
|
49
|
-
);
|
|
50
|
-
}
|
|
51
|
+
<DotContentAnalyticsProvider config={config}>
|
|
52
|
+
<App />
|
|
53
|
+
</DotContentAnalyticsProvider>;
|
|
51
54
|
```
|
|
52
55
|
|
|
53
|
-
|
|
56
|
+
## 📘 Core Concepts
|
|
54
57
|
|
|
55
|
-
|
|
58
|
+
### Events
|
|
56
59
|
|
|
57
|
-
|
|
58
|
-
import { useContentAnalytics } from '@dotcms/analytics/react';
|
|
60
|
+
Track any user action as an event using `track('event-name', { payload })`.
|
|
59
61
|
|
|
60
|
-
|
|
61
|
-
const { track } = useContentAnalytics();
|
|
62
|
+
### Page Views
|
|
62
63
|
|
|
63
|
-
|
|
64
|
-
// Second parameter: object with properties you want to track
|
|
64
|
+
Tracked automatically (or manually) on route changes.
|
|
65
65
|
|
|
66
|
-
|
|
67
|
-
|
|
66
|
+
### Sessions
|
|
67
|
+
|
|
68
|
+
- 30-minute timeout
|
|
69
|
+
- Resets at midnight UTC
|
|
70
|
+
- New session if UTM campaign changes
|
|
71
|
+
|
|
72
|
+
### Identity
|
|
73
|
+
|
|
74
|
+
- Anonymous user ID persisted across sessions
|
|
75
|
+
- Stored in `dot_analytics_user_id`
|
|
76
|
+
|
|
77
|
+
## ⚙️ Configuration Options
|
|
78
|
+
|
|
79
|
+
| Option | Type | Required | Default | Description |
|
|
80
|
+
| -------------- | ---------- | -------- | ------- | -------------------------------------- |
|
|
81
|
+
| `siteKey` | `string` | ✅ | - | Site key from dotCMS Analytics app |
|
|
82
|
+
| `server` | `string` | ✅ | - | Your dotCMS server URL |
|
|
83
|
+
| `debug` | `boolean` | ❌ | `false` | Enable verbose logging |
|
|
84
|
+
| `autoPageView` | `boolean` | ❌ | `true` | Auto track page views on route changes |
|
|
85
|
+
| `redirectFn` | `function` | ❌ | - | Custom handler for redirects |
|
|
86
|
+
|
|
87
|
+
## 🛠️ Usage Examples
|
|
88
|
+
|
|
89
|
+
### Vanilla JavaScript
|
|
90
|
+
|
|
91
|
+
**Manual Page View & Events**
|
|
92
|
+
|
|
93
|
+
```javascript
|
|
94
|
+
// After init with the <script> tag the dotAnalytics is added to the window.
|
|
95
|
+
window.dotAnalytics.track('cta-click', { button: 'Buy Now' });
|
|
96
|
+
window.dotAnalytics.pageView();
|
|
68
97
|
```
|
|
69
98
|
|
|
70
|
-
|
|
99
|
+
**Advanced: Manual Init with Custom Properties**
|
|
100
|
+
|
|
101
|
+
```javascript
|
|
102
|
+
const analytics = initializeContentAnalytics({
|
|
103
|
+
siteKey: 'abc123',
|
|
104
|
+
server: 'https://your-dotcms.com',
|
|
105
|
+
debug: true,
|
|
106
|
+
autoPageView: false
|
|
107
|
+
});
|
|
71
108
|
|
|
72
|
-
|
|
109
|
+
analytics.track('custom-event', {
|
|
110
|
+
category: 'Marketing',
|
|
111
|
+
value: 'Banner Clicked'
|
|
112
|
+
});
|
|
113
|
+
|
|
114
|
+
analytics.pageView();
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
### React
|
|
118
|
+
|
|
119
|
+
**Track Events**
|
|
73
120
|
|
|
74
121
|
```tsx
|
|
75
|
-
const
|
|
76
|
-
|
|
77
|
-
server: 'https://your-dotcms-instance.com',
|
|
78
|
-
autoPageView: false // Disable automatic tracking
|
|
79
|
-
};
|
|
122
|
+
const { track } = useContentAnalytics();
|
|
123
|
+
track('cta-click', { label: 'Download PDF' });
|
|
80
124
|
```
|
|
81
125
|
|
|
82
|
-
|
|
126
|
+
**Manual Page View**
|
|
83
127
|
|
|
84
128
|
```tsx
|
|
85
|
-
|
|
129
|
+
const { pageView } = useContentAnalytics();
|
|
130
|
+
useEffect(() => {
|
|
131
|
+
pageView();
|
|
132
|
+
}, []);
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
**Advanced: Manual Tracking with Router**
|
|
136
|
+
|
|
137
|
+
```tsx
|
|
138
|
+
import { useLocation } from 'react-router-dom';
|
|
139
|
+
const { pageView } = useContentAnalytics();
|
|
140
|
+
const location = useLocation();
|
|
86
141
|
|
|
87
|
-
|
|
88
|
-
|
|
142
|
+
useEffect(() => {
|
|
143
|
+
pageView();
|
|
144
|
+
}, [location]);
|
|
145
|
+
```
|
|
89
146
|
|
|
90
|
-
|
|
91
|
-
pageView({
|
|
92
|
-
// Add any custom properties you want to track
|
|
93
|
-
myCustomValue: '2'
|
|
94
|
-
});
|
|
95
|
-
}, []);
|
|
147
|
+
## API Reference
|
|
96
148
|
|
|
97
|
-
|
|
149
|
+
```typescript
|
|
150
|
+
interface DotCMSAnalytics {
|
|
151
|
+
track: (eventName: string, payload?: Record<string, unknown>) => void;
|
|
152
|
+
pageView: () => void;
|
|
98
153
|
}
|
|
99
154
|
```
|
|
100
155
|
|
|
101
|
-
|
|
156
|
+
**Enriched AnalyticsEvent includes:**
|
|
102
157
|
|
|
103
|
-
|
|
158
|
+
- `context`: siteKey, sessionId, userId
|
|
159
|
+
- `page`: URL, title, referrer, path
|
|
160
|
+
- `device`: screen size, language, viewport
|
|
161
|
+
- `utm`: source, medium, campaign, term, etc.
|
|
104
162
|
|
|
105
|
-
|
|
106
|
-
- **data-analytics-debug**: Enables debug logging
|
|
107
|
-
- **data-analytics-auto-page-view**: Recommended for IIFE implementation. Enables automatic page view tracking
|
|
108
|
-
- **data-analytics-key**: **(Required)** API key for authentication
|
|
163
|
+
## Under the Hood
|
|
109
164
|
|
|
110
|
-
|
|
111
|
-
<!-- Example configuration -->
|
|
112
|
-
<script
|
|
113
|
-
src="ca.min.js"
|
|
114
|
-
data-analytics-server="http://localhost:8080"
|
|
115
|
-
data-analytics-key="dev-key-123"
|
|
116
|
-
data-analytics-auto-page-view
|
|
117
|
-
data-analytics-debug></script>
|
|
165
|
+
### Storage Keys
|
|
118
166
|
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
167
|
+
- `dot_analytics_user_id`
|
|
168
|
+
- `dot_analytics_session_id`
|
|
169
|
+
- `dot_analytics_session_utm`
|
|
170
|
+
- `dot_analytics_session_start`
|
|
171
|
+
|
|
172
|
+
### Editor Detection
|
|
173
|
+
|
|
174
|
+
Analytics are disabled when inside the dotCMS editor.
|
|
175
|
+
|
|
176
|
+
## Debugging & Troubleshooting
|
|
177
|
+
|
|
178
|
+
**Not seeing events?**
|
|
179
|
+
|
|
180
|
+
- Ensure `siteKey` & `server` are correct
|
|
181
|
+
- Enable debug mode
|
|
182
|
+
- Check network requests to: `https://your-server/api/v1/analytics/content/event`
|
|
183
|
+
- Avoid using inside dotCMS editor (auto-disabled)
|
|
126
184
|
|
|
127
185
|
## Roadmap
|
|
128
186
|
|
|
129
|
-
|
|
187
|
+
- Scroll depth & file download tracking
|
|
188
|
+
- Form interaction analytics
|
|
189
|
+
- Angular & Vue support
|
|
190
|
+
- Realtime dashboard
|
|
191
|
+
|
|
192
|
+
## dotCMS Support
|
|
193
|
+
|
|
194
|
+
We offer multiple channels to get help with the dotCMS React SDK:
|
|
130
195
|
|
|
131
|
-
|
|
132
|
-
|
|
196
|
+
- **GitHub Issues**: For bug reports and feature requests, please [open an issue](https://github.com/dotCMS/core/issues/new/choose) in the GitHub repository.
|
|
197
|
+
- **Community Forum**: Join our [community discussions](https://community.dotcms.com/) to ask questions and share solutions.
|
|
198
|
+
- **Stack Overflow**: Use the tag `dotcms-react` when posting questions.
|
|
199
|
+
- **Enterprise Support**: Enterprise customers can access premium support through the [dotCMS Support Portal](https://helpdesk.dotcms.com/support/).
|
|
133
200
|
|
|
134
|
-
|
|
201
|
+
When reporting issues, please include:
|
|
135
202
|
|
|
136
|
-
|
|
203
|
+
- SDK version you're using
|
|
204
|
+
- React version
|
|
205
|
+
- Minimal reproduction steps
|
|
206
|
+
- Expected vs. actual behavior
|
|
137
207
|
|
|
138
|
-
##
|
|
208
|
+
## How To Contribute
|
|
139
209
|
|
|
140
|
-
|
|
210
|
+
GitHub pull requests are the preferred method to contribute code to dotCMS. We welcome contributions to the DotCMS UVE SDK! If you'd like to contribute, please follow these steps:
|
|
141
211
|
|
|
142
|
-
|
|
212
|
+
1. Fork the repository [dotCMS/core](https://github.com/dotCMS/core)
|
|
213
|
+
2. Create a feature branch (`git checkout -b feature/amazing-feature`)
|
|
214
|
+
3. Commit your changes (`git commit -m 'Add some amazing feature'`)
|
|
215
|
+
4. Push to the branch (`git push origin feature/amazing-feature`)
|
|
216
|
+
5. Open a Pull Request
|
|
143
217
|
|
|
144
|
-
|
|
218
|
+
Please ensure your code follows the existing style and includes appropriate tests.
|
|
145
219
|
|
|
146
|
-
##
|
|
220
|
+
## Licensing Information
|
|
147
221
|
|
|
148
|
-
|
|
222
|
+
dotCMS comes in multiple editions and as such is dual-licensed. The dotCMS Community Edition is licensed under the GPL 3.0 and is freely available for download, customization, and deployment for use within organizations of all stripes. dotCMS Enterprise Editions (EE) adds several enterprise features and is available via a supported, indemnified commercial license from dotCMS. For the differences between the editions, see [the feature page](http://www.dotcms.com/cms-platform/features).
|
|
149
223
|
|
|
150
|
-
|
|
224
|
+
This SDK is part of dotCMS's dual-licensed platform (GPL 3.0 for Community, commercial license for Enterprise).
|
|
151
225
|
|
|
152
|
-
|
|
153
|
-
| --------------- | ------------------------------------------------------------------- |
|
|
154
|
-
| Installation | [Installation](https://dotcms.com/docs/latest/installation) |
|
|
155
|
-
| Documentation | [Documentation](https://dotcms.com/docs/latest/table-of-contents) |
|
|
156
|
-
| Videos | [Helpful Videos](http://dotcms.com/videos/) |
|
|
157
|
-
| Forums/Listserv | [via Google Groups](https://groups.google.com/forum/#!forum/dotCMS) |
|
|
158
|
-
| Twitter | @dotCMS |
|
|
159
|
-
| Main Site | [dotCMS.com](https://dotcms.com/) |
|
|
226
|
+
[Learn more ](https://www.dotcms.com)at [dotcms.com](https://www.dotcms.com).
|
|
@@ -1,54 +1,79 @@
|
|
|
1
|
+
import { EVENT_TYPES as y } from "../shared/dot-content-analytics.constants.js";
|
|
1
2
|
import { sendAnalyticsEventToServer as c } from "../shared/dot-content-analytics.http.js";
|
|
2
|
-
const
|
|
3
|
-
let
|
|
3
|
+
const p = (v) => {
|
|
4
|
+
let r = !1;
|
|
4
5
|
return {
|
|
5
6
|
name: "dot-analytics",
|
|
6
|
-
config:
|
|
7
|
+
config: v,
|
|
7
8
|
/**
|
|
8
9
|
* Initialize the plugin
|
|
9
10
|
*/
|
|
10
|
-
initialize: () => (
|
|
11
|
+
initialize: () => (r = !0, Promise.resolve()),
|
|
11
12
|
/**
|
|
12
13
|
* Track a page view event
|
|
13
14
|
* Takes enriched data from properties and creates final structured event
|
|
14
15
|
*/
|
|
15
|
-
page: (
|
|
16
|
-
const { config:
|
|
17
|
-
if (!
|
|
16
|
+
page: (a) => {
|
|
17
|
+
const { config: t, payload: e } = a, { context: n, page: o, device: i, utm: s, local_time: l } = e;
|
|
18
|
+
if (!r)
|
|
18
19
|
throw new Error("DotAnalytics: Plugin not initialized");
|
|
19
|
-
|
|
20
|
-
|
|
20
|
+
if (!n || !o || !i || !l)
|
|
21
|
+
throw new Error("DotAnalytics: Missing required payload data for pageview event");
|
|
22
|
+
const d = {
|
|
23
|
+
context: n,
|
|
21
24
|
events: [
|
|
22
25
|
{
|
|
23
|
-
event_type:
|
|
24
|
-
local_time:
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
26
|
+
event_type: y.PAGEVIEW,
|
|
27
|
+
local_time: l,
|
|
28
|
+
data: {
|
|
29
|
+
page: o,
|
|
30
|
+
device: i,
|
|
31
|
+
...s && { utm: s }
|
|
32
|
+
}
|
|
28
33
|
}
|
|
29
34
|
]
|
|
30
35
|
};
|
|
31
|
-
return
|
|
36
|
+
return t.debug && console.warn("DotAnalytics: Pageview event to send:", d), c(d, t);
|
|
32
37
|
},
|
|
38
|
+
// TODO: Fix this when we haver the final design for the track event
|
|
33
39
|
/**
|
|
34
40
|
* Track a custom event
|
|
35
41
|
* Takes enriched data and sends it to the analytics server
|
|
36
42
|
*/
|
|
37
|
-
track: (
|
|
38
|
-
const { config:
|
|
39
|
-
if (!
|
|
43
|
+
track: (a) => {
|
|
44
|
+
const { config: t, payload: e } = a;
|
|
45
|
+
if (!r)
|
|
40
46
|
throw new Error("DotAnalytics: Plugin not initialized");
|
|
41
|
-
|
|
42
|
-
|
|
47
|
+
if ("events" in e && Array.isArray(e.events)) {
|
|
48
|
+
const o = e, i = {
|
|
49
|
+
context: o.context,
|
|
50
|
+
events: o.events
|
|
51
|
+
};
|
|
52
|
+
return t.debug && console.warn("DotAnalytics: Track event to send:", i), c(i, t);
|
|
53
|
+
}
|
|
54
|
+
if (!e.context || !e.local_time)
|
|
55
|
+
throw new Error("DotAnalytics: Missing required payload data for track event");
|
|
56
|
+
const n = {
|
|
57
|
+
context: e.context,
|
|
58
|
+
events: [
|
|
59
|
+
{
|
|
60
|
+
event_type: y.TRACK,
|
|
61
|
+
local_time: e.local_time,
|
|
62
|
+
data: {
|
|
63
|
+
event: e.event,
|
|
64
|
+
...e.properties
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
]
|
|
43
68
|
};
|
|
44
|
-
return c(
|
|
69
|
+
return t.debug && console.warn("DotAnalytics: Track event to send (fallback):", n), c(n, t);
|
|
45
70
|
},
|
|
46
71
|
/**
|
|
47
72
|
* Check if the plugin is loaded
|
|
48
73
|
*/
|
|
49
|
-
loaded: () =>
|
|
74
|
+
loaded: () => r
|
|
50
75
|
};
|
|
51
76
|
};
|
|
52
77
|
export {
|
|
53
|
-
|
|
78
|
+
p as dotAnalytics
|
|
54
79
|
};
|
|
@@ -19,13 +19,12 @@ export declare const dotAnalyticsEnricherPlugin: () => {
|
|
|
19
19
|
}) => {
|
|
20
20
|
local_time: string;
|
|
21
21
|
utm?: import('../../shared/dot-content-analytics.model').DotCMSUtmData | undefined;
|
|
22
|
-
page: import('analytics').
|
|
22
|
+
page: import('../../shared/dot-content-analytics.model').DotCMSPageData;
|
|
23
23
|
device: import('../../shared/dot-content-analytics.model').DotCMSDeviceData;
|
|
24
|
-
type: string;
|
|
25
|
-
properties: Record<string, unknown>;
|
|
26
24
|
event: string;
|
|
25
|
+
properties: Record<string, unknown>;
|
|
27
26
|
options: Record<string, unknown>;
|
|
28
|
-
context
|
|
27
|
+
context?: import('../../shared/dot-content-analytics.model').DotCMSAnalyticsContext | undefined;
|
|
29
28
|
};
|
|
30
29
|
/**
|
|
31
30
|
* TRACK EVENT ENRICHMENT - Runs after identity context injection
|
|
@@ -35,11 +34,11 @@ export declare const dotAnalyticsEnricherPlugin: () => {
|
|
|
35
34
|
payload: DotCMSAnalyticsPayload;
|
|
36
35
|
}) => {
|
|
37
36
|
events: {
|
|
38
|
-
event_type:
|
|
39
|
-
custom_event: string;
|
|
37
|
+
event_type: "track";
|
|
40
38
|
local_time: string;
|
|
41
|
-
|
|
39
|
+
data: {
|
|
42
40
|
src: string;
|
|
41
|
+
event: string;
|
|
43
42
|
};
|
|
44
43
|
}[];
|
|
45
44
|
};
|
|
@@ -1,27 +1,28 @@
|
|
|
1
|
-
import { ANALYTICS_SOURCE_TYPE as r } from "../../shared/dot-content-analytics.constants.js";
|
|
2
|
-
import { getLocalTime as
|
|
3
|
-
const
|
|
1
|
+
import { ANALYTICS_SOURCE_TYPE as n, EVENT_TYPES as r } from "../../shared/dot-content-analytics.constants.js";
|
|
2
|
+
import { getLocalTime as a, enrichPagePayloadOptimized as i } from "../../shared/dot-content-analytics.utils.js";
|
|
3
|
+
const l = () => ({
|
|
4
4
|
name: "enrich-dot-analytics",
|
|
5
5
|
/**
|
|
6
6
|
* PAGE VIEW ENRICHMENT - Runs after identity context injection
|
|
7
7
|
* Uses optimized enrichment that leverages analytics.js payload data
|
|
8
8
|
*/
|
|
9
|
-
"page:dot-analytics": ({ payload: e }) =>
|
|
9
|
+
"page:dot-analytics": ({ payload: e }) => i(e),
|
|
10
|
+
// TODO: Fix this when we haver the final design for the track event
|
|
10
11
|
/**
|
|
11
12
|
* TRACK EVENT ENRICHMENT - Runs after identity context injection
|
|
12
13
|
* Creates structured track events with pre-injected context
|
|
13
14
|
*/
|
|
14
15
|
"track:dot-analytics": ({ payload: e }) => {
|
|
15
|
-
const t =
|
|
16
|
+
const t = a();
|
|
16
17
|
return {
|
|
17
18
|
events: [
|
|
18
19
|
{
|
|
19
|
-
event_type:
|
|
20
|
-
custom_event: e.event,
|
|
20
|
+
event_type: r.TRACK,
|
|
21
21
|
local_time: t,
|
|
22
|
-
|
|
22
|
+
data: {
|
|
23
|
+
event: e.event,
|
|
23
24
|
...e.properties,
|
|
24
|
-
src:
|
|
25
|
+
src: n
|
|
25
26
|
}
|
|
26
27
|
}
|
|
27
28
|
]
|
|
@@ -29,5 +30,5 @@ const s = () => ({
|
|
|
29
30
|
}
|
|
30
31
|
});
|
|
31
32
|
export {
|
|
32
|
-
|
|
33
|
+
l as dotAnalyticsEnricherPlugin
|
|
33
34
|
};
|
|
@@ -1,7 +1,18 @@
|
|
|
1
1
|
export declare const ANALYTICS_WINDOWS_KEY = "dotAnalytics";
|
|
2
2
|
export declare const ANALYTICS_SOURCE_TYPE = "dotAnalytics";
|
|
3
3
|
export declare const ANALYTICS_ENDPOINT = "/api/v1/analytics/content/event";
|
|
4
|
-
|
|
4
|
+
/**
|
|
5
|
+
* Event Types
|
|
6
|
+
* Only two event types are supported in DotCMS Analytics
|
|
7
|
+
*/
|
|
8
|
+
export declare const EVENT_TYPES: {
|
|
9
|
+
readonly PAGEVIEW: "pageview";
|
|
10
|
+
readonly TRACK: "track";
|
|
11
|
+
};
|
|
12
|
+
/**
|
|
13
|
+
* Expected UTM parameter keys for campaign tracking
|
|
14
|
+
*/
|
|
15
|
+
export declare const EXPECTED_UTM_KEYS: readonly ["utm_source", "utm_medium", "utm_campaign", "utm_term", "utm_content", "utm_id"];
|
|
5
16
|
/**
|
|
6
17
|
* Session configuration constants
|
|
7
18
|
*/
|
|
@@ -19,4 +30,4 @@ export declare const USER_ID_KEY = "dot_analytics_user_id";
|
|
|
19
30
|
* - click: Detects real user interaction with minimal performance impact
|
|
20
31
|
* - visibilitychange: Handled separately to detect tab changes
|
|
21
32
|
*/
|
|
22
|
-
export declare const ACTIVITY_EVENTS:
|
|
33
|
+
export declare const ACTIVITY_EVENTS: readonly ["click"];
|
|
@@ -1,10 +1,14 @@
|
|
|
1
|
-
const
|
|
1
|
+
const t = "dotAnalytics", E = t, _ = "/api/v1/analytics/content/event", c = {
|
|
2
|
+
PAGEVIEW: "pageview",
|
|
3
|
+
TRACK: "track"
|
|
4
|
+
}, n = 30, s = "dot_analytics_session_id", S = "dot_analytics_user_id", T = ["click"];
|
|
2
5
|
export {
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
6
|
+
T as ACTIVITY_EVENTS,
|
|
7
|
+
_ as ANALYTICS_ENDPOINT,
|
|
8
|
+
E as ANALYTICS_SOURCE_TYPE,
|
|
9
|
+
t as ANALYTICS_WINDOWS_KEY,
|
|
10
|
+
n as DEFAULT_SESSION_TIMEOUT_MINUTES,
|
|
11
|
+
c as EVENT_TYPES,
|
|
12
|
+
s as SESSION_STORAGE_KEY,
|
|
9
13
|
S as USER_ID_KEY
|
|
10
14
|
};
|
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import { DotCMSAnalyticsConfig,
|
|
1
|
+
import { DotCMSAnalyticsConfig, DotCMSAnalyticsRequestBody } from './dot-content-analytics.model';
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
4
|
* Send an analytics event to the server
|
|
5
|
-
* @param
|
|
6
|
-
* @param
|
|
7
|
-
* @returns A promise that resolves
|
|
5
|
+
* @param payload - The event payload data
|
|
6
|
+
* @param config - The analytics configuration
|
|
7
|
+
* @returns A promise that resolves when the request is complete
|
|
8
8
|
*/
|
|
9
|
-
export declare const sendAnalyticsEventToServer: (payload:
|
|
9
|
+
export declare const sendAnalyticsEventToServer: (payload: DotCMSAnalyticsRequestBody, config: DotCMSAnalyticsConfig) => Promise<void>;
|
|
@@ -1,16 +1,29 @@
|
|
|
1
|
-
import { ANALYTICS_ENDPOINT as
|
|
2
|
-
const
|
|
1
|
+
import { ANALYTICS_ENDPOINT as a } from "./dot-content-analytics.constants.js";
|
|
2
|
+
const i = async (o, t) => {
|
|
3
3
|
try {
|
|
4
|
-
const e = await fetch(`${t.server}${
|
|
4
|
+
const e = await fetch(`${t.server}${a}`, {
|
|
5
5
|
method: "POST",
|
|
6
6
|
headers: { "Content-Type": "application/json" },
|
|
7
|
-
body: JSON.stringify(
|
|
7
|
+
body: JSON.stringify(o)
|
|
8
8
|
});
|
|
9
|
-
e.ok
|
|
9
|
+
if (!e.ok) {
|
|
10
|
+
const n = e.statusText || "Unknown Error", s = `HTTP ${e.status}: ${n}`;
|
|
11
|
+
try {
|
|
12
|
+
const r = await e.json();
|
|
13
|
+
r.message ? console.warn(`DotAnalytics: ${r.message} (${s})`) : console.warn(
|
|
14
|
+
`DotAnalytics: ${s} - No error message in response`
|
|
15
|
+
);
|
|
16
|
+
} catch (r) {
|
|
17
|
+
console.warn(
|
|
18
|
+
`DotAnalytics: ${s} - Failed to parse error response:`,
|
|
19
|
+
r
|
|
20
|
+
);
|
|
21
|
+
}
|
|
22
|
+
}
|
|
10
23
|
} catch (e) {
|
|
11
24
|
console.error("DotAnalytics: Error sending event:", e);
|
|
12
25
|
}
|
|
13
26
|
};
|
|
14
27
|
export {
|
|
15
|
-
|
|
28
|
+
i as sendAnalyticsEventToServer
|
|
16
29
|
};
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { EVENT_TYPES } from './dot-content-analytics.constants';
|
|
2
|
+
|
|
1
3
|
declare global {
|
|
2
4
|
interface Window {
|
|
3
5
|
__dotAnalyticsCleanup?: () => void;
|
|
@@ -32,21 +34,49 @@ export interface DotCMSAnalyticsConfig {
|
|
|
32
34
|
redirectFn?: (url: string) => void;
|
|
33
35
|
}
|
|
34
36
|
/**
|
|
35
|
-
*
|
|
36
|
-
*
|
|
37
|
+
* Supported event types in DotCMS Analytics.
|
|
38
|
+
* Only two event types are supported: pageview and track.
|
|
37
39
|
*/
|
|
38
|
-
export
|
|
40
|
+
export type DotCMSEventType = (typeof EVENT_TYPES)[keyof typeof EVENT_TYPES];
|
|
41
|
+
/**
|
|
42
|
+
* Base structure for all analytics events.
|
|
43
|
+
* All events share this common structure.
|
|
44
|
+
*/
|
|
45
|
+
export interface DotCMSEventBase {
|
|
39
46
|
/** The type of event being tracked */
|
|
40
|
-
event_type:
|
|
41
|
-
/** Page data associated with the event */
|
|
42
|
-
page: DotCMSPageData;
|
|
43
|
-
/** Device and browser information */
|
|
44
|
-
device: DotCMSDeviceData;
|
|
45
|
-
/** UTM parameters for campaign tracking */
|
|
46
|
-
utm?: DotCMSUtmData;
|
|
47
|
+
event_type: DotCMSEventType;
|
|
47
48
|
/** Local timestamp when the event occurred */
|
|
48
49
|
local_time: string;
|
|
49
50
|
}
|
|
51
|
+
/**
|
|
52
|
+
* Pageview-specific analytics event structure.
|
|
53
|
+
* Contains data specific to page view tracking.
|
|
54
|
+
*/
|
|
55
|
+
export interface DotCMSPageViewEvent extends DotCMSEventBase {
|
|
56
|
+
event_type: 'pageview';
|
|
57
|
+
/** Pageview-specific event data with structured format */
|
|
58
|
+
data: {
|
|
59
|
+
/** Page data associated with the event */
|
|
60
|
+
page: DotCMSPageData;
|
|
61
|
+
/** Device and browser information */
|
|
62
|
+
device: DotCMSDeviceData;
|
|
63
|
+
/** UTM parameters for campaign tracking (optional) */
|
|
64
|
+
utm?: DotCMSUtmData;
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* Track-specific analytics event structure.
|
|
69
|
+
* Contains data specific to custom event tracking.
|
|
70
|
+
*/
|
|
71
|
+
export interface DotCMSTrackEvent extends DotCMSEventBase {
|
|
72
|
+
event_type: 'track';
|
|
73
|
+
/** Track-specific event data with flexible structure */
|
|
74
|
+
data: Record<string, unknown>;
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* Union type for all possible analytics events.
|
|
78
|
+
*/
|
|
79
|
+
export type DotCMSEvent = DotCMSPageViewEvent | DotCMSTrackEvent;
|
|
50
80
|
/**
|
|
51
81
|
* Analytics request body for page view events in DotCMS.
|
|
52
82
|
* Structure sent to the DotCMS analytics server for page tracking.
|
|
@@ -54,8 +84,8 @@ export interface DotCMSAnalyticsEvent {
|
|
|
54
84
|
export interface DotCMSPageViewRequestBody {
|
|
55
85
|
/** Context information shared across all events */
|
|
56
86
|
context: DotCMSAnalyticsContext;
|
|
57
|
-
/** Array of analytics events to be tracked */
|
|
58
|
-
events:
|
|
87
|
+
/** Array of pageview analytics events to be tracked */
|
|
88
|
+
events: DotCMSPageViewEvent[];
|
|
59
89
|
}
|
|
60
90
|
/**
|
|
61
91
|
* Analytics request body for track events in DotCMS.
|
|
@@ -64,8 +94,22 @@ export interface DotCMSPageViewRequestBody {
|
|
|
64
94
|
export interface DotCMSTrackRequestBody {
|
|
65
95
|
/** Context information shared across all events */
|
|
66
96
|
context: DotCMSAnalyticsContext;
|
|
67
|
-
/** Array of analytics events to be tracked */
|
|
68
|
-
events
|
|
97
|
+
/** Array of track analytics events to be tracked */
|
|
98
|
+
events: DotCMSTrackEvent[];
|
|
99
|
+
}
|
|
100
|
+
/**
|
|
101
|
+
* Union type for all possible request bodies.
|
|
102
|
+
*/
|
|
103
|
+
export type DotCMSAnalyticsRequestBody = DotCMSPageViewRequestBody | DotCMSTrackRequestBody;
|
|
104
|
+
/**
|
|
105
|
+
* Enriched payload structure returned by the enricher plugin.
|
|
106
|
+
* Contains pre-structured events and context for direct use in analytics requests.
|
|
107
|
+
*/
|
|
108
|
+
export interface DotCMSEnrichedPayload {
|
|
109
|
+
/** Analytics context shared across events */
|
|
110
|
+
context: DotCMSAnalyticsContext;
|
|
111
|
+
/** Array of pre-structured analytics events */
|
|
112
|
+
events: DotCMSEvent[];
|
|
69
113
|
}
|
|
70
114
|
/**
|
|
71
115
|
* Browser event data collected from the user's session in DotCMS.
|
|
@@ -108,22 +152,22 @@ export interface DotCMSBrowserEventData {
|
|
|
108
152
|
* The payload structure for DotCMS analytics events.
|
|
109
153
|
* This interface represents the complete data structure that flows through
|
|
110
154
|
* the analytics pipeline, including original event data and enriched context.
|
|
155
|
+
*
|
|
156
|
+
* This is the internal payload used by Analytics.js and our plugins.
|
|
111
157
|
*/
|
|
112
158
|
export interface DotCMSAnalyticsPayload {
|
|
113
|
-
/** The type of analytics event */
|
|
114
|
-
type: string;
|
|
115
|
-
/** Additional properties associated with the event */
|
|
116
|
-
properties: Record<string, unknown>;
|
|
117
159
|
/** The event name or identifier */
|
|
118
160
|
event: string;
|
|
161
|
+
/** Additional properties associated with the event */
|
|
162
|
+
properties: Record<string, unknown>;
|
|
119
163
|
/** Configuration options for the event */
|
|
120
164
|
options: Record<string, unknown>;
|
|
121
165
|
/** Analytics context shared across events */
|
|
122
|
-
context
|
|
166
|
+
context?: DotCMSAnalyticsContext;
|
|
123
167
|
/** Page data for the current page */
|
|
124
|
-
page
|
|
168
|
+
page?: DotCMSPageData;
|
|
125
169
|
/** Device and browser information */
|
|
126
|
-
device
|
|
170
|
+
device?: DotCMSDeviceData;
|
|
127
171
|
/** UTM parameters for campaign tracking */
|
|
128
172
|
utm?: DotCMSUtmData;
|
|
129
173
|
/** Local timestamp when the event occurred */
|
|
@@ -146,9 +190,8 @@ export interface DotCMSAnalyticsParams {
|
|
|
146
190
|
export interface DotCMSAnalytics {
|
|
147
191
|
/**
|
|
148
192
|
* Track a page view event.
|
|
149
|
-
* @param payload - Optional additional data to include with the page view
|
|
150
193
|
*/
|
|
151
|
-
pageView: (
|
|
194
|
+
pageView: () => void;
|
|
152
195
|
/**
|
|
153
196
|
* Track a custom event.
|
|
154
197
|
* @param eventName - The name/type of the event to track
|
|
@@ -217,14 +260,12 @@ export interface DotCMSPageData {
|
|
|
217
260
|
doc_protocol: string | undefined;
|
|
218
261
|
/** Document search parameters */
|
|
219
262
|
doc_search: string;
|
|
220
|
-
/**
|
|
221
|
-
|
|
222
|
-
/**
|
|
223
|
-
|
|
263
|
+
/** Document host domain */
|
|
264
|
+
doc_host: string | undefined;
|
|
265
|
+
/** Document path */
|
|
266
|
+
doc_path: string | undefined;
|
|
224
267
|
/** Page title */
|
|
225
268
|
title: string | undefined;
|
|
226
|
-
/** User agent string */
|
|
227
|
-
user_agent?: string;
|
|
228
269
|
/** Language identifier */
|
|
229
270
|
language_id?: string;
|
|
230
271
|
/** Persona identifier */
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { DotCMSAnalyticsConfig, DotCMSAnalyticsContext, DotCMSAnalyticsPayload, DotCMSBrowserEventData, DotCMSDeviceData, DotCMSUtmData } from './dot-content-analytics.model';
|
|
1
|
+
import { DotCMSAnalyticsConfig, DotCMSAnalyticsContext, DotCMSAnalyticsPayload, DotCMSBrowserEventData, DotCMSDeviceData, DotCMSPageData, DotCMSUtmData } from './dot-content-analytics.model';
|
|
2
2
|
import { PageData } from 'analytics';
|
|
3
3
|
|
|
4
4
|
export { cleanupActivityTracking, getLastActivity, getSessionInfo, initializeActivityTracking, isUserInactive, updateSessionActivity } from './dot-content-analytics.activity-tracker';
|
|
@@ -52,11 +52,7 @@ export declare const extractUTMParameters: (location: Location) => Record<string
|
|
|
52
52
|
*/
|
|
53
53
|
export declare const defaultRedirectFn: (href: string) => string;
|
|
54
54
|
/**
|
|
55
|
-
*
|
|
56
|
-
*/
|
|
57
|
-
export declare const isInsideEditor: () => boolean;
|
|
58
|
-
/**
|
|
59
|
-
* Gets local time in ISO format
|
|
55
|
+
* Gets local time in ISO format without milliseconds
|
|
60
56
|
*/
|
|
61
57
|
export declare const getLocalTime: () => string;
|
|
62
58
|
/**
|
|
@@ -83,13 +79,12 @@ export declare const enrichWithUtmData: (payload: DotCMSAnalyticsPayload) => Dot
|
|
|
83
79
|
export declare const enrichPagePayloadOptimized: (payload: DotCMSAnalyticsPayload, location?: Location) => {
|
|
84
80
|
local_time: string;
|
|
85
81
|
utm?: DotCMSUtmData | undefined;
|
|
86
|
-
page:
|
|
82
|
+
page: DotCMSPageData;
|
|
87
83
|
device: DotCMSDeviceData;
|
|
88
|
-
type: string;
|
|
89
|
-
properties: Record<string, unknown>;
|
|
90
84
|
event: string;
|
|
85
|
+
properties: Record<string, unknown>;
|
|
91
86
|
options: Record<string, unknown>;
|
|
92
|
-
context
|
|
87
|
+
context?: DotCMSAnalyticsContext | undefined;
|
|
93
88
|
};
|
|
94
89
|
/**
|
|
95
90
|
* @deprecated Use enrichPagePayloadOptimized instead to avoid data duplication
|
|
@@ -102,10 +97,9 @@ export declare const enrichPagePayload: (payload: DotCMSAnalyticsPayload, locati
|
|
|
102
97
|
utm?: DotCMSUtmData | undefined;
|
|
103
98
|
page: PageData;
|
|
104
99
|
device: DotCMSDeviceData;
|
|
105
|
-
type: string;
|
|
106
|
-
properties: Record<string, unknown>;
|
|
107
100
|
event: string;
|
|
101
|
+
properties: Record<string, unknown>;
|
|
108
102
|
options: Record<string, unknown>;
|
|
109
|
-
context
|
|
103
|
+
context?: DotCMSAnalyticsContext | undefined;
|
|
110
104
|
};
|
|
111
105
|
};
|
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import { SESSION_STORAGE_KEY as
|
|
2
|
-
let
|
|
1
|
+
import { SESSION_STORAGE_KEY as h, DEFAULT_SESSION_TIMEOUT_MINUTES as S, USER_ID_KEY as m } from "./dot-content-analytics.constants.js";
|
|
2
|
+
let d = null;
|
|
3
3
|
const u = (t) => {
|
|
4
4
|
const e = Date.now(), n = Math.random().toString(36).substr(2, 9), o = Math.random().toString(36).substr(2, 9);
|
|
5
5
|
return `${t}_${e}_${n}${o}`;
|
|
6
|
-
},
|
|
6
|
+
}, l = {
|
|
7
7
|
getItem: (t) => {
|
|
8
8
|
try {
|
|
9
9
|
return localStorage.getItem(t);
|
|
@@ -19,9 +19,9 @@ const u = (t) => {
|
|
|
19
19
|
}
|
|
20
20
|
}
|
|
21
21
|
}, f = () => {
|
|
22
|
-
let t =
|
|
23
|
-
return t || (t = u("user"),
|
|
24
|
-
},
|
|
22
|
+
let t = l.getItem(m);
|
|
23
|
+
return t || (t = u("user"), l.setItem(m, t)), t;
|
|
24
|
+
}, _ = (t) => {
|
|
25
25
|
const e = new Date(t), n = /* @__PURE__ */ new Date(), o = new Date(
|
|
26
26
|
e.getUTCFullYear(),
|
|
27
27
|
e.getUTCMonth(),
|
|
@@ -33,15 +33,15 @@ const u = (t) => {
|
|
|
33
33
|
if (typeof window > "u")
|
|
34
34
|
return u("session_fallback");
|
|
35
35
|
try {
|
|
36
|
-
const e = sessionStorage.getItem(
|
|
36
|
+
const e = sessionStorage.getItem(h);
|
|
37
37
|
if (e) {
|
|
38
|
-
const { sessionId: s, startTime:
|
|
39
|
-
if (
|
|
38
|
+
const { sessionId: s, startTime: c, lastActivity: i } = JSON.parse(e), g = !_(c), a = t - i < S * 60 * 1e3;
|
|
39
|
+
if (g && a)
|
|
40
40
|
return sessionStorage.setItem(
|
|
41
|
-
|
|
41
|
+
h,
|
|
42
42
|
JSON.stringify({
|
|
43
43
|
sessionId: s,
|
|
44
|
-
startTime:
|
|
44
|
+
startTime: c,
|
|
45
45
|
lastActivity: t
|
|
46
46
|
})
|
|
47
47
|
), s;
|
|
@@ -51,7 +51,7 @@ const u = (t) => {
|
|
|
51
51
|
startTime: t,
|
|
52
52
|
lastActivity: t
|
|
53
53
|
};
|
|
54
|
-
return sessionStorage.setItem(
|
|
54
|
+
return sessionStorage.setItem(h, JSON.stringify(o)), n;
|
|
55
55
|
} catch {
|
|
56
56
|
return u("session_fallback");
|
|
57
57
|
}
|
|
@@ -65,67 +65,59 @@ const u = (t) => {
|
|
|
65
65
|
session_id: e,
|
|
66
66
|
user_id: n
|
|
67
67
|
};
|
|
68
|
-
},
|
|
68
|
+
}, w = () => d || (d = {
|
|
69
69
|
user_language: navigator.language || void 0,
|
|
70
70
|
doc_encoding: document.characterSet || document.charset || void 0,
|
|
71
71
|
screen_resolution: typeof screen < "u" && screen.width && screen.height ? `${screen.width}x${screen.height}` : void 0
|
|
72
|
-
},
|
|
73
|
-
if (typeof window > "u")
|
|
74
|
-
return !1;
|
|
75
|
-
try {
|
|
76
|
-
const t = window.self !== window.top, e = window.location.href.includes("mode=EDIT_MODE"), n = window.location.href.includes("/vtl/");
|
|
77
|
-
return t || e || n;
|
|
78
|
-
} catch {
|
|
79
|
-
return !1;
|
|
80
|
-
}
|
|
81
|
-
}, D = () => {
|
|
72
|
+
}, d), I = () => {
|
|
82
73
|
try {
|
|
83
74
|
const t = (/* @__PURE__ */ new Date()).getTimezoneOffset(), e = t > 0 ? "-" : "+", n = Math.abs(t), o = Math.floor(n / 60), s = n % 60;
|
|
84
75
|
return `${e}${o.toString().padStart(2, "0")}:${s.toString().padStart(2, "0")}`;
|
|
85
76
|
} catch {
|
|
86
77
|
return "+00:00";
|
|
87
78
|
}
|
|
88
|
-
},
|
|
79
|
+
}, D = () => {
|
|
89
80
|
try {
|
|
90
|
-
const t = /* @__PURE__ */ new Date(), e =
|
|
91
|
-
return `${n}-${o}-${s}T${
|
|
81
|
+
const t = /* @__PURE__ */ new Date(), e = I(), n = t.getFullYear(), o = (t.getMonth() + 1).toString().padStart(2, "0"), s = t.getDate().toString().padStart(2, "0"), c = t.getHours().toString().padStart(2, "0"), i = t.getMinutes().toString().padStart(2, "0"), g = t.getSeconds().toString().padStart(2, "0");
|
|
82
|
+
return `${n}-${o}-${s}T${c}:${i}:${g}${e}`;
|
|
92
83
|
} catch {
|
|
93
84
|
return (/* @__PURE__ */ new Date()).toISOString();
|
|
94
85
|
}
|
|
95
|
-
},
|
|
96
|
-
const n =
|
|
86
|
+
}, $ = (t, e = typeof window < "u" ? window.location : {}) => {
|
|
87
|
+
const n = D(), o = w(), { properties: s } = t, { utm: c } = s, i = {
|
|
97
88
|
url: s.url ?? e.href,
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
89
|
+
doc_encoding: o.doc_encoding,
|
|
90
|
+
doc_hash: s.hash ?? e.hash ?? "",
|
|
91
|
+
doc_protocol: e.protocol,
|
|
92
|
+
doc_search: s.search ?? e.search ?? "",
|
|
93
|
+
doc_host: e.hostname,
|
|
94
|
+
doc_path: s.path ?? e.pathname,
|
|
101
95
|
title: s.title ?? (document == null ? void 0 : document.title),
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
}, d = {
|
|
96
|
+
language_id: void 0,
|
|
97
|
+
persona: void 0
|
|
98
|
+
}, g = {
|
|
106
99
|
screen_resolution: o.screen_resolution,
|
|
107
100
|
language: o.user_language,
|
|
108
101
|
viewport_width: String(s.width),
|
|
109
102
|
viewport_height: String(s.height)
|
|
110
|
-
},
|
|
111
|
-
if (
|
|
112
|
-
const
|
|
113
|
-
|
|
103
|
+
}, a = {};
|
|
104
|
+
if (c && typeof c == "object") {
|
|
105
|
+
const r = c;
|
|
106
|
+
r.medium && (a.medium = r.medium), r.source && (a.source = r.source), r.campaign && (a.campaign = r.campaign), r.term && (a.term = r.term), r.content && (a.content = r.content);
|
|
114
107
|
}
|
|
115
108
|
return {
|
|
116
109
|
...t,
|
|
117
|
-
page:
|
|
118
|
-
device:
|
|
119
|
-
...Object.keys(
|
|
110
|
+
page: i,
|
|
111
|
+
device: g,
|
|
112
|
+
...Object.keys(a).length > 0 && { utm: a },
|
|
120
113
|
local_time: n
|
|
121
114
|
};
|
|
122
115
|
};
|
|
123
116
|
export {
|
|
124
|
-
|
|
117
|
+
$ as enrichPagePayloadOptimized,
|
|
125
118
|
u as generateSecureId,
|
|
126
119
|
T as getAnalyticsContext,
|
|
127
|
-
|
|
120
|
+
D as getLocalTime,
|
|
128
121
|
p as getSessionId,
|
|
129
|
-
f as getUserId
|
|
130
|
-
$ as isInsideEditor
|
|
122
|
+
f as getUserId
|
|
131
123
|
};
|
|
@@ -1,34 +1,32 @@
|
|
|
1
|
-
import { useContext as c, useRef as u, useCallback as
|
|
2
|
-
import {
|
|
1
|
+
import { useContext as c, useRef as u, useCallback as a } from "react";
|
|
2
|
+
import { getUVEState as i } from "../../../uve/src/lib/core/core.utils.js";
|
|
3
|
+
import "../../../uve/src/internal/constants.js";
|
|
3
4
|
import l from "../contexts/DotContentAnalyticsContext.js";
|
|
4
|
-
const
|
|
5
|
-
const t = c(l),
|
|
5
|
+
const S = () => {
|
|
6
|
+
const t = c(l), n = u(null);
|
|
6
7
|
if (!t)
|
|
7
8
|
throw new Error(
|
|
8
9
|
"useContentAnalytics must be used within a DotContentAnalyticsProvider and analytics must be successfully initialized"
|
|
9
10
|
);
|
|
10
|
-
const
|
|
11
|
-
(
|
|
12
|
-
|
|
13
|
-
...
|
|
11
|
+
const s = a(
|
|
12
|
+
(o, e = {}) => {
|
|
13
|
+
i() || t.track(o, {
|
|
14
|
+
...e,
|
|
14
15
|
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
15
16
|
});
|
|
16
17
|
},
|
|
17
18
|
[t]
|
|
18
|
-
),
|
|
19
|
-
(
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
},
|
|
25
|
-
[t]
|
|
26
|
-
);
|
|
19
|
+
), r = a(() => {
|
|
20
|
+
if (!i()) {
|
|
21
|
+
const e = window.location.pathname;
|
|
22
|
+
e !== n.current && (n.current = e, t.pageView());
|
|
23
|
+
}
|
|
24
|
+
}, [t]);
|
|
27
25
|
return {
|
|
28
|
-
track:
|
|
29
|
-
pageView:
|
|
26
|
+
track: s,
|
|
27
|
+
pageView: r
|
|
30
28
|
};
|
|
31
29
|
};
|
|
32
30
|
export {
|
|
33
|
-
|
|
31
|
+
S as useContentAnalytics
|
|
34
32
|
};
|
|
@@ -1,13 +1,14 @@
|
|
|
1
|
-
import { useRef as
|
|
2
|
-
import {
|
|
3
|
-
|
|
4
|
-
|
|
1
|
+
import { useRef as u, useEffect as i } from "react";
|
|
2
|
+
import { getUVEState as a } from "../../../uve/src/lib/core/core.utils.js";
|
|
3
|
+
import "../../../uve/src/internal/constants.js";
|
|
4
|
+
function p(t) {
|
|
5
|
+
const n = u(null);
|
|
5
6
|
i(() => {
|
|
6
7
|
if (!t)
|
|
7
8
|
return;
|
|
8
9
|
function e() {
|
|
9
|
-
const o = window.location.pathname;
|
|
10
|
-
o !== n.current && !
|
|
10
|
+
const o = window.location.pathname, r = a();
|
|
11
|
+
o !== n.current && !r && t && (n.current = o, t.pageView());
|
|
11
12
|
}
|
|
12
13
|
return e(), window.addEventListener("popstate", e), window.addEventListener("beforeunload", e), () => {
|
|
13
14
|
window.removeEventListener("popstate", e), window.removeEventListener("beforeunload", e);
|
|
@@ -15,5 +16,5 @@ function s(t) {
|
|
|
15
16
|
}, [t]);
|
|
16
17
|
}
|
|
17
18
|
export {
|
|
18
|
-
|
|
19
|
+
p as useRouterTracker
|
|
19
20
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@dotcms/analytics",
|
|
3
|
-
"version": "0.0.1-beta.
|
|
3
|
+
"version": "0.0.1-beta.45",
|
|
4
4
|
"description": "Official JavaScript library for Content Analytics with DotCMS.",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -23,7 +23,8 @@
|
|
|
23
23
|
"analytics": "^0.8.14"
|
|
24
24
|
},
|
|
25
25
|
"peerDependencies": {
|
|
26
|
-
"react": "19.1.0"
|
|
26
|
+
"react": "19.1.0",
|
|
27
|
+
"@dotcms/uve": "0.0.1-beta.45"
|
|
27
28
|
},
|
|
28
29
|
"devDependencies": {
|
|
29
30
|
"@testing-library/jest-dom": "^6.1.6",
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
var N = /* @__PURE__ */ ((r) => (r.EDIT = "EDIT_MODE", r.PREVIEW = "PREVIEW_MODE", r.LIVE = "LIVE", r.UNKNOWN = "UNKNOWN", r))(N || {}), E = /* @__PURE__ */ ((r) => (r.CONTENT_CHANGES = "changes", r.PAGE_RELOAD = "page-reload", r.REQUEST_BOUNDS = "request-bounds", r.IFRAME_SCROLL = "iframe-scroll", r.CONTENTLET_HOVERED = "contentlet-hovered", r))(E || {});
|
|
2
|
+
export {
|
|
3
|
+
E as UVEEventType,
|
|
4
|
+
N as UVE_MODE
|
|
5
|
+
};
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
var t = /* @__PURE__ */ ((e) => (e.UVE_RELOAD_PAGE = "uve-reload-page", e.UVE_REQUEST_BOUNDS = "uve-request-bounds", e.UVE_EDITOR_PONG = "uve-editor-pong", e.UVE_SCROLL_INSIDE_IFRAME = "uve-scroll-inside-iframe", e.UVE_SET_PAGE_DATA = "uve-set-page-data", e.UVE_COPY_CONTENTLET_INLINE_EDITING_SUCCESS = "uve-copy-contentlet-inline-editing-success", e))(t || {});
|
|
2
|
+
export {
|
|
3
|
+
t as __DOTCMS_UVE_EVENT__
|
|
4
|
+
};
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
import { UVEEventType as n } from "../../../types/src/lib/editor/public.js";
|
|
2
|
+
import { onContentletHovered as r, onIframeScroll as o, onRequestBounds as t, onPageReload as E, onContentChanges as u } from "./events.js";
|
|
3
|
+
n.CONTENT_CHANGES + "", n.PAGE_RELOAD + "", n.REQUEST_BOUNDS + "", n.IFRAME_SCROLL + "", n.CONTENTLET_HOVERED + "";
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
import { UVEEventType as a } from "../../../types/src/lib/editor/public.js";
|
|
2
|
+
import { __DOTCMS_UVE_EVENT__ as s } from "../../../types/src/lib/events/internal.js";
|
|
3
|
+
import { findDotCMSElement as M, findDotCMSVTLData as p, getClosestDotCMSContainerData as w, getDotCMSPageBounds as S } from "../lib/dom/dom.utils.js";
|
|
4
|
+
function U(o) {
|
|
5
|
+
const t = (n) => {
|
|
6
|
+
n.data.name === s.UVE_SET_PAGE_DATA && o(n.data.payload);
|
|
7
|
+
};
|
|
8
|
+
return window.addEventListener("message", t), {
|
|
9
|
+
unsubscribe: () => {
|
|
10
|
+
window.removeEventListener("message", t);
|
|
11
|
+
},
|
|
12
|
+
event: a.CONTENT_CHANGES
|
|
13
|
+
};
|
|
14
|
+
}
|
|
15
|
+
function V(o) {
|
|
16
|
+
const t = (n) => {
|
|
17
|
+
n.data.name === s.UVE_RELOAD_PAGE && o();
|
|
18
|
+
};
|
|
19
|
+
return window.addEventListener("message", t), {
|
|
20
|
+
unsubscribe: () => {
|
|
21
|
+
window.removeEventListener("message", t);
|
|
22
|
+
},
|
|
23
|
+
event: a.PAGE_RELOAD
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
function I(o) {
|
|
27
|
+
const t = (n) => {
|
|
28
|
+
if (n.data.name === s.UVE_REQUEST_BOUNDS) {
|
|
29
|
+
const e = Array.from(
|
|
30
|
+
document.querySelectorAll('[data-dot-object="container"]')
|
|
31
|
+
), i = S(e);
|
|
32
|
+
o(i);
|
|
33
|
+
}
|
|
34
|
+
};
|
|
35
|
+
return window.addEventListener("message", t), {
|
|
36
|
+
unsubscribe: () => {
|
|
37
|
+
window.removeEventListener("message", t);
|
|
38
|
+
},
|
|
39
|
+
event: a.REQUEST_BOUNDS
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
function Y(o) {
|
|
43
|
+
const t = (n) => {
|
|
44
|
+
if (n.data.name === s.UVE_SCROLL_INSIDE_IFRAME) {
|
|
45
|
+
const e = n.data.direction;
|
|
46
|
+
o(e);
|
|
47
|
+
}
|
|
48
|
+
};
|
|
49
|
+
return window.addEventListener("message", t), {
|
|
50
|
+
unsubscribe: () => {
|
|
51
|
+
window.removeEventListener("message", t);
|
|
52
|
+
},
|
|
53
|
+
event: a.IFRAME_SCROLL
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
function B(o) {
|
|
57
|
+
const t = (n) => {
|
|
58
|
+
var d, r, E, c, T, l, m, _, u, C;
|
|
59
|
+
const e = M(n.target);
|
|
60
|
+
if (!e)
|
|
61
|
+
return;
|
|
62
|
+
const { x: i, y: g, width: v, height: N } = e.getBoundingClientRect(), f = ((d = e.dataset) == null ? void 0 : d.dotObject) === "container", L = {
|
|
63
|
+
identifier: "TEMP_EMPTY_CONTENTLET",
|
|
64
|
+
title: "TEMP_EMPTY_CONTENTLET",
|
|
65
|
+
contentType: "TEMP_EMPTY_CONTENTLET_TYPE",
|
|
66
|
+
inode: "TEMPY_EMPTY_CONTENTLET_INODE",
|
|
67
|
+
widgetTitle: "TEMP_EMPTY_CONTENTLET",
|
|
68
|
+
baseType: "TEMP_EMPTY_CONTENTLET",
|
|
69
|
+
onNumberOfPages: 1
|
|
70
|
+
}, P = {
|
|
71
|
+
identifier: (r = e.dataset) == null ? void 0 : r.dotIdentifier,
|
|
72
|
+
title: (E = e.dataset) == null ? void 0 : E.dotTitle,
|
|
73
|
+
inode: (c = e.dataset) == null ? void 0 : c.dotInode,
|
|
74
|
+
contentType: (T = e.dataset) == null ? void 0 : T.dotType,
|
|
75
|
+
baseType: (l = e.dataset) == null ? void 0 : l.dotBasetype,
|
|
76
|
+
widgetTitle: (m = e.dataset) == null ? void 0 : m.dotWidgetTitle,
|
|
77
|
+
onNumberOfPages: (_ = e.dataset) == null ? void 0 : _.dotOnNumberOfPages
|
|
78
|
+
}, O = p(e), b = {
|
|
79
|
+
container: (
|
|
80
|
+
// Here extract dot-container from contentlet if it is Headless
|
|
81
|
+
// or search in parent container if it is VTL
|
|
82
|
+
(u = e.dataset) != null && u.dotContainer ? JSON.parse((C = e.dataset) == null ? void 0 : C.dotContainer) : w(e)
|
|
83
|
+
),
|
|
84
|
+
contentlet: f ? L : P,
|
|
85
|
+
vtlFiles: O
|
|
86
|
+
};
|
|
87
|
+
o({
|
|
88
|
+
x: i,
|
|
89
|
+
y: g,
|
|
90
|
+
width: v,
|
|
91
|
+
height: N,
|
|
92
|
+
payload: b
|
|
93
|
+
});
|
|
94
|
+
};
|
|
95
|
+
return document.addEventListener("pointermove", t), {
|
|
96
|
+
unsubscribe: () => {
|
|
97
|
+
document.removeEventListener("pointermove", t);
|
|
98
|
+
},
|
|
99
|
+
event: a.CONTENTLET_HOVERED
|
|
100
|
+
};
|
|
101
|
+
}
|
|
102
|
+
export {
|
|
103
|
+
U as onContentChanges,
|
|
104
|
+
B as onContentletHovered,
|
|
105
|
+
Y as onIframeScroll,
|
|
106
|
+
V as onPageReload,
|
|
107
|
+
I as onRequestBounds
|
|
108
|
+
};
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { UVE_MODE as t } from "../../../../types/src/lib/editor/public.js";
|
|
2
|
+
import "../../internal/constants.js";
|
|
3
|
+
function p() {
|
|
4
|
+
if (typeof window > "u" || window.parent === window || !window.location)
|
|
5
|
+
return;
|
|
6
|
+
const e = new URL(window.location.href), o = Object.values(t);
|
|
7
|
+
let a = e.searchParams.get("mode") ?? t.EDIT;
|
|
8
|
+
const s = e.searchParams.get("language_id"), n = e.searchParams.get("personaId"), r = e.searchParams.get("variantName"), i = e.searchParams.get("experimentId"), c = e.searchParams.get("publishDate"), d = e.searchParams.get("dotCMSHost");
|
|
9
|
+
return o.includes(a) || (a = t.EDIT), {
|
|
10
|
+
mode: a,
|
|
11
|
+
languageId: s,
|
|
12
|
+
persona: n,
|
|
13
|
+
variantName: r,
|
|
14
|
+
experimentId: i,
|
|
15
|
+
publishDate: c,
|
|
16
|
+
dotCMSHost: d
|
|
17
|
+
};
|
|
18
|
+
}
|
|
19
|
+
export {
|
|
20
|
+
p as getUVEState
|
|
21
|
+
};
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import "../../internal/constants.js";
|
|
2
|
+
function C(t) {
|
|
3
|
+
return t.map((a) => {
|
|
4
|
+
const o = a.getBoundingClientRect(), n = Array.from(
|
|
5
|
+
a.querySelectorAll('[data-dot-object="contentlet"]')
|
|
6
|
+
);
|
|
7
|
+
return {
|
|
8
|
+
x: o.x,
|
|
9
|
+
y: o.y,
|
|
10
|
+
width: o.width,
|
|
11
|
+
height: o.height,
|
|
12
|
+
payload: JSON.stringify({
|
|
13
|
+
container: u(a)
|
|
14
|
+
}),
|
|
15
|
+
contentlets: f(o, n)
|
|
16
|
+
};
|
|
17
|
+
});
|
|
18
|
+
}
|
|
19
|
+
function f(t, a) {
|
|
20
|
+
return a.map((o) => {
|
|
21
|
+
var d, r, i, e, s, c;
|
|
22
|
+
const n = o.getBoundingClientRect();
|
|
23
|
+
return {
|
|
24
|
+
x: 0,
|
|
25
|
+
y: n.y - t.y,
|
|
26
|
+
width: n.width,
|
|
27
|
+
height: n.height,
|
|
28
|
+
payload: JSON.stringify({
|
|
29
|
+
container: (d = o.dataset) != null && d.dotContainer ? JSON.parse((r = o.dataset) == null ? void 0 : r.dotContainer) : y(o),
|
|
30
|
+
contentlet: {
|
|
31
|
+
identifier: (i = o.dataset) == null ? void 0 : i.dotIdentifier,
|
|
32
|
+
title: (e = o.dataset) == null ? void 0 : e.dotTitle,
|
|
33
|
+
inode: (s = o.dataset) == null ? void 0 : s.dotInode,
|
|
34
|
+
contentType: (c = o.dataset) == null ? void 0 : c.dotType
|
|
35
|
+
}
|
|
36
|
+
})
|
|
37
|
+
};
|
|
38
|
+
});
|
|
39
|
+
}
|
|
40
|
+
function u(t) {
|
|
41
|
+
var a, o, n, d;
|
|
42
|
+
return {
|
|
43
|
+
acceptTypes: ((a = t.dataset) == null ? void 0 : a.dotAcceptTypes) || "",
|
|
44
|
+
identifier: ((o = t.dataset) == null ? void 0 : o.dotIdentifier) || "",
|
|
45
|
+
maxContentlets: ((n = t.dataset) == null ? void 0 : n.maxContentlets) || "",
|
|
46
|
+
uuid: ((d = t.dataset) == null ? void 0 : d.dotUuid) || ""
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
function y(t) {
|
|
50
|
+
const a = t.closest('[data-dot-object="container"]');
|
|
51
|
+
return a ? u(a) : (console.warn("No container found for the contentlet"), null);
|
|
52
|
+
}
|
|
53
|
+
function g(t) {
|
|
54
|
+
var o, n, d;
|
|
55
|
+
if (!t)
|
|
56
|
+
return null;
|
|
57
|
+
const a = t.querySelector('[data-dot-object="empty-content"]');
|
|
58
|
+
return ((o = t == null ? void 0 : t.dataset) == null ? void 0 : o.dotObject) === "contentlet" || // The container inside Headless components have a span with the data-dot-object="container" attribute
|
|
59
|
+
((n = t == null ? void 0 : t.dataset) == null ? void 0 : n.dotObject) === "container" && a || // The container inside Traditional have no content inside
|
|
60
|
+
((d = t == null ? void 0 : t.dataset) == null ? void 0 : d.dotObject) === "container" && t.children.length === 0 ? t : g(t == null ? void 0 : t.parentElement);
|
|
61
|
+
}
|
|
62
|
+
function h(t) {
|
|
63
|
+
const a = t.querySelectorAll(
|
|
64
|
+
'[data-dot-object="vtl-file"]'
|
|
65
|
+
);
|
|
66
|
+
return a.length ? Array.from(a).map((o) => {
|
|
67
|
+
var n, d;
|
|
68
|
+
return {
|
|
69
|
+
inode: (n = o.dataset) == null ? void 0 : n.dotInode,
|
|
70
|
+
name: (d = o.dataset) == null ? void 0 : d.dotUrl
|
|
71
|
+
};
|
|
72
|
+
}) : null;
|
|
73
|
+
}
|
|
74
|
+
export {
|
|
75
|
+
g as findDotCMSElement,
|
|
76
|
+
h as findDotCMSVTLData,
|
|
77
|
+
y as getClosestDotCMSContainerData,
|
|
78
|
+
u as getDotCMSContainerData,
|
|
79
|
+
f as getDotCMSContentletsBound,
|
|
80
|
+
C as getDotCMSPageBounds
|
|
81
|
+
};
|