@shware/analytics 8.2.0 → 8.3.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 +63 -2
- package/dist/click-id/index.cjs +57 -3
- package/dist/click-id/index.cjs.map +1 -1
- package/dist/click-id/index.d.cts +16 -1
- package/dist/click-id/index.d.cts.map +1 -1
- package/dist/click-id/index.d.mts +16 -1
- package/dist/click-id/index.d.mts.map +1 -1
- package/dist/click-id/index.mjs +54 -4
- package/dist/click-id/index.mjs.map +1 -1
- package/dist/server/google-data-manager.cjs +2 -0
- package/dist/server/google-data-manager.cjs.map +1 -1
- package/dist/server/google-data-manager.d.cts.map +1 -1
- package/dist/server/google-data-manager.d.mts.map +1 -1
- package/dist/server/google-data-manager.mjs +2 -0
- package/dist/server/google-data-manager.mjs.map +1 -1
- package/dist/server/index.cjs +8 -0
- package/dist/server/index.d.cts +5 -4
- package/dist/server/index.d.mts +5 -4
- package/dist/server/index.mjs +5 -4
- package/dist/server/microsoft-conversions-api.cjs +171 -0
- package/dist/server/microsoft-conversions-api.cjs.map +1 -0
- package/dist/server/microsoft-conversions-api.d.cts +117 -0
- package/dist/server/microsoft-conversions-api.d.cts.map +1 -0
- package/dist/server/microsoft-conversions-api.d.mts +117 -0
- package/dist/server/microsoft-conversions-api.d.mts.map +1 -0
- package/dist/server/microsoft-conversions-api.mjs +168 -0
- package/dist/server/microsoft-conversions-api.mjs.map +1 -0
- package/dist/tanstack/middleware.cjs +2 -1
- package/dist/tanstack/middleware.cjs.map +1 -1
- package/dist/tanstack/middleware.d.cts +2 -1
- package/dist/tanstack/middleware.d.cts.map +1 -1
- package/dist/tanstack/middleware.d.mts +2 -1
- package/dist/tanstack/middleware.d.mts.map +1 -1
- package/dist/tanstack/middleware.mjs +2 -1
- package/dist/tanstack/middleware.mjs.map +1 -1
- package/dist/third-parties/index.cjs +5 -0
- package/dist/third-parties/index.d.cts +3 -1
- package/dist/third-parties/index.d.mts +3 -1
- package/dist/third-parties/index.mjs +2 -1
- package/dist/third-parties/microsoft-uet.cjs +119 -0
- package/dist/third-parties/microsoft-uet.cjs.map +1 -0
- package/dist/third-parties/microsoft-uet.d.cts +70 -0
- package/dist/third-parties/microsoft-uet.d.cts.map +1 -0
- package/dist/third-parties/microsoft-uet.d.mts +70 -0
- package/dist/third-parties/microsoft-uet.d.mts.map +1 -0
- package/dist/third-parties/microsoft-uet.mjs +115 -0
- package/dist/third-parties/microsoft-uet.mjs.map +1 -0
- package/dist/third-parties/openai-pixel.cjs +2 -7
- package/dist/third-parties/openai-pixel.cjs.map +1 -1
- package/dist/third-parties/openai-pixel.d.cts.map +1 -1
- package/dist/third-parties/openai-pixel.d.mts.map +1 -1
- package/dist/third-parties/openai-pixel.mjs +1 -6
- package/dist/third-parties/openai-pixel.mjs.map +1 -1
- package/dist/track/uetq.cjs +80 -0
- package/dist/track/uetq.cjs.map +1 -0
- package/dist/track/uetq.d.cts +111 -0
- package/dist/track/uetq.d.cts.map +1 -0
- package/dist/track/uetq.d.mts +111 -0
- package/dist/track/uetq.d.mts.map +1 -0
- package/dist/track/uetq.mjs +78 -0
- package/dist/track/uetq.mjs.map +1 -0
- package/dist/utils/sha256.cjs +19 -0
- package/dist/utils/sha256.cjs.map +1 -0
- package/dist/utils/sha256.d.cts +13 -0
- package/dist/utils/sha256.d.cts.map +1 -0
- package/dist/utils/sha256.d.mts +13 -0
- package/dist/utils/sha256.d.mts.map +1 -0
- package/dist/utils/sha256.mjs +18 -0
- package/dist/utils/sha256.mjs.map +1 -0
- package/dist/web/index.cjs +1 -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 +2 -2
- package/dist/web/index.mjs.map +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import { UpdateVisitorDTO } from "../schema/index.cjs";
|
|
2
|
+
import { EventName, TrackName, TrackProperties } from "../track/types.cjs";
|
|
3
|
+
import { UETConsent, UETQ } from "../track/uetq.cjs";
|
|
4
|
+
//#region src/third-parties/microsoft-uet.d.ts
|
|
5
|
+
declare global {
|
|
6
|
+
interface Window {
|
|
7
|
+
/**
|
|
8
|
+
* Defined by the UET snippet before `bat.js` loads (as a plain array), so pushes made early
|
|
9
|
+
* are queued rather than lost. Undefined only where the snippet was never installed.
|
|
10
|
+
*/
|
|
11
|
+
uetq?: UETQ;
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Forward an internal track event to the Microsoft Advertising UET tag.
|
|
16
|
+
*
|
|
17
|
+
* `eventId` is reused as UET's `event_id` so the browser event deduplicates against the same
|
|
18
|
+
* conversion sent through the Conversions API (`sendMicrosoftEvents`). The action name is the
|
|
19
|
+
* internal name on both channels — see `mapUETEvent`.
|
|
20
|
+
*
|
|
21
|
+
* `page_view` is not forwarded: bat.js turns that action into its page-load beacon, which the
|
|
22
|
+
* snippet already fires on load and, with `enableAutoSpaTracking: true` (the setting in
|
|
23
|
+
* Microsoft's generated snippet), on every history change — forwarding the SDK's own page views
|
|
24
|
+
* would report each page twice. The tag owns page loads; this tracker owns everything else.
|
|
25
|
+
*/
|
|
26
|
+
declare function sendUETEvent<T extends EventName>(name: TrackName<T>, properties?: TrackProperties<T>, eventId?: string): void;
|
|
27
|
+
/**
|
|
28
|
+
* Enhanced conversions: hand the tag the visitor's email and phone so Microsoft can match a
|
|
29
|
+
* conversion to an ad click without a third-party cookie. A `set` applies to every event fired
|
|
30
|
+
* from the page afterwards, `pageLoad` included, and the tag fires a `pid` beacon on its own
|
|
31
|
+
* when the page load already went out. Values are sent raw — the tag normalizes and SHA-256
|
|
32
|
+
* hashes them in the browser (bat.js `validatePid`) before anything leaves the page.
|
|
33
|
+
*
|
|
34
|
+
* No tag id parameter: unlike the Meta/Reddit pixels, UET is bound to its tag by the snippet
|
|
35
|
+
* and `set` addresses whichever tag drains `uetq`.
|
|
36
|
+
*/
|
|
37
|
+
declare function setUETUser(): ({ user_data }: UpdateVisitorDTO) => void;
|
|
38
|
+
/**
|
|
39
|
+
* UET consent mode. Push `default` before the tag loads (the tag processes `consent` early, ahead
|
|
40
|
+
* of the queued events) and `update` when the visitor decides. With `ad_storage: 'denied'` the
|
|
41
|
+
* tag neither writes its cookies nor reads the `msclkid` from the URL.
|
|
42
|
+
* https://help.ads.microsoft.com/#apex/ads/en/60119/1
|
|
43
|
+
*/
|
|
44
|
+
declare function setUETConsent(mode: 'default' | 'update', consent: UETConsent): void;
|
|
45
|
+
/**
|
|
46
|
+
* The Conversions API's client-side ID Sync pixel: ties the visitor id sent server-side as
|
|
47
|
+
* `anonymousId` to Microsoft's own identifier, so server events can be attributed across
|
|
48
|
+
* contexts (view-through, audiences). Microsoft requires it for remarketing built from CAPI
|
|
49
|
+
* events and recommends it for measurement quality; fire it at least once per session, ideally
|
|
50
|
+
* on the first page view. A pixel rather than a fetch on purpose: Microsoft must observe the
|
|
51
|
+
* browser context of the sync, which a server-side call cannot carry.
|
|
52
|
+
*
|
|
53
|
+
* `customerId` is the Microsoft Advertising customer id (`cid` in the UI's URLs) — not the UET
|
|
54
|
+
* tag id. `visitorId` is the SDK visitor id (`getVisitor().id`), which is what
|
|
55
|
+
* `sendMicrosoftEvents` sends as `anonymousId`; Microsoft requires the two to be equal.
|
|
56
|
+
* `userId` is the raw signed-in user id: it is SHA-256 hashed here, exactly as the server sender
|
|
57
|
+
* hashes it into `externalId`, so the pair matches as well. Hashing is asynchronous, so the
|
|
58
|
+
* pixel fires once the digest resolves; where SubtleCrypto is unavailable (an insecure context)
|
|
59
|
+
* the pixel goes out without `UID` rather than with a raw id.
|
|
60
|
+
* https://learn.microsoft.com/en-us/advertising/guides/uet-conversion-api-integration#id-sync-and-why-it-matters
|
|
61
|
+
*/
|
|
62
|
+
declare function sendUETIdSync(options: {
|
|
63
|
+
customerId: string | number;
|
|
64
|
+
visitorId: string;
|
|
65
|
+
/** The signed-in user's id, raw; hashed before it leaves the page. */
|
|
66
|
+
userId?: string;
|
|
67
|
+
}): void;
|
|
68
|
+
//#endregion
|
|
69
|
+
export { sendUETEvent, sendUETIdSync, setUETConsent, setUETUser };
|
|
70
|
+
//# sourceMappingURL=microsoft-uet.d.cts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"microsoft-uet.d.cts","names":[],"sources":["../../src/third-parties/microsoft-uet.ts"],"mappings":";;;;QAMQ;YACI;;;;;IAKR,OAAO;;;;;;;;;;;;;;;iBA4BK,aAAa,UAAU,WACrC,MAAM,UAAU,IAChB,aAAa,gBAAgB,IAC7B;;;;;;;;;;;iBAuBc,iBAAU,aACD;;;;;;;iBAoBT,cAAc,4BAA4B,SAAS;;;;;;;;;;;;;;;;;;iBAuBnD,cAAc;EAC5B;EACA;;EAEA"}
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import { UpdateVisitorDTO } from "../schema/index.mjs";
|
|
2
|
+
import { EventName, TrackName, TrackProperties } from "../track/types.mjs";
|
|
3
|
+
import { UETConsent, UETQ } from "../track/uetq.mjs";
|
|
4
|
+
//#region src/third-parties/microsoft-uet.d.ts
|
|
5
|
+
declare global {
|
|
6
|
+
interface Window {
|
|
7
|
+
/**
|
|
8
|
+
* Defined by the UET snippet before `bat.js` loads (as a plain array), so pushes made early
|
|
9
|
+
* are queued rather than lost. Undefined only where the snippet was never installed.
|
|
10
|
+
*/
|
|
11
|
+
uetq?: UETQ;
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Forward an internal track event to the Microsoft Advertising UET tag.
|
|
16
|
+
*
|
|
17
|
+
* `eventId` is reused as UET's `event_id` so the browser event deduplicates against the same
|
|
18
|
+
* conversion sent through the Conversions API (`sendMicrosoftEvents`). The action name is the
|
|
19
|
+
* internal name on both channels — see `mapUETEvent`.
|
|
20
|
+
*
|
|
21
|
+
* `page_view` is not forwarded: bat.js turns that action into its page-load beacon, which the
|
|
22
|
+
* snippet already fires on load and, with `enableAutoSpaTracking: true` (the setting in
|
|
23
|
+
* Microsoft's generated snippet), on every history change — forwarding the SDK's own page views
|
|
24
|
+
* would report each page twice. The tag owns page loads; this tracker owns everything else.
|
|
25
|
+
*/
|
|
26
|
+
declare function sendUETEvent<T extends EventName>(name: TrackName<T>, properties?: TrackProperties<T>, eventId?: string): void;
|
|
27
|
+
/**
|
|
28
|
+
* Enhanced conversions: hand the tag the visitor's email and phone so Microsoft can match a
|
|
29
|
+
* conversion to an ad click without a third-party cookie. A `set` applies to every event fired
|
|
30
|
+
* from the page afterwards, `pageLoad` included, and the tag fires a `pid` beacon on its own
|
|
31
|
+
* when the page load already went out. Values are sent raw — the tag normalizes and SHA-256
|
|
32
|
+
* hashes them in the browser (bat.js `validatePid`) before anything leaves the page.
|
|
33
|
+
*
|
|
34
|
+
* No tag id parameter: unlike the Meta/Reddit pixels, UET is bound to its tag by the snippet
|
|
35
|
+
* and `set` addresses whichever tag drains `uetq`.
|
|
36
|
+
*/
|
|
37
|
+
declare function setUETUser(): ({ user_data }: UpdateVisitorDTO) => void;
|
|
38
|
+
/**
|
|
39
|
+
* UET consent mode. Push `default` before the tag loads (the tag processes `consent` early, ahead
|
|
40
|
+
* of the queued events) and `update` when the visitor decides. With `ad_storage: 'denied'` the
|
|
41
|
+
* tag neither writes its cookies nor reads the `msclkid` from the URL.
|
|
42
|
+
* https://help.ads.microsoft.com/#apex/ads/en/60119/1
|
|
43
|
+
*/
|
|
44
|
+
declare function setUETConsent(mode: 'default' | 'update', consent: UETConsent): void;
|
|
45
|
+
/**
|
|
46
|
+
* The Conversions API's client-side ID Sync pixel: ties the visitor id sent server-side as
|
|
47
|
+
* `anonymousId` to Microsoft's own identifier, so server events can be attributed across
|
|
48
|
+
* contexts (view-through, audiences). Microsoft requires it for remarketing built from CAPI
|
|
49
|
+
* events and recommends it for measurement quality; fire it at least once per session, ideally
|
|
50
|
+
* on the first page view. A pixel rather than a fetch on purpose: Microsoft must observe the
|
|
51
|
+
* browser context of the sync, which a server-side call cannot carry.
|
|
52
|
+
*
|
|
53
|
+
* `customerId` is the Microsoft Advertising customer id (`cid` in the UI's URLs) — not the UET
|
|
54
|
+
* tag id. `visitorId` is the SDK visitor id (`getVisitor().id`), which is what
|
|
55
|
+
* `sendMicrosoftEvents` sends as `anonymousId`; Microsoft requires the two to be equal.
|
|
56
|
+
* `userId` is the raw signed-in user id: it is SHA-256 hashed here, exactly as the server sender
|
|
57
|
+
* hashes it into `externalId`, so the pair matches as well. Hashing is asynchronous, so the
|
|
58
|
+
* pixel fires once the digest resolves; where SubtleCrypto is unavailable (an insecure context)
|
|
59
|
+
* the pixel goes out without `UID` rather than with a raw id.
|
|
60
|
+
* https://learn.microsoft.com/en-us/advertising/guides/uet-conversion-api-integration#id-sync-and-why-it-matters
|
|
61
|
+
*/
|
|
62
|
+
declare function sendUETIdSync(options: {
|
|
63
|
+
customerId: string | number;
|
|
64
|
+
visitorId: string;
|
|
65
|
+
/** The signed-in user's id, raw; hashed before it leaves the page. */
|
|
66
|
+
userId?: string;
|
|
67
|
+
}): void;
|
|
68
|
+
//#endregion
|
|
69
|
+
export { sendUETEvent, sendUETIdSync, setUETConsent, setUETUser };
|
|
70
|
+
//# sourceMappingURL=microsoft-uet.d.mts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"microsoft-uet.d.mts","names":[],"sources":["../../src/third-parties/microsoft-uet.ts"],"mappings":";;;;QAMQ;YACI;;;;;IAKR,OAAO;;;;;;;;;;;;;;;iBA4BK,aAAa,UAAU,WACrC,MAAM,UAAU,IAChB,aAAa,gBAAgB,IAC7B;;;;;;;;;;;iBAuBc,iBAAU,aACD;;;;;;;iBAoBT,cAAc,4BAA4B,SAAS;;;;;;;;;;;;;;;;;;iBAuBnD,cAAc;EAC5B;EACA;;EAEA"}
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
import { getFirst } from "../utils/field.mjs";
|
|
2
|
+
import { mapUETEvent } from "../track/uetq.mjs";
|
|
3
|
+
import { sha256 } from "../utils/sha256.mjs";
|
|
4
|
+
//#region src/third-parties/microsoft-uet.ts
|
|
5
|
+
const metrics = [
|
|
6
|
+
"CLS",
|
|
7
|
+
"FCP",
|
|
8
|
+
"FID",
|
|
9
|
+
"INP",
|
|
10
|
+
"LCP",
|
|
11
|
+
"TTFB"
|
|
12
|
+
];
|
|
13
|
+
/** Drop `undefined` fields: bat.js validates every key it receives, and `undefined` is a value. */
|
|
14
|
+
function clean(value) {
|
|
15
|
+
return JSON.parse(JSON.stringify(value));
|
|
16
|
+
}
|
|
17
|
+
function isLocal() {
|
|
18
|
+
return window.location.host.includes("127.0.0.1") || window.location.host.includes("localhost");
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Forward an internal track event to the Microsoft Advertising UET tag.
|
|
22
|
+
*
|
|
23
|
+
* `eventId` is reused as UET's `event_id` so the browser event deduplicates against the same
|
|
24
|
+
* conversion sent through the Conversions API (`sendMicrosoftEvents`). The action name is the
|
|
25
|
+
* internal name on both channels — see `mapUETEvent`.
|
|
26
|
+
*
|
|
27
|
+
* `page_view` is not forwarded: bat.js turns that action into its page-load beacon, which the
|
|
28
|
+
* snippet already fires on load and, with `enableAutoSpaTracking: true` (the setting in
|
|
29
|
+
* Microsoft's generated snippet), on every history change — forwarding the SDK's own page views
|
|
30
|
+
* would report each page twice. The tag owns page loads; this tracker owns everything else.
|
|
31
|
+
*/
|
|
32
|
+
function sendUETEvent(name, properties, eventId) {
|
|
33
|
+
if (typeof window === "undefined" || !window.uetq) {
|
|
34
|
+
console.warn("uetq has not been initialized");
|
|
35
|
+
return;
|
|
36
|
+
}
|
|
37
|
+
if (metrics.includes(name) || name === "page_view") return;
|
|
38
|
+
if (isLocal()) return;
|
|
39
|
+
const [action, params] = mapUETEvent(name, properties, eventId);
|
|
40
|
+
window.uetq.push("event", action, clean(params));
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Enhanced conversions: hand the tag the visitor's email and phone so Microsoft can match a
|
|
44
|
+
* conversion to an ad click without a third-party cookie. A `set` applies to every event fired
|
|
45
|
+
* from the page afterwards, `pageLoad` included, and the tag fires a `pid` beacon on its own
|
|
46
|
+
* when the page load already went out. Values are sent raw — the tag normalizes and SHA-256
|
|
47
|
+
* hashes them in the browser (bat.js `validatePid`) before anything leaves the page.
|
|
48
|
+
*
|
|
49
|
+
* No tag id parameter: unlike the Meta/Reddit pixels, UET is bound to its tag by the snippet
|
|
50
|
+
* and `set` addresses whichever tag drains `uetq`.
|
|
51
|
+
*/
|
|
52
|
+
function setUETUser() {
|
|
53
|
+
return ({ user_data }) => {
|
|
54
|
+
if (typeof window === "undefined" || !window.uetq) {
|
|
55
|
+
console.warn("uetq has not been initialized");
|
|
56
|
+
return;
|
|
57
|
+
}
|
|
58
|
+
const em = getFirst(user_data?.email);
|
|
59
|
+
const ph = getFirst(user_data?.phone_number);
|
|
60
|
+
if (!em && !ph) return;
|
|
61
|
+
window.uetq.push("set", { pid: clean({
|
|
62
|
+
em,
|
|
63
|
+
ph
|
|
64
|
+
}) });
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* UET consent mode. Push `default` before the tag loads (the tag processes `consent` early, ahead
|
|
69
|
+
* of the queued events) and `update` when the visitor decides. With `ad_storage: 'denied'` the
|
|
70
|
+
* tag neither writes its cookies nor reads the `msclkid` from the URL.
|
|
71
|
+
* https://help.ads.microsoft.com/#apex/ads/en/60119/1
|
|
72
|
+
*/
|
|
73
|
+
function setUETConsent(mode, consent) {
|
|
74
|
+
if (typeof window === "undefined") return;
|
|
75
|
+
window.uetq ??= [];
|
|
76
|
+
window.uetq.push("consent", mode, consent);
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* The Conversions API's client-side ID Sync pixel: ties the visitor id sent server-side as
|
|
80
|
+
* `anonymousId` to Microsoft's own identifier, so server events can be attributed across
|
|
81
|
+
* contexts (view-through, audiences). Microsoft requires it for remarketing built from CAPI
|
|
82
|
+
* events and recommends it for measurement quality; fire it at least once per session, ideally
|
|
83
|
+
* on the first page view. A pixel rather than a fetch on purpose: Microsoft must observe the
|
|
84
|
+
* browser context of the sync, which a server-side call cannot carry.
|
|
85
|
+
*
|
|
86
|
+
* `customerId` is the Microsoft Advertising customer id (`cid` in the UI's URLs) — not the UET
|
|
87
|
+
* tag id. `visitorId` is the SDK visitor id (`getVisitor().id`), which is what
|
|
88
|
+
* `sendMicrosoftEvents` sends as `anonymousId`; Microsoft requires the two to be equal.
|
|
89
|
+
* `userId` is the raw signed-in user id: it is SHA-256 hashed here, exactly as the server sender
|
|
90
|
+
* hashes it into `externalId`, so the pair matches as well. Hashing is asynchronous, so the
|
|
91
|
+
* pixel fires once the digest resolves; where SubtleCrypto is unavailable (an insecure context)
|
|
92
|
+
* the pixel goes out without `UID` rather than with a raw id.
|
|
93
|
+
* https://learn.microsoft.com/en-us/advertising/guides/uet-conversion-api-integration#id-sync-and-why-it-matters
|
|
94
|
+
*/
|
|
95
|
+
function sendUETIdSync(options) {
|
|
96
|
+
if (typeof window === "undefined" || typeof Image === "undefined") return;
|
|
97
|
+
if (isLocal()) return;
|
|
98
|
+
const fire = (uid) => {
|
|
99
|
+
const params = new URLSearchParams({
|
|
100
|
+
Red3: `BACID_${options.customerId}`,
|
|
101
|
+
VID: options.visitorId
|
|
102
|
+
});
|
|
103
|
+
if (uid) params.set("UID", uid);
|
|
104
|
+
new Image().src = `https://c.bing.com/c.gif?${params.toString()}`;
|
|
105
|
+
};
|
|
106
|
+
if (!options.userId) {
|
|
107
|
+
fire();
|
|
108
|
+
return;
|
|
109
|
+
}
|
|
110
|
+
sha256(options.userId).then(fire, () => fire());
|
|
111
|
+
}
|
|
112
|
+
//#endregion
|
|
113
|
+
export { sendUETEvent, sendUETIdSync, setUETConsent, setUETUser };
|
|
114
|
+
|
|
115
|
+
//# sourceMappingURL=microsoft-uet.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"microsoft-uet.mjs","names":[],"sources":["../../src/third-parties/microsoft-uet.ts"],"sourcesContent":["import type { UpdateVisitorDTO } from '../schema/index';\nimport type { EventName, TrackName, TrackProperties } from '../track/types';\nimport { type UETConsent, type UETQ, mapUETEvent } from '../track/uetq';\nimport { getFirst } from '../utils/field';\nimport { sha256 } from '../utils/sha256';\n\ndeclare global {\n interface Window {\n /**\n * Defined by the UET snippet before `bat.js` loads (as a plain array), so pushes made early\n * are queued rather than lost. Undefined only where the snippet was never installed.\n */\n uetq?: UETQ;\n }\n}\n\nconst metrics = ['CLS', 'FCP', 'FID', 'INP', 'LCP', 'TTFB'];\n\n/** Drop `undefined` fields: bat.js validates every key it receives, and `undefined` is a value. */\n// oxlint-disable-next-line @typescript-eslint/no-explicit-any\nfunction clean(value: unknown): any {\n return JSON.parse(JSON.stringify(value));\n}\n\nfunction isLocal(): boolean {\n return window.location.host.includes('127.0.0.1') || window.location.host.includes('localhost');\n}\n\n/**\n * Forward an internal track event to the Microsoft Advertising UET tag.\n *\n * `eventId` is reused as UET's `event_id` so the browser event deduplicates against the same\n * conversion sent through the Conversions API (`sendMicrosoftEvents`). The action name is the\n * internal name on both channels — see `mapUETEvent`.\n *\n * `page_view` is not forwarded: bat.js turns that action into its page-load beacon, which the\n * snippet already fires on load and, with `enableAutoSpaTracking: true` (the setting in\n * Microsoft's generated snippet), on every history change — forwarding the SDK's own page views\n * would report each page twice. The tag owns page loads; this tracker owns everything else.\n */\nexport function sendUETEvent<T extends EventName>(\n name: TrackName<T>,\n properties?: TrackProperties<T>,\n eventId?: string\n) {\n if (typeof window === 'undefined' || !window.uetq) {\n console.warn('uetq has not been initialized');\n return;\n }\n if (metrics.includes(name) || name === 'page_view') return;\n if (isLocal()) return;\n\n const [action, params] = mapUETEvent(name, properties, eventId);\n window.uetq.push('event', action, clean(params));\n}\n\n/**\n * Enhanced conversions: hand the tag the visitor's email and phone so Microsoft can match a\n * conversion to an ad click without a third-party cookie. A `set` applies to every event fired\n * from the page afterwards, `pageLoad` included, and the tag fires a `pid` beacon on its own\n * when the page load already went out. Values are sent raw — the tag normalizes and SHA-256\n * hashes them in the browser (bat.js `validatePid`) before anything leaves the page.\n *\n * No tag id parameter: unlike the Meta/Reddit pixels, UET is bound to its tag by the snippet\n * and `set` addresses whichever tag drains `uetq`.\n */\nexport function setUETUser() {\n return ({ user_data }: UpdateVisitorDTO) => {\n if (typeof window === 'undefined' || !window.uetq) {\n console.warn('uetq has not been initialized');\n return;\n }\n\n const em = getFirst(user_data?.email);\n const ph = getFirst(user_data?.phone_number);\n if (!em && !ph) return;\n\n window.uetq.push('set', { pid: clean({ em, ph }) });\n };\n}\n\n/**\n * UET consent mode. Push `default` before the tag loads (the tag processes `consent` early, ahead\n * of the queued events) and `update` when the visitor decides. With `ad_storage: 'denied'` the\n * tag neither writes its cookies nor reads the `msclkid` from the URL.\n * https://help.ads.microsoft.com/#apex/ads/en/60119/1\n */\nexport function setUETConsent(mode: 'default' | 'update', consent: UETConsent) {\n if (typeof window === 'undefined') return;\n window.uetq ??= [] as unknown as UETQ;\n window.uetq.push('consent', mode, consent);\n}\n\n/**\n * The Conversions API's client-side ID Sync pixel: ties the visitor id sent server-side as\n * `anonymousId` to Microsoft's own identifier, so server events can be attributed across\n * contexts (view-through, audiences). Microsoft requires it for remarketing built from CAPI\n * events and recommends it for measurement quality; fire it at least once per session, ideally\n * on the first page view. A pixel rather than a fetch on purpose: Microsoft must observe the\n * browser context of the sync, which a server-side call cannot carry.\n *\n * `customerId` is the Microsoft Advertising customer id (`cid` in the UI's URLs) — not the UET\n * tag id. `visitorId` is the SDK visitor id (`getVisitor().id`), which is what\n * `sendMicrosoftEvents` sends as `anonymousId`; Microsoft requires the two to be equal.\n * `userId` is the raw signed-in user id: it is SHA-256 hashed here, exactly as the server sender\n * hashes it into `externalId`, so the pair matches as well. Hashing is asynchronous, so the\n * pixel fires once the digest resolves; where SubtleCrypto is unavailable (an insecure context)\n * the pixel goes out without `UID` rather than with a raw id.\n * https://learn.microsoft.com/en-us/advertising/guides/uet-conversion-api-integration#id-sync-and-why-it-matters\n */\nexport function sendUETIdSync(options: {\n customerId: string | number;\n visitorId: string;\n /** The signed-in user's id, raw; hashed before it leaves the page. */\n userId?: string;\n}) {\n if (typeof window === 'undefined' || typeof Image === 'undefined') return;\n if (isLocal()) return;\n\n const fire = (uid?: string) => {\n const params = new URLSearchParams({\n Red3: `BACID_${options.customerId}`,\n VID: options.visitorId,\n });\n if (uid) params.set('UID', uid);\n new Image().src = `https://c.bing.com/c.gif?${params.toString()}`;\n };\n\n if (!options.userId) {\n fire();\n return;\n }\n sha256(options.userId).then(fire, () => fire());\n}\n"],"mappings":";;;;AAgBA,MAAM,UAAU;CAAC;CAAO;CAAO;CAAO;CAAO;CAAO;AAAM;;AAI1D,SAAS,MAAM,OAAqB;CAClC,OAAO,KAAK,MAAM,KAAK,UAAU,KAAK,CAAC;AACzC;AAEA,SAAS,UAAmB;CAC1B,OAAO,OAAO,SAAS,KAAK,SAAS,WAAW,KAAK,OAAO,SAAS,KAAK,SAAS,WAAW;AAChG;;;;;;;;;;;;;AAcA,SAAgB,aACd,MACA,YACA,SACA;CACA,IAAI,OAAO,WAAW,eAAe,CAAC,OAAO,MAAM;EACjD,QAAQ,KAAK,+BAA+B;EAC5C;CACF;CACA,IAAI,QAAQ,SAAS,IAAI,KAAK,SAAS,aAAa;CACpD,IAAI,QAAQ,GAAG;CAEf,MAAM,CAAC,QAAQ,UAAU,YAAY,MAAM,YAAY,OAAO;CAC9D,OAAO,KAAK,KAAK,SAAS,QAAQ,MAAM,MAAM,CAAC;AACjD;;;;;;;;;;;AAYA,SAAgB,aAAa;CAC3B,QAAQ,EAAE,gBAAkC;EAC1C,IAAI,OAAO,WAAW,eAAe,CAAC,OAAO,MAAM;GACjD,QAAQ,KAAK,+BAA+B;GAC5C;EACF;EAEA,MAAM,KAAK,SAAS,WAAW,KAAK;EACpC,MAAM,KAAK,SAAS,WAAW,YAAY;EAC3C,IAAI,CAAC,MAAM,CAAC,IAAI;EAEhB,OAAO,KAAK,KAAK,OAAO,EAAE,KAAK,MAAM;GAAE;GAAI;EAAG,CAAC,EAAE,CAAC;CACpD;AACF;;;;;;;AAQA,SAAgB,cAAc,MAA4B,SAAqB;CAC7E,IAAI,OAAO,WAAW,aAAa;CACnC,OAAO,SAAS,CAAC;CACjB,OAAO,KAAK,KAAK,WAAW,MAAM,OAAO;AAC3C;;;;;;;;;;;;;;;;;;AAmBA,SAAgB,cAAc,SAK3B;CACD,IAAI,OAAO,WAAW,eAAe,OAAO,UAAU,aAAa;CACnE,IAAI,QAAQ,GAAG;CAEf,MAAM,QAAQ,QAAiB;EAC7B,MAAM,SAAS,IAAI,gBAAgB;GACjC,MAAM,SAAS,QAAQ;GACvB,KAAK,QAAQ;EACf,CAAC;EACD,IAAI,KAAK,OAAO,IAAI,OAAO,GAAG;EAC9B,IAAI,MAAM,CAAC,CAAC,MAAM,4BAA4B,OAAO,SAAS;CAChE;CAEA,IAAI,CAAC,QAAQ,QAAQ;EACnB,KAAK;EACL;CACF;CACA,OAAO,QAAQ,MAAM,CAAC,CAAC,KAAK,YAAY,KAAK,CAAC;AAChD"}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
2
|
const require_utils_field = require("../utils/field.cjs");
|
|
3
3
|
const require_track_oaiq = require("../track/oaiq.cjs");
|
|
4
|
+
const require_utils_sha256 = require("../utils/sha256.cjs");
|
|
4
5
|
//#region src/third-parties/openai-pixel.ts
|
|
5
6
|
/** Drop `undefined` fields so the SDK only receives populated values. */
|
|
6
7
|
function clean(value) {
|
|
@@ -26,12 +27,6 @@ function sendOpenAIEvent(name, properties, eventId) {
|
|
|
26
27
|
});
|
|
27
28
|
else window.oaiq("measure", type, clean(data), { event_id: eventId });
|
|
28
29
|
}
|
|
29
|
-
/** SHA-256 hex digest, lowercase — the format OpenAI expects for hashed identity fields. */
|
|
30
|
-
async function sha256(value) {
|
|
31
|
-
const bytes = new TextEncoder().encode(value);
|
|
32
|
-
const digest = await crypto.subtle.digest("SHA-256", bytes);
|
|
33
|
-
return Array.from(new Uint8Array(digest)).map((byte) => byte.toString(16).padStart(2, "0")).join("");
|
|
34
|
-
}
|
|
35
30
|
/**
|
|
36
31
|
* Re-initialize the pixel with hashed user identity for better conversion matching. Email and
|
|
37
32
|
* external id are hashed client-side; geographic fields are sent raw. Hashing is asynchronous,
|
|
@@ -60,7 +55,7 @@ function setOpenAIUser(pixelId) {
|
|
|
60
55
|
})
|
|
61
56
|
});
|
|
62
57
|
};
|
|
63
|
-
Promise.all([email ? sha256(email) : void 0, user_id ? sha256(user_id) : void 0]).then(([email_sha256, external_id_sha256]) => init({
|
|
58
|
+
Promise.all([email ? require_utils_sha256.sha256(email) : void 0, user_id ? require_utils_sha256.sha256(user_id) : void 0]).then(([email_sha256, external_id_sha256]) => init({
|
|
64
59
|
email_sha256,
|
|
65
60
|
external_id_sha256
|
|
66
61
|
})).catch(() => init({}));
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"openai-pixel.cjs","names":["NON_AD_EVENTS","mapOAIEvent","getFirst"],"sources":["../../src/third-parties/openai-pixel.ts"],"sourcesContent":["import type { UpdateVisitorDTO } from '../schema/index';\nimport { NON_AD_EVENTS, type OAIQ, type OAIQUser, mapOAIEvent } from '../track/oaiq';\nimport type { EventName, TrackName, TrackProperties } from '../track/types';\nimport { getFirst } from '../utils/field';\n\ndeclare global {\n interface Window {\n /** Undefined until the OpenAI pixel script has loaded. */\n oaiq?: OAIQ['oaiq'];\n }\n}\n\n/** Drop `undefined` fields so the SDK only receives populated values. */\n// oxlint-disable-next-line @typescript-eslint/no-explicit-any\nfunction clean(value: unknown): any {\n return JSON.parse(JSON.stringify(value));\n}\n\n/**\n * Forward an internal track event to the OpenAI measurement pixel.\n * `eventId` is reused as the OpenAI `event_id` so browser events deduplicate against the\n * Conversions API. https://developers.openai.com/ads/measurement-pixel\n */\nexport function sendOpenAIEvent<T extends EventName>(\n name: TrackName<T>,\n properties?: TrackProperties<T>,\n eventId?: string\n) {\n if (typeof window === 'undefined' || !window.oaiq) {\n console.warn('oaiq has not been initialized');\n return;\n }\n if (NON_AD_EVENTS.includes(name)) return;\n if (window.location.host.includes('127.0.0.1')) return;\n if (window.location.host.includes('localhost')) return;\n\n const { type, data } = mapOAIEvent(name, properties);\n if (type === 'custom') {\n window.oaiq('measure', 'custom', clean(data), { event_id: eventId, custom_event_name: name });\n } else {\n window.oaiq('measure', type, clean(data), { event_id: eventId });\n }\n}\n\n
|
|
1
|
+
{"version":3,"file":"openai-pixel.cjs","names":["NON_AD_EVENTS","mapOAIEvent","getFirst","sha256"],"sources":["../../src/third-parties/openai-pixel.ts"],"sourcesContent":["import type { UpdateVisitorDTO } from '../schema/index';\nimport { NON_AD_EVENTS, type OAIQ, type OAIQUser, mapOAIEvent } from '../track/oaiq';\nimport type { EventName, TrackName, TrackProperties } from '../track/types';\nimport { getFirst } from '../utils/field';\nimport { sha256 } from '../utils/sha256';\n\ndeclare global {\n interface Window {\n /** Undefined until the OpenAI pixel script has loaded. */\n oaiq?: OAIQ['oaiq'];\n }\n}\n\n/** Drop `undefined` fields so the SDK only receives populated values. */\n// oxlint-disable-next-line @typescript-eslint/no-explicit-any\nfunction clean(value: unknown): any {\n return JSON.parse(JSON.stringify(value));\n}\n\n/**\n * Forward an internal track event to the OpenAI measurement pixel.\n * `eventId` is reused as the OpenAI `event_id` so browser events deduplicate against the\n * Conversions API. https://developers.openai.com/ads/measurement-pixel\n */\nexport function sendOpenAIEvent<T extends EventName>(\n name: TrackName<T>,\n properties?: TrackProperties<T>,\n eventId?: string\n) {\n if (typeof window === 'undefined' || !window.oaiq) {\n console.warn('oaiq has not been initialized');\n return;\n }\n if (NON_AD_EVENTS.includes(name)) return;\n if (window.location.host.includes('127.0.0.1')) return;\n if (window.location.host.includes('localhost')) return;\n\n const { type, data } = mapOAIEvent(name, properties);\n if (type === 'custom') {\n window.oaiq('measure', 'custom', clean(data), { event_id: eventId, custom_event_name: name });\n } else {\n window.oaiq('measure', type, clean(data), { event_id: eventId });\n }\n}\n\n/**\n * Re-initialize the pixel with hashed user identity for better conversion matching. Email and\n * external id are hashed client-side; geographic fields are sent raw. Hashing is asynchronous,\n * so the `init` call is deferred until the digests resolve.\n */\nexport function setOpenAIUser(pixelId: string) {\n return ({ user_id, user_data }: UpdateVisitorDTO) => {\n if (typeof window === 'undefined' || !window.oaiq) {\n console.warn('oaiq has not been initialized');\n return;\n }\n // Capture the narrowed reference so the deferred `init` closure keeps it non-optional.\n const oaiq = window.oaiq;\n\n const email = getFirst(user_data?.email)?.trim().toLowerCase();\n const address = getFirst(user_data?.address);\n\n const base: OAIQUser = {\n country: address?.country?.trim().toUpperCase(),\n city: address?.city?.trim().toLowerCase(),\n zip_code: address?.postal_code,\n };\n\n const init = (hashed: Partial<OAIQUser>) => {\n oaiq('init', { pixelId, user: clean({ ...base, ...hashed }) });\n };\n\n Promise.all([email ? sha256(email) : undefined, user_id ? sha256(user_id) : undefined])\n .then(([email_sha256, external_id_sha256]) => init({ email_sha256, external_id_sha256 }))\n .catch(() => init({}));\n };\n}\n"],"mappings":";;;;;;AAeA,SAAS,MAAM,OAAqB;CAClC,OAAO,KAAK,MAAM,KAAK,UAAU,KAAK,CAAC;AACzC;;;;;;AAOA,SAAgB,gBACd,MACA,YACA,SACA;CACA,IAAI,OAAO,WAAW,eAAe,CAAC,OAAO,MAAM;EACjD,QAAQ,KAAK,+BAA+B;EAC5C;CACF;CACA,IAAIA,mBAAAA,cAAc,SAAS,IAAI,GAAG;CAClC,IAAI,OAAO,SAAS,KAAK,SAAS,WAAW,GAAG;CAChD,IAAI,OAAO,SAAS,KAAK,SAAS,WAAW,GAAG;CAEhD,MAAM,EAAE,MAAM,SAASC,mBAAAA,YAAY,MAAM,UAAU;CACnD,IAAI,SAAS,UACX,OAAO,KAAK,WAAW,UAAU,MAAM,IAAI,GAAG;EAAE,UAAU;EAAS,mBAAmB;CAAK,CAAC;MAE5F,OAAO,KAAK,WAAW,MAAM,MAAM,IAAI,GAAG,EAAE,UAAU,QAAQ,CAAC;AAEnE;;;;;;AAOA,SAAgB,cAAc,SAAiB;CAC7C,QAAQ,EAAE,SAAS,gBAAkC;EACnD,IAAI,OAAO,WAAW,eAAe,CAAC,OAAO,MAAM;GACjD,QAAQ,KAAK,+BAA+B;GAC5C;EACF;EAEA,MAAM,OAAO,OAAO;EAEpB,MAAM,QAAQC,oBAAAA,SAAS,WAAW,KAAK,CAAC,EAAE,KAAK,CAAC,CAAC,YAAY;EAC7D,MAAM,UAAUA,oBAAAA,SAAS,WAAW,OAAO;EAE3C,MAAM,OAAiB;GACrB,SAAS,SAAS,SAAS,KAAK,CAAC,CAAC,YAAY;GAC9C,MAAM,SAAS,MAAM,KAAK,CAAC,CAAC,YAAY;GACxC,UAAU,SAAS;EACrB;EAEA,MAAM,QAAQ,WAA8B;GAC1C,KAAK,QAAQ;IAAE;IAAS,MAAM,MAAM;KAAE,GAAG;KAAM,GAAG;IAAO,CAAC;GAAE,CAAC;EAC/D;EAEA,QAAQ,IAAI,CAAC,QAAQC,qBAAAA,OAAO,KAAK,IAAI,KAAA,GAAW,UAAUA,qBAAAA,OAAO,OAAO,IAAI,KAAA,CAAS,CAAC,CAAC,CACpF,MAAM,CAAC,cAAc,wBAAwB,KAAK;GAAE;GAAc;EAAmB,CAAC,CAAC,CAAC,CACxF,YAAY,KAAK,CAAC,CAAC,CAAC;CACzB;AACF"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"openai-pixel.d.cts","names":[],"sources":["../../src/third-parties/openai-pixel.ts"],"mappings":";;;;
|
|
1
|
+
{"version":3,"file":"openai-pixel.d.cts","names":[],"sources":["../../src/third-parties/openai-pixel.ts"],"mappings":";;;;QAMQ;YACI;;IAER,OAAO;;;;;;;;iBAeK,gBAAgB,UAAU,WACxC,MAAM,UAAU,IAChB,aAAa,gBAAgB,IAC7B;;;;;;iBAuBc,cAAc,qBAAe,SAAA,aACX"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"openai-pixel.d.mts","names":[],"sources":["../../src/third-parties/openai-pixel.ts"],"mappings":";;;;
|
|
1
|
+
{"version":3,"file":"openai-pixel.d.mts","names":[],"sources":["../../src/third-parties/openai-pixel.ts"],"mappings":";;;;QAMQ;YACI;;IAER,OAAO;;;;;;;;iBAeK,gBAAgB,UAAU,WACxC,MAAM,UAAU,IAChB,aAAa,gBAAgB,IAC7B;;;;;;iBAuBc,cAAc,qBAAe,SAAA,aACX"}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { getFirst } from "../utils/field.mjs";
|
|
2
2
|
import { NON_AD_EVENTS, mapOAIEvent } from "../track/oaiq.mjs";
|
|
3
|
+
import { sha256 } from "../utils/sha256.mjs";
|
|
3
4
|
//#region src/third-parties/openai-pixel.ts
|
|
4
5
|
/** Drop `undefined` fields so the SDK only receives populated values. */
|
|
5
6
|
function clean(value) {
|
|
@@ -25,12 +26,6 @@ function sendOpenAIEvent(name, properties, eventId) {
|
|
|
25
26
|
});
|
|
26
27
|
else window.oaiq("measure", type, clean(data), { event_id: eventId });
|
|
27
28
|
}
|
|
28
|
-
/** SHA-256 hex digest, lowercase — the format OpenAI expects for hashed identity fields. */
|
|
29
|
-
async function sha256(value) {
|
|
30
|
-
const bytes = new TextEncoder().encode(value);
|
|
31
|
-
const digest = await crypto.subtle.digest("SHA-256", bytes);
|
|
32
|
-
return Array.from(new Uint8Array(digest)).map((byte) => byte.toString(16).padStart(2, "0")).join("");
|
|
33
|
-
}
|
|
34
29
|
/**
|
|
35
30
|
* Re-initialize the pixel with hashed user identity for better conversion matching. Email and
|
|
36
31
|
* external id are hashed client-side; geographic fields are sent raw. Hashing is asynchronous,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"openai-pixel.mjs","names":[],"sources":["../../src/third-parties/openai-pixel.ts"],"sourcesContent":["import type { UpdateVisitorDTO } from '../schema/index';\nimport { NON_AD_EVENTS, type OAIQ, type OAIQUser, mapOAIEvent } from '../track/oaiq';\nimport type { EventName, TrackName, TrackProperties } from '../track/types';\nimport { getFirst } from '../utils/field';\n\ndeclare global {\n interface Window {\n /** Undefined until the OpenAI pixel script has loaded. */\n oaiq?: OAIQ['oaiq'];\n }\n}\n\n/** Drop `undefined` fields so the SDK only receives populated values. */\n// oxlint-disable-next-line @typescript-eslint/no-explicit-any\nfunction clean(value: unknown): any {\n return JSON.parse(JSON.stringify(value));\n}\n\n/**\n * Forward an internal track event to the OpenAI measurement pixel.\n * `eventId` is reused as the OpenAI `event_id` so browser events deduplicate against the\n * Conversions API. https://developers.openai.com/ads/measurement-pixel\n */\nexport function sendOpenAIEvent<T extends EventName>(\n name: TrackName<T>,\n properties?: TrackProperties<T>,\n eventId?: string\n) {\n if (typeof window === 'undefined' || !window.oaiq) {\n console.warn('oaiq has not been initialized');\n return;\n }\n if (NON_AD_EVENTS.includes(name)) return;\n if (window.location.host.includes('127.0.0.1')) return;\n if (window.location.host.includes('localhost')) return;\n\n const { type, data } = mapOAIEvent(name, properties);\n if (type === 'custom') {\n window.oaiq('measure', 'custom', clean(data), { event_id: eventId, custom_event_name: name });\n } else {\n window.oaiq('measure', type, clean(data), { event_id: eventId });\n }\n}\n\n
|
|
1
|
+
{"version":3,"file":"openai-pixel.mjs","names":[],"sources":["../../src/third-parties/openai-pixel.ts"],"sourcesContent":["import type { UpdateVisitorDTO } from '../schema/index';\nimport { NON_AD_EVENTS, type OAIQ, type OAIQUser, mapOAIEvent } from '../track/oaiq';\nimport type { EventName, TrackName, TrackProperties } from '../track/types';\nimport { getFirst } from '../utils/field';\nimport { sha256 } from '../utils/sha256';\n\ndeclare global {\n interface Window {\n /** Undefined until the OpenAI pixel script has loaded. */\n oaiq?: OAIQ['oaiq'];\n }\n}\n\n/** Drop `undefined` fields so the SDK only receives populated values. */\n// oxlint-disable-next-line @typescript-eslint/no-explicit-any\nfunction clean(value: unknown): any {\n return JSON.parse(JSON.stringify(value));\n}\n\n/**\n * Forward an internal track event to the OpenAI measurement pixel.\n * `eventId` is reused as the OpenAI `event_id` so browser events deduplicate against the\n * Conversions API. https://developers.openai.com/ads/measurement-pixel\n */\nexport function sendOpenAIEvent<T extends EventName>(\n name: TrackName<T>,\n properties?: TrackProperties<T>,\n eventId?: string\n) {\n if (typeof window === 'undefined' || !window.oaiq) {\n console.warn('oaiq has not been initialized');\n return;\n }\n if (NON_AD_EVENTS.includes(name)) return;\n if (window.location.host.includes('127.0.0.1')) return;\n if (window.location.host.includes('localhost')) return;\n\n const { type, data } = mapOAIEvent(name, properties);\n if (type === 'custom') {\n window.oaiq('measure', 'custom', clean(data), { event_id: eventId, custom_event_name: name });\n } else {\n window.oaiq('measure', type, clean(data), { event_id: eventId });\n }\n}\n\n/**\n * Re-initialize the pixel with hashed user identity for better conversion matching. Email and\n * external id are hashed client-side; geographic fields are sent raw. Hashing is asynchronous,\n * so the `init` call is deferred until the digests resolve.\n */\nexport function setOpenAIUser(pixelId: string) {\n return ({ user_id, user_data }: UpdateVisitorDTO) => {\n if (typeof window === 'undefined' || !window.oaiq) {\n console.warn('oaiq has not been initialized');\n return;\n }\n // Capture the narrowed reference so the deferred `init` closure keeps it non-optional.\n const oaiq = window.oaiq;\n\n const email = getFirst(user_data?.email)?.trim().toLowerCase();\n const address = getFirst(user_data?.address);\n\n const base: OAIQUser = {\n country: address?.country?.trim().toUpperCase(),\n city: address?.city?.trim().toLowerCase(),\n zip_code: address?.postal_code,\n };\n\n const init = (hashed: Partial<OAIQUser>) => {\n oaiq('init', { pixelId, user: clean({ ...base, ...hashed }) });\n };\n\n Promise.all([email ? sha256(email) : undefined, user_id ? sha256(user_id) : undefined])\n .then(([email_sha256, external_id_sha256]) => init({ email_sha256, external_id_sha256 }))\n .catch(() => init({}));\n };\n}\n"],"mappings":";;;;;AAeA,SAAS,MAAM,OAAqB;CAClC,OAAO,KAAK,MAAM,KAAK,UAAU,KAAK,CAAC;AACzC;;;;;;AAOA,SAAgB,gBACd,MACA,YACA,SACA;CACA,IAAI,OAAO,WAAW,eAAe,CAAC,OAAO,MAAM;EACjD,QAAQ,KAAK,+BAA+B;EAC5C;CACF;CACA,IAAI,cAAc,SAAS,IAAI,GAAG;CAClC,IAAI,OAAO,SAAS,KAAK,SAAS,WAAW,GAAG;CAChD,IAAI,OAAO,SAAS,KAAK,SAAS,WAAW,GAAG;CAEhD,MAAM,EAAE,MAAM,SAAS,YAAY,MAAM,UAAU;CACnD,IAAI,SAAS,UACX,OAAO,KAAK,WAAW,UAAU,MAAM,IAAI,GAAG;EAAE,UAAU;EAAS,mBAAmB;CAAK,CAAC;MAE5F,OAAO,KAAK,WAAW,MAAM,MAAM,IAAI,GAAG,EAAE,UAAU,QAAQ,CAAC;AAEnE;;;;;;AAOA,SAAgB,cAAc,SAAiB;CAC7C,QAAQ,EAAE,SAAS,gBAAkC;EACnD,IAAI,OAAO,WAAW,eAAe,CAAC,OAAO,MAAM;GACjD,QAAQ,KAAK,+BAA+B;GAC5C;EACF;EAEA,MAAM,OAAO,OAAO;EAEpB,MAAM,QAAQ,SAAS,WAAW,KAAK,CAAC,EAAE,KAAK,CAAC,CAAC,YAAY;EAC7D,MAAM,UAAU,SAAS,WAAW,OAAO;EAE3C,MAAM,OAAiB;GACrB,SAAS,SAAS,SAAS,KAAK,CAAC,CAAC,YAAY;GAC9C,MAAM,SAAS,MAAM,KAAK,CAAC,CAAC,YAAY;GACxC,UAAU,SAAS;EACrB;EAEA,MAAM,QAAQ,WAA8B;GAC1C,KAAK,QAAQ;IAAE;IAAS,MAAM,MAAM;KAAE,GAAG;KAAM,GAAG;IAAO,CAAC;GAAE,CAAC;EAC/D;EAEA,QAAQ,IAAI,CAAC,QAAQ,OAAO,KAAK,IAAI,KAAA,GAAW,UAAU,OAAO,OAAO,IAAI,KAAA,CAAS,CAAC,CAAC,CACpF,MAAM,CAAC,cAAc,wBAAwB,KAAK;GAAE;GAAc;EAAmB,CAAC,CAAC,CAAC,CACxF,YAAY,KAAK,CAAC,CAAC,CAAC;CACzB;AACF"}
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
|
+
//#region src/track/uetq.ts
|
|
3
|
+
/** GA4 `Item` → UET `items[]` entry. Undefined fields are left for the caller to strip. */
|
|
4
|
+
function mapItems(items) {
|
|
5
|
+
if (!items || items.length === 0) return void 0;
|
|
6
|
+
return items.map((item) => ({
|
|
7
|
+
id: item.item_id,
|
|
8
|
+
name: item.item_name,
|
|
9
|
+
brand: item.item_brand,
|
|
10
|
+
category: item.item_category,
|
|
11
|
+
variant: item.item_variant,
|
|
12
|
+
price: item.price,
|
|
13
|
+
quantity: item.quantity,
|
|
14
|
+
list_name: item.item_list_name,
|
|
15
|
+
list_position: item.index,
|
|
16
|
+
location_id: item.location_id
|
|
17
|
+
}));
|
|
18
|
+
}
|
|
19
|
+
function number(value) {
|
|
20
|
+
return typeof value === "number" && Number.isFinite(value) ? value : void 0;
|
|
21
|
+
}
|
|
22
|
+
function string(value) {
|
|
23
|
+
return typeof value === "string" && value !== "" ? value : void 0;
|
|
24
|
+
}
|
|
25
|
+
function currency(value) {
|
|
26
|
+
return string(value)?.toUpperCase();
|
|
27
|
+
}
|
|
28
|
+
const PAGE_TYPES = [
|
|
29
|
+
"home",
|
|
30
|
+
"searchresults",
|
|
31
|
+
"category",
|
|
32
|
+
"product",
|
|
33
|
+
"cart",
|
|
34
|
+
"purchase",
|
|
35
|
+
"other"
|
|
36
|
+
];
|
|
37
|
+
function pageType(value) {
|
|
38
|
+
const lowered = string(value)?.toLowerCase();
|
|
39
|
+
return lowered && PAGE_TYPES.includes(lowered) ? lowered : void 0;
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Map an internal track event onto a UET action and its parameters.
|
|
43
|
+
*
|
|
44
|
+
* The action is always the internal name: for the GA4-named standard events that is also the
|
|
45
|
+
* name UET knows, and for everything else it is the custom action a UET event goal is configured
|
|
46
|
+
* with. Keeping the name identical on both channels is what lets the Conversions API sender
|
|
47
|
+
* (`eventName`) deduplicate against the browser (`event_id`).
|
|
48
|
+
*
|
|
49
|
+
* `page_view` is special to the tag: bat.js routes that action to its page-load beacon (the same
|
|
50
|
+
* one the snippet fires on load and `enableAutoSpaTracking` fires on history changes), so the
|
|
51
|
+
* browser tracker does not forward it — see `sendUETEvent`.
|
|
52
|
+
*/
|
|
53
|
+
function mapUETEvent(name, properties, eventId) {
|
|
54
|
+
const p = properties ?? {};
|
|
55
|
+
const params = { event_id: eventId };
|
|
56
|
+
params.revenue_value = number(p.value) ?? number(p.revenue_value);
|
|
57
|
+
params.currency = currency(p.currency);
|
|
58
|
+
params.transaction_id = string(p.transaction_id);
|
|
59
|
+
params.tax = number(p.tax);
|
|
60
|
+
params.shipping = number(p.shipping);
|
|
61
|
+
params.coupon = string(p.coupon);
|
|
62
|
+
params.items = Array.isArray(p.items) ? mapItems(p.items) : void 0;
|
|
63
|
+
params.search_term = string(p.search_term);
|
|
64
|
+
params.method = string(p.method);
|
|
65
|
+
params.content_type = string(p.content_type);
|
|
66
|
+
params.content_id = string(p.content_id);
|
|
67
|
+
params.event_category = string(p.event_category);
|
|
68
|
+
params.event_label = string(p.event_label);
|
|
69
|
+
params.event_value = number(p.event_value);
|
|
70
|
+
params.ecomm_pagetype = pageType(p.ecomm_pagetype);
|
|
71
|
+
params.ecomm_prodid = params.ecomm_pagetype ? Array.isArray(p.ecomm_prodid) ? p.ecomm_prodid : string(p.ecomm_prodid) : void 0;
|
|
72
|
+
params.ecomm_totalvalue = number(p.ecomm_totalvalue);
|
|
73
|
+
params.ecomm_category = string(p.ecomm_category);
|
|
74
|
+
return [name, params];
|
|
75
|
+
}
|
|
76
|
+
//#endregion
|
|
77
|
+
exports.mapItems = mapItems;
|
|
78
|
+
exports.mapUETEvent = mapUETEvent;
|
|
79
|
+
|
|
80
|
+
//# sourceMappingURL=uetq.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"uetq.cjs","names":[],"sources":["../../src/track/uetq.ts"],"sourcesContent":["import type { Item } from './gtag';\nimport type { EventName, TrackName, TrackProperties } from './types';\n\n/**\n * Microsoft Advertising Universal Event Tracking (UET) — the `uetq` queue that `bat.js` drains.\n *\n * UET's modern event API is gtag-shaped: `uetq.push('event', '<action>', { ...params })`, and the\n * actions it knows (`purchase`, `add_to_cart`, `sign_up`, …) are GA4's recommended event names.\n * Since this SDK's own event names are GA4's too, a standard event maps onto UET under the same\n * name — only the parameter spellings differ (`value` → `revenue_value`, `Item` → `items[]`). An\n * unknown name goes out unchanged as a custom event, which is what a UET *event goal* matches on.\n *\n * Parameter names below are taken from `bat.js` itself (`knownParams`); the tag silently drops\n * any key it does not know, so an unmapped property is lost rather than an error. (bat.js's\n * `knownEvents` list is not an allow-list — the tag only uses it to inherit page-level `set`\n * parameters onto the events that carry them.)\n *\n * https://help.ads.microsoft.com/#apex/ads/en/56684/2 (custom events)\n * https://help.ads.microsoft.com/#apex/ads/en/56910/1 (retail / dynamic remarketing)\n * https://learn.microsoft.com/en-us/advertising/msa-help/hlp_ba_conc_uet_enhancedconversions\n */\n\n/** The retail `ecomm_pagetype` values UET validates against. */\nexport type UETPageType =\n | 'home'\n | 'searchresults'\n | 'category'\n | 'product'\n | 'cart'\n | 'purchase'\n | 'other';\n\n/** One entry of the `items` array (`items.*` in bat.js's `knownParams`). */\nexport interface UETItem {\n id?: string;\n name?: string;\n brand?: string;\n category?: string;\n variant?: string;\n price?: number;\n quantity?: number;\n list_name?: string;\n list_position?: number;\n location_id?: string;\n}\n\n/**\n * Enhanced conversions identifiers. Sent RAW: the tag normalizes and SHA-256 hashes them in the\n * browser before anything leaves the page (bat.js `validatePid`), and rejects a phone that is\n * not E.164 after stripping separators.\n */\nexport interface UETPid {\n em?: string;\n ph?: string;\n}\n\n/** The parameters of one `uetq.push('event', action, params)` call. */\nexport interface UETEventParams {\n /** Deduplicates against the same event sent through the Conversions API. */\n event_id?: string;\n event_category?: string;\n event_label?: string;\n event_value?: number;\n /** Variable revenue (`gv` on the wire). */\n revenue_value?: number;\n /** ISO 4217, uppercase (`gc` on the wire). */\n currency?: string;\n transaction_id?: string;\n tax?: number;\n shipping?: number;\n coupon?: string;\n items?: UETItem[];\n search_term?: string;\n method?: string;\n content_type?: string;\n content_id?: string;\n ecomm_prodid?: string | string[];\n ecomm_pagetype?: UETPageType;\n ecomm_totalvalue?: number;\n ecomm_category?: string;\n pid?: UETPid;\n}\n\nexport type UETConsent = {\n ad_storage: 'granted' | 'denied';\n /**\n * `default` only: milliseconds to hold events for an `update` before acting on the default,\n * so a CMP that answers quickly does not cost the landing page. bat.js reads exactly this\n * lowercase key (some community snippets spell it `Wait_for_update`, which it ignores) and\n * caps it at 10,000.\n */\n wait_for_update?: number;\n};\n\n/**\n * `window.uetq` is a plain array until `bat.js` loads (the snippet does `w[u] = w[u] || []`), then\n * a `UET` instance that drains the queue; both expose `push`, which is all this SDK uses.\n */\nexport interface UETQ {\n push(command: 'event', action: string, params?: UETEventParams): void;\n push(command: 'pageLoad'): void;\n push(command: 'set', params: { pid?: UETPid }): void;\n push(command: 'consent', mode: 'default' | 'update', consent: UETConsent): void;\n}\n\n/** GA4 `Item` → UET `items[]` entry. Undefined fields are left for the caller to strip. */\nexport function mapItems(items?: Item[]): undefined | UETItem[] {\n if (!items || items.length === 0) return undefined;\n return items.map((item) => ({\n id: item.item_id,\n name: item.item_name,\n brand: item.item_brand,\n category: item.item_category,\n variant: item.item_variant,\n price: item.price,\n quantity: item.quantity,\n list_name: item.item_list_name,\n list_position: item.index,\n location_id: item.location_id,\n }));\n}\n\nfunction number(value: unknown): number | undefined {\n return typeof value === 'number' && Number.isFinite(value) ? value : undefined;\n}\n\nfunction string(value: unknown): string | undefined {\n return typeof value === 'string' && value !== '' ? value : undefined;\n}\n\nfunction currency(value: unknown): string | undefined {\n return string(value)?.toUpperCase();\n}\n\nconst PAGE_TYPES: string[] = [\n 'home',\n 'searchresults',\n 'category',\n 'product',\n 'cart',\n 'purchase',\n 'other',\n];\n\nfunction pageType(value: unknown): UETPageType | undefined {\n const lowered = string(value)?.toLowerCase();\n return lowered && PAGE_TYPES.includes(lowered) ? (lowered as UETPageType) : undefined;\n}\n\n/**\n * Map an internal track event onto a UET action and its parameters.\n *\n * The action is always the internal name: for the GA4-named standard events that is also the\n * name UET knows, and for everything else it is the custom action a UET event goal is configured\n * with. Keeping the name identical on both channels is what lets the Conversions API sender\n * (`eventName`) deduplicate against the browser (`event_id`).\n *\n * `page_view` is special to the tag: bat.js routes that action to its page-load beacon (the same\n * one the snippet fires on load and `enableAutoSpaTracking` fires on history changes), so the\n * browser tracker does not forward it — see `sendUETEvent`.\n */\nexport function mapUETEvent<T extends EventName>(\n name: TrackName<T>,\n properties?: TrackProperties<T>,\n eventId?: string\n): [string, UETEventParams] {\n // A loose view of whatever the caller passed: every branch below reads only the keys it\n // knows and validates their types, so an event with foreign properties cannot leak them.\n const p = (properties ?? {}) as Record<string, unknown>;\n const params: UETEventParams = { event_id: eventId };\n\n // GA4's commerce parameters, spelled the UET way. `value` is GA4's revenue field and becomes\n // UET's variable revenue; every key here is one bat.js lists for the events that carry it.\n params.revenue_value = number(p.value) ?? number(p.revenue_value);\n params.currency = currency(p.currency);\n params.transaction_id = string(p.transaction_id);\n params.tax = number(p.tax);\n params.shipping = number(p.shipping);\n params.coupon = string(p.coupon);\n params.items = Array.isArray(p.items) ? mapItems(p.items as Item[]) : undefined;\n params.search_term = string(p.search_term);\n params.method = string(p.method);\n params.content_type = string(p.content_type);\n params.content_id = string(p.content_id);\n\n // The custom-event goal fields and the retail (dynamic remarketing) fields pass through under\n // their UET names, so a host can attach them to any event it defines.\n params.event_category = string(p.event_category);\n params.event_label = string(p.event_label);\n params.event_value = number(p.event_value);\n // bat.js throws on a `prodid` without a `pagetype` (`missingPageTypeException`), which would\n // cost the whole event; a product id on its own is dropped instead.\n params.ecomm_pagetype = pageType(p.ecomm_pagetype);\n params.ecomm_prodid = params.ecomm_pagetype\n ? Array.isArray(p.ecomm_prodid)\n ? (p.ecomm_prodid as string[])\n : string(p.ecomm_prodid)\n : undefined;\n params.ecomm_totalvalue = number(p.ecomm_totalvalue);\n params.ecomm_category = string(p.ecomm_category);\n\n return [name, params];\n}\n"],"mappings":";;;AA0GA,SAAgB,SAAS,OAAuC;CAC9D,IAAI,CAAC,SAAS,MAAM,WAAW,GAAG,OAAO,KAAA;CACzC,OAAO,MAAM,KAAK,UAAU;EAC1B,IAAI,KAAK;EACT,MAAM,KAAK;EACX,OAAO,KAAK;EACZ,UAAU,KAAK;EACf,SAAS,KAAK;EACd,OAAO,KAAK;EACZ,UAAU,KAAK;EACf,WAAW,KAAK;EAChB,eAAe,KAAK;EACpB,aAAa,KAAK;CACpB,EAAE;AACJ;AAEA,SAAS,OAAO,OAAoC;CAClD,OAAO,OAAO,UAAU,YAAY,OAAO,SAAS,KAAK,IAAI,QAAQ,KAAA;AACvE;AAEA,SAAS,OAAO,OAAoC;CAClD,OAAO,OAAO,UAAU,YAAY,UAAU,KAAK,QAAQ,KAAA;AAC7D;AAEA,SAAS,SAAS,OAAoC;CACpD,OAAO,OAAO,KAAK,CAAC,EAAE,YAAY;AACpC;AAEA,MAAM,aAAuB;CAC3B;CACA;CACA;CACA;CACA;CACA;CACA;AACF;AAEA,SAAS,SAAS,OAAyC;CACzD,MAAM,UAAU,OAAO,KAAK,CAAC,EAAE,YAAY;CAC3C,OAAO,WAAW,WAAW,SAAS,OAAO,IAAK,UAA0B,KAAA;AAC9E;;;;;;;;;;;;;AAcA,SAAgB,YACd,MACA,YACA,SAC0B;CAG1B,MAAM,IAAK,cAAc,CAAC;CAC1B,MAAM,SAAyB,EAAE,UAAU,QAAQ;CAInD,OAAO,gBAAgB,OAAO,EAAE,KAAK,KAAK,OAAO,EAAE,aAAa;CAChE,OAAO,WAAW,SAAS,EAAE,QAAQ;CACrC,OAAO,iBAAiB,OAAO,EAAE,cAAc;CAC/C,OAAO,MAAM,OAAO,EAAE,GAAG;CACzB,OAAO,WAAW,OAAO,EAAE,QAAQ;CACnC,OAAO,SAAS,OAAO,EAAE,MAAM;CAC/B,OAAO,QAAQ,MAAM,QAAQ,EAAE,KAAK,IAAI,SAAS,EAAE,KAAe,IAAI,KAAA;CACtE,OAAO,cAAc,OAAO,EAAE,WAAW;CACzC,OAAO,SAAS,OAAO,EAAE,MAAM;CAC/B,OAAO,eAAe,OAAO,EAAE,YAAY;CAC3C,OAAO,aAAa,OAAO,EAAE,UAAU;CAIvC,OAAO,iBAAiB,OAAO,EAAE,cAAc;CAC/C,OAAO,cAAc,OAAO,EAAE,WAAW;CACzC,OAAO,cAAc,OAAO,EAAE,WAAW;CAGzC,OAAO,iBAAiB,SAAS,EAAE,cAAc;CACjD,OAAO,eAAe,OAAO,iBACzB,MAAM,QAAQ,EAAE,YAAY,IACzB,EAAE,eACH,OAAO,EAAE,YAAY,IACvB,KAAA;CACJ,OAAO,mBAAmB,OAAO,EAAE,gBAAgB;CACnD,OAAO,iBAAiB,OAAO,EAAE,cAAc;CAE/C,OAAO,CAAC,MAAM,MAAM;AACtB"}
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
import { Item } from "./gtag.cjs";
|
|
2
|
+
import { EventName, TrackName, TrackProperties } from "./types.cjs";
|
|
3
|
+
//#region src/track/uetq.d.ts
|
|
4
|
+
/**
|
|
5
|
+
* Microsoft Advertising Universal Event Tracking (UET) — the `uetq` queue that `bat.js` drains.
|
|
6
|
+
*
|
|
7
|
+
* UET's modern event API is gtag-shaped: `uetq.push('event', '<action>', { ...params })`, and the
|
|
8
|
+
* actions it knows (`purchase`, `add_to_cart`, `sign_up`, …) are GA4's recommended event names.
|
|
9
|
+
* Since this SDK's own event names are GA4's too, a standard event maps onto UET under the same
|
|
10
|
+
* name — only the parameter spellings differ (`value` → `revenue_value`, `Item` → `items[]`). An
|
|
11
|
+
* unknown name goes out unchanged as a custom event, which is what a UET *event goal* matches on.
|
|
12
|
+
*
|
|
13
|
+
* Parameter names below are taken from `bat.js` itself (`knownParams`); the tag silently drops
|
|
14
|
+
* any key it does not know, so an unmapped property is lost rather than an error. (bat.js's
|
|
15
|
+
* `knownEvents` list is not an allow-list — the tag only uses it to inherit page-level `set`
|
|
16
|
+
* parameters onto the events that carry them.)
|
|
17
|
+
*
|
|
18
|
+
* https://help.ads.microsoft.com/#apex/ads/en/56684/2 (custom events)
|
|
19
|
+
* https://help.ads.microsoft.com/#apex/ads/en/56910/1 (retail / dynamic remarketing)
|
|
20
|
+
* https://learn.microsoft.com/en-us/advertising/msa-help/hlp_ba_conc_uet_enhancedconversions
|
|
21
|
+
*/
|
|
22
|
+
/** The retail `ecomm_pagetype` values UET validates against. */
|
|
23
|
+
type UETPageType = 'home' | 'searchresults' | 'category' | 'product' | 'cart' | 'purchase' | 'other';
|
|
24
|
+
/** One entry of the `items` array (`items.*` in bat.js's `knownParams`). */
|
|
25
|
+
interface UETItem {
|
|
26
|
+
id?: string;
|
|
27
|
+
name?: string;
|
|
28
|
+
brand?: string;
|
|
29
|
+
category?: string;
|
|
30
|
+
variant?: string;
|
|
31
|
+
price?: number;
|
|
32
|
+
quantity?: number;
|
|
33
|
+
list_name?: string;
|
|
34
|
+
list_position?: number;
|
|
35
|
+
location_id?: string;
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Enhanced conversions identifiers. Sent RAW: the tag normalizes and SHA-256 hashes them in the
|
|
39
|
+
* browser before anything leaves the page (bat.js `validatePid`), and rejects a phone that is
|
|
40
|
+
* not E.164 after stripping separators.
|
|
41
|
+
*/
|
|
42
|
+
interface UETPid {
|
|
43
|
+
em?: string;
|
|
44
|
+
ph?: string;
|
|
45
|
+
}
|
|
46
|
+
/** The parameters of one `uetq.push('event', action, params)` call. */
|
|
47
|
+
interface UETEventParams {
|
|
48
|
+
/** Deduplicates against the same event sent through the Conversions API. */
|
|
49
|
+
event_id?: string;
|
|
50
|
+
event_category?: string;
|
|
51
|
+
event_label?: string;
|
|
52
|
+
event_value?: number;
|
|
53
|
+
/** Variable revenue (`gv` on the wire). */
|
|
54
|
+
revenue_value?: number;
|
|
55
|
+
/** ISO 4217, uppercase (`gc` on the wire). */
|
|
56
|
+
currency?: string;
|
|
57
|
+
transaction_id?: string;
|
|
58
|
+
tax?: number;
|
|
59
|
+
shipping?: number;
|
|
60
|
+
coupon?: string;
|
|
61
|
+
items?: UETItem[];
|
|
62
|
+
search_term?: string;
|
|
63
|
+
method?: string;
|
|
64
|
+
content_type?: string;
|
|
65
|
+
content_id?: string;
|
|
66
|
+
ecomm_prodid?: string | string[];
|
|
67
|
+
ecomm_pagetype?: UETPageType;
|
|
68
|
+
ecomm_totalvalue?: number;
|
|
69
|
+
ecomm_category?: string;
|
|
70
|
+
pid?: UETPid;
|
|
71
|
+
}
|
|
72
|
+
type UETConsent = {
|
|
73
|
+
ad_storage: 'granted' | 'denied';
|
|
74
|
+
/**
|
|
75
|
+
* `default` only: milliseconds to hold events for an `update` before acting on the default,
|
|
76
|
+
* so a CMP that answers quickly does not cost the landing page. bat.js reads exactly this
|
|
77
|
+
* lowercase key (some community snippets spell it `Wait_for_update`, which it ignores) and
|
|
78
|
+
* caps it at 10,000.
|
|
79
|
+
*/
|
|
80
|
+
wait_for_update?: number;
|
|
81
|
+
};
|
|
82
|
+
/**
|
|
83
|
+
* `window.uetq` is a plain array until `bat.js` loads (the snippet does `w[u] = w[u] || []`), then
|
|
84
|
+
* a `UET` instance that drains the queue; both expose `push`, which is all this SDK uses.
|
|
85
|
+
*/
|
|
86
|
+
interface UETQ {
|
|
87
|
+
push(command: 'event', action: string, params?: UETEventParams): void;
|
|
88
|
+
push(command: 'pageLoad'): void;
|
|
89
|
+
push(command: 'set', params: {
|
|
90
|
+
pid?: UETPid;
|
|
91
|
+
}): void;
|
|
92
|
+
push(command: 'consent', mode: 'default' | 'update', consent: UETConsent): void;
|
|
93
|
+
}
|
|
94
|
+
/** GA4 `Item` → UET `items[]` entry. Undefined fields are left for the caller to strip. */
|
|
95
|
+
declare function mapItems(items?: Item[]): undefined | UETItem[];
|
|
96
|
+
/**
|
|
97
|
+
* Map an internal track event onto a UET action and its parameters.
|
|
98
|
+
*
|
|
99
|
+
* The action is always the internal name: for the GA4-named standard events that is also the
|
|
100
|
+
* name UET knows, and for everything else it is the custom action a UET event goal is configured
|
|
101
|
+
* with. Keeping the name identical on both channels is what lets the Conversions API sender
|
|
102
|
+
* (`eventName`) deduplicate against the browser (`event_id`).
|
|
103
|
+
*
|
|
104
|
+
* `page_view` is special to the tag: bat.js routes that action to its page-load beacon (the same
|
|
105
|
+
* one the snippet fires on load and `enableAutoSpaTracking` fires on history changes), so the
|
|
106
|
+
* browser tracker does not forward it — see `sendUETEvent`.
|
|
107
|
+
*/
|
|
108
|
+
declare function mapUETEvent<T extends EventName>(name: TrackName<T>, properties?: TrackProperties<T>, eventId?: string): [string, UETEventParams];
|
|
109
|
+
//#endregion
|
|
110
|
+
export { UETConsent, UETEventParams, UETItem, UETPageType, UETPid, UETQ, mapItems, mapUETEvent };
|
|
111
|
+
//# sourceMappingURL=uetq.d.cts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"uetq.d.cts","names":[],"sources":["../../src/track/uetq.ts"],"mappings":";;;;;;;;;;;;;;;;;;;;;;KAuBY;;UAUK;EACf;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;;;;;;UAQe;EACf;EACA;;;UAIe;;EAEf;EACA;EACA;EACA;;EAEA;;EAEA;EACA;EACA;EACA;EACA;EACA,QAAQ;EACR;EACA;EACA;EACA;EACA;EACA,iBAAiB;EACjB;EACA;EACA,MAAM;;KAGI;EACV;;;;;;;EAOA;;;;;;UAOe;EACf,KAAK,kBAAkB,gBAAgB,SAAS;EAChD,KAAK;EACL,KAAK,gBAAgB;IAAU,MAAM;;EACrC,KAAK,oBAAoB,4BAA4B,SAAS;;;iBAIhD,SAAS,QAAQ,qBAAqB;;;;;;;;;;;;;iBAuDtC,YAAY,UAAU,WACpC,MAAM,UAAU,IAChB,aAAa,gBAAgB,IAC7B,4BACU"}
|