@dotcms/events 0.0.0-stage → 26.10.5-1-alpha.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (57) hide show
  1. package/README.md +516 -2
  2. package/decision.cjs.js +108 -0
  3. package/decision.esm.js +92 -0
  4. package/index.cjs.default.js +1 -0
  5. package/index.cjs.js +3674 -0
  6. package/index.cjs.mjs +2 -0
  7. package/index.d.ts +1 -0
  8. package/index.esm.js +3672 -0
  9. package/markup.cjs.default.js +1 -0
  10. package/markup.cjs.js +161 -0
  11. package/markup.cjs.mjs +2 -0
  12. package/markup.d.ts +1 -0
  13. package/markup.esm.js +159 -0
  14. package/package.json +77 -4
  15. package/react.cjs.default.js +1 -0
  16. package/react.cjs.js +39 -0
  17. package/react.cjs.mjs +2 -0
  18. package/react.d.ts +1 -0
  19. package/react.esm.js +37 -0
  20. package/src/index.d.ts +8 -0
  21. package/src/lib/clicks/constants.d.ts +14 -0
  22. package/src/lib/clicks/plugin.d.ts +35 -0
  23. package/src/lib/clicks/tracker.d.ts +42 -0
  24. package/src/lib/clicks/utils.d.ts +13 -0
  25. package/src/lib/contentlets/constants.d.ts +36 -0
  26. package/src/lib/contentlets/utils.d.ts +64 -0
  27. package/src/lib/contentlets/viewport.d.ts +23 -0
  28. package/src/lib/events.d.ts +17 -0
  29. package/src/lib/experiments/api.d.ts +9 -0
  30. package/src/lib/experiments/boot.d.ts +53 -0
  31. package/src/lib/experiments/constants.d.ts +46 -0
  32. package/src/lib/experiments/decision.d.ts +53 -0
  33. package/src/lib/experiments/dom.d.ts +62 -0
  34. package/src/lib/experiments/engine.d.ts +66 -0
  35. package/src/lib/experiments/markup.d.ts +17 -0
  36. package/src/lib/experiments/models.d.ts +62 -0
  37. package/src/lib/experiments/plugin.d.ts +27 -0
  38. package/src/lib/experiments/store.d.ts +54 -0
  39. package/src/lib/impressions/constants.d.ts +28 -0
  40. package/src/lib/impressions/plugin.d.ts +38 -0
  41. package/src/lib/impressions/tracker.d.ts +67 -0
  42. package/src/lib/impressions/utils.d.ts +10 -0
  43. package/src/lib/models.d.ts +130 -0
  44. package/src/lib/pipeline/constants.d.ts +88 -0
  45. package/src/lib/pipeline/enricher/plugin.d.ts +32 -0
  46. package/src/lib/pipeline/identity/activity.d.ts +19 -0
  47. package/src/lib/pipeline/identity/plugin.d.ts +60 -0
  48. package/src/lib/pipeline/logger.d.ts +74 -0
  49. package/src/lib/pipeline/models.d.ts +588 -0
  50. package/src/lib/pipeline/navigation.d.ts +21 -0
  51. package/src/lib/pipeline/sender/http.d.ts +9 -0
  52. package/src/lib/pipeline/sender/plugin.d.ts +47 -0
  53. package/src/lib/pipeline/sender/queue.d.ts +33 -0
  54. package/src/lib/pipeline/utils.d.ts +119 -0
  55. package/src/lib/react/DotCMSExperiment.d.ts +36 -0
  56. package/src/markup.d.ts +7 -0
  57. package/src/react.d.ts +8 -0
package/README.md CHANGED
@@ -1,3 +1,517 @@
1
- # Temporary Holding Version
1
+ # dotCMS Events SDK
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
3
+ The `@dotcms/events` SDK is the official dotCMS library for everything your pages report back to dotCMS: pageviews, conversions, content impressions, content clicks, and A/B testing experiments. You configure it once, and a single object, `dotEvents`, takes care of the rest.
4
+
5
+ ## Overview
6
+
7
+ ### When to Use It
8
+
9
+ - Measuring how visitors use your dotCMS-powered pages: which pages they visit, which content they see, and what they click
10
+ - Recording conversions when a visitor completes a goal, such as a sign-up or a download
11
+ - Running the A/B testing experiments you create in dotCMS on a headless site
12
+ - Replacing `@dotcms/analytics` and `@dotcms/experiments` with a single package
13
+
14
+ ### Key Features
15
+
16
+ - **Automatic pageviews**: on the first load, on every client-side navigation, and when the visitor comes back to a page with the browser's Back and Forward buttons
17
+ - **Content impressions**: records which contentlets the visitor actually saw, and for how long they had to be on screen
18
+ - **Content clicks**: records clicks on the links and buttons inside your contentlets
19
+ - **Conversions**: one call when a visitor completes a goal
20
+ - **Experiments without flicker**: sends each visitor to their variant, and keeps the content hidden for a moment so nobody sees the wrong one
21
+ - **One setup**: one `init` call configures everything, and every module of your app uses the same object
22
+ - **No cookies**: the SDK stores anonymous ids in the browser's storage, never in cookies
23
+ - **Safe in the editor**: inside the dotCMS Universal Visual Editor (UVE), nothing is sent
24
+
25
+ ## Table of Contents
26
+
27
+ - [Overview](#overview)
28
+ - [Installation](#installation)
29
+ - [Which SDK Version Should I Use?](#which-sdk-version-should-i-use)
30
+ - [Quick Start (Next.js)](#quick-start-nextjs)
31
+ - [Example Project](#example-project)
32
+ - [How It Works](#how-it-works)
33
+ - [Configuration](#configuration)
34
+ - [Usage](#usage)
35
+ - [API Reference](#api-reference)
36
+ - [Migrating from @dotcms/analytics and @dotcms/experiments](#migrating-from-dotcmsanalytics-and-dotcmsexperiments)
37
+ - [Under the Hood](#under-the-hood)
38
+ - [Troubleshooting](#troubleshooting)
39
+ - [Support](#support)
40
+ - [Contributing](#contributing)
41
+ - [Licensing](#licensing)
42
+
43
+ ## Installation
44
+
45
+ Install the package via npm:
46
+
47
+ ```bash
48
+ npm install @dotcms/events
49
+ ```
50
+
51
+ Or using Yarn:
52
+
53
+ ```bash
54
+ yarn add @dotcms/events
55
+ ```
56
+
57
+ Requirements:
58
+
59
+ - A dotCMS instance with the **Content Analytics** app configured for your site. You need its **Site Auth**.
60
+ - **Next.js 16 or later**, if you use Next.js.
61
+ - **React 18 or later**, for `DotCMSExperiment` (`@dotcms/events/react`).
62
+
63
+ The package has three entry points:
64
+
65
+ | Import | What it gives you | Use it in |
66
+ | ----------------------- | ------------------------------------------------------------------------- | ------------------------------------------ |
67
+ | `@dotcms/events` | `dotEvents`: starts the SDK, records conversions and sends pageviews | Every app |
68
+ | `@dotcms/events/react` | `DotCMSExperiment`: runs a page's experiment on the content it wraps | React and Next.js pages that run experiments |
69
+ | `@dotcms/events/markup` | `experimentMarkup`: what a server-rendered page prints to run its experiment | Other frameworks, rendered on the server |
70
+
71
+ ## Which SDK Version Should I Use?
72
+
73
+ dotCMS SDKs are published in lockstep with dotCMS itself: every `@dotcms/*` package ships
74
+ at the **exact same version number** as the dotCMS release it was built for (e.g. dotCMS
75
+ `26.7.14-1` → `@dotcms/client@26.7.14-1`, `@dotcms/react@26.7.14-1`, and so on).
76
+
77
+ **Simple rule of thumb: use the SDK version that matches your dotCMS instance's version.**
78
+
79
+ You don't have to upgrade the SDK every time dotCMS releases a new version (or vice versa).
80
+ Most releases don't change anything the SDKs rely on, so an older SDK usually keeps working
81
+ fine against a newer dotCMS instance. Occasionally, though, a release does include a real
82
+ breaking change — and if your SDK is older than that point, it will stop working correctly.
83
+
84
+ You don't need to track this yourself: your dotCMS instance always knows the oldest SDK
85
+ version it still supports, and the SDK checks itself against it automatically. If you're
86
+ using an SDK that's too old, you'll see a clear warning in your console telling you to
87
+ upgrade.
88
+
89
+ **Recommendation:** pin your SDKs to the same version as your dotCMS instance, and only bump
90
+ them when you upgrade dotCMS — or when the console tells you to.
91
+
92
+ > **On an LTS release?** LTS releases don't currently get their own matching SDK version.
93
+ > Until that's addressed, use the SDK version published for the closest regular release at
94
+ > or before your LTS version.
95
+ >
96
+ > Want more background on how dotCMS releases and support windows work? See
97
+ > [Release & Support Lifecycle](https://dev.dotcms.com/docs/release-support-lifecycle).
98
+
99
+ ## Quick Start (Next.js)
100
+
101
+ ### 1. Add the environment variables
102
+
103
+ Add these to your `.env.local` file:
104
+
105
+ ```bash
106
+ NEXT_PUBLIC_DOTCMS_HOST=https://your-dotcms-instance.com
107
+ NEXT_PUBLIC_DOTCMS_SITE_AUTH=your-site-auth
108
+ ```
109
+
110
+ | Variable | Description |
111
+ | ------------------------------ | ---------------------------------------------------------------- |
112
+ | `NEXT_PUBLIC_DOTCMS_HOST` | The URL of your dotCMS instance |
113
+ | `NEXT_PUBLIC_DOTCMS_SITE_AUTH` | The Site Auth from the Content Analytics app in dotCMS |
114
+
115
+ ### 2. Start the SDK once
116
+
117
+ Create `src/instrumentation-client.ts`. Next.js runs this file once in the browser, before your app becomes interactive:
118
+
119
+ ```ts
120
+ // src/instrumentation-client.ts
121
+ import { dotEvents } from '@dotcms/events';
122
+
123
+ dotEvents.init({
124
+ dotcmsUrl: process.env.NEXT_PUBLIC_DOTCMS_HOST!,
125
+ siteAuth: process.env.NEXT_PUBLIC_DOTCMS_SITE_AUTH!,
126
+ impressions: true,
127
+ clicks: true
128
+ });
129
+ ```
130
+
131
+ That's all pageviews, impressions, clicks and experiments need. Impressions and clicks are optional; pageviews and experiments are on by default.
132
+
133
+ ### 3. Record conversions
134
+
135
+ Import the same object wherever a visitor completes a goal, and record the conversion once it succeeds:
136
+
137
+ ```tsx
138
+ 'use client';
139
+
140
+ import { dotEvents } from '@dotcms/events';
141
+
142
+ export function SignupForm() {
143
+ const onSubmit = async (event: React.FormEvent) => {
144
+ event.preventDefault();
145
+ await createAccount(); // your own code
146
+
147
+ // Only after the goal is reached, never on the attempt
148
+ dotEvents.conversion('signup');
149
+ };
150
+
151
+ return <form onSubmit={onSubmit}>{/* fields */}</form>;
152
+ }
153
+ ```
154
+
155
+ ### 4. Run experiments on your pages
156
+
157
+ Wrap the content your experiments change in `DotCMSExperiment`, and pass the URL's `variantName` to the page request:
158
+
159
+ ```tsx
160
+ // src/app/[[...slug]]/page.tsx
161
+ import { DotCMSExperiment } from '@dotcms/events/react';
162
+
163
+ import { dotCMSClient } from '@/lib/dotCMSClient'; // your @dotcms/client instance
164
+
165
+ interface PageProps {
166
+ params: Promise<{ slug?: string[] }>;
167
+ searchParams: Promise<{ variantName?: string }>;
168
+ }
169
+
170
+ export default async function Page({ params, searchParams }: PageProps) {
171
+ const { slug } = await params;
172
+ const { variantName } = await searchParams;
173
+ const path = slug?.length ? `/${slug.join('/')}` : '/';
174
+
175
+ // variantName makes dotCMS render the visitor's variant
176
+ const pageContent = await dotCMSClient.page.get(path, { variantName });
177
+
178
+ return (
179
+ <DotCMSExperiment page={pageContent.pageAsset}>
180
+ <MyPage pageContent={pageContent} />
181
+ </DotCMSExperiment>
182
+ );
183
+ }
184
+ ```
185
+
186
+ `MyPage` is your view of the page, for example one that renders it with `DotCMSLayoutBody` from `@dotcms/react`. On a page that runs no experiment, `DotCMSExperiment` renders its children as they are, so you can wrap every page the same way.
187
+
188
+ ### Example Project
189
+
190
+ The [Next.js experiments example](https://github.com/dotCMS/core/tree/main/examples/nextjs-experiments) runs on this package. It shows:
191
+
192
+ - The `init` call in `instrumentation-client.ts`, with the configuration in one file
193
+ - Pageviews, impressions and clicks on every page
194
+ - Experiments on the home page and on a blog listing, including content that streams in
195
+ - A conversion from a contact form
196
+
197
+ ## How It Works
198
+
199
+ ### Pageviews
200
+
201
+ A pageview is sent when the page first loads, on every client-side navigation, and when the visitor returns to a page with the Back or Forward button. Each one carries the page's URL, title and referrer, the campaign (UTM) parameters, and the visitor's device and screen.
202
+
203
+ To send them yourself instead, set `autoPageView: false` and call [`dotEvents.pageView()`](#doteventspageviewdata).
204
+
205
+ ### Content Impressions
206
+
207
+ With `impressions` on, the SDK records each contentlet the visitor actually sees: at least half of it in view, for at least 750 ms. Each contentlet counts once per page view, and nothing counts while the tab is hidden or the window is behind others.
208
+
209
+ Impressions and clicks need the contentlets to carry dotCMS's attributes (the `dotcms-contentlet` class and its `data-dot-*` attributes). The dotCMS React, Angular and Vue SDKs add them on their own while this SDK is active, so pages rendered with `DotCMSLayoutBody` need nothing more.
210
+
211
+ ### Content Clicks
212
+
213
+ With `clicks` on, the SDK records clicks on the links and buttons inside your contentlets: which contentlet, the element's text and link, and where the contentlet sits on the page. Repeated clicks on the same contentlet within 300 ms count once.
214
+
215
+ ### Conversions
216
+
217
+ A conversion records that a visitor reached a goal. dotCMS stores its name and the page it happened on. Record it only once the goal is reached: a completed purchase, a finished download, a created account, not the click that started it.
218
+
219
+ ### Experiments
220
+
221
+ Experiments are on by default. When a visitor opens a page that runs an experiment:
222
+
223
+ 1. The content the experiment changes stays hidden for a moment, so the visitor never sees the wrong variant.
224
+ 2. The SDK asks dotCMS which variant the visitor gets, and remembers the answer in the browser.
225
+ 3. If the page shows another variant, the SDK sends the visitor to theirs: the same URL with `?variantName=<variant>`.
226
+ 4. Otherwise it shows the content.
227
+
228
+ A returning visitor is sent to their variant right away, before the page finishes loading. If dotCMS takes more than 3 seconds to answer a new visitor, the original content shows, and the assignment applies from the next page on.
229
+
230
+ Events sent from a page that runs an experiment the visitor is in carry the experiment and the visitor's variant, together with the other experiments the visitor joined during the same session. Events from other pages carry none.
231
+
232
+ ### How Events Are Sent
233
+
234
+ Events are sent in batches: every 5 seconds, or as soon as 15 are waiting. When the visitor switches tabs, closes the tab or leaves the page, whatever is waiting goes out right away. Nothing is retried, and nothing the SDK does can throw an error into your page: failures go to [`onError`](#errors).
235
+
236
+ ### Visitors and Sessions
237
+
238
+ Each visitor gets an anonymous id, kept in the browser's localStorage, and each visit a session id. A session ends after 30 minutes without activity, or at midnight (UTC). The SDK sets no cookies.
239
+
240
+ ### In the Universal Visual Editor
241
+
242
+ Inside the dotCMS Universal Visual Editor, the SDK sends nothing, and experiment content shows as it is, so editors can work on the page. On the server, every call does nothing.
243
+
244
+ ## Configuration
245
+
246
+ ### Config Options
247
+
248
+ | Option | Type | Required | Default | Description |
249
+ | -------------- | ---------------------------------------- | -------- | ------- | --------------------------------------------------------------------------- |
250
+ | `dotcmsUrl` | `string` | Yes | — | The URL of your dotCMS instance, the same value `createDotCMSClient` takes |
251
+ | `siteAuth` | `string` | Yes | — | The Site Auth from the Content Analytics app |
252
+ | `autoPageView` | `boolean` | No | `true` | Send pageviews automatically |
253
+ | `experiments` | `boolean \| DotCMSEventsExperimentsConfig` | No | `true` | Run experiments; `false` turns them off |
254
+ | `impressions` | `boolean \| DotCMSEventsImpressionsConfig` | No | `false` | Record content impressions |
255
+ | `clicks` | `boolean` | No | `false` | Record content clicks |
256
+ | `queue` | `boolean \| DotCMSEventsQueueConfig` | No | `true` | Send events in batches; `false` sends each one at once |
257
+ | `debug` | `boolean` | No | `false` | Log what the SDK does to the browser console |
258
+ | `logLevel` | `'debug' \| 'info' \| 'warn' \| 'error'` | No | — | The least important level the console shows |
259
+ | `onError` | `(error: DotCMSEventsError) => void` | No | — | Called when something fails that the page can't see |
260
+
261
+ ### Impressions
262
+
263
+ `true` turns impressions on with the defaults. An object changes them:
264
+
265
+ | Option | Default | Description |
266
+ | --------------------- | ------- | ------------------------------------------------------------------ |
267
+ | `visibilityThreshold` | `0.5` | How much of the contentlet must be in view, from `0` to `1` |
268
+ | `dwellMs` | `750` | How long, in milliseconds, it must stay in view |
269
+ | `maxNodes` | `100` | The most contentlets tracked on a page |
270
+
271
+ ```ts
272
+ dotEvents.init({
273
+ dotcmsUrl: process.env.NEXT_PUBLIC_DOTCMS_HOST!,
274
+ siteAuth: process.env.NEXT_PUBLIC_DOTCMS_SITE_AUTH!,
275
+ impressions: { visibilityThreshold: 0.7, dwellMs: 1000 }
276
+ });
277
+ ```
278
+
279
+ ### Experiments
280
+
281
+ | Option | Default | Description |
282
+ | --------- | ------- | --------------------------------------------------------------------------------------------------------------------------------- |
283
+ | `timeout` | `3000` | How long, in milliseconds, a new visitor's page waits for their variant before showing the original. 3000 is also the most: past it the content shows on its own |
284
+
285
+ ### Queue
286
+
287
+ | Option | Default | Description |
288
+ | ---------------- | ------- | -------------------------------------------- |
289
+ | `eventBatchSize` | `15` | The most events in one batch |
290
+ | `flushInterval` | `5000` | How often, in milliseconds, a batch is sent |
291
+
292
+ ### Errors
293
+
294
+ `onError` receives what failed, with a `code`:
295
+
296
+ | Code | When |
297
+ | ------------- | ------------------------------------------------------------------------------------------------------------ |
298
+ | `REJECTED` | dotCMS refused an events request, or some of its events. `status` and `detail` say why |
299
+ | `NETWORK` | An events request got no answer |
300
+ | `EXPERIMENTS` | Asking dotCMS for the visitor's variant failed. A 403 means experiments are off for the site; the SDK stops asking for a day |
301
+
302
+ ```ts
303
+ dotEvents.init({
304
+ dotcmsUrl: process.env.NEXT_PUBLIC_DOTCMS_HOST!,
305
+ siteAuth: process.env.NEXT_PUBLIC_DOTCMS_SITE_AUTH!,
306
+ onError: (error) => console.warn(`dotCMS events: ${error.code}`, error.message)
307
+ });
308
+ ```
309
+
310
+ ## Usage
311
+
312
+ ### Next.js App Router
313
+
314
+ Follow the [Quick Start](#quick-start-nextjs). An experiment page also needs to:
315
+
316
+ - **Pass `variantName` to the page request.** Without it, the variant's URL shows the original content.
317
+ - **Render on every request.** Reading `searchParams` does that. A page built ahead of time can't run an experiment.
318
+ - **Use `DotCMSExperiment` in a server component when it can.** It then adds no JavaScript to the browser; in a client component it adds about 1.5 KB.
319
+
320
+ Content that streams in, behind `<Suspense>` or `loading.tsx`, works too: the SDK decides it when it arrives. On a site with a nonce-based Content-Security-Policy, pass the nonce: `<DotCMSExperiment page={pageAsset} nonce={nonce}>`.
321
+
322
+ ### Other React Apps
323
+
324
+ Call `init` once, where your app starts, before it renders. `DotCMSExperiment` works the same way.
325
+
326
+ ### Other Frameworks
327
+
328
+ `dotEvents` works with any framework. To run experiments, print what `experimentMarkup` returns in the HTML your server sends:
329
+
330
+ ```ts
331
+ import { experimentMarkup } from '@dotcms/events/markup';
332
+
333
+ const markup = experimentMarkup(pageAsset); // null when the page runs no experiment
334
+
335
+ // <style>{markup.style}</style>
336
+ // <script>{markup.script}</script>
337
+ // <div {...markup.attributes}>the content the experiment changes</div>
338
+ ```
339
+
340
+ ### Traditional Pages
341
+
342
+ Pages dotCMS renders itself (VTL) don't need this package: dotCMS adds the tracking to them on its own. You turn it on and configure it in the Content Analytics app.
343
+
344
+ ### Plain Scripts
345
+
346
+ After `init`, any script on the page can reach the same object as `window.dotEvents`:
347
+
348
+ ```html
349
+ <script>
350
+ window.dotEvents?.conversion('newsletter');
351
+ </script>
352
+ ```
353
+
354
+ ### Sending Pageviews Yourself
355
+
356
+ With `autoPageView: false`, call `dotEvents.pageView()` on each page, optionally with your own data. Call it as well when the browser brings back a page the visitor returns to with the Back button, which it can do without loading it again:
357
+
358
+ ```ts
359
+ dotEvents.pageView({ campaign: 'spring' });
360
+
361
+ window.addEventListener('pageshow', (event) => {
362
+ if (event.persisted) {
363
+ dotEvents.pageView();
364
+ }
365
+ });
366
+ ```
367
+
368
+ ## API Reference
369
+
370
+ ### `dotEvents.init(config)`
371
+
372
+ Starts the SDK with your [configuration](#config-options). Call it once, as early as possible. A second call with the same configuration does nothing; one with another configuration is ignored with a warning. Calls made before the SDK is ready wait and run once it is.
373
+
374
+ ### `dotEvents.conversion(name)`
375
+
376
+ Records a conversion with its `name`. dotCMS records the name and the page; it accepts no other data.
377
+
378
+ ### `dotEvents.pageView(data?)`
379
+
380
+ Sends a pageview, with optional custom data. Only needed with `autoPageView: false`.
381
+
382
+ ### `DotCMSExperiment`
383
+
384
+ ```tsx
385
+ import { DotCMSExperiment } from '@dotcms/events/react';
386
+ ```
387
+
388
+ | Prop | Type | Required | Description |
389
+ | ----------- | ----------- | -------- | -------------------------------------------------------------------- |
390
+ | `page` | page asset | Yes | The page asset, requested with the URL's `variantName` |
391
+ | `children` | `ReactNode` | Yes | What the experiment changes: the page's layout, or part of it |
392
+ | `nonce` | `string` | No | The nonce of a nonce-based Content-Security-Policy |
393
+ | `className` | `string` | No | A class for the element that wraps the content |
394
+
395
+ ### `experimentMarkup(page)`
396
+
397
+ ```ts
398
+ import { experimentMarkup } from '@dotcms/events/markup';
399
+ ```
400
+
401
+ Returns what a server-rendered page prints to run its experiment, or `null` when the page runs none: `style` and `script`, which go before the content, and `attributes`, for the element that wraps it.
402
+
403
+ ### Types
404
+
405
+ Every type the options, methods and errors use comes from the main entry:
406
+
407
+ ```ts
408
+ import type { DotCMSEventsConfig, DotCMSEventsError } from '@dotcms/events';
409
+ ```
410
+
411
+ ## Migrating from @dotcms/analytics and @dotcms/experiments
412
+
413
+ | Before | With `@dotcms/events` |
414
+ | ------------------------------------------------------------- | ------------------------------------------------------------------------------- |
415
+ | `<DotContentAnalytics config={...} />` in the root layout | `dotEvents.init({...})` in `src/instrumentation-client.ts` |
416
+ | `server` in the analytics config | `dotcmsUrl` |
417
+ | `useContentAnalytics(config).conversion(name, data)` | `dotEvents.conversion(name)`: dotCMS records the name only |
418
+ | `useContentAnalytics(config).pageView(data)` | `dotEvents.pageView(data)`, with `autoPageView: false` |
419
+ | `useContentAnalytics(config).track(name, data)` | Not available: dotCMS accepts pageviews, conversions, impressions and clicks only |
420
+ | `withExperiments(DotCMSLayoutBody, config)` | `<DotCMSExperiment page={pageAsset}>` around the content |
421
+ | A separate experiments config, with its own key | The same `init` call: experiments are on by default |
422
+
423
+ Visitor and session ids are stored under new names (`dot_events_*`), so visitors start with new ids after the switch.
424
+
425
+ ## Under the Hood
426
+
427
+ ### What It Stores
428
+
429
+ No cookies. Everything carries the `dot_events_` prefix:
430
+
431
+ | Key | Storage | Contents |
432
+ | ---------------------------------- | -------------- | ------------------------------------------------------------ |
433
+ | `dot_events_user_id` | localStorage | The visitor's anonymous id |
434
+ | `dot_events_experiments` | localStorage | The visitor's experiment assignments |
435
+ | `dot_events_experiments_off_until` | localStorage | Set when experiments are off for the site, for a day |
436
+ | `dot_events_session_id` | sessionStorage | The session's id |
437
+ | `dot_events_tab_id` | sessionStorage | The tab's id |
438
+ | `dot_events_queue_<tab id>` | sessionStorage | Events not sent yet, kept until the next page sends them |
439
+ | `dot_events_experiments_checked` | sessionStorage | This tab already asked for the visitor's assignments |
440
+ | `dot_events_session_experiments` | sessionStorage | The experiments the visitor joined during the session |
441
+
442
+ ### Endpoints
443
+
444
+ | Request | What for |
445
+ | ------------------------------------------ | ----------------------------------------- |
446
+ | `POST {dotcmsUrl}/api/v1/analytics/content/event` | Every event |
447
+ | `POST {dotcmsUrl}/api/v1/experiments/isUserIncluded` | The visitor's experiment assignments |
448
+
449
+ ## Troubleshooting
450
+
451
+ ### Common Issues & Solutions
452
+
453
+ | Symptom | Likely cause | What to do |
454
+ | ------------------------------------------------- | ------------------------------------------------------------------------------ | ---------------------------------------------------------------------------------------------- |
455
+ | No events in the Network tab | Missing `dotcmsUrl` or `siteAuth`, or you're inside the Universal Visual Editor | Check both values, test outside the editor, and turn on `debug: true` |
456
+ | Environment variables are empty | Next.js only exposes variables that start with `NEXT_PUBLIC_` to the browser | Rename them, and restart the dev server after changing `.env.local` |
457
+ | `onError` reports `REJECTED` | dotCMS refused the events | Check that `siteAuth` is the Site Auth of the same site, and read `detail` |
458
+ | No impressions | `impressions` is off, the contentlets lack dotCMS's attributes, or the tab is hidden | Turn it on, render with a dotCMS SDK, and keep the page in view while testing |
459
+ | No clicks | `clicks` is off, or the element isn't a link or button inside a contentlet | Turn it on, and click a link or button inside a contentlet |
460
+ | The variant never shows | The route doesn't pass `variantName` to the page request, or the page is built ahead of time | Pass `variantName`, and render the page on every request |
461
+ | The page reloads once on a first visit | Expected: the visitor is being sent to their variant | Nothing to do |
462
+ | The experiment content appears after 3 seconds | dotCMS took longer than 3 seconds to answer | The original shows; the visitor gets their variant from the next page on |
463
+ | Nothing about experiments in the Network tab | The experiment isn't running in dotCMS, or experiments are off | Check the experiment's status in dotCMS, and look for `EXPERIMENTS` in `onError` |
464
+
465
+ ### Debugging Tips
466
+
467
+ 1. **Turn on debug mode.** `debug: true` logs what the SDK does to the browser console, and warns about setup mistakes, such as a route that ignores `variantName`.
468
+ 2. **Watch the Network tab.** Filter by `/api/v1/analytics/content/event` to see each batch of events, and by `isUserIncluded` to see the experiments check.
469
+ 3. **Check the storage.** In DevTools > Application, look for the `dot_events_*` keys in localStorage and sessionStorage. Clearing them makes you a new visitor, with a new experiment assignment.
470
+
471
+ ### Still Having Issues?
472
+
473
+ If you're still experiencing problems after trying these solutions:
474
+
475
+ 1. Search existing [GitHub issues](https://github.com/dotCMS/core/issues)
476
+ 2. Ask questions on the [community forum](https://community.dotcms.com/) to engage with other users.
477
+ 3. Create a new issue with:
478
+ - Detailed reproduction steps
479
+ - Environment information
480
+ - Error messages
481
+ - Code samples
482
+
483
+ ## Support
484
+
485
+ We offer multiple channels to get help with the dotCMS Events SDK:
486
+
487
+ - **GitHub Issues**: For bug reports and feature requests, please [open an issue](https://github.com/dotCMS/core/issues/new/choose) in the GitHub repository.
488
+ - **Community Forum**: Join our [community discussions](https://community.dotcms.com/) to ask questions and share solutions.
489
+ - **Stack Overflow**: Use the tag `dotcms-events` when posting questions.
490
+ - **Enterprise Support**: Enterprise customers can access premium support through the [dotCMS Support Portal](https://www.dotcms.com/support).
491
+
492
+ When reporting issues, please include:
493
+
494
+ - SDK version you're using
495
+ - Framework/library version (if applicable)
496
+ - Minimal reproduction steps
497
+ - Expected vs. actual behavior
498
+
499
+ ## Contributing
500
+
501
+ GitHub pull requests are the preferred method to contribute code to dotCMS. We welcome contributions to the dotCMS Events SDK! If you'd like to contribute, please follow these steps:
502
+
503
+ 1. Fork the repository [dotCMS/core](https://github.com/dotCMS/core)
504
+ 2. Create a feature branch (`git checkout -b feature/amazing-feature`)
505
+ 3. Commit your changes (`git commit -m 'Add some amazing feature'`)
506
+ 4. Push to the branch (`git push origin feature/amazing-feature`)
507
+ 5. Open a Pull Request
508
+
509
+ Please ensure your code follows the existing style and includes appropriate tests.
510
+
511
+ ## Licensing
512
+
513
+ dotCMS is available under either the [Business Source License 1.1 (BSL)](https://www.dotcms.com/bsl) or a commercial license.
514
+
515
+ Under the BSL, dotCMS can be used at no cost by individual developers, small businesses or agencies under $5M in total finances, and by larger organizations in non-production environments. Every BSL release automatically converts to GPL v3 four years after its release date. For full terms and FAQs, visit [dotcms.com/bsl](https://www.dotcms.com/bsl) and [dotcms.com/bsl-faq](https://www.dotcms.com/bsl-faq).
516
+
517
+ Production use in larger organizations, along with access to managed cloud, SLAs, support, and enterprise capabilities, is available under a commercial license from dotCMS. For details on commercial plans, features, and support options, see [dotcms.com/pricing](https://www.dotcms.com/pricing).
@@ -0,0 +1,108 @@
1
+ 'use strict';
2
+
3
+ /**
4
+ * Attribute on the element that wraps what an experiment varies, with the experiment's id.
5
+ * `DotCMSExperiment` and `experimentMarkup` put it there; the engine reads it.
6
+ */
7
+ const EXPERIMENT_ATTRIBUTE = 'data-dot-experiment';
8
+ /** Attribute with the variant the server rendered in that element. */
9
+ const VARIANT_ATTRIBUTE = 'data-dot-variant';
10
+ /** Name dotCMS gives the original variant. */
11
+ const DEFAULT_VARIANT = 'DEFAULT';
12
+ /** Query parameter the page API reads to render a variant. */
13
+ const VARIANT_QUERY_PARAM = 'variantName';
14
+ /** Endpoint that assigns the visitor to the running experiments. */
15
+ const IS_USER_INCLUDED_PATH = '/api/v1/experiments/isUserIncluded';
16
+ /**
17
+ * How long an experiment's content may stay hidden while its variant is decided. Past it the
18
+ * hiding rule shows the content, the boot script no longer redirects, and the engine's default
19
+ * wait ends.
20
+ */
21
+ const DECISION_TIMEOUT_MS = 3000;
22
+ /** Default wait for the assignment on the pages the engine decides itself. */
23
+ const DEFAULT_ASSIGNMENT_TIMEOUT_MS = DECISION_TIMEOUT_MS;
24
+ /** The request itself may run longer than the wait, so the next page can use its answer. */
25
+ const IS_USER_INCLUDED_REQUEST_TIMEOUT_MS = 10000;
26
+ const ONE_DAY_MS = 24 * 60 * 60 * 1000;
27
+ /** How long the page being replaced keeps its new requests waiting. */
28
+ const QUIET_MS = 5000;
29
+ /**
30
+ * Rule printed before an experiment's content: the marked element stays hidden until the
31
+ * variant is decided, and shows on its own after `DECISION_TIMEOUT_MS` if nothing decides it
32
+ * (JavaScript off, SDK missing).
33
+ */
34
+ const HIDING_RULE = `[${EXPERIMENT_ATTRIBUTE}]{visibility:hidden;animation:dotcms-experiment-timeout 0s linear ${DECISION_TIMEOUT_MS / 1000}s forwards}` +
35
+ `@keyframes dotcms-experiment-timeout{to{visibility:visible}}`;
36
+ const STORAGE_KEYS = {
37
+ /** localStorage: assignments and the experiment ids already evaluated. */
38
+ assignments: 'dot_events_experiments',
39
+ /** localStorage: until when the site answered 403 (Experiments off). */
40
+ disabledUntil: 'dot_events_experiments_off_until',
41
+ /** sessionStorage: this tab already asked `isUserIncluded`. */
42
+ checkedThisTab: 'dot_events_experiments_checked',
43
+ /** sessionStorage: the cumulative `context.experiments` of the analytics session. */
44
+ sessionExperiments: 'dot_events_session_experiments'
45
+ };
46
+ /** Window property where the boot script leaves its decision on the page it ran on. */
47
+ const BOOT_STATE_KEY = '__dotEventsExperimentBoot';
48
+ /** Id of the style element that reveals decided content. */
49
+ const REVEAL_STYLE_ID = 'dotcms-experiment-reveal';
50
+
51
+ /**
52
+ * Decides an experiment's content from what is stored. It is pure, and the boot script prints
53
+ * it with `Function.prototype.toString`, so it must not reference anything outside itself:
54
+ * the engine and the boot script run this same function.
55
+ *
56
+ * @param input - The page's experiment, the URL, the stored assignments and the shared names
57
+ * @returns What to do with the content
58
+ */
59
+ function decideVariant(input) {
60
+ if (input.disabled) {
61
+ return { kind: 'excluded' };
62
+ }
63
+ const stored = (input.stored || {});
64
+ const experiments = Array.isArray(stored.experiments)
65
+ ? stored.experiments
66
+ : [];
67
+ const evaluated = Array.isArray(stored.evaluatedIds) ? stored.evaluatedIds : [];
68
+ const assigned = experiments.find((experiment) => !!experiment &&
69
+ experiment.id === input.experimentId &&
70
+ (experiment.expiresAt || 0) > input.now);
71
+ const variant = assigned && assigned.variant && assigned.variant.name;
72
+ if (!variant) {
73
+ return evaluated.indexOf(input.experimentId) >= 0
74
+ ? { kind: 'excluded' }
75
+ : { kind: 'unknown' };
76
+ }
77
+ if (variant === input.rendered) {
78
+ return { kind: 'assigned', variant };
79
+ }
80
+ const url = new URL(input.href);
81
+ const requested = variant === input.defaultVariant ? null : variant;
82
+ if (url.searchParams.get(input.param) === requested) {
83
+ return { kind: 'ignored', variant };
84
+ }
85
+ if (requested) {
86
+ url.searchParams.set(input.param, requested);
87
+ }
88
+ else {
89
+ url.searchParams.delete(input.param);
90
+ }
91
+ return { kind: 'redirect', variant, url: url.toString() };
92
+ }
93
+
94
+ exports.BOOT_STATE_KEY = BOOT_STATE_KEY;
95
+ exports.DECISION_TIMEOUT_MS = DECISION_TIMEOUT_MS;
96
+ exports.DEFAULT_ASSIGNMENT_TIMEOUT_MS = DEFAULT_ASSIGNMENT_TIMEOUT_MS;
97
+ exports.DEFAULT_VARIANT = DEFAULT_VARIANT;
98
+ exports.EXPERIMENT_ATTRIBUTE = EXPERIMENT_ATTRIBUTE;
99
+ exports.HIDING_RULE = HIDING_RULE;
100
+ exports.IS_USER_INCLUDED_PATH = IS_USER_INCLUDED_PATH;
101
+ exports.IS_USER_INCLUDED_REQUEST_TIMEOUT_MS = IS_USER_INCLUDED_REQUEST_TIMEOUT_MS;
102
+ exports.ONE_DAY_MS = ONE_DAY_MS;
103
+ exports.QUIET_MS = QUIET_MS;
104
+ exports.REVEAL_STYLE_ID = REVEAL_STYLE_ID;
105
+ exports.STORAGE_KEYS = STORAGE_KEYS;
106
+ exports.VARIANT_ATTRIBUTE = VARIANT_ATTRIBUTE;
107
+ exports.VARIANT_QUERY_PARAM = VARIANT_QUERY_PARAM;
108
+ exports.decideVariant = decideVariant;