@dotcms/analytics 1.1.1-next.3 → 1.1.1-next.5
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 +65 -6
- package/lib/core/plugin/dot-analytics.plugin.d.ts +5 -4
- package/lib/core/plugin/dot-analytics.plugin.js +21 -26
- package/lib/core/plugin/enricher/dot-analytics.enricher.plugin.d.ts +2 -2
- package/lib/core/plugin/enricher/dot-analytics.enricher.plugin.js +14 -15
- package/lib/core/shared/constants/dot-content-analytics.constants.d.ts +16 -0
- package/lib/core/shared/constants/dot-content-analytics.constants.js +17 -11
- package/lib/core/shared/dot-content-analytics.http.d.ts +12 -3
- package/lib/core/shared/dot-content-analytics.http.js +22 -11
- package/lib/core/shared/dot-content-analytics.utils.d.ts +9 -1
- package/lib/core/shared/dot-content-analytics.utils.js +55 -51
- package/lib/core/shared/models/data.model.d.ts +2 -0
- package/lib/core/shared/models/event.model.d.ts +2 -4
- package/lib/core/shared/models/library.model.d.ts +19 -4
- package/lib/core/shared/queue/dot-analytics.queue.utils.d.ts +28 -0
- package/lib/core/shared/queue/dot-analytics.queue.utils.js +73 -0
- package/lib/core/shared/queue/index.d.ts +1 -0
- package/package.json +2 -1
package/README.md
CHANGED
|
@@ -115,6 +115,59 @@ track(eventName: string, properties?: Record<string, unknown>): void
|
|
|
115
115
|
| `server` | `string` | ✅ | - | Your dotCMS server URL |
|
|
116
116
|
| `debug` | `boolean` | ❌ | `false` | Enable verbose logging |
|
|
117
117
|
| `autoPageView` | `boolean` | ❌ | React: `true` / Standalone: `false` | Auto track page views on route changes |
|
|
118
|
+
| `queueConfig` | `QueueConfig` | ❌ | See below | Event batching configuration |
|
|
119
|
+
|
|
120
|
+
### Queue Configuration
|
|
121
|
+
|
|
122
|
+
The `queueConfig` option controls event batching:
|
|
123
|
+
|
|
124
|
+
- **`false`**: Disable queuing, send events immediately
|
|
125
|
+
- **`undefined` (default)**: Enable queuing with default settings
|
|
126
|
+
- **`QueueConfig` object**: Enable queuing with custom settings
|
|
127
|
+
|
|
128
|
+
| Option | Type | Default | Description |
|
|
129
|
+
| ---------------- | -------- | ------- | ------------------------------------------------ |
|
|
130
|
+
| `eventBatchSize` | `number` | `15` | Max events per batch - auto-sends when reached |
|
|
131
|
+
| `flushInterval` | `number` | `5000` | Time between flushes - sends pending events (ms) |
|
|
132
|
+
|
|
133
|
+
**How it works:**
|
|
134
|
+
|
|
135
|
+
- ✅ Send immediately when `eventBatchSize` reached (e.g., 15 events)
|
|
136
|
+
- ✅ Send pending events every `flushInterval` (e.g., 5 seconds)
|
|
137
|
+
- ✅ Auto-flush on page navigation/close using `visibilitychange` + `pagehide` events
|
|
138
|
+
- Example: If you have 10 events and 5 seconds pass → sends those 10
|
|
139
|
+
|
|
140
|
+
**About page unload handling:**
|
|
141
|
+
|
|
142
|
+
The SDK uses modern APIs (`visibilitychange` + `pagehide`) instead of `beforeunload`/`unload` to ensure:
|
|
143
|
+
|
|
144
|
+
- ✅ Better reliability on mobile devices
|
|
145
|
+
- ✅ Compatible with browser back/forward cache (bfcache)
|
|
146
|
+
- ✅ Events are sent via `navigator.sendBeacon()` for guaranteed delivery
|
|
147
|
+
- ✅ No negative impact on page performance
|
|
148
|
+
|
|
149
|
+
**Example: Disable queuing for immediate sends**
|
|
150
|
+
|
|
151
|
+
```javascript
|
|
152
|
+
const analytics = initializeContentAnalytics({
|
|
153
|
+
siteAuth: 'abc123',
|
|
154
|
+
server: 'https://your-dotcms.com',
|
|
155
|
+
queue: false // Send events immediately without batching
|
|
156
|
+
});
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
**Example: Custom queue config**
|
|
160
|
+
|
|
161
|
+
```javascript
|
|
162
|
+
const analytics = initializeContentAnalytics({
|
|
163
|
+
siteAuth: 'abc123',
|
|
164
|
+
server: 'https://your-dotcms.com',
|
|
165
|
+
queue: {
|
|
166
|
+
eventBatchSize: 10, // Auto-send when 10 events queued
|
|
167
|
+
flushInterval: 3000 // Or send every 3 seconds
|
|
168
|
+
}
|
|
169
|
+
});
|
|
170
|
+
```
|
|
118
171
|
|
|
119
172
|
## 🛠️ Usage Examples
|
|
120
173
|
|
|
@@ -262,6 +315,12 @@ When you call `pageView(customData?)`, the SDK **automatically enriches** the ev
|
|
|
262
315
|
site_key: string; // Your site key
|
|
263
316
|
session_id: string; // Current session ID
|
|
264
317
|
user_id: string; // Anonymous user ID
|
|
318
|
+
device: { // 🤖 AUTOMATIC - Device & Browser Info
|
|
319
|
+
screen_resolution: string; // Screen size
|
|
320
|
+
language: string; // Browser language
|
|
321
|
+
viewport_width: string; // Viewport width
|
|
322
|
+
viewport_height: string; // Viewport height
|
|
323
|
+
}
|
|
265
324
|
},
|
|
266
325
|
events: [{
|
|
267
326
|
event_type: "pageview",
|
|
@@ -278,12 +337,6 @@ When you call `pageView(customData?)`, the SDK **automatically enriches** the ev
|
|
|
278
337
|
doc_hash: string; // URL hash
|
|
279
338
|
doc_encoding: string; // Character encoding
|
|
280
339
|
},
|
|
281
|
-
device: { // 🤖 AUTOMATIC - Device & Browser Info
|
|
282
|
-
screen_resolution: string; // Screen size
|
|
283
|
-
language: string; // Browser language
|
|
284
|
-
viewport_width: string; // Viewport width
|
|
285
|
-
viewport_height: string; // Viewport height
|
|
286
|
-
},
|
|
287
340
|
utm?: { // 🤖 AUTOMATIC - Campaign Tracking (if present in URL)
|
|
288
341
|
source: string; // utm_source
|
|
289
342
|
medium: string; // utm_medium
|
|
@@ -319,6 +372,12 @@ When you call `track(eventName, properties)`, the following structure is sent:
|
|
|
319
372
|
site_key: string; // Your site key
|
|
320
373
|
session_id: string; // Current session ID
|
|
321
374
|
user_id: string; // Anonymous user ID
|
|
375
|
+
device: { // 🤖 AUTOMATIC - Device & Browser Info
|
|
376
|
+
screen_resolution: string; // Screen size
|
|
377
|
+
language: string; // Browser language
|
|
378
|
+
viewport_width: string; // Viewport width
|
|
379
|
+
viewport_height: string; // Viewport height
|
|
380
|
+
}
|
|
322
381
|
},
|
|
323
382
|
events: [{
|
|
324
383
|
event_type: string, // Your custom event name
|
|
@@ -3,28 +3,29 @@ import { DotCMSAnalyticsConfig, DotCMSAnalyticsParams } from '../shared/models';
|
|
|
3
3
|
* Analytics plugin for tracking page views and custom events in DotCMS applications.
|
|
4
4
|
* This plugin handles sending analytics data to the DotCMS server, managing initialization,
|
|
5
5
|
* and processing both automatic and manual tracking events.
|
|
6
|
+
* Supports optional queue management for batching events before sending.
|
|
6
7
|
*
|
|
7
8
|
* @param {DotCMSAnalyticsConfig} config - Configuration object containing API key, server URL,
|
|
8
|
-
* debug mode
|
|
9
|
+
* debug mode, auto page view settings, and queue config
|
|
9
10
|
* @returns {Object} Plugin object with methods for initialization and event tracking
|
|
10
11
|
*/
|
|
11
12
|
export declare const dotAnalytics: (config: DotCMSAnalyticsConfig) => {
|
|
12
13
|
name: string;
|
|
13
14
|
config: DotCMSAnalyticsConfig;
|
|
14
15
|
/**
|
|
15
|
-
* Initialize the plugin
|
|
16
|
+
* Initialize the plugin with optional queue management
|
|
16
17
|
*/
|
|
17
18
|
initialize: () => Promise<void>;
|
|
18
19
|
/**
|
|
19
20
|
* Track a page view event
|
|
20
21
|
* The enricher plugin has already built the complete request body
|
|
21
22
|
*/
|
|
22
|
-
page: (params: DotCMSAnalyticsParams) =>
|
|
23
|
+
page: (params: DotCMSAnalyticsParams) => void;
|
|
23
24
|
/**
|
|
24
25
|
* Track a custom event
|
|
25
26
|
* The enricher plugin has already built the complete request body
|
|
26
27
|
*/
|
|
27
|
-
track: (params: DotCMSAnalyticsParams) =>
|
|
28
|
+
track: (params: DotCMSAnalyticsParams) => void;
|
|
28
29
|
/**
|
|
29
30
|
* Check if the plugin is loaded
|
|
30
31
|
*/
|
|
@@ -1,41 +1,36 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
1
|
+
import { sendAnalyticsEvent as u } from "../shared/dot-content-analytics.http.js";
|
|
2
|
+
import { createAnalyticsQueue as d } from "../shared/queue/dot-analytics.queue.utils.js";
|
|
3
|
+
const v = (t) => {
|
|
3
4
|
let n = !1;
|
|
5
|
+
const i = t.queue !== !1;
|
|
6
|
+
let e = null;
|
|
7
|
+
const o = (r) => {
|
|
8
|
+
const { config: c, payload: l } = r;
|
|
9
|
+
if (!n)
|
|
10
|
+
throw new Error("DotCMS Analytics: Plugin not initialized");
|
|
11
|
+
const a = l.events[0], s = l.context;
|
|
12
|
+
i && e ? e.enqueue(a, s) : u({
|
|
13
|
+
context: s,
|
|
14
|
+
events: [a]
|
|
15
|
+
}, c);
|
|
16
|
+
};
|
|
4
17
|
return {
|
|
5
18
|
name: "dot-analytics",
|
|
6
|
-
config:
|
|
19
|
+
config: t,
|
|
7
20
|
/**
|
|
8
|
-
* Initialize the plugin
|
|
21
|
+
* Initialize the plugin with optional queue management
|
|
9
22
|
*/
|
|
10
|
-
initialize: () => (n = !0, Promise.resolve()),
|
|
23
|
+
initialize: () => (n = !0, i && (e = d(t), e.initialize()), Promise.resolve()),
|
|
11
24
|
/**
|
|
12
25
|
* Track a page view event
|
|
13
26
|
* The enricher plugin has already built the complete request body
|
|
14
27
|
*/
|
|
15
|
-
page:
|
|
16
|
-
const { config: o, payload: t } = e;
|
|
17
|
-
if (!n)
|
|
18
|
-
throw new Error("DotCMS Analytics: Plugin not initialized");
|
|
19
|
-
const i = {
|
|
20
|
-
context: t.context,
|
|
21
|
-
events: t.events
|
|
22
|
-
};
|
|
23
|
-
return r(i, o);
|
|
24
|
-
},
|
|
28
|
+
page: o,
|
|
25
29
|
/**
|
|
26
30
|
* Track a custom event
|
|
27
31
|
* The enricher plugin has already built the complete request body
|
|
28
32
|
*/
|
|
29
|
-
track:
|
|
30
|
-
const { config: o, payload: t } = e;
|
|
31
|
-
if (!n)
|
|
32
|
-
throw new Error("DotCMS Analytics: Plugin not initialized");
|
|
33
|
-
const i = {
|
|
34
|
-
context: t.context,
|
|
35
|
-
events: t.events
|
|
36
|
-
};
|
|
37
|
-
return r(i, o);
|
|
38
|
-
},
|
|
33
|
+
track: o,
|
|
39
34
|
/**
|
|
40
35
|
* Check if the plugin is loaded
|
|
41
36
|
*/
|
|
@@ -43,5 +38,5 @@ const s = (a) => {
|
|
|
43
38
|
};
|
|
44
39
|
};
|
|
45
40
|
export {
|
|
46
|
-
|
|
41
|
+
v as dotAnalytics
|
|
47
42
|
};
|
|
@@ -2,8 +2,8 @@ import { AnalyticsBasePayloadWithContext, AnalyticsTrackPayloadWithContext, DotC
|
|
|
2
2
|
/**
|
|
3
3
|
* Plugin that enriches the analytics payload data based on the event type.
|
|
4
4
|
* Uses Analytics.js lifecycle events to inject context before processing.
|
|
5
|
-
* The identity plugin runs FIRST to inject context: { session_id, site_auth, user_id }
|
|
6
|
-
* This enricher plugin runs SECOND to add page/
|
|
5
|
+
* The identity plugin runs FIRST to inject context: { session_id, site_auth, user_id, device }
|
|
6
|
+
* This enricher plugin runs SECOND to add page/utm/custom data.
|
|
7
7
|
*
|
|
8
8
|
* Returns the final request body structure ready to send to the server.
|
|
9
9
|
*/
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { DotCMSPredefinedEventType as
|
|
2
|
-
import { getLocalTime as
|
|
3
|
-
const
|
|
1
|
+
import { DotCMSPredefinedEventType as i } from "../../shared/constants/dot-content-analytics.constants.js";
|
|
2
|
+
import { getLocalTime as c, enrichPagePayloadOptimized as s } from "../../shared/dot-content-analytics.utils.js";
|
|
3
|
+
const m = () => ({
|
|
4
4
|
name: "enrich-dot-analytics",
|
|
5
5
|
/**
|
|
6
6
|
* PAGE VIEW ENRICHMENT - Runs after identity context injection
|
|
@@ -10,20 +10,19 @@ const p = () => ({
|
|
|
10
10
|
"page:dot-analytics": ({
|
|
11
11
|
payload: o
|
|
12
12
|
}) => {
|
|
13
|
-
const { context:
|
|
14
|
-
if (!t
|
|
15
|
-
throw new Error("DotCMS Analytics: Missing required page
|
|
13
|
+
const { context: a, page: t, utm: e, custom: n, local_time: r } = s(o);
|
|
14
|
+
if (!t)
|
|
15
|
+
throw new Error("DotCMS Analytics: Missing required page data");
|
|
16
16
|
return {
|
|
17
|
-
context:
|
|
17
|
+
context: a,
|
|
18
18
|
events: [
|
|
19
19
|
{
|
|
20
|
-
event_type:
|
|
21
|
-
local_time:
|
|
20
|
+
event_type: i.PAGEVIEW,
|
|
21
|
+
local_time: r,
|
|
22
22
|
data: {
|
|
23
23
|
page: t,
|
|
24
|
-
|
|
25
|
-
...n && {
|
|
26
|
-
...a && { custom: a }
|
|
24
|
+
...e && { utm: e },
|
|
25
|
+
...n && { custom: n }
|
|
27
26
|
}
|
|
28
27
|
}
|
|
29
28
|
]
|
|
@@ -37,12 +36,12 @@ const p = () => ({
|
|
|
37
36
|
"track:dot-analytics": ({
|
|
38
37
|
payload: o
|
|
39
38
|
}) => {
|
|
40
|
-
const { event:
|
|
39
|
+
const { event: a, properties: t, context: e } = o, n = c();
|
|
41
40
|
return {
|
|
42
41
|
context: e,
|
|
43
42
|
events: [
|
|
44
43
|
{
|
|
45
|
-
event_type:
|
|
44
|
+
event_type: a,
|
|
46
45
|
local_time: n,
|
|
47
46
|
data: {
|
|
48
47
|
custom: t
|
|
@@ -53,5 +52,5 @@ const p = () => ({
|
|
|
53
52
|
}
|
|
54
53
|
});
|
|
55
54
|
export {
|
|
56
|
-
|
|
55
|
+
m as dotAnalyticsEnricherPlugin
|
|
57
56
|
};
|
|
@@ -29,8 +29,17 @@ export declare const EXPECTED_UTM_KEYS: readonly ["utm_source", "utm_medium", "u
|
|
|
29
29
|
* Session configuration constants
|
|
30
30
|
*/
|
|
31
31
|
export declare const DEFAULT_SESSION_TIMEOUT_MINUTES = 30;
|
|
32
|
+
/**
|
|
33
|
+
* Session storage key for session ID
|
|
34
|
+
*/
|
|
32
35
|
export declare const SESSION_STORAGE_KEY = "dot_analytics_session_id";
|
|
36
|
+
/**
|
|
37
|
+
* Session storage key for session start time
|
|
38
|
+
*/
|
|
33
39
|
export declare const SESSION_START_KEY = "dot_analytics_session_start";
|
|
40
|
+
/**
|
|
41
|
+
* Session storage key for session UTM data
|
|
42
|
+
*/
|
|
34
43
|
export declare const SESSION_UTM_KEY = "dot_analytics_session_utm";
|
|
35
44
|
/**
|
|
36
45
|
* User ID configuration constants
|
|
@@ -43,6 +52,13 @@ export declare const USER_ID_KEY = "dot_analytics_user_id";
|
|
|
43
52
|
* - visibilitychange: Handled separately to detect tab changes
|
|
44
53
|
*/
|
|
45
54
|
export declare const ACTIVITY_EVENTS: readonly ["click"];
|
|
55
|
+
/**
|
|
56
|
+
* Default queue configuration for event batching
|
|
57
|
+
*/
|
|
58
|
+
export declare const DEFAULT_QUEUE_CONFIG: {
|
|
59
|
+
readonly eventBatchSize: 15;
|
|
60
|
+
readonly flushInterval: 5000;
|
|
61
|
+
};
|
|
46
62
|
/**
|
|
47
63
|
* The name of the analytics minified script.
|
|
48
64
|
*/
|
|
@@ -1,12 +1,17 @@
|
|
|
1
|
-
const
|
|
1
|
+
const E = "/api/v1/analytics/content/event", e = {
|
|
2
2
|
PAGEVIEW: "pageview"
|
|
3
|
-
},
|
|
3
|
+
}, n = [
|
|
4
4
|
"utm_source",
|
|
5
5
|
"utm_medium",
|
|
6
6
|
"utm_campaign",
|
|
7
7
|
"utm_term",
|
|
8
8
|
"utm_content"
|
|
9
|
-
],
|
|
9
|
+
], c = 30, s = "dot_analytics_session_id", T = "dot_analytics_user_id", t = 15, _ = 5e3, o = ["click"], S = {
|
|
10
|
+
eventBatchSize: t,
|
|
11
|
+
// Max events per batch - auto-sends when reached
|
|
12
|
+
flushInterval: _
|
|
13
|
+
// Time between flushes - sends whatever is queued
|
|
14
|
+
}, I = [
|
|
10
15
|
"title",
|
|
11
16
|
"url",
|
|
12
17
|
"path",
|
|
@@ -17,12 +22,13 @@ const t = "/api/v1/analytics/content/event", _ = {
|
|
|
17
22
|
"referrer"
|
|
18
23
|
];
|
|
19
24
|
export {
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
e as
|
|
26
|
-
n as
|
|
27
|
-
|
|
25
|
+
o as ACTIVITY_EVENTS,
|
|
26
|
+
E as ANALYTICS_ENDPOINT,
|
|
27
|
+
I as ANALYTICS_JS_DEFAULT_PROPERTIES,
|
|
28
|
+
S as DEFAULT_QUEUE_CONFIG,
|
|
29
|
+
c as DEFAULT_SESSION_TIMEOUT_MINUTES,
|
|
30
|
+
e as DotCMSPredefinedEventType,
|
|
31
|
+
n as EXPECTED_UTM_KEYS,
|
|
32
|
+
s as SESSION_STORAGE_KEY,
|
|
33
|
+
T as USER_ID_KEY
|
|
28
34
|
};
|
|
@@ -1,8 +1,17 @@
|
|
|
1
1
|
import { DotCMSAnalyticsConfig, DotCMSEvent, DotCMSRequestBody } from './models';
|
|
2
2
|
/**
|
|
3
|
-
*
|
|
3
|
+
* Available transport methods for sending analytics events
|
|
4
|
+
*/
|
|
5
|
+
export declare const TRANSPORT_TYPES: {
|
|
6
|
+
readonly FETCH: "fetch";
|
|
7
|
+
readonly BEACON: "beacon";
|
|
8
|
+
};
|
|
9
|
+
export type TransportType = (typeof TRANSPORT_TYPES)[keyof typeof TRANSPORT_TYPES];
|
|
10
|
+
/**
|
|
11
|
+
* Send analytics events to the server
|
|
4
12
|
* @param payload - The event payload data
|
|
5
13
|
* @param config - The analytics configuration
|
|
6
|
-
* @
|
|
14
|
+
* @param transportType - Transport method: 'fetch' (default) or 'beacon' (for page unload)
|
|
15
|
+
* @returns A promise that resolves when the request is complete (fetch only)
|
|
7
16
|
*/
|
|
8
|
-
export declare const
|
|
17
|
+
export declare const sendAnalyticsEvent: (payload: DotCMSRequestBody<DotCMSEvent>, config: DotCMSAnalyticsConfig, transportType?: TransportType) => Promise<void>;
|
|
@@ -1,23 +1,34 @@
|
|
|
1
|
-
import { ANALYTICS_ENDPOINT as
|
|
2
|
-
const
|
|
1
|
+
import { ANALYTICS_ENDPOINT as l } from "./constants/dot-content-analytics.constants.js";
|
|
2
|
+
const d = async (s, n, o = "fetch") => {
|
|
3
|
+
const a = `${n.server}${l}`, c = JSON.stringify(s);
|
|
4
|
+
if (n.debug && console.warn(
|
|
5
|
+
`DotCMS Analytics: Sending ${s.events.length} event(s) via ${o}`,
|
|
6
|
+
o === "fetch" ? { payload: s } : void 0
|
|
7
|
+
), o === "beacon") {
|
|
8
|
+
if (navigator.sendBeacon) {
|
|
9
|
+
const e = new Blob([c], { type: "application/json" });
|
|
10
|
+
!navigator.sendBeacon(a, e) && n.debug && console.warn("DotCMS Analytics: sendBeacon failed (queue might be full)");
|
|
11
|
+
} else
|
|
12
|
+
return n.debug && console.warn("DotCMS Analytics: sendBeacon not available, using fetch fallback"), d(s, n, "fetch");
|
|
13
|
+
return;
|
|
14
|
+
}
|
|
3
15
|
try {
|
|
4
|
-
|
|
5
|
-
const e = await fetch(`${t.server}${a}`, {
|
|
16
|
+
const e = await fetch(a, {
|
|
6
17
|
method: "POST",
|
|
7
18
|
headers: { "Content-Type": "application/json" },
|
|
8
|
-
body:
|
|
19
|
+
body: c
|
|
9
20
|
});
|
|
10
21
|
if (!e.ok) {
|
|
11
|
-
const
|
|
22
|
+
const i = e.statusText || "Unknown Error", r = `HTTP ${e.status}: ${i}`;
|
|
12
23
|
try {
|
|
13
|
-
const
|
|
14
|
-
|
|
24
|
+
const t = await e.json();
|
|
25
|
+
t.message ? console.warn(`DotCMS Analytics: ${t.message} (${r})`) : console.warn(
|
|
15
26
|
`DotCMS Analytics: ${r} - No error message in response`
|
|
16
27
|
);
|
|
17
|
-
} catch (
|
|
28
|
+
} catch (t) {
|
|
18
29
|
console.warn(
|
|
19
30
|
`DotCMS Analytics: ${r} - Failed to parse error response:`,
|
|
20
|
-
|
|
31
|
+
t
|
|
21
32
|
);
|
|
22
33
|
}
|
|
23
34
|
}
|
|
@@ -26,5 +37,5 @@ const i = async (o, t) => {
|
|
|
26
37
|
}
|
|
27
38
|
};
|
|
28
39
|
export {
|
|
29
|
-
|
|
40
|
+
d as sendAnalyticsEvent
|
|
30
41
|
};
|
|
@@ -60,6 +60,14 @@ export interface AnalyticsConfigResult {
|
|
|
60
60
|
* @returns The analytics configuration object
|
|
61
61
|
*/
|
|
62
62
|
export declare const getAnalyticsConfig: () => DotCMSAnalyticsConfig;
|
|
63
|
+
/**
|
|
64
|
+
* Gets current device data for analytics.
|
|
65
|
+
* Combines static browser data with dynamic viewport information.
|
|
66
|
+
* Used by the identity plugin to inject device data into context.
|
|
67
|
+
*
|
|
68
|
+
* @returns Device data with screen resolution, language, and viewport dimensions
|
|
69
|
+
*/
|
|
70
|
+
export declare const getDeviceDataForContext: () => DotCMSEventDeviceData;
|
|
63
71
|
/**
|
|
64
72
|
* Retrieves the browser event data - optimized but accurate.
|
|
65
73
|
* @internal This function is for internal use only.
|
|
@@ -126,7 +134,7 @@ export declare const enrichWithUtmData: <T extends Record<string, unknown>>(payl
|
|
|
126
134
|
*
|
|
127
135
|
* @param payload - The Analytics.js payload with context already injected by identity plugin
|
|
128
136
|
* @param location - The Location object to extract page data from (defaults to window.location)
|
|
129
|
-
* @returns Enriched payload with page,
|
|
137
|
+
* @returns Enriched payload with page, UTM, custom data, and local_time (device is in context)
|
|
130
138
|
*/
|
|
131
139
|
export declare const enrichPagePayloadOptimized: (payload: AnalyticsBasePayloadWithContext, location?: Location) => EnrichedAnalyticsPayload;
|
|
132
140
|
/**
|
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import { ANALYTICS_JS_DEFAULT_PROPERTIES as
|
|
1
|
+
import { ANALYTICS_JS_DEFAULT_PROPERTIES as w, SESSION_STORAGE_KEY as h, DEFAULT_SESSION_TIMEOUT_MINUTES as _, USER_ID_KEY as l, EXPECTED_UTM_KEYS as f } from "./constants/dot-content-analytics.constants.js";
|
|
2
2
|
let g = null, u = null;
|
|
3
3
|
const d = (t) => {
|
|
4
|
-
const e = Date.now(),
|
|
5
|
-
return `${t}_${e}_${
|
|
6
|
-
},
|
|
4
|
+
const e = Date.now(), n = Math.random().toString(36).substr(2, 9), s = Math.random().toString(36).substr(2, 9);
|
|
5
|
+
return `${t}_${e}_${n}${s}`;
|
|
6
|
+
}, S = {
|
|
7
7
|
getItem: (t) => {
|
|
8
8
|
try {
|
|
9
9
|
return localStorage.getItem(t);
|
|
@@ -19,26 +19,26 @@ const d = (t) => {
|
|
|
19
19
|
}
|
|
20
20
|
}
|
|
21
21
|
}, p = () => {
|
|
22
|
-
let t =
|
|
23
|
-
return t || (t = d("user"),
|
|
24
|
-
},
|
|
25
|
-
const e = new Date(t),
|
|
22
|
+
let t = S.getItem(l);
|
|
23
|
+
return t || (t = d("user"), S.setItem(l, t)), t;
|
|
24
|
+
}, D = (t) => {
|
|
25
|
+
const e = new Date(t), n = /* @__PURE__ */ new Date(), s = new Date(
|
|
26
26
|
e.getUTCFullYear(),
|
|
27
27
|
e.getUTCMonth(),
|
|
28
28
|
e.getUTCDate()
|
|
29
|
-
), o = new Date(
|
|
30
|
-
return
|
|
31
|
-
},
|
|
29
|
+
), o = new Date(n.getUTCFullYear(), n.getUTCMonth(), n.getUTCDate());
|
|
30
|
+
return s.getTime() !== o.getTime();
|
|
31
|
+
}, T = () => {
|
|
32
32
|
const t = Date.now();
|
|
33
33
|
if (typeof window > "u")
|
|
34
34
|
return d("session_fallback");
|
|
35
35
|
try {
|
|
36
|
-
const e = sessionStorage.getItem(
|
|
36
|
+
const e = sessionStorage.getItem(h);
|
|
37
37
|
if (e) {
|
|
38
|
-
const { sessionId: o, startTime: r, lastActivity: a } = JSON.parse(e), c = !
|
|
38
|
+
const { sessionId: o, startTime: r, lastActivity: a } = JSON.parse(e), c = !D(r), i = t - a < _ * 60 * 1e3;
|
|
39
39
|
if (c && i)
|
|
40
40
|
return sessionStorage.setItem(
|
|
41
|
-
|
|
41
|
+
h,
|
|
42
42
|
JSON.stringify({
|
|
43
43
|
sessionId: o,
|
|
44
44
|
startTime: r,
|
|
@@ -46,91 +46,95 @@ const d = (t) => {
|
|
|
46
46
|
})
|
|
47
47
|
), o;
|
|
48
48
|
}
|
|
49
|
-
const
|
|
50
|
-
sessionId:
|
|
49
|
+
const n = d("session"), s = {
|
|
50
|
+
sessionId: n,
|
|
51
51
|
startTime: t,
|
|
52
52
|
lastActivity: t
|
|
53
53
|
};
|
|
54
|
-
return sessionStorage.setItem(
|
|
54
|
+
return sessionStorage.setItem(h, JSON.stringify(s)), n;
|
|
55
55
|
} catch {
|
|
56
56
|
return d("session_fallback");
|
|
57
57
|
}
|
|
58
|
-
},
|
|
59
|
-
const e =
|
|
58
|
+
}, U = (t) => {
|
|
59
|
+
const e = T(), n = p(), s = I();
|
|
60
60
|
return t.debug && console.warn("DotCMS Analytics Identity Context:", {
|
|
61
61
|
sessionId: e,
|
|
62
|
-
userId:
|
|
62
|
+
userId: n
|
|
63
63
|
}), {
|
|
64
64
|
site_auth: t.siteAuth,
|
|
65
65
|
session_id: e,
|
|
66
|
-
user_id:
|
|
66
|
+
user_id: n,
|
|
67
|
+
device: s
|
|
67
68
|
};
|
|
68
|
-
},
|
|
69
|
+
}, m = () => g || (g = {
|
|
69
70
|
user_language: navigator.language,
|
|
70
71
|
doc_encoding: document.characterSet || document.charset,
|
|
71
72
|
screen_resolution: typeof screen < "u" && screen.width && screen.height ? `${screen.width}x${screen.height}` : ""
|
|
72
|
-
}, g),
|
|
73
|
+
}, g), I = () => {
|
|
74
|
+
const t = m(), e = window.innerWidth || document.documentElement.clientWidth || 0, n = window.innerHeight || document.documentElement.clientHeight || 0;
|
|
75
|
+
return {
|
|
76
|
+
screen_resolution: t.screen_resolution ?? "",
|
|
77
|
+
language: t.user_language ?? "",
|
|
78
|
+
viewport_width: String(e),
|
|
79
|
+
viewport_height: String(n)
|
|
80
|
+
};
|
|
81
|
+
}, y = (t) => {
|
|
73
82
|
const e = t.search;
|
|
74
83
|
if (u && u.search === e)
|
|
75
84
|
return u.params;
|
|
76
|
-
const
|
|
77
|
-
return
|
|
78
|
-
const r =
|
|
85
|
+
const n = new URLSearchParams(e), s = {};
|
|
86
|
+
return f.forEach((o) => {
|
|
87
|
+
const r = n.get(o);
|
|
79
88
|
if (r) {
|
|
80
89
|
const a = o.replace("utm_", "");
|
|
81
|
-
|
|
90
|
+
s[a] = r;
|
|
82
91
|
}
|
|
83
|
-
}), u = { search: e, params:
|
|
92
|
+
}), u = { search: e, params: s }, s;
|
|
84
93
|
}, E = () => {
|
|
85
94
|
try {
|
|
86
|
-
const t = (/* @__PURE__ */ new Date()).getTimezoneOffset(), e = t > 0 ? "-" : "+",
|
|
87
|
-
return `${e}${
|
|
95
|
+
const t = (/* @__PURE__ */ new Date()).getTimezoneOffset(), e = t > 0 ? "-" : "+", n = Math.abs(t), s = Math.floor(n / 60), o = n % 60;
|
|
96
|
+
return `${e}${s.toString().padStart(2, "0")}:${o.toString().padStart(2, "0")}`;
|
|
88
97
|
} catch {
|
|
89
98
|
return "+00:00";
|
|
90
99
|
}
|
|
91
|
-
},
|
|
100
|
+
}, C = () => {
|
|
92
101
|
try {
|
|
93
|
-
const t = /* @__PURE__ */ new Date(), e = E(),
|
|
94
|
-
return `${
|
|
102
|
+
const t = /* @__PURE__ */ new Date(), e = E(), n = t.getFullYear(), s = (t.getMonth() + 1).toString().padStart(2, "0"), o = t.getDate().toString().padStart(2, "0"), r = t.getHours().toString().padStart(2, "0"), a = t.getMinutes().toString().padStart(2, "0"), c = t.getSeconds().toString().padStart(2, "0");
|
|
103
|
+
return `${n}-${s}-${o}T${r}:${a}:${c}${e}`;
|
|
95
104
|
} catch {
|
|
96
105
|
return (/* @__PURE__ */ new Date()).toISOString();
|
|
97
106
|
}
|
|
98
|
-
},
|
|
99
|
-
const
|
|
100
|
-
Object.keys(o).forEach((
|
|
101
|
-
|
|
107
|
+
}, $ = (t, e = typeof window < "u" ? window.location : {}) => {
|
|
108
|
+
const n = C(), s = m(), { properties: o } = t, r = {};
|
|
109
|
+
Object.keys(o).forEach((i) => {
|
|
110
|
+
w.includes(i) || (r[i] = o[i]);
|
|
102
111
|
});
|
|
103
112
|
const a = {
|
|
104
113
|
url: e.href,
|
|
105
|
-
doc_encoding:
|
|
114
|
+
doc_encoding: s.doc_encoding,
|
|
106
115
|
doc_hash: e.hash,
|
|
107
116
|
doc_protocol: e.protocol,
|
|
108
117
|
doc_search: e.search,
|
|
109
118
|
doc_host: e.hostname,
|
|
110
119
|
doc_path: e.pathname,
|
|
111
120
|
title: o.title ?? (document == null ? void 0 : document.title)
|
|
112
|
-
}, c =
|
|
113
|
-
screen_resolution: n.screen_resolution ?? "",
|
|
114
|
-
language: n.user_language ?? "",
|
|
115
|
-
viewport_width: String(o.width),
|
|
116
|
-
viewport_height: String(o.height)
|
|
117
|
-
}, i = y(e);
|
|
121
|
+
}, c = y(e);
|
|
118
122
|
return {
|
|
119
123
|
...t,
|
|
120
124
|
page: a,
|
|
121
|
-
|
|
122
|
-
...Object.keys(i).length > 0 && { utm: i },
|
|
125
|
+
...Object.keys(c).length > 0 && { utm: c },
|
|
123
126
|
// Only include custom if there are user-provided properties
|
|
124
127
|
...Object.keys(r).length > 0 && { custom: r },
|
|
125
|
-
local_time:
|
|
128
|
+
local_time: n
|
|
126
129
|
};
|
|
127
130
|
};
|
|
128
131
|
export {
|
|
129
|
-
|
|
132
|
+
$ as enrichPagePayloadOptimized,
|
|
130
133
|
y as extractUTMParameters,
|
|
131
134
|
d as generateSecureId,
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
+
U as getAnalyticsContext,
|
|
136
|
+
I as getDeviceDataForContext,
|
|
137
|
+
C as getLocalTime,
|
|
138
|
+
T as getSessionId,
|
|
135
139
|
p as getUserId
|
|
136
140
|
};
|
|
@@ -53,6 +53,8 @@ export interface DotCMSAnalyticsEventContext {
|
|
|
53
53
|
session_id: string;
|
|
54
54
|
/** Unique user identifier */
|
|
55
55
|
user_id: string;
|
|
56
|
+
/** Device and browser information */
|
|
57
|
+
device: DotCMSEventDeviceData;
|
|
56
58
|
}
|
|
57
59
|
/**
|
|
58
60
|
* Device and browser information for DotCMS analytics tracking.
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { DotCMSEventPageData, DotCMSEventUtmData } from './data.model';
|
|
2
2
|
import { DotCMSCustomEventType, DotCMSEventType, DotCMSPredefinedEventType } from '../constants/dot-content-analytics.constants';
|
|
3
3
|
/**
|
|
4
4
|
* JSON value type for analytics custom data.
|
|
@@ -32,13 +32,11 @@ export interface DotCMSEventBase<TEventType extends DotCMSEventType, TData> {
|
|
|
32
32
|
}
|
|
33
33
|
/**
|
|
34
34
|
* Data structure for pageview events.
|
|
35
|
-
* Contains page
|
|
35
|
+
* Contains page and optional UTM/custom data.
|
|
36
36
|
*/
|
|
37
37
|
export type DotCMSPageViewEventData = {
|
|
38
38
|
/** Page data associated with the event */
|
|
39
39
|
page: DotCMSEventPageData;
|
|
40
|
-
/** Device and browser information */
|
|
41
|
-
device: DotCMSEventDeviceData;
|
|
42
40
|
/** UTM parameters for campaign tracking (optional) */
|
|
43
41
|
utm?: DotCMSEventUtmData;
|
|
44
42
|
/** Custom data associated with the event (any valid JSON) */
|
|
@@ -1,7 +1,17 @@
|
|
|
1
|
-
import { DotCMSAnalyticsEventContext,
|
|
1
|
+
import { DotCMSAnalyticsEventContext, DotCMSEventPageData, DotCMSEventUtmData } from './data.model';
|
|
2
2
|
import { JsonObject } from './event.model';
|
|
3
3
|
import { DotCMSAnalyticsRequestBody } from './request.model';
|
|
4
4
|
import { DotCMSCustomEventType } from '../constants';
|
|
5
|
+
/**
|
|
6
|
+
* Configuration for event queue management.
|
|
7
|
+
* Controls how events are batched before sending to the server.
|
|
8
|
+
*/
|
|
9
|
+
export interface QueueConfig {
|
|
10
|
+
/** Maximum events per batch - auto-sends when reached (default: 15) */
|
|
11
|
+
eventBatchSize?: number;
|
|
12
|
+
/** Time in milliseconds between flushes - sends pending events (default: 5000) */
|
|
13
|
+
flushInterval?: number;
|
|
14
|
+
}
|
|
5
15
|
/**
|
|
6
16
|
* Main interface for the DotCMS Analytics SDK.
|
|
7
17
|
* Provides the core methods for tracking page views and custom events.
|
|
@@ -40,6 +50,13 @@ export interface DotCMSAnalyticsConfig {
|
|
|
40
50
|
* The site auth for authenticating with the Analytics service.
|
|
41
51
|
*/
|
|
42
52
|
siteAuth: string;
|
|
53
|
+
/**
|
|
54
|
+
* Queue configuration for event batching:
|
|
55
|
+
* - `false`: Disable queuing, send events immediately
|
|
56
|
+
* - `true` or `undefined` (default): Enable queuing with default settings
|
|
57
|
+
* - `QueueConfig`: Enable queuing with custom settings
|
|
58
|
+
*/
|
|
59
|
+
queue?: QueueConfig | boolean;
|
|
43
60
|
}
|
|
44
61
|
/**
|
|
45
62
|
* Track event payload with context.
|
|
@@ -117,13 +134,11 @@ export interface AnalyticsBasePayloadWithContext extends AnalyticsBasePayload {
|
|
|
117
134
|
/**
|
|
118
135
|
* Enriched analytics payload with DotCMS-specific data.
|
|
119
136
|
* This is the result of enriching the base Analytics.js payload with context (from identity plugin)
|
|
120
|
-
* and then adding page,
|
|
137
|
+
* and then adding page, UTM, and custom data (from enricher plugin).
|
|
121
138
|
*/
|
|
122
139
|
export type EnrichedAnalyticsPayload = AnalyticsBasePayloadWithContext & {
|
|
123
140
|
/** Page data for the current page */
|
|
124
141
|
page: DotCMSEventPageData;
|
|
125
|
-
/** Device and browser information */
|
|
126
|
-
device: DotCMSEventDeviceData;
|
|
127
142
|
/** UTM parameters for campaign tracking */
|
|
128
143
|
utm?: DotCMSEventUtmData;
|
|
129
144
|
/** Custom data associated with the event (any valid JSON) */
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { DotCMSAnalyticsConfig, DotCMSAnalyticsEventContext, DotCMSEvent } from '../models';
|
|
2
|
+
/**
|
|
3
|
+
* Creates a queue manager for batching analytics events.
|
|
4
|
+
* Uses factory function pattern consistent with the plugin architecture.
|
|
5
|
+
*/
|
|
6
|
+
export declare const createAnalyticsQueue: (config: DotCMSAnalyticsConfig) => {
|
|
7
|
+
/**
|
|
8
|
+
* Initialize the queue with smart batching
|
|
9
|
+
*/
|
|
10
|
+
initialize: () => void;
|
|
11
|
+
/**
|
|
12
|
+
* Add event to queue
|
|
13
|
+
* smartQueue handles all batching logic automatically:
|
|
14
|
+
* - Sends immediately when eventBatchSize reached (with throttle: false)
|
|
15
|
+
* - Sends pending events every flushInterval
|
|
16
|
+
*/
|
|
17
|
+
enqueue: (event: DotCMSEvent, context: DotCMSAnalyticsEventContext) => void;
|
|
18
|
+
/**
|
|
19
|
+
* Get queue size for debugging
|
|
20
|
+
* Returns the number of events in smartQueue
|
|
21
|
+
*/
|
|
22
|
+
size: () => number;
|
|
23
|
+
/**
|
|
24
|
+
* Clean up queue resources
|
|
25
|
+
* Flushes remaining events and cleans up listeners
|
|
26
|
+
*/
|
|
27
|
+
cleanup: () => void;
|
|
28
|
+
};
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import p from "@analytics/queue-utils";
|
|
2
|
+
import { DEFAULT_QUEUE_CONFIG as c } from "../constants/dot-content-analytics.constants.js";
|
|
3
|
+
import { sendAnalyticsEvent as y } from "../dot-content-analytics.http.js";
|
|
4
|
+
const z = (n) => {
|
|
5
|
+
let e = null, i = null, o = "fetch";
|
|
6
|
+
const l = {
|
|
7
|
+
...c,
|
|
8
|
+
...typeof n.queue == "object" ? n.queue : {}
|
|
9
|
+
}, f = (t) => {
|
|
10
|
+
if (!i) return;
|
|
11
|
+
n.debug && console.log(`DotCMS Analytics Queue: Sending batch of ${t.length} event(s)`, {
|
|
12
|
+
events: t,
|
|
13
|
+
transport: o
|
|
14
|
+
}), y({ context: i, events: t }, n, o);
|
|
15
|
+
}, u = () => {
|
|
16
|
+
!e || e.size() === 0 || !i || (n.debug && console.warn(
|
|
17
|
+
`DotCMS Analytics: Flushing ${e.size()} events (page hidden/unload)`
|
|
18
|
+
), o = "beacon", e.flush(!0));
|
|
19
|
+
}, d = () => {
|
|
20
|
+
document.visibilityState === "hidden" && u();
|
|
21
|
+
};
|
|
22
|
+
return {
|
|
23
|
+
/**
|
|
24
|
+
* Initialize the queue with smart batching
|
|
25
|
+
*/
|
|
26
|
+
initialize: () => {
|
|
27
|
+
e = p(
|
|
28
|
+
(t) => {
|
|
29
|
+
f(t);
|
|
30
|
+
},
|
|
31
|
+
{
|
|
32
|
+
max: l.eventBatchSize,
|
|
33
|
+
interval: l.flushInterval,
|
|
34
|
+
throttle: !1
|
|
35
|
+
// Always false - enables both batch size and interval triggers
|
|
36
|
+
}
|
|
37
|
+
), typeof window < "u" && typeof document < "u" && (document.addEventListener("visibilitychange", d), window.addEventListener("pagehide", u));
|
|
38
|
+
},
|
|
39
|
+
/**
|
|
40
|
+
* Add event to queue
|
|
41
|
+
* smartQueue handles all batching logic automatically:
|
|
42
|
+
* - Sends immediately when eventBatchSize reached (with throttle: false)
|
|
43
|
+
* - Sends pending events every flushInterval
|
|
44
|
+
*/
|
|
45
|
+
enqueue: (t, s) => {
|
|
46
|
+
if (i = s, !!e) {
|
|
47
|
+
if (n.debug) {
|
|
48
|
+
const a = e.size() + 1, r = l.eventBatchSize ?? c.eventBatchSize, h = a >= r;
|
|
49
|
+
console.log(
|
|
50
|
+
`DotCMS Analytics Queue: Event added. Queue size: ${a}/${r}${h ? " (full, sending...)" : ""}`,
|
|
51
|
+
{ eventType: t.event_type, event: t }
|
|
52
|
+
);
|
|
53
|
+
}
|
|
54
|
+
e.push(t);
|
|
55
|
+
}
|
|
56
|
+
},
|
|
57
|
+
/**
|
|
58
|
+
* Get queue size for debugging
|
|
59
|
+
* Returns the number of events in smartQueue
|
|
60
|
+
*/
|
|
61
|
+
size: () => (e == null ? void 0 : e.size()) ?? 0,
|
|
62
|
+
/**
|
|
63
|
+
* Clean up queue resources
|
|
64
|
+
* Flushes remaining events and cleans up listeners
|
|
65
|
+
*/
|
|
66
|
+
cleanup: () => {
|
|
67
|
+
u(), typeof window < "u" && typeof document < "u" && (document.removeEventListener("visibilitychange", d), window.removeEventListener("pagehide", u)), e = null, i = null, o = "fetch";
|
|
68
|
+
}
|
|
69
|
+
};
|
|
70
|
+
};
|
|
71
|
+
export {
|
|
72
|
+
z as createAnalyticsQueue
|
|
73
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { createAnalyticsQueue } from './dot-analytics.queue.utils';
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@dotcms/analytics",
|
|
3
|
-
"version": "1.1.1-next.
|
|
3
|
+
"version": "1.1.1-next.5",
|
|
4
4
|
"description": "Official JavaScript library for Content Analytics with DotCMS.",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -23,6 +23,7 @@
|
|
|
23
23
|
"analytics": "^0.8.0",
|
|
24
24
|
"@analytics/core": "^0.13.0",
|
|
25
25
|
"@analytics/storage-utils": "^0.4.0",
|
|
26
|
+
"@analytics/queue-utils": "^0.1.3",
|
|
26
27
|
"@dotcms/uve": "latest"
|
|
27
28
|
},
|
|
28
29
|
"peerDependencies": {
|