@shware/analytics 8.3.0 → 8.5.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 +23 -8
- 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 +38 -2
- package/dist/next/index.cjs.map +1 -1
- package/dist/next/index.d.cts +3 -1
- package/dist/next/index.d.cts.map +1 -1
- package/dist/next/index.d.mts +3 -1
- package/dist/next/index.d.mts.map +1 -1
- package/dist/next/index.mjs +39 -3
- package/dist/next/index.mjs.map +1 -1
- package/dist/react-router/index.cjs +25 -3
- package/dist/react-router/index.cjs.map +1 -1
- package/dist/react-router/index.d.cts +3 -1
- package/dist/react-router/index.d.cts.map +1 -1
- package/dist/react-router/index.d.mts +3 -1
- package/dist/react-router/index.d.mts.map +1 -1
- package/dist/react-router/index.mjs +25 -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 +7 -7
- 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 +7 -7
- package/dist/server/microsoft-conversions-api.mjs.map +1 -1
- package/dist/tanstack/analytics.cjs +25 -3
- package/dist/tanstack/analytics.cjs.map +1 -1
- package/dist/tanstack/analytics.d.cts +3 -1
- package/dist/tanstack/analytics.d.cts.map +1 -1
- package/dist/tanstack/analytics.d.mts +3 -1
- package/dist/tanstack/analytics.d.mts.map +1 -1
- package/dist/tanstack/analytics.mjs +25 -3
- package/dist/tanstack/analytics.mjs.map +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,7 +139,10 @@ 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 to the framework `Analytics` component (`<Analytics uetTagId="97267979" />`
|
|
143
|
+
from `@shware/analytics/tanstack`, `/next` or `/react-router`), which inlines Microsoft's snippet
|
|
144
|
+
with `enableAutoSpaTracking: true`: the tag owns page loads, `sendUETEvent` drops `page_view` and
|
|
145
|
+
forwards everything else. Then:
|
|
134
146
|
|
|
135
147
|
```ts
|
|
136
148
|
import {
|
|
@@ -146,10 +158,12 @@ setupAnalytics({
|
|
|
146
158
|
thirdPartyUserSetters: [setUETUser()],
|
|
147
159
|
});
|
|
148
160
|
// Consent mode (EEA/UK/CH): push `default` before the tag loads, `update` on the visitor's choice.
|
|
149
|
-
setUETConsent('default', { ad_storage: 'denied' });
|
|
161
|
+
setUETConsent('default', { ad_storage: 'denied', wait_for_update: 2000 });
|
|
150
162
|
// 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
|
-
|
|
163
|
+
// Required for remarketing built from CAPI events, recommended for measurement. `VID` must equal
|
|
164
|
+
// `anonymousId` and `UID` must equal `externalId`: pass the SDK visitor id and the raw user id —
|
|
165
|
+
// the pixel hashes the user id with the same SHA-256 the server sender uses.
|
|
166
|
+
sendUETIdSync({ customerId: 255004870, visitorId: (await getVisitor()).id, userId: user.id });
|
|
153
167
|
```
|
|
154
168
|
|
|
155
169
|
Server — the token comes from the UET tag's "Use Conversions API" step (or the Campaign Management
|
|
@@ -180,9 +194,10 @@ await sendMicrosoftEvents(process.env.MS_ADS_CAPI_TOKEN, 97267979, events, userD
|
|
|
180
194
|
- em / ph: SHA-256 of the normalized email (dots and `+alias` stripped from the user part for every
|
|
181
195
|
domain, lowercase) and of the E.164 phone.
|
|
182
196
|
- `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
|
-
|
|
197
|
+
`pageLoads: true` for a CAPI-only site to send them as `pageLoad` events, each `custom` event
|
|
198
|
+
then carrying the `pageLoadId` of the page load it happened on (the `page_load_id` tag, one
|
|
199
|
+
v4 UUID per page load). Not yet modelled in that mode: the revenue-only `custom` event a
|
|
200
|
+
destination-URL goal with variable revenue needs alongside its `pageLoad`. With the tag on the
|
|
201
|
+
page neither applies — the tag reports page loads itself.
|
|
187
202
|
|
|
188
203
|
- [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
|
|
@@ -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
|
@@ -4,15 +4,25 @@ 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
6
|
const require_hooks_use_web_analytics = require("../hooks/use-web-analytics.cjs");
|
|
7
|
+
let react = require("react");
|
|
7
8
|
let next_navigation_js = require("next/navigation.js");
|
|
8
9
|
let next_script_js = require("next/script.js");
|
|
9
10
|
next_script_js = require_runtime.__toESM(next_script_js, 1);
|
|
10
11
|
let next_web_vitals_js = require("next/web-vitals.js");
|
|
11
12
|
let react_jsx_runtime = require("react/jsx-runtime");
|
|
12
13
|
//#region src/next/index.tsx
|
|
13
|
-
|
|
14
|
+
/**
|
|
15
|
+
* Its own component under a Suspense boundary: `useSearchParams` in a statically rendered route
|
|
16
|
+
* bails the whole tree out to client rendering unless something above it suspends, and Next
|
|
17
|
+
* fails the build for a page that lets it. The boundary is here so a host does not have to know.
|
|
18
|
+
*/
|
|
19
|
+
function PageViews() {
|
|
14
20
|
const pathname = (0, next_navigation_js.usePathname)();
|
|
15
|
-
|
|
21
|
+
const searchParams = (0, next_navigation_js.useSearchParams)();
|
|
22
|
+
require_hooks_use_web_analytics.useWebAnalytics(pathname, searchParams.toString());
|
|
23
|
+
return null;
|
|
24
|
+
}
|
|
25
|
+
function Analytics({ gaId, gaSrc, nonce, debugMode, metaPixelId, hotjarId, redditPixelId, linkedInPartnerId, uetTagId, facebookAppId, reportWebVitals = true }) {
|
|
16
26
|
require_hooks_use_outbound_click_analytics.useOutboundClickAnalytics();
|
|
17
27
|
(0, next_web_vitals_js.useReportWebVitals)((metric) => {
|
|
18
28
|
if (!reportWebVitals) return;
|
|
@@ -28,6 +38,10 @@ function Analytics({ gaId, gaSrc, nonce, debugMode, metaPixelId, hotjarId, reddi
|
|
|
28
38
|
require_track_index.track(metric.name, properties);
|
|
29
39
|
});
|
|
30
40
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [
|
|
41
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(react.Suspense, {
|
|
42
|
+
fallback: null,
|
|
43
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(PageViews, {})
|
|
44
|
+
}),
|
|
31
45
|
facebookAppId && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("meta", {
|
|
32
46
|
property: "fb:app_id",
|
|
33
47
|
content: facebookAppId
|
|
@@ -111,6 +125,28 @@ function Analytics({ gaId, gaSrc, nonce, debugMode, metaPixelId, hotjarId, reddi
|
|
|
111
125
|
s.parentNode.insertBefore(b, s);
|
|
112
126
|
})(window.lintrk);
|
|
113
127
|
` }
|
|
128
|
+
}),
|
|
129
|
+
uetTagId && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(next_script_js.default, {
|
|
130
|
+
strategy: "afterInteractive",
|
|
131
|
+
id: "uet-tag",
|
|
132
|
+
dangerouslySetInnerHTML: { __html: `
|
|
133
|
+
(function (w, d, t, u, o) {
|
|
134
|
+
w[u] = w[u] || [], o.ts = (new Date).getTime();
|
|
135
|
+
var n = d.createElement(t);
|
|
136
|
+
n.src = "https://bat.bing.net/bat.js?ti=" + o.ti + ("uetq" != u ? "&q=" + u : ""),
|
|
137
|
+
n.async = 1, n.onload = n.onreadystatechange = function() {
|
|
138
|
+
var s = this.readyState;
|
|
139
|
+
s && "loaded" !== s && "complete" !== s ||
|
|
140
|
+
(o.q = w[u], w[u] = new UET(o), w[u].push("pageLoad"),
|
|
141
|
+
n.onload = n.onreadystatechange = null)
|
|
142
|
+
};
|
|
143
|
+
var i = d.getElementsByTagName(t)[0];
|
|
144
|
+
i.parentNode.insertBefore(n, i);
|
|
145
|
+
})(window, document, "script", "uetq", {
|
|
146
|
+
ti: "${uetTagId}",
|
|
147
|
+
enableAutoSpaTracking: true
|
|
148
|
+
});
|
|
149
|
+
` }
|
|
114
150
|
}),
|
|
115
151
|
hotjarId && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(next_script_js.default, {
|
|
116
152
|
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 { 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 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 facebookAppId,\n reportWebVitals = true,\n}: Props) {\n useOutboundClickAnalytics();\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":";;;;;;;;;;;;;;;;;;AAkCA,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,kBAAkB,QACV;CACR,2CAAA,0BAA0B;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,14 @@ 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}`;
|
|
13
15
|
facebookAppId?: string;
|
|
14
16
|
nonce?: string;
|
|
15
17
|
debugMode?: boolean;
|
|
16
18
|
reportWebVitals?: boolean;
|
|
17
19
|
}
|
|
18
|
-
declare function Analytics({ gaId, gaSrc, nonce, debugMode, metaPixelId, hotjarId, redditPixelId, linkedInPartnerId, facebookAppId, reportWebVitals }: Props): import("react").JSX.Element;
|
|
20
|
+
declare function Analytics({ gaId, gaSrc, nonce, debugMode, metaPixelId, hotjarId, redditPixelId, linkedInPartnerId, uetTagId, facebookAppId, reportWebVitals }: Props): import("react").JSX.Element;
|
|
19
21
|
//#endregion
|
|
20
22
|
export { Analytics };
|
|
21
23
|
//# 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":";;;;UAaU;EACR,OAAO;EACP;EACA,QAAQ;EACR;EACA,cAAc;EACd,gBAAgB;EAChB;;EAEA;EACA;EACA;EACA;EACA;;iBAec,YACd,MACA,OACA,OACA,WACA,aACA,UACA,eACA,mBACA,UACA,eACA,mBACC,wBAAK,IAAA"}
|
package/dist/next/index.d.mts
CHANGED
|
@@ -10,12 +10,14 @@ 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}`;
|
|
13
15
|
facebookAppId?: string;
|
|
14
16
|
nonce?: string;
|
|
15
17
|
debugMode?: boolean;
|
|
16
18
|
reportWebVitals?: boolean;
|
|
17
19
|
}
|
|
18
|
-
declare function Analytics({ gaId, gaSrc, nonce, debugMode, metaPixelId, hotjarId, redditPixelId, linkedInPartnerId, facebookAppId, reportWebVitals }: Props): import("react").JSX.Element;
|
|
20
|
+
declare function Analytics({ gaId, gaSrc, nonce, debugMode, metaPixelId, hotjarId, redditPixelId, linkedInPartnerId, uetTagId, facebookAppId, reportWebVitals }: Props): import("react").JSX.Element;
|
|
19
21
|
//#endregion
|
|
20
22
|
export { Analytics };
|
|
21
23
|
//# 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":";;;;UAaU;EACR,OAAO;EACP;EACA,QAAQ;EACR;EACA,cAAc;EACd,gBAAgB;EAChB;;EAEA;EACA;EACA;EACA;EACA;;iBAec,YACd,MACA,OACA,OACA,WACA,aACA,UACA,eACA,mBACA,UACA,eACA,mBACC,wBAAK,IAAA"}
|
package/dist/next/index.mjs
CHANGED
|
@@ -2,14 +2,24 @@
|
|
|
2
2
|
import { track } from "../track/index.mjs";
|
|
3
3
|
import { useOutboundClickAnalytics } from "../hooks/use-outbound-click-analytics.mjs";
|
|
4
4
|
import { useWebAnalytics } from "../hooks/use-web-analytics.mjs";
|
|
5
|
-
import {
|
|
5
|
+
import { Suspense } from "react";
|
|
6
|
+
import { usePathname, useSearchParams } from "next/navigation.js";
|
|
6
7
|
import Script from "next/script.js";
|
|
7
8
|
import { useReportWebVitals } from "next/web-vitals.js";
|
|
8
9
|
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
9
10
|
//#region src/next/index.tsx
|
|
10
|
-
|
|
11
|
+
/**
|
|
12
|
+
* Its own component under a Suspense boundary: `useSearchParams` in a statically rendered route
|
|
13
|
+
* bails the whole tree out to client rendering unless something above it suspends, and Next
|
|
14
|
+
* fails the build for a page that lets it. The boundary is here so a host does not have to know.
|
|
15
|
+
*/
|
|
16
|
+
function PageViews() {
|
|
11
17
|
const pathname = usePathname();
|
|
12
|
-
|
|
18
|
+
const searchParams = useSearchParams();
|
|
19
|
+
useWebAnalytics(pathname, searchParams.toString());
|
|
20
|
+
return null;
|
|
21
|
+
}
|
|
22
|
+
function Analytics({ gaId, gaSrc, nonce, debugMode, metaPixelId, hotjarId, redditPixelId, linkedInPartnerId, uetTagId, facebookAppId, reportWebVitals = true }) {
|
|
13
23
|
useOutboundClickAnalytics();
|
|
14
24
|
useReportWebVitals((metric) => {
|
|
15
25
|
if (!reportWebVitals) return;
|
|
@@ -25,6 +35,10 @@ function Analytics({ gaId, gaSrc, nonce, debugMode, metaPixelId, hotjarId, reddi
|
|
|
25
35
|
track(metric.name, properties);
|
|
26
36
|
});
|
|
27
37
|
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
38
|
+
/* @__PURE__ */ jsx(Suspense, {
|
|
39
|
+
fallback: null,
|
|
40
|
+
children: /* @__PURE__ */ jsx(PageViews, {})
|
|
41
|
+
}),
|
|
28
42
|
facebookAppId && /* @__PURE__ */ jsx("meta", {
|
|
29
43
|
property: "fb:app_id",
|
|
30
44
|
content: facebookAppId
|
|
@@ -108,6 +122,28 @@ function Analytics({ gaId, gaSrc, nonce, debugMode, metaPixelId, hotjarId, reddi
|
|
|
108
122
|
s.parentNode.insertBefore(b, s);
|
|
109
123
|
})(window.lintrk);
|
|
110
124
|
` }
|
|
125
|
+
}),
|
|
126
|
+
uetTagId && /* @__PURE__ */ jsx(Script, {
|
|
127
|
+
strategy: "afterInteractive",
|
|
128
|
+
id: "uet-tag",
|
|
129
|
+
dangerouslySetInnerHTML: { __html: `
|
|
130
|
+
(function (w, d, t, u, o) {
|
|
131
|
+
w[u] = w[u] || [], o.ts = (new Date).getTime();
|
|
132
|
+
var n = d.createElement(t);
|
|
133
|
+
n.src = "https://bat.bing.net/bat.js?ti=" + o.ti + ("uetq" != u ? "&q=" + u : ""),
|
|
134
|
+
n.async = 1, n.onload = n.onreadystatechange = function() {
|
|
135
|
+
var s = this.readyState;
|
|
136
|
+
s && "loaded" !== s && "complete" !== s ||
|
|
137
|
+
(o.q = w[u], w[u] = new UET(o), w[u].push("pageLoad"),
|
|
138
|
+
n.onload = n.onreadystatechange = null)
|
|
139
|
+
};
|
|
140
|
+
var i = d.getElementsByTagName(t)[0];
|
|
141
|
+
i.parentNode.insertBefore(n, i);
|
|
142
|
+
})(window, document, "script", "uetq", {
|
|
143
|
+
ti: "${uetTagId}",
|
|
144
|
+
enableAutoSpaTracking: true
|
|
145
|
+
});
|
|
146
|
+
` }
|
|
111
147
|
}),
|
|
112
148
|
hotjarId && /* @__PURE__ */ jsx(Script, {
|
|
113
149
|
id: "hotjar",
|
package/dist/next/index.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.mjs","names":[],"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.mjs","names":[],"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 { 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 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 facebookAppId,\n reportWebVitals = true,\n}: Props) {\n useOutboundClickAnalytics();\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":";;;;;;;;;;;;;;;AAkCA,SAAS,YAAY;CACnB,MAAM,WAAW,YAAY;CAC7B,MAAM,eAAe,gBAAgB;CACrC,gBAAgB,UAAU,aAAa,SAAS,CAAC;CACjD,OAAO;AACT;AAEA,SAAgB,UAAU,EACxB,MACA,OACA,OACA,WACA,aACA,UACA,eACA,mBACA,UACA,eACA,kBAAkB,QACV;CACR,0BAA0B;CAE1B,oBAAoB,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,MAAM,OAAO,MAAM,UAAU;CAC/B,CAAC;CAED,OACE,qBAAA,UAAA,EAAA,UAAA;EACE,oBAAC,UAAD;GAAU,UAAU;GAClB,UAAA,oBAAC,WAAD,CAAY,CAAA;EACJ,CAAA;EACT,iBAAiB,oBAAC,QAAD;GAAM,UAAS;GAAY,SAAS;EAAgB,CAAA;EACrE,QACC,qBAAA,UAAA,EAAA,UAAA,CACE,oBAAC,QAAD;GACE,IAAG;GACI;GACP,KAAK,SAAS,+CAA+C;EAC9D,CAAA,GACD,oBAAC,QAAD;GACS;GACP,IAAG;GACH,yBAAyB,EACvB,QAAQ;;;;gCAIU,KAAK,GAAG,YAAY,6BAA6B,GAAG;cAExE;EACD,CAAA,CACD,EAAA,CAAA;EAEH,eACC,oBAAC,QAAD;GACE,IAAG;GACH,UAAS;GACT,yBAAyB,EACvB,QAAQ;;;;;;;;;;;;;;;;;2BAiBO,YAAY;uCAE7B;EACD,CAAA;EAEF,iBACC,oBAAC,QAAD;GACE,IAAG;GACH,UAAS;GACT,yBAAyB,EACvB,QAAQ;;;;;;;;;;;;;;2BAcO,cAAc;wCAE/B;EACD,CAAA;EAEF,qBACC,oBAAC,QAAD;GACE,IAAG;GACH,UAAS;GACT,yBAAyB,EACvB,QAAQ;wCACoB,kBAAkB;;;;;;;;;;;;;;;;;gBAkBhD;EACD,CAAA;EAEF,YACC,oBAAC,QAAD;GACE,UAAS;GACT,IAAG;GACH,yBAAyB,EACvB,QAAQ;;;;;;;;;;;;;;uBAcG,SAAS;;;cAItB;EACD,CAAA;EAEF,YACC,oBAAC,QAAD;GACE,IAAG;GACH,UAAS;GACT,yBAAyB,EACvB,QAAQ;;;oCAGgB,SAAS;;;;;;cAOnC;EACD,CAAA;CAEH,EAAA,CAAA;AAEN"}
|
|
@@ -6,9 +6,9 @@ const require_hooks_use_web_analytics = require("../hooks/use-web-analytics.cjs"
|
|
|
6
6
|
let react_jsx_runtime = require("react/jsx-runtime");
|
|
7
7
|
let react_router = require("react-router");
|
|
8
8
|
//#region src/react-router/index.tsx
|
|
9
|
-
function Analytics({ gaId, gaSrc, nonce, debugMode, metaPixelId, redditPixelId, linkedInPartnerId, hotjarId, facebookAppId, reportWebVitals = true }) {
|
|
10
|
-
const { pathname } = (0, react_router.useLocation)();
|
|
11
|
-
require_hooks_use_web_analytics.useWebAnalytics(pathname);
|
|
9
|
+
function Analytics({ gaId, gaSrc, nonce, debugMode, metaPixelId, redditPixelId, linkedInPartnerId, uetTagId, hotjarId, facebookAppId, reportWebVitals = true }) {
|
|
10
|
+
const { pathname, search } = (0, react_router.useLocation)();
|
|
11
|
+
require_hooks_use_web_analytics.useWebAnalytics(pathname, search);
|
|
12
12
|
require_hooks_use_outbound_click_analytics.useOutboundClickAnalytics();
|
|
13
13
|
require_hooks_use_report_web_vitals.useReportWebVitals((metric) => {
|
|
14
14
|
if (!reportWebVitals) return;
|
|
@@ -108,6 +108,28 @@ function Analytics({ gaId, gaSrc, nonce, debugMode, metaPixelId, redditPixelId,
|
|
|
108
108
|
b.src = "https://snap.licdn.com/li.lms-analytics/insight.min.js";
|
|
109
109
|
s.parentNode.insertBefore(b, s);
|
|
110
110
|
})(window.lintrk);
|
|
111
|
+
` }
|
|
112
|
+
}),
|
|
113
|
+
uetTagId && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("script", {
|
|
114
|
+
async: true,
|
|
115
|
+
id: "uet-tag",
|
|
116
|
+
dangerouslySetInnerHTML: { __html: `
|
|
117
|
+
(function (w, d, t, u, o) {
|
|
118
|
+
w[u] = w[u] || [], o.ts = (new Date).getTime();
|
|
119
|
+
var n = d.createElement(t);
|
|
120
|
+
n.src = "https://bat.bing.net/bat.js?ti=" + o.ti + ("uetq" != u ? "&q=" + u : ""),
|
|
121
|
+
n.async = 1, n.onload = n.onreadystatechange = function() {
|
|
122
|
+
var s = this.readyState;
|
|
123
|
+
s && "loaded" !== s && "complete" !== s ||
|
|
124
|
+
(o.q = w[u], w[u] = new UET(o), w[u].push("pageLoad"),
|
|
125
|
+
n.onload = n.onreadystatechange = null)
|
|
126
|
+
};
|
|
127
|
+
var i = d.getElementsByTagName(t)[0];
|
|
128
|
+
i.parentNode.insertBefore(n, i);
|
|
129
|
+
})(window, document, "script", "uetq", {
|
|
130
|
+
ti: "${uetTagId}",
|
|
131
|
+
enableAutoSpaTracking: true
|
|
132
|
+
});
|
|
111
133
|
` }
|
|
112
134
|
}),
|
|
113
135
|
hotjarId && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("script", {
|