@shware/analytics 8.3.0 → 8.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +28 -16
- package/dist/hooks/use-uet-id-sync.cjs +19 -0
- package/dist/hooks/use-uet-id-sync.cjs.map +1 -0
- package/dist/hooks/use-uet-id-sync.d.cts +10 -0
- package/dist/hooks/use-uet-id-sync.d.cts.map +1 -0
- package/dist/hooks/use-uet-id-sync.d.mts +10 -0
- package/dist/hooks/use-uet-id-sync.d.mts.map +1 -0
- package/dist/hooks/use-uet-id-sync.mjs +18 -0
- package/dist/hooks/use-uet-id-sync.mjs.map +1 -0
- package/dist/hooks/use-web-analytics.cjs +11 -5
- package/dist/hooks/use-web-analytics.cjs.map +1 -1
- package/dist/hooks/use-web-analytics.d.cts +4 -1
- package/dist/hooks/use-web-analytics.d.cts.map +1 -1
- package/dist/hooks/use-web-analytics.d.mts +4 -1
- package/dist/hooks/use-web-analytics.d.mts.map +1 -1
- package/dist/hooks/use-web-analytics.mjs +11 -5
- package/dist/hooks/use-web-analytics.mjs.map +1 -1
- package/dist/next/index.cjs +40 -2
- package/dist/next/index.cjs.map +1 -1
- package/dist/next/index.d.cts +5 -1
- package/dist/next/index.d.cts.map +1 -1
- package/dist/next/index.d.mts +5 -1
- package/dist/next/index.d.mts.map +1 -1
- package/dist/next/index.mjs +41 -3
- package/dist/next/index.mjs.map +1 -1
- package/dist/react-router/index.cjs +27 -3
- package/dist/react-router/index.cjs.map +1 -1
- package/dist/react-router/index.d.cts +5 -1
- package/dist/react-router/index.d.cts.map +1 -1
- package/dist/react-router/index.d.mts +5 -1
- package/dist/react-router/index.d.mts.map +1 -1
- package/dist/react-router/index.mjs +27 -3
- package/dist/react-router/index.mjs.map +1 -1
- package/dist/schema/index.cjs +1 -0
- package/dist/schema/index.cjs.map +1 -1
- package/dist/schema/index.d.cts +4 -0
- package/dist/schema/index.d.cts.map +1 -1
- package/dist/schema/index.d.mts +4 -0
- package/dist/schema/index.d.mts.map +1 -1
- package/dist/schema/index.mjs +1 -0
- package/dist/schema/index.mjs.map +1 -1
- package/dist/server/index.d.cts +2 -2
- package/dist/server/index.d.mts +2 -2
- package/dist/server/microsoft-conversions-api.cjs +8 -8
- package/dist/server/microsoft-conversions-api.cjs.map +1 -1
- package/dist/server/microsoft-conversions-api.d.cts +15 -8
- package/dist/server/microsoft-conversions-api.d.cts.map +1 -1
- package/dist/server/microsoft-conversions-api.d.mts +15 -8
- package/dist/server/microsoft-conversions-api.d.mts.map +1 -1
- package/dist/server/microsoft-conversions-api.mjs +8 -8
- package/dist/server/microsoft-conversions-api.mjs.map +1 -1
- package/dist/server/openai-conversions-api.cjs +1 -1
- package/dist/server/openai-conversions-api.mjs +1 -1
- package/dist/server/reddit-conversions-api.cjs +1 -1
- package/dist/server/reddit-conversions-api.mjs +1 -1
- package/dist/tanstack/analytics.cjs +27 -3
- package/dist/tanstack/analytics.cjs.map +1 -1
- package/dist/tanstack/analytics.d.cts +5 -1
- package/dist/tanstack/analytics.d.cts.map +1 -1
- package/dist/tanstack/analytics.d.mts +5 -1
- package/dist/tanstack/analytics.d.mts.map +1 -1
- package/dist/tanstack/analytics.mjs +27 -3
- package/dist/tanstack/analytics.mjs.map +1 -1
- package/dist/third-parties/index.cjs +3 -1
- package/dist/third-parties/index.d.cts +2 -2
- package/dist/third-parties/index.d.mts +2 -2
- package/dist/third-parties/index.mjs +2 -2
- package/dist/third-parties/meta-pixel.cjs +1 -1
- package/dist/third-parties/meta-pixel.mjs +1 -1
- package/dist/third-parties/microsoft-uet.cjs +45 -17
- package/dist/third-parties/microsoft-uet.cjs.map +1 -1
- package/dist/third-parties/microsoft-uet.d.cts +13 -19
- package/dist/third-parties/microsoft-uet.d.cts.map +1 -1
- package/dist/third-parties/microsoft-uet.d.mts +13 -19
- package/dist/third-parties/microsoft-uet.d.mts.map +1 -1
- package/dist/third-parties/microsoft-uet.mjs +44 -18
- package/dist/third-parties/microsoft-uet.mjs.map +1 -1
- package/dist/third-parties/openai-pixel.cjs +1 -1
- package/dist/third-parties/openai-pixel.mjs +1 -1
- package/dist/third-parties/reddit-pixel.cjs +1 -1
- package/dist/third-parties/reddit-pixel.mjs +1 -1
- package/dist/track/types.d.cts +7 -0
- package/dist/track/types.d.cts.map +1 -1
- package/dist/track/types.d.mts +7 -0
- package/dist/track/types.d.mts.map +1 -1
- package/dist/web/index.cjs +25 -1
- package/dist/web/index.cjs.map +1 -1
- package/dist/web/index.d.cts.map +1 -1
- package/dist/web/index.d.mts.map +1 -1
- package/dist/web/index.mjs +25 -1
- package/dist/web/index.mjs.map +1 -1
- package/dist/web/page-key.cjs +53 -0
- package/dist/web/page-key.cjs.map +1 -0
- package/dist/web/page-key.d.cts +24 -0
- package/dist/web/page-key.d.cts.map +1 -0
- package/dist/web/page-key.d.mts +24 -0
- package/dist/web/page-key.d.mts.map +1 -0
- package/dist/web/page-key.mjs +52 -0
- package/dist/web/page-key.mjs.map +1 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -46,6 +46,15 @@ function Button() {
|
|
|
46
46
|
}
|
|
47
47
|
```
|
|
48
48
|
|
|
49
|
+
## Page views
|
|
50
|
+
|
|
51
|
+
`page_view` fires on a path or query change, never on a hash change — GA4's enhanced measurement,
|
|
52
|
+
Next.js's `usePathname` + `useSearchParams` pattern and PostHog's `history_change` all draw the
|
|
53
|
+
line there (`?page=2` is another page to a funnel, `#faq` is not). Tracking parameters (`utm_*`
|
|
54
|
+
and the ad click ids) are ignored when comparing, so a landing page that cleans them out of its
|
|
55
|
+
URL with `replaceState` does not count itself twice. `page_load_id` rotates on exactly the same
|
|
56
|
+
changes. The framework `Analytics` components wire this up.
|
|
57
|
+
|
|
49
58
|
## Backend API
|
|
50
59
|
|
|
51
60
|
- /analytics/tracks: track events
|
|
@@ -130,28 +139,30 @@ Microsoft recommends the UET tag and the Conversions API (CAPI) together. The tw
|
|
|
130
139
|
(`tagId`, `eventName`, `eventId`): the browser sends the internal event name as the UET action with
|
|
131
140
|
the event id as `event_id`, the server sends the same name as `eventName` with the same id.
|
|
132
141
|
|
|
133
|
-
Browser —
|
|
142
|
+
Browser — pass the tag id and the customer id to the framework `Analytics` component
|
|
143
|
+
(`<Analytics uetTagId="97267979" uetCustomerId="255004870" />` from `@shware/analytics/tanstack`,
|
|
144
|
+
`/next` or `/react-router`). It inlines Microsoft's snippet with `enableAutoSpaTracking: true` (the
|
|
145
|
+
tag owns page loads, `sendUETEvent` drops `page_view` and forwards everything else) and fires the
|
|
146
|
+
Conversions API's ID Sync pixel once per visit for the SDK visitor — anonymous visitors included,
|
|
147
|
+
they are who remarketing audiences are built from. Then:
|
|
134
148
|
|
|
135
149
|
```ts
|
|
136
|
-
import {
|
|
137
|
-
sendUETEvent,
|
|
138
|
-
setUETUser,
|
|
139
|
-
setUETConsent,
|
|
140
|
-
sendUETIdSync,
|
|
141
|
-
} from '@shware/analytics/third-parties';
|
|
150
|
+
import { sendUETEvent, setUETUser, setUETConsent } from '@shware/analytics/third-parties';
|
|
142
151
|
|
|
143
152
|
setupAnalytics({
|
|
144
153
|
thirdPartyTrackers: [sendUETEvent],
|
|
145
154
|
// Enhanced conversions: the tag hashes the email/phone itself before they leave the page.
|
|
146
|
-
thirdPartyUserSetters: [setUETUser
|
|
155
|
+
thirdPartyUserSetters: [setUETUser],
|
|
147
156
|
});
|
|
148
157
|
// Consent mode (EEA/UK/CH): push `default` before the tag loads, `update` on the visitor's choice.
|
|
149
|
-
setUETConsent('default', { ad_storage: 'denied' });
|
|
150
|
-
// ID Sync, once per session: ties the visitor id CAPI sends as `anonymousId` to Microsoft's ids.
|
|
151
|
-
// Required for remarketing built from CAPI events, recommended for measurement.
|
|
152
|
-
sendUETIdSync({ customerId: 255004870, visitorId: (await getVisitor()).id });
|
|
158
|
+
setUETConsent('default', { ad_storage: 'denied', wait_for_update: 2000 });
|
|
153
159
|
```
|
|
154
160
|
|
|
161
|
+
ID Sync ties the visitor id CAPI receives as `anonymousId` to Microsoft's ids — required for
|
|
162
|
+
remarketing built from CAPI events, recommended for measurement. `setUETUser` re-syncs on sign-in
|
|
163
|
+
with the user id hashed into `UID` (the same SHA-256 the server sends as `externalId`). Outside the
|
|
164
|
+
`Analytics` components, `configureUET({ customerId })` + `syncUETVisitor()` do the same.
|
|
165
|
+
|
|
155
166
|
Server — the token comes from the UET tag's "Use Conversions API" step (or the Campaign Management
|
|
156
167
|
API's `UetTagAuthKey/Query`; an account whose tag setup lacks that option has to ask Microsoft
|
|
157
168
|
Advertising support), `tagId` is the UET tag id:
|
|
@@ -180,9 +191,10 @@ await sendMicrosoftEvents(process.env.MS_ADS_CAPI_TOKEN, 97267979, events, userD
|
|
|
180
191
|
- em / ph: SHA-256 of the normalized email (dots and `+alias` stripped from the user part for every
|
|
181
192
|
domain, lowercase) and of the E.164 phone.
|
|
182
193
|
- `page_view` events are dropped by default (the tag already reports every page load); pass
|
|
183
|
-
`pageLoads: true` for a CAPI-only site to send them as `pageLoad` events
|
|
184
|
-
|
|
185
|
-
the revenue-only `custom` event a
|
|
186
|
-
|
|
194
|
+
`pageLoads: true` for a CAPI-only site to send them as `pageLoad` events, each `custom` event
|
|
195
|
+
then carrying the `pageLoadId` of the page load it happened on (the `page_load_id` tag, one
|
|
196
|
+
v4 UUID per page load). Not yet modelled in that mode: the revenue-only `custom` event a
|
|
197
|
+
destination-URL goal with variable revenue needs alongside its `pageLoad`. With the tag on the
|
|
198
|
+
page neither applies — the tag reports page loads itself.
|
|
187
199
|
|
|
188
200
|
- [Click IDs](https://learn.microsoft.com/en-us/linkedin/marketing/conversions/enabling-first-party-cookies?view=li-lms-2025-10&source=recommendations): get li_fat_id from url params and cookie
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
|
+
const require_third_parties_microsoft_uet = require("../third-parties/microsoft-uet.cjs");
|
|
3
|
+
let react = require("react");
|
|
4
|
+
//#region src/hooks/use-uet-id-sync.ts
|
|
5
|
+
/**
|
|
6
|
+
* Microsoft Advertising ID Sync from the framework `Analytics` components: record the customer
|
|
7
|
+
* id for `setUETUser` and sync the visitor once per mount — the first page view of the visit,
|
|
8
|
+
* which is where Microsoft wants it. No-op without a customer id.
|
|
9
|
+
*/
|
|
10
|
+
function useUETIdSync(customerId) {
|
|
11
|
+
(0, react.useEffect)(() => {
|
|
12
|
+
require_third_parties_microsoft_uet.configureUET({ customerId });
|
|
13
|
+
if (customerId !== void 0) require_third_parties_microsoft_uet.syncUETVisitor();
|
|
14
|
+
}, [customerId]);
|
|
15
|
+
}
|
|
16
|
+
//#endregion
|
|
17
|
+
exports.useUETIdSync = useUETIdSync;
|
|
18
|
+
|
|
19
|
+
//# sourceMappingURL=use-uet-id-sync.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"use-uet-id-sync.cjs","names":["syncUETVisitor"],"sources":["../../src/hooks/use-uet-id-sync.ts"],"sourcesContent":["import { useEffect } from 'react';\nimport { configureUET, syncUETVisitor } from '../third-parties/microsoft-uet';\n\n/**\n * Microsoft Advertising ID Sync from the framework `Analytics` components: record the customer\n * id for `setUETUser` and sync the visitor once per mount — the first page view of the visit,\n * which is where Microsoft wants it. No-op without a customer id.\n */\nexport function useUETIdSync(customerId: string | number | undefined) {\n useEffect(() => {\n configureUET({ customerId });\n if (customerId !== undefined) void syncUETVisitor();\n }, [customerId]);\n}\n"],"mappings":";;;;;;;;;AAQA,SAAgB,aAAa,YAAyC;CACpE,CAAA,GAAA,MAAA,UAAA,OAAgB;EACd,oCAAA,aAAa,EAAE,WAAW,CAAC;EAC3B,IAAI,eAAe,KAAA,GAAW,oCAAKA,eAAe;CACpD,GAAG,CAAC,UAAU,CAAC;AACjB"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
//#region src/hooks/use-uet-id-sync.d.ts
|
|
2
|
+
/**
|
|
3
|
+
* Microsoft Advertising ID Sync from the framework `Analytics` components: record the customer
|
|
4
|
+
* id for `setUETUser` and sync the visitor once per mount — the first page view of the visit,
|
|
5
|
+
* which is where Microsoft wants it. No-op without a customer id.
|
|
6
|
+
*/
|
|
7
|
+
declare function useUETIdSync(customerId: string | number | undefined): void;
|
|
8
|
+
//#endregion
|
|
9
|
+
export { useUETIdSync };
|
|
10
|
+
//# sourceMappingURL=use-uet-id-sync.d.cts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"use-uet-id-sync.d.cts","names":[],"sources":["../../src/hooks/use-uet-id-sync.ts"],"mappings":";;;;;;iBAQgB,aAAa"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
//#region src/hooks/use-uet-id-sync.d.ts
|
|
2
|
+
/**
|
|
3
|
+
* Microsoft Advertising ID Sync from the framework `Analytics` components: record the customer
|
|
4
|
+
* id for `setUETUser` and sync the visitor once per mount — the first page view of the visit,
|
|
5
|
+
* which is where Microsoft wants it. No-op without a customer id.
|
|
6
|
+
*/
|
|
7
|
+
declare function useUETIdSync(customerId: string | number | undefined): void;
|
|
8
|
+
//#endregion
|
|
9
|
+
export { useUETIdSync };
|
|
10
|
+
//# sourceMappingURL=use-uet-id-sync.d.mts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"use-uet-id-sync.d.mts","names":[],"sources":["../../src/hooks/use-uet-id-sync.ts"],"mappings":";;;;;;iBAQgB,aAAa"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { configureUET, syncUETVisitor } from "../third-parties/microsoft-uet.mjs";
|
|
2
|
+
import { useEffect } from "react";
|
|
3
|
+
//#region src/hooks/use-uet-id-sync.ts
|
|
4
|
+
/**
|
|
5
|
+
* Microsoft Advertising ID Sync from the framework `Analytics` components: record the customer
|
|
6
|
+
* id for `setUETUser` and sync the visitor once per mount — the first page view of the visit,
|
|
7
|
+
* which is where Microsoft wants it. No-op without a customer id.
|
|
8
|
+
*/
|
|
9
|
+
function useUETIdSync(customerId) {
|
|
10
|
+
useEffect(() => {
|
|
11
|
+
configureUET({ customerId });
|
|
12
|
+
if (customerId !== void 0) syncUETVisitor();
|
|
13
|
+
}, [customerId]);
|
|
14
|
+
}
|
|
15
|
+
//#endregion
|
|
16
|
+
export { useUETIdSync };
|
|
17
|
+
|
|
18
|
+
//# sourceMappingURL=use-uet-id-sync.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"use-uet-id-sync.mjs","names":[],"sources":["../../src/hooks/use-uet-id-sync.ts"],"sourcesContent":["import { useEffect } from 'react';\nimport { configureUET, syncUETVisitor } from '../third-parties/microsoft-uet';\n\n/**\n * Microsoft Advertising ID Sync from the framework `Analytics` components: record the customer\n * id for `setUETUser` and sync the visitor once per mount — the first page view of the visit,\n * which is where Microsoft wants it. No-op without a customer id.\n */\nexport function useUETIdSync(customerId: string | number | undefined) {\n useEffect(() => {\n configureUET({ customerId });\n if (customerId !== undefined) void syncUETVisitor();\n }, [customerId]);\n}\n"],"mappings":";;;;;;;;AAQA,SAAgB,aAAa,YAAyC;CACpE,gBAAgB;EACd,aAAa,EAAE,WAAW,CAAC;EAC3B,IAAI,eAAe,KAAA,GAAW,eAAoB;CACpD,GAAG,CAAC,UAAU,CAAC;AACjB"}
|
|
@@ -4,6 +4,7 @@ const require_constants_storage = require("../constants/storage.cjs");
|
|
|
4
4
|
const require_setup_session = require("../setup/session.cjs");
|
|
5
5
|
const require_track_index = require("../track/index.cjs");
|
|
6
6
|
const require_hooks_use_previous = require("./use-previous.cjs");
|
|
7
|
+
const require_web_page_key = require("../web/page-key.cjs");
|
|
7
8
|
let _shware_utils = require("@shware/utils");
|
|
8
9
|
let react = require("react");
|
|
9
10
|
//#region src/hooks/use-web-analytics.ts
|
|
@@ -51,13 +52,18 @@ function onVisibilityChange() {
|
|
|
51
52
|
* 1. send session_start event when the page is loaded
|
|
52
53
|
* 2. send scroll event when the user scrolls more than 90% of the page
|
|
53
54
|
* 3. send user_engagement event when the page is hidden or the user is not focused
|
|
55
|
+
* 4. send page_view when the page changes — path or query, see `getPageKey`; `search` is the
|
|
56
|
+
* router's query string (`?a=1`, or `''`), required so no caller forgets that the query is
|
|
57
|
+
* part of what makes a page
|
|
54
58
|
*/
|
|
55
|
-
function useWebAnalytics(pathname) {
|
|
59
|
+
function useWebAnalytics(pathname, search) {
|
|
60
|
+
const page = require_web_page_key.getPageKey(pathname, search);
|
|
61
|
+
const prevPage = require_hooks_use_previous.usePrevious(page);
|
|
56
62
|
const prevPathname = require_hooks_use_previous.usePrevious(pathname);
|
|
57
63
|
const hasSendScroll = (0, react.useRef)(false);
|
|
58
64
|
(0, react.useEffect)(() => {
|
|
59
65
|
hasSendScroll.current = false;
|
|
60
|
-
}, [
|
|
66
|
+
}, [page]);
|
|
61
67
|
(0, react.useEffect)(() => {
|
|
62
68
|
const session = require_setup_session.getSession();
|
|
63
69
|
sendFirstVisit(pathname);
|
|
@@ -103,10 +109,10 @@ function useWebAnalytics(pathname) {
|
|
|
103
109
|
page_title: document.title,
|
|
104
110
|
page_referrer: document.referrer,
|
|
105
111
|
page_location: window.location.href,
|
|
106
|
-
previous_page_path:
|
|
107
|
-
engagement_time_msec:
|
|
112
|
+
previous_page_path: prevPage === void 0 ? void 0 : prevPathname,
|
|
113
|
+
engagement_time_msec: prevPage === void 0 ? void 0 : require_setup_session.getSession().flush()
|
|
108
114
|
});
|
|
109
|
-
}, [
|
|
115
|
+
}, [page]);
|
|
110
116
|
}
|
|
111
117
|
//#endregion
|
|
112
118
|
exports.useWebAnalytics = useWebAnalytics;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"use-web-analytics.cjs","names":["config","keys","getSession","usePrevious","useRef","throttle"],"sources":["../../src/hooks/use-web-analytics.ts"],"sourcesContent":["import { throttle } from '@shware/utils';\nimport { useEffect, useRef } from 'react';\nimport { keys } from '../constants/storage';\nimport { config } from '../setup/index';\nimport { getSession } from '../setup/session';\nimport { sendBeacon, track } from '../track/index';\nimport { usePrevious } from './use-previous';\n\nfunction sendFirstVisit(pathname: string) {\n if (config.storage.getItem(keys.first_visit_time)) return;\n track('first_visit', {\n page_path: pathname,\n page_title: document.title,\n page_referrer: document.referrer,\n page_location: window.location.href,\n });\n config.storage.setItem(keys.first_visit_time, new Date().toISOString());\n}\n\nfunction sendUserEngagement(trigger: 'pagehide' | 'visibilitychange') {\n const engagement_time_msec = getSession().flush();\n if (engagement_time_msec <= 0) return;\n sendBeacon('user_engagement', { engagement_time_msec, trigger });\n}\n\n/** Whether the event was actually sent — a zero-engagement crossing reports nothing. */\nfunction sendScroll(): boolean {\n const engagement_time_msec = getSession().flush();\n if (engagement_time_msec <= 0) return false;\n track('scroll', { engagement_time_msec });\n return true;\n}\n\nfunction getScrollPercent() {\n const scrollTop = window.scrollY || document.documentElement.scrollTop;\n const windowHeight = window.innerHeight;\n const docHeight = document.documentElement.scrollHeight;\n if (docHeight === 0) return 0;\n return ((scrollTop + windowHeight) * 100) / docHeight;\n}\n\nfunction onPageHide() {\n getSession().pagehide();\n sendUserEngagement('pagehide');\n}\n\nfunction onVisibilityChange() {\n getSession().visibilitychange(document.visibilityState);\n if (document.visibilityState === 'hidden') {\n sendUserEngagement('visibilitychange');\n }\n}\n\n/**\n * 1. send session_start event when the page is loaded\n * 2. send scroll event when the user scrolls more than 90% of the page\n * 3. send user_engagement event when the page is hidden or the user is not focused\n */\nexport function useWebAnalytics(pathname: string) {\n const prevPathname = usePrevious(pathname);\n\n // reset state when the
|
|
1
|
+
{"version":3,"file":"use-web-analytics.cjs","names":["config","keys","getSession","getPageKey","usePrevious","useRef","throttle"],"sources":["../../src/hooks/use-web-analytics.ts"],"sourcesContent":["import { throttle } from '@shware/utils';\nimport { useEffect, useRef } from 'react';\nimport { keys } from '../constants/storage';\nimport { config } from '../setup/index';\nimport { getSession } from '../setup/session';\nimport { sendBeacon, track } from '../track/index';\nimport { getPageKey } from '../web/page-key';\nimport { usePrevious } from './use-previous';\n\nfunction sendFirstVisit(pathname: string) {\n if (config.storage.getItem(keys.first_visit_time)) return;\n track('first_visit', {\n page_path: pathname,\n page_title: document.title,\n page_referrer: document.referrer,\n page_location: window.location.href,\n });\n config.storage.setItem(keys.first_visit_time, new Date().toISOString());\n}\n\nfunction sendUserEngagement(trigger: 'pagehide' | 'visibilitychange') {\n const engagement_time_msec = getSession().flush();\n if (engagement_time_msec <= 0) return;\n sendBeacon('user_engagement', { engagement_time_msec, trigger });\n}\n\n/** Whether the event was actually sent — a zero-engagement crossing reports nothing. */\nfunction sendScroll(): boolean {\n const engagement_time_msec = getSession().flush();\n if (engagement_time_msec <= 0) return false;\n track('scroll', { engagement_time_msec });\n return true;\n}\n\nfunction getScrollPercent() {\n const scrollTop = window.scrollY || document.documentElement.scrollTop;\n const windowHeight = window.innerHeight;\n const docHeight = document.documentElement.scrollHeight;\n if (docHeight === 0) return 0;\n return ((scrollTop + windowHeight) * 100) / docHeight;\n}\n\nfunction onPageHide() {\n getSession().pagehide();\n sendUserEngagement('pagehide');\n}\n\nfunction onVisibilityChange() {\n getSession().visibilitychange(document.visibilityState);\n if (document.visibilityState === 'hidden') {\n sendUserEngagement('visibilitychange');\n }\n}\n\n/**\n * 1. send session_start event when the page is loaded\n * 2. send scroll event when the user scrolls more than 90% of the page\n * 3. send user_engagement event when the page is hidden or the user is not focused\n * 4. send page_view when the page changes — path or query, see `getPageKey`; `search` is the\n * router's query string (`?a=1`, or `''`), required so no caller forgets that the query is\n * part of what makes a page\n */\nexport function useWebAnalytics(pathname: string, search: string) {\n const page = getPageKey(pathname, search);\n const prevPage = usePrevious(page);\n const prevPathname = usePrevious(pathname);\n\n // reset state when the page changes and send scroll when the user navigates to a new page\n const hasSendScroll = useRef(false);\n useEffect(() => {\n hasSendScroll.current = false;\n }, [page]);\n\n useEffect(() => {\n // One lookup for the whole effect: `addEventListener` and its matching\n // `removeEventListener` have to be handed the very same function.\n const session = getSession();\n\n sendFirstVisit(pathname);\n\n const onScroll = throttle(() => {\n session.updateAccumulator();\n if (hasSendScroll.current) return;\n // only send scroll when the user has scrolled more than 90% of the page\n if (getScrollPercent() < 90) return;\n // A crossing with no engaged time (a restored scroll position in an unfocused window) must\n // not consume the page's one shot: the flag is set only once the event is actually sent.\n hasSendScroll.current = sendScroll();\n }, 500);\n\n const checkpointEvents = ['mousedown', 'keydown', 'touchstart'];\n const checkpoint = throttle(session.updateAccumulator, 1000);\n\n window.addEventListener('focus', session.focus);\n window.addEventListener('blur', session.blur);\n window.addEventListener('scroll', onScroll, { passive: true });\n window.addEventListener('pageshow', session.pageshow);\n window.addEventListener('pagehide', onPageHide);\n document.addEventListener('visibilitychange', onVisibilityChange);\n\n // save checkpoint\n checkpointEvents.forEach((e) => {\n window.addEventListener(e, checkpoint, { passive: true, capture: true });\n });\n\n return () => {\n window.removeEventListener('focus', session.focus);\n window.removeEventListener('blur', session.blur);\n window.removeEventListener('scroll', onScroll);\n window.removeEventListener('pageshow', session.pageshow);\n window.removeEventListener('pagehide', onPageHide);\n document.removeEventListener('visibilitychange', onVisibilityChange);\n checkpointEvents.forEach((e) => window.removeEventListener(e, checkpoint, { capture: true }));\n\n onScroll.cancel();\n checkpoint.cancel();\n };\n }, []);\n\n useEffect(() => {\n // `page_path` stays the path — the query is in `page_location` — and the previous page is\n // named by its path too, so a query-only change reports the same path as its predecessor.\n track('page_view', {\n page_path: pathname,\n page_title: document.title,\n page_referrer: document.referrer,\n page_location: window.location.href,\n previous_page_path: prevPage === undefined ? undefined : prevPathname,\n engagement_time_msec: prevPage === undefined ? undefined : getSession().flush(),\n });\n }, [page]);\n}\n"],"mappings":"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"}
|
|
@@ -3,8 +3,11 @@
|
|
|
3
3
|
* 1. send session_start event when the page is loaded
|
|
4
4
|
* 2. send scroll event when the user scrolls more than 90% of the page
|
|
5
5
|
* 3. send user_engagement event when the page is hidden or the user is not focused
|
|
6
|
+
* 4. send page_view when the page changes — path or query, see `getPageKey`; `search` is the
|
|
7
|
+
* router's query string (`?a=1`, or `''`), required so no caller forgets that the query is
|
|
8
|
+
* part of what makes a page
|
|
6
9
|
*/
|
|
7
|
-
declare function useWebAnalytics(pathname: string): void;
|
|
10
|
+
declare function useWebAnalytics(pathname: string, search: string): void;
|
|
8
11
|
//#endregion
|
|
9
12
|
export { useWebAnalytics };
|
|
10
13
|
//# sourceMappingURL=use-web-analytics.d.cts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"use-web-analytics.d.cts","names":[],"sources":["../../src/hooks/use-web-analytics.ts"],"mappings":"
|
|
1
|
+
{"version":3,"file":"use-web-analytics.d.cts","names":[],"sources":["../../src/hooks/use-web-analytics.ts"],"mappings":";;;;;;;;;iBA8DgB,gBAAgB,kBAAkB"}
|
|
@@ -3,8 +3,11 @@
|
|
|
3
3
|
* 1. send session_start event when the page is loaded
|
|
4
4
|
* 2. send scroll event when the user scrolls more than 90% of the page
|
|
5
5
|
* 3. send user_engagement event when the page is hidden or the user is not focused
|
|
6
|
+
* 4. send page_view when the page changes — path or query, see `getPageKey`; `search` is the
|
|
7
|
+
* router's query string (`?a=1`, or `''`), required so no caller forgets that the query is
|
|
8
|
+
* part of what makes a page
|
|
6
9
|
*/
|
|
7
|
-
declare function useWebAnalytics(pathname: string): void;
|
|
10
|
+
declare function useWebAnalytics(pathname: string, search: string): void;
|
|
8
11
|
//#endregion
|
|
9
12
|
export { useWebAnalytics };
|
|
10
13
|
//# sourceMappingURL=use-web-analytics.d.mts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"use-web-analytics.d.mts","names":[],"sources":["../../src/hooks/use-web-analytics.ts"],"mappings":"
|
|
1
|
+
{"version":3,"file":"use-web-analytics.d.mts","names":[],"sources":["../../src/hooks/use-web-analytics.ts"],"mappings":";;;;;;;;;iBA8DgB,gBAAgB,kBAAkB"}
|
|
@@ -3,6 +3,7 @@ import { keys } from "../constants/storage.mjs";
|
|
|
3
3
|
import { getSession } from "../setup/session.mjs";
|
|
4
4
|
import { sendBeacon, track } from "../track/index.mjs";
|
|
5
5
|
import { usePrevious } from "./use-previous.mjs";
|
|
6
|
+
import { getPageKey } from "../web/page-key.mjs";
|
|
6
7
|
import { throttle } from "@shware/utils";
|
|
7
8
|
import { useEffect, useRef } from "react";
|
|
8
9
|
//#region src/hooks/use-web-analytics.ts
|
|
@@ -50,13 +51,18 @@ function onVisibilityChange() {
|
|
|
50
51
|
* 1. send session_start event when the page is loaded
|
|
51
52
|
* 2. send scroll event when the user scrolls more than 90% of the page
|
|
52
53
|
* 3. send user_engagement event when the page is hidden or the user is not focused
|
|
54
|
+
* 4. send page_view when the page changes — path or query, see `getPageKey`; `search` is the
|
|
55
|
+
* router's query string (`?a=1`, or `''`), required so no caller forgets that the query is
|
|
56
|
+
* part of what makes a page
|
|
53
57
|
*/
|
|
54
|
-
function useWebAnalytics(pathname) {
|
|
58
|
+
function useWebAnalytics(pathname, search) {
|
|
59
|
+
const page = getPageKey(pathname, search);
|
|
60
|
+
const prevPage = usePrevious(page);
|
|
55
61
|
const prevPathname = usePrevious(pathname);
|
|
56
62
|
const hasSendScroll = useRef(false);
|
|
57
63
|
useEffect(() => {
|
|
58
64
|
hasSendScroll.current = false;
|
|
59
|
-
}, [
|
|
65
|
+
}, [page]);
|
|
60
66
|
useEffect(() => {
|
|
61
67
|
const session = getSession();
|
|
62
68
|
sendFirstVisit(pathname);
|
|
@@ -102,10 +108,10 @@ function useWebAnalytics(pathname) {
|
|
|
102
108
|
page_title: document.title,
|
|
103
109
|
page_referrer: document.referrer,
|
|
104
110
|
page_location: window.location.href,
|
|
105
|
-
previous_page_path:
|
|
106
|
-
engagement_time_msec:
|
|
111
|
+
previous_page_path: prevPage === void 0 ? void 0 : prevPathname,
|
|
112
|
+
engagement_time_msec: prevPage === void 0 ? void 0 : getSession().flush()
|
|
107
113
|
});
|
|
108
|
-
}, [
|
|
114
|
+
}, [page]);
|
|
109
115
|
}
|
|
110
116
|
//#endregion
|
|
111
117
|
export { useWebAnalytics };
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"use-web-analytics.mjs","names":[],"sources":["../../src/hooks/use-web-analytics.ts"],"sourcesContent":["import { throttle } from '@shware/utils';\nimport { useEffect, useRef } from 'react';\nimport { keys } from '../constants/storage';\nimport { config } from '../setup/index';\nimport { getSession } from '../setup/session';\nimport { sendBeacon, track } from '../track/index';\nimport { usePrevious } from './use-previous';\n\nfunction sendFirstVisit(pathname: string) {\n if (config.storage.getItem(keys.first_visit_time)) return;\n track('first_visit', {\n page_path: pathname,\n page_title: document.title,\n page_referrer: document.referrer,\n page_location: window.location.href,\n });\n config.storage.setItem(keys.first_visit_time, new Date().toISOString());\n}\n\nfunction sendUserEngagement(trigger: 'pagehide' | 'visibilitychange') {\n const engagement_time_msec = getSession().flush();\n if (engagement_time_msec <= 0) return;\n sendBeacon('user_engagement', { engagement_time_msec, trigger });\n}\n\n/** Whether the event was actually sent — a zero-engagement crossing reports nothing. */\nfunction sendScroll(): boolean {\n const engagement_time_msec = getSession().flush();\n if (engagement_time_msec <= 0) return false;\n track('scroll', { engagement_time_msec });\n return true;\n}\n\nfunction getScrollPercent() {\n const scrollTop = window.scrollY || document.documentElement.scrollTop;\n const windowHeight = window.innerHeight;\n const docHeight = document.documentElement.scrollHeight;\n if (docHeight === 0) return 0;\n return ((scrollTop + windowHeight) * 100) / docHeight;\n}\n\nfunction onPageHide() {\n getSession().pagehide();\n sendUserEngagement('pagehide');\n}\n\nfunction onVisibilityChange() {\n getSession().visibilitychange(document.visibilityState);\n if (document.visibilityState === 'hidden') {\n sendUserEngagement('visibilitychange');\n }\n}\n\n/**\n * 1. send session_start event when the page is loaded\n * 2. send scroll event when the user scrolls more than 90% of the page\n * 3. send user_engagement event when the page is hidden or the user is not focused\n */\nexport function useWebAnalytics(pathname: string) {\n const prevPathname = usePrevious(pathname);\n\n // reset state when the
|
|
1
|
+
{"version":3,"file":"use-web-analytics.mjs","names":[],"sources":["../../src/hooks/use-web-analytics.ts"],"sourcesContent":["import { throttle } from '@shware/utils';\nimport { useEffect, useRef } from 'react';\nimport { keys } from '../constants/storage';\nimport { config } from '../setup/index';\nimport { getSession } from '../setup/session';\nimport { sendBeacon, track } from '../track/index';\nimport { getPageKey } from '../web/page-key';\nimport { usePrevious } from './use-previous';\n\nfunction sendFirstVisit(pathname: string) {\n if (config.storage.getItem(keys.first_visit_time)) return;\n track('first_visit', {\n page_path: pathname,\n page_title: document.title,\n page_referrer: document.referrer,\n page_location: window.location.href,\n });\n config.storage.setItem(keys.first_visit_time, new Date().toISOString());\n}\n\nfunction sendUserEngagement(trigger: 'pagehide' | 'visibilitychange') {\n const engagement_time_msec = getSession().flush();\n if (engagement_time_msec <= 0) return;\n sendBeacon('user_engagement', { engagement_time_msec, trigger });\n}\n\n/** Whether the event was actually sent — a zero-engagement crossing reports nothing. */\nfunction sendScroll(): boolean {\n const engagement_time_msec = getSession().flush();\n if (engagement_time_msec <= 0) return false;\n track('scroll', { engagement_time_msec });\n return true;\n}\n\nfunction getScrollPercent() {\n const scrollTop = window.scrollY || document.documentElement.scrollTop;\n const windowHeight = window.innerHeight;\n const docHeight = document.documentElement.scrollHeight;\n if (docHeight === 0) return 0;\n return ((scrollTop + windowHeight) * 100) / docHeight;\n}\n\nfunction onPageHide() {\n getSession().pagehide();\n sendUserEngagement('pagehide');\n}\n\nfunction onVisibilityChange() {\n getSession().visibilitychange(document.visibilityState);\n if (document.visibilityState === 'hidden') {\n sendUserEngagement('visibilitychange');\n }\n}\n\n/**\n * 1. send session_start event when the page is loaded\n * 2. send scroll event when the user scrolls more than 90% of the page\n * 3. send user_engagement event when the page is hidden or the user is not focused\n * 4. send page_view when the page changes — path or query, see `getPageKey`; `search` is the\n * router's query string (`?a=1`, or `''`), required so no caller forgets that the query is\n * part of what makes a page\n */\nexport function useWebAnalytics(pathname: string, search: string) {\n const page = getPageKey(pathname, search);\n const prevPage = usePrevious(page);\n const prevPathname = usePrevious(pathname);\n\n // reset state when the page changes and send scroll when the user navigates to a new page\n const hasSendScroll = useRef(false);\n useEffect(() => {\n hasSendScroll.current = false;\n }, [page]);\n\n useEffect(() => {\n // One lookup for the whole effect: `addEventListener` and its matching\n // `removeEventListener` have to be handed the very same function.\n const session = getSession();\n\n sendFirstVisit(pathname);\n\n const onScroll = throttle(() => {\n session.updateAccumulator();\n if (hasSendScroll.current) return;\n // only send scroll when the user has scrolled more than 90% of the page\n if (getScrollPercent() < 90) return;\n // A crossing with no engaged time (a restored scroll position in an unfocused window) must\n // not consume the page's one shot: the flag is set only once the event is actually sent.\n hasSendScroll.current = sendScroll();\n }, 500);\n\n const checkpointEvents = ['mousedown', 'keydown', 'touchstart'];\n const checkpoint = throttle(session.updateAccumulator, 1000);\n\n window.addEventListener('focus', session.focus);\n window.addEventListener('blur', session.blur);\n window.addEventListener('scroll', onScroll, { passive: true });\n window.addEventListener('pageshow', session.pageshow);\n window.addEventListener('pagehide', onPageHide);\n document.addEventListener('visibilitychange', onVisibilityChange);\n\n // save checkpoint\n checkpointEvents.forEach((e) => {\n window.addEventListener(e, checkpoint, { passive: true, capture: true });\n });\n\n return () => {\n window.removeEventListener('focus', session.focus);\n window.removeEventListener('blur', session.blur);\n window.removeEventListener('scroll', onScroll);\n window.removeEventListener('pageshow', session.pageshow);\n window.removeEventListener('pagehide', onPageHide);\n document.removeEventListener('visibilitychange', onVisibilityChange);\n checkpointEvents.forEach((e) => window.removeEventListener(e, checkpoint, { capture: true }));\n\n onScroll.cancel();\n checkpoint.cancel();\n };\n }, []);\n\n useEffect(() => {\n // `page_path` stays the path — the query is in `page_location` — and the previous page is\n // named by its path too, so a query-only change reports the same path as its predecessor.\n track('page_view', {\n page_path: pathname,\n page_title: document.title,\n page_referrer: document.referrer,\n page_location: window.location.href,\n previous_page_path: prevPage === undefined ? undefined : prevPathname,\n engagement_time_msec: prevPage === undefined ? undefined : getSession().flush(),\n });\n }, [page]);\n}\n"],"mappings":"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"}
|
package/dist/next/index.cjs
CHANGED
|
@@ -3,17 +3,29 @@ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
|
3
3
|
const require_runtime = require("../_virtual/_rolldown/runtime.cjs");
|
|
4
4
|
const require_track_index = require("../track/index.cjs");
|
|
5
5
|
const require_hooks_use_outbound_click_analytics = require("../hooks/use-outbound-click-analytics.cjs");
|
|
6
|
+
const require_hooks_use_uet_id_sync = require("../hooks/use-uet-id-sync.cjs");
|
|
6
7
|
const require_hooks_use_web_analytics = require("../hooks/use-web-analytics.cjs");
|
|
8
|
+
let react = require("react");
|
|
7
9
|
let next_navigation_js = require("next/navigation.js");
|
|
8
10
|
let next_script_js = require("next/script.js");
|
|
9
11
|
next_script_js = require_runtime.__toESM(next_script_js, 1);
|
|
10
12
|
let next_web_vitals_js = require("next/web-vitals.js");
|
|
11
13
|
let react_jsx_runtime = require("react/jsx-runtime");
|
|
12
14
|
//#region src/next/index.tsx
|
|
13
|
-
|
|
15
|
+
/**
|
|
16
|
+
* Its own component under a Suspense boundary: `useSearchParams` in a statically rendered route
|
|
17
|
+
* bails the whole tree out to client rendering unless something above it suspends, and Next
|
|
18
|
+
* fails the build for a page that lets it. The boundary is here so a host does not have to know.
|
|
19
|
+
*/
|
|
20
|
+
function PageViews() {
|
|
14
21
|
const pathname = (0, next_navigation_js.usePathname)();
|
|
15
|
-
|
|
22
|
+
const searchParams = (0, next_navigation_js.useSearchParams)();
|
|
23
|
+
require_hooks_use_web_analytics.useWebAnalytics(pathname, searchParams.toString());
|
|
24
|
+
return null;
|
|
25
|
+
}
|
|
26
|
+
function Analytics({ gaId, gaSrc, nonce, debugMode, metaPixelId, hotjarId, redditPixelId, linkedInPartnerId, uetTagId, uetCustomerId, facebookAppId, reportWebVitals = true }) {
|
|
16
27
|
require_hooks_use_outbound_click_analytics.useOutboundClickAnalytics();
|
|
28
|
+
require_hooks_use_uet_id_sync.useUETIdSync(uetCustomerId);
|
|
17
29
|
(0, next_web_vitals_js.useReportWebVitals)((metric) => {
|
|
18
30
|
if (!reportWebVitals) return;
|
|
19
31
|
const properties = {
|
|
@@ -28,6 +40,10 @@ function Analytics({ gaId, gaSrc, nonce, debugMode, metaPixelId, hotjarId, reddi
|
|
|
28
40
|
require_track_index.track(metric.name, properties);
|
|
29
41
|
});
|
|
30
42
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [
|
|
43
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(react.Suspense, {
|
|
44
|
+
fallback: null,
|
|
45
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(PageViews, {})
|
|
46
|
+
}),
|
|
31
47
|
facebookAppId && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("meta", {
|
|
32
48
|
property: "fb:app_id",
|
|
33
49
|
content: facebookAppId
|
|
@@ -111,6 +127,28 @@ function Analytics({ gaId, gaSrc, nonce, debugMode, metaPixelId, hotjarId, reddi
|
|
|
111
127
|
s.parentNode.insertBefore(b, s);
|
|
112
128
|
})(window.lintrk);
|
|
113
129
|
` }
|
|
130
|
+
}),
|
|
131
|
+
uetTagId && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(next_script_js.default, {
|
|
132
|
+
strategy: "afterInteractive",
|
|
133
|
+
id: "uet-tag",
|
|
134
|
+
dangerouslySetInnerHTML: { __html: `
|
|
135
|
+
(function (w, d, t, u, o) {
|
|
136
|
+
w[u] = w[u] || [], o.ts = (new Date).getTime();
|
|
137
|
+
var n = d.createElement(t);
|
|
138
|
+
n.src = "https://bat.bing.net/bat.js?ti=" + o.ti + ("uetq" != u ? "&q=" + u : ""),
|
|
139
|
+
n.async = 1, n.onload = n.onreadystatechange = function() {
|
|
140
|
+
var s = this.readyState;
|
|
141
|
+
s && "loaded" !== s && "complete" !== s ||
|
|
142
|
+
(o.q = w[u], w[u] = new UET(o), w[u].push("pageLoad"),
|
|
143
|
+
n.onload = n.onreadystatechange = null)
|
|
144
|
+
};
|
|
145
|
+
var i = d.getElementsByTagName(t)[0];
|
|
146
|
+
i.parentNode.insertBefore(n, i);
|
|
147
|
+
})(window, document, "script", "uetq", {
|
|
148
|
+
ti: "${uetTagId}",
|
|
149
|
+
enableAutoSpaTracking: true
|
|
150
|
+
});
|
|
151
|
+
` }
|
|
114
152
|
}),
|
|
115
153
|
hotjarId && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(next_script_js.default, {
|
|
116
154
|
id: "hotjar",
|
package/dist/next/index.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.cjs","names":["usePathname","Script"],"sources":["../../src/next/index.tsx"],"sourcesContent":["'use client';\n\nimport { usePathname } from 'next/navigation';\nimport Script from 'next/script';\nimport { useReportWebVitals } from 'next/web-vitals';\nimport { useOutboundClickAnalytics } from '../hooks/use-outbound-click-analytics';\nimport { useWebAnalytics } from '../hooks/use-web-analytics';\nimport type { PixelId as MetaPixelId } from '../track/fbq';\nimport type { GaId, GtmId } from '../track/gtag';\nimport { track } from '../track/index';\nimport type { PixelId as RedditPixelId } from '../track/rdt';\n\ninterface Props {\n gaId?: GaId;\n gaSrc?: string;\n gtmId?: GtmId;\n hotjarId?: `${number}`;\n metaPixelId?: MetaPixelId;\n redditPixelId?: RedditPixelId;\n linkedInPartnerId?: `${number}`;\n facebookAppId?: string;\n nonce?: string;\n debugMode?: boolean;\n reportWebVitals?: boolean;\n}\n\nexport function Analytics({\n gaId,\n gaSrc,\n nonce,\n debugMode,\n metaPixelId,\n hotjarId,\n redditPixelId,\n linkedInPartnerId,\n facebookAppId,\n reportWebVitals = true,\n}: Props) {\n
|
|
1
|
+
{"version":3,"file":"index.cjs","names":["usePathname","useSearchParams","Suspense","Script"],"sources":["../../src/next/index.tsx"],"sourcesContent":["'use client';\n\nimport { usePathname, useSearchParams } from 'next/navigation';\nimport Script from 'next/script';\nimport { useReportWebVitals } from 'next/web-vitals';\nimport { Suspense } from 'react';\nimport { useOutboundClickAnalytics } from '../hooks/use-outbound-click-analytics';\nimport { useUETIdSync } from '../hooks/use-uet-id-sync';\nimport { useWebAnalytics } from '../hooks/use-web-analytics';\nimport type { PixelId as MetaPixelId } from '../track/fbq';\nimport type { GaId, GtmId } from '../track/gtag';\nimport { track } from '../track/index';\nimport type { PixelId as RedditPixelId } from '../track/rdt';\n\ninterface Props {\n gaId?: GaId;\n gaSrc?: string;\n gtmId?: GtmId;\n hotjarId?: `${number}`;\n metaPixelId?: MetaPixelId;\n redditPixelId?: RedditPixelId;\n linkedInPartnerId?: `${number}`;\n /** Microsoft Advertising UET tag id. The tag reports page loads itself (`enableAutoSpaTracking`). */\n uetTagId?: `${number}`;\n /** Microsoft Advertising customer id (`cid` in the ads UI's URLs): enables the Conversions API's ID Sync pixel. */\n uetCustomerId?: `${number}`;\n facebookAppId?: string;\n nonce?: string;\n debugMode?: boolean;\n reportWebVitals?: boolean;\n}\n\n/**\n * Its own component under a Suspense boundary: `useSearchParams` in a statically rendered route\n * bails the whole tree out to client rendering unless something above it suspends, and Next\n * fails the build for a page that lets it. The boundary is here so a host does not have to know.\n */\nfunction PageViews() {\n const pathname = usePathname();\n const searchParams = useSearchParams();\n useWebAnalytics(pathname, searchParams.toString());\n return null;\n}\n\nexport function Analytics({\n gaId,\n gaSrc,\n nonce,\n debugMode,\n metaPixelId,\n hotjarId,\n redditPixelId,\n linkedInPartnerId,\n uetTagId,\n uetCustomerId,\n facebookAppId,\n reportWebVitals = true,\n}: Props) {\n useOutboundClickAnalytics();\n useUETIdSync(uetCustomerId);\n\n useReportWebVitals((metric) => {\n if (!reportWebVitals) return;\n const properties = {\n value: metric.delta,\n metric_id: metric.id,\n metric_value: metric.value,\n metric_delta: metric.delta,\n metric_rating: metric.rating,\n metric_navigation_type: metric.navigationType,\n non_interaction: true, // avoids affecting bounce rate.\n };\n track(metric.name, properties);\n });\n\n return (\n <>\n <Suspense fallback={null}>\n <PageViews />\n </Suspense>\n {facebookAppId && <meta property=\"fb:app_id\" content={facebookAppId} />}\n {gaId && (\n <>\n <Script\n id=\"gtag\"\n nonce={nonce}\n src={gaSrc ?? `https://www.googletagmanager.com/gtag/js?id=${gaId}`}\n />\n <Script\n nonce={nonce}\n id=\"gtag-init\"\n dangerouslySetInnerHTML={{\n __html: `\n window.dataLayer = window.dataLayer || [];\n function gtag(){dataLayer.push(arguments);}\n gtag('js', new Date());\n gtag('config', '${gaId}'${debugMode ? \" ,{ 'debug_mode': true }\" : ''});\n `,\n }}\n />\n </>\n )}\n {metaPixelId && (\n <Script\n id=\"meta-pixel\"\n strategy=\"afterInteractive\"\n dangerouslySetInnerHTML={{\n __html: `\n !(function (f, b, e, v, n, t, s) {\n if (f.fbq) return;\n n = f.fbq = function () {\n n.callMethod ? n.callMethod.apply(n, arguments) : n.queue.push(arguments);\n };\n if (!f._fbq) f._fbq = n;\n n.push = n;\n n.loaded = !0;\n n.version = '2.0';\n n.queue = [];\n t = b.createElement(e);\n t.async = !0;\n t.src = v;\n s = b.getElementsByTagName(e)[0];\n s.parentNode.insertBefore(t, s);\n })(window, document, 'script', 'https://connect.facebook.net/en_US/fbevents.js');\n fbq('init', '${metaPixelId}');\n fbq('track', 'PageView');`,\n }}\n />\n )}\n {redditPixelId && (\n <Script\n id=\"reddit-pixel\"\n strategy=\"afterInteractive\"\n dangerouslySetInnerHTML={{\n __html: `\n !function(w,d) {\n if(!w.rdt) {\n var p = w.rdt = function() {\n p.sendEvent ? p.sendEvent.apply(p,arguments) : p.callQueue.push(arguments)\n };\n p.callQueue = [];\n var t = d.createElement(\"script\");\n t.src = \"https://www.redditstatic.com/ads/pixel.js\";\n t.async = !0;\n var s = d.getElementsByTagName(\"script\")[0];\n s.parentNode.insertBefore(t,s)\n }\n }(window, document);\n rdt('init', '${redditPixelId}');\n rdt('track', 'PageVisit');`,\n }}\n />\n )}\n {linkedInPartnerId && (\n <Script\n id=\"linkedin-insight-tag\"\n strategy=\"afterInteractive\"\n dangerouslySetInnerHTML={{\n __html: `\n _linkedin_partner_id = \"${linkedInPartnerId}\";\n window._linkedin_data_partner_ids = window._linkedin_data_partner_ids || [];\n window._linkedin_data_partner_ids.push(_linkedin_partner_id);\n\n (function(l) {\n if (!l){\n window.lintrk = function(a,b){ \n window.lintrk.q.push([a,b])\n };\n window.lintrk.q=[]\n }\n var s = document.getElementsByTagName(\"script\")[0];\n var b = document.createElement(\"script\");\n b.type = \"text/javascript\";b.async = true;\n b.src = \"https://snap.licdn.com/li.lms-analytics/insight.min.js\";\n s.parentNode.insertBefore(b, s);\n })(window.lintrk);\n `,\n }}\n />\n )}\n {uetTagId && (\n <Script\n strategy=\"afterInteractive\"\n id=\"uet-tag\"\n dangerouslySetInnerHTML={{\n __html: `\n (function (w, d, t, u, o) {\n w[u] = w[u] || [], o.ts = (new Date).getTime();\n var n = d.createElement(t);\n n.src = \"https://bat.bing.net/bat.js?ti=\" + o.ti + (\"uetq\" != u ? \"&q=\" + u : \"\"),\n n.async = 1, n.onload = n.onreadystatechange = function() {\n var s = this.readyState;\n s && \"loaded\" !== s && \"complete\" !== s ||\n (o.q = w[u], w[u] = new UET(o), w[u].push(\"pageLoad\"),\n n.onload = n.onreadystatechange = null)\n };\n var i = d.getElementsByTagName(t)[0];\n i.parentNode.insertBefore(n, i);\n })(window, document, \"script\", \"uetq\", {\n ti: \"${uetTagId}\",\n enableAutoSpaTracking: true\n });\n `,\n }}\n />\n )}\n {hotjarId && (\n <Script\n id=\"hotjar\"\n strategy=\"afterInteractive\"\n dangerouslySetInnerHTML={{\n __html: `\n (function(h,o,t,j,a,r){\n h.hj=h.hj||function(){(h.hj.q=h.hj.q||[]).push(arguments)};\n h._hjSettings={hjid:${hotjarId},hjsv:6};\n a=o.getElementsByTagName('head')[0];\n r=o.createElement('script');r.async=1;\n r.src=t+h._hjSettings.hjid+j+h._hjSettings.hjsv;\n a.appendChild(r);\n })(window,document,'https://static.hotjar.com/c/hotjar-','.js?sv=');\n `,\n }}\n />\n )}\n </>\n );\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;AAqCA,SAAS,YAAY;CACnB,MAAM,YAAA,GAAWA,mBAAAA,YAAAA,CAAY;CAC7B,MAAM,gBAAA,GAAeC,mBAAAA,gBAAAA,CAAgB;CACrC,gCAAA,gBAAgB,UAAU,aAAa,SAAS,CAAC;CACjD,OAAO;AACT;AAEA,SAAgB,UAAU,EACxB,MACA,OACA,OACA,WACA,aACA,UACA,eACA,mBACA,UACA,eACA,eACA,kBAAkB,QACV;CACR,2CAAA,0BAA0B;CAC1B,8BAAA,aAAa,aAAa;CAE1B,CAAA,GAAA,mBAAA,mBAAA,EAAoB,WAAW;EAC7B,IAAI,CAAC,iBAAiB;EACtB,MAAM,aAAa;GACjB,OAAO,OAAO;GACd,WAAW,OAAO;GAClB,cAAc,OAAO;GACrB,cAAc,OAAO;GACrB,eAAe,OAAO;GACtB,wBAAwB,OAAO;GAC/B,iBAAiB;EACnB;EACA,oBAAA,MAAM,OAAO,MAAM,UAAU;CAC/B,CAAC;CAED,OACE,iBAAA,GAAA,kBAAA,KAAA,CAAA,kBAAA,UAAA,EAAA,UAAA;EACE,iBAAA,GAAA,kBAAA,IAAA,CAACC,MAAAA,UAAD;GAAU,UAAU;GAClB,UAAA,iBAAA,GAAA,kBAAA,IAAA,CAAC,WAAD,CAAY,CAAA;EACJ,CAAA;EACT,iBAAiB,iBAAA,GAAA,kBAAA,IAAA,CAAC,QAAD;GAAM,UAAS;GAAY,SAAS;EAAgB,CAAA;EACrE,QACC,iBAAA,GAAA,kBAAA,KAAA,CAAA,kBAAA,UAAA,EAAA,UAAA,CACE,iBAAA,GAAA,kBAAA,IAAA,CAACC,eAAAA,SAAD;GACE,IAAG;GACI;GACP,KAAK,SAAS,+CAA+C;EAC9D,CAAA,GACD,iBAAA,GAAA,kBAAA,IAAA,CAACA,eAAAA,SAAD;GACS;GACP,IAAG;GACH,yBAAyB,EACvB,QAAQ;;;;gCAIU,KAAK,GAAG,YAAY,6BAA6B,GAAG;cAExE;EACD,CAAA,CACD,EAAA,CAAA;EAEH,eACC,iBAAA,GAAA,kBAAA,IAAA,CAACA,eAAAA,SAAD;GACE,IAAG;GACH,UAAS;GACT,yBAAyB,EACvB,QAAQ;;;;;;;;;;;;;;;;;2BAiBO,YAAY;uCAE7B;EACD,CAAA;EAEF,iBACC,iBAAA,GAAA,kBAAA,IAAA,CAACA,eAAAA,SAAD;GACE,IAAG;GACH,UAAS;GACT,yBAAyB,EACvB,QAAQ;;;;;;;;;;;;;;2BAcO,cAAc;wCAE/B;EACD,CAAA;EAEF,qBACC,iBAAA,GAAA,kBAAA,IAAA,CAACA,eAAAA,SAAD;GACE,IAAG;GACH,UAAS;GACT,yBAAyB,EACvB,QAAQ;wCACoB,kBAAkB;;;;;;;;;;;;;;;;;gBAkBhD;EACD,CAAA;EAEF,YACC,iBAAA,GAAA,kBAAA,IAAA,CAACA,eAAAA,SAAD;GACE,UAAS;GACT,IAAG;GACH,yBAAyB,EACvB,QAAQ;;;;;;;;;;;;;;uBAcG,SAAS;;;cAItB;EACD,CAAA;EAEF,YACC,iBAAA,GAAA,kBAAA,IAAA,CAACA,eAAAA,SAAD;GACE,IAAG;GACH,UAAS;GACT,yBAAyB,EACvB,QAAQ;;;oCAGgB,SAAS;;;;;;cAOnC;EACD,CAAA;CAEH,EAAA,CAAA;AAEN"}
|
package/dist/next/index.d.cts
CHANGED
|
@@ -10,12 +10,16 @@ interface Props {
|
|
|
10
10
|
metaPixelId?: PixelId;
|
|
11
11
|
redditPixelId?: PixelId$1;
|
|
12
12
|
linkedInPartnerId?: `${number}`;
|
|
13
|
+
/** Microsoft Advertising UET tag id. The tag reports page loads itself (`enableAutoSpaTracking`). */
|
|
14
|
+
uetTagId?: `${number}`;
|
|
15
|
+
/** Microsoft Advertising customer id (`cid` in the ads UI's URLs): enables the Conversions API's ID Sync pixel. */
|
|
16
|
+
uetCustomerId?: `${number}`;
|
|
13
17
|
facebookAppId?: string;
|
|
14
18
|
nonce?: string;
|
|
15
19
|
debugMode?: boolean;
|
|
16
20
|
reportWebVitals?: boolean;
|
|
17
21
|
}
|
|
18
|
-
declare function Analytics({ gaId, gaSrc, nonce, debugMode, metaPixelId, hotjarId, redditPixelId, linkedInPartnerId, facebookAppId, reportWebVitals }: Props): import("react").JSX.Element;
|
|
22
|
+
declare function Analytics({ gaId, gaSrc, nonce, debugMode, metaPixelId, hotjarId, redditPixelId, linkedInPartnerId, uetTagId, uetCustomerId, facebookAppId, reportWebVitals }: Props): import("react").JSX.Element;
|
|
19
23
|
//#endregion
|
|
20
24
|
export { Analytics };
|
|
21
25
|
//# sourceMappingURL=index.d.cts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.cts","names":[],"sources":["../../src/next/index.tsx"],"mappings":";;;;
|
|
1
|
+
{"version":3,"file":"index.d.cts","names":[],"sources":["../../src/next/index.tsx"],"mappings":";;;;UAcU;EACR,OAAO;EACP;EACA,QAAQ;EACR;EACA,cAAc;EACd,gBAAgB;EAChB;;EAEA;;EAEA;EACA;EACA;EACA;EACA;;iBAec,YACd,MACA,OACA,OACA,WACA,aACA,UACA,eACA,mBACA,UACA,eACA,eACA,mBACC,wBAAK,IAAA"}
|
package/dist/next/index.d.mts
CHANGED
|
@@ -10,12 +10,16 @@ interface Props {
|
|
|
10
10
|
metaPixelId?: PixelId;
|
|
11
11
|
redditPixelId?: PixelId$1;
|
|
12
12
|
linkedInPartnerId?: `${number}`;
|
|
13
|
+
/** Microsoft Advertising UET tag id. The tag reports page loads itself (`enableAutoSpaTracking`). */
|
|
14
|
+
uetTagId?: `${number}`;
|
|
15
|
+
/** Microsoft Advertising customer id (`cid` in the ads UI's URLs): enables the Conversions API's ID Sync pixel. */
|
|
16
|
+
uetCustomerId?: `${number}`;
|
|
13
17
|
facebookAppId?: string;
|
|
14
18
|
nonce?: string;
|
|
15
19
|
debugMode?: boolean;
|
|
16
20
|
reportWebVitals?: boolean;
|
|
17
21
|
}
|
|
18
|
-
declare function Analytics({ gaId, gaSrc, nonce, debugMode, metaPixelId, hotjarId, redditPixelId, linkedInPartnerId, facebookAppId, reportWebVitals }: Props): import("react").JSX.Element;
|
|
22
|
+
declare function Analytics({ gaId, gaSrc, nonce, debugMode, metaPixelId, hotjarId, redditPixelId, linkedInPartnerId, uetTagId, uetCustomerId, facebookAppId, reportWebVitals }: Props): import("react").JSX.Element;
|
|
19
23
|
//#endregion
|
|
20
24
|
export { Analytics };
|
|
21
25
|
//# sourceMappingURL=index.d.mts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.mts","names":[],"sources":["../../src/next/index.tsx"],"mappings":";;;;
|
|
1
|
+
{"version":3,"file":"index.d.mts","names":[],"sources":["../../src/next/index.tsx"],"mappings":";;;;UAcU;EACR,OAAO;EACP;EACA,QAAQ;EACR;EACA,cAAc;EACd,gBAAgB;EAChB;;EAEA;;EAEA;EACA;EACA;EACA;EACA;;iBAec,YACd,MACA,OACA,OACA,WACA,aACA,UACA,eACA,mBACA,UACA,eACA,eACA,mBACC,wBAAK,IAAA"}
|
package/dist/next/index.mjs
CHANGED
|
@@ -1,16 +1,28 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import { track } from "../track/index.mjs";
|
|
3
3
|
import { useOutboundClickAnalytics } from "../hooks/use-outbound-click-analytics.mjs";
|
|
4
|
+
import { useUETIdSync } from "../hooks/use-uet-id-sync.mjs";
|
|
4
5
|
import { useWebAnalytics } from "../hooks/use-web-analytics.mjs";
|
|
5
|
-
import {
|
|
6
|
+
import { Suspense } from "react";
|
|
7
|
+
import { usePathname, useSearchParams } from "next/navigation.js";
|
|
6
8
|
import Script from "next/script.js";
|
|
7
9
|
import { useReportWebVitals } from "next/web-vitals.js";
|
|
8
10
|
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
9
11
|
//#region src/next/index.tsx
|
|
10
|
-
|
|
12
|
+
/**
|
|
13
|
+
* Its own component under a Suspense boundary: `useSearchParams` in a statically rendered route
|
|
14
|
+
* bails the whole tree out to client rendering unless something above it suspends, and Next
|
|
15
|
+
* fails the build for a page that lets it. The boundary is here so a host does not have to know.
|
|
16
|
+
*/
|
|
17
|
+
function PageViews() {
|
|
11
18
|
const pathname = usePathname();
|
|
12
|
-
|
|
19
|
+
const searchParams = useSearchParams();
|
|
20
|
+
useWebAnalytics(pathname, searchParams.toString());
|
|
21
|
+
return null;
|
|
22
|
+
}
|
|
23
|
+
function Analytics({ gaId, gaSrc, nonce, debugMode, metaPixelId, hotjarId, redditPixelId, linkedInPartnerId, uetTagId, uetCustomerId, facebookAppId, reportWebVitals = true }) {
|
|
13
24
|
useOutboundClickAnalytics();
|
|
25
|
+
useUETIdSync(uetCustomerId);
|
|
14
26
|
useReportWebVitals((metric) => {
|
|
15
27
|
if (!reportWebVitals) return;
|
|
16
28
|
const properties = {
|
|
@@ -25,6 +37,10 @@ function Analytics({ gaId, gaSrc, nonce, debugMode, metaPixelId, hotjarId, reddi
|
|
|
25
37
|
track(metric.name, properties);
|
|
26
38
|
});
|
|
27
39
|
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
40
|
+
/* @__PURE__ */ jsx(Suspense, {
|
|
41
|
+
fallback: null,
|
|
42
|
+
children: /* @__PURE__ */ jsx(PageViews, {})
|
|
43
|
+
}),
|
|
28
44
|
facebookAppId && /* @__PURE__ */ jsx("meta", {
|
|
29
45
|
property: "fb:app_id",
|
|
30
46
|
content: facebookAppId
|
|
@@ -108,6 +124,28 @@ function Analytics({ gaId, gaSrc, nonce, debugMode, metaPixelId, hotjarId, reddi
|
|
|
108
124
|
s.parentNode.insertBefore(b, s);
|
|
109
125
|
})(window.lintrk);
|
|
110
126
|
` }
|
|
127
|
+
}),
|
|
128
|
+
uetTagId && /* @__PURE__ */ jsx(Script, {
|
|
129
|
+
strategy: "afterInteractive",
|
|
130
|
+
id: "uet-tag",
|
|
131
|
+
dangerouslySetInnerHTML: { __html: `
|
|
132
|
+
(function (w, d, t, u, o) {
|
|
133
|
+
w[u] = w[u] || [], o.ts = (new Date).getTime();
|
|
134
|
+
var n = d.createElement(t);
|
|
135
|
+
n.src = "https://bat.bing.net/bat.js?ti=" + o.ti + ("uetq" != u ? "&q=" + u : ""),
|
|
136
|
+
n.async = 1, n.onload = n.onreadystatechange = function() {
|
|
137
|
+
var s = this.readyState;
|
|
138
|
+
s && "loaded" !== s && "complete" !== s ||
|
|
139
|
+
(o.q = w[u], w[u] = new UET(o), w[u].push("pageLoad"),
|
|
140
|
+
n.onload = n.onreadystatechange = null)
|
|
141
|
+
};
|
|
142
|
+
var i = d.getElementsByTagName(t)[0];
|
|
143
|
+
i.parentNode.insertBefore(n, i);
|
|
144
|
+
})(window, document, "script", "uetq", {
|
|
145
|
+
ti: "${uetTagId}",
|
|
146
|
+
enableAutoSpaTracking: true
|
|
147
|
+
});
|
|
148
|
+
` }
|
|
111
149
|
}),
|
|
112
150
|
hotjarId && /* @__PURE__ */ jsx(Script, {
|
|
113
151
|
id: "hotjar",
|