react-marketing-tools 1.0.0-alpha.1 → 1.0.0-alpha.3
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/dist/chunks/core.js +258 -0
- package/dist/core/consent.d.ts +20 -0
- package/dist/core/errors.d.ts +1 -1
- package/dist/core/types.d.ts +48 -3
- package/dist/core.d.ts +1 -1
- package/dist/core.js +2 -154
- package/dist/destinations/ga4.d.ts +6 -0
- package/dist/destinations/googleTag.d.ts +22 -0
- package/dist/destinations/gtm.d.ts +1 -1
- package/dist/destinations/loadScript.d.ts +6 -0
- package/dist/index.d.ts +2 -26
- package/dist/index.js +14 -32770
- package/dist/react/AnalyticsProvider.d.ts +10 -0
- package/package.json +2 -17
- package/dist/analytics/analyticsEventService.d.ts +0 -13
- package/dist/analytics/ga4GoogleAnalyticsEventTracking.d.ts +0 -3
- package/dist/analytics/handleDataLayerPush.d.ts +0 -3
- package/dist/analytics/helpers.d.ts +0 -41
- package/dist/buildConfig/index.d.ts +0 -8
- package/dist/ipInfo/index.d.ts +0 -3
- package/dist/types/index.d.ts +0 -130
- package/dist/utilities/assertValueCheckers.d.ts +0 -1
- package/dist/utilities/commonFunctions.d.ts +0 -5
- package/dist/utilities/cookies.d.ts +0 -1
|
@@ -0,0 +1,258 @@
|
|
|
1
|
+
//#region lib/core/consent.ts
|
|
2
|
+
var e = [
|
|
3
|
+
"analytics",
|
|
4
|
+
"ads",
|
|
5
|
+
"adUserData",
|
|
6
|
+
"adPersonalization"
|
|
7
|
+
], t = (e) => e === "granted" || e === "denied", n = ({ consent: e, gpc: t }) => {
|
|
8
|
+
let n = t ? "denied" : e;
|
|
9
|
+
return {
|
|
10
|
+
analytics: e,
|
|
11
|
+
ads: n,
|
|
12
|
+
adUserData: n,
|
|
13
|
+
adPersonalization: n
|
|
14
|
+
};
|
|
15
|
+
}, r = (e, t) => ({
|
|
16
|
+
analytics: t.analytics ?? e.analytics,
|
|
17
|
+
ads: t.ads ?? e.ads,
|
|
18
|
+
adUserData: t.adUserData ?? t.ads ?? e.adUserData,
|
|
19
|
+
adPersonalization: t.adPersonalization ?? t.ads ?? e.adPersonalization
|
|
20
|
+
}), i = (e) => ({
|
|
21
|
+
analytics_storage: e.analytics,
|
|
22
|
+
ad_storage: e.ads,
|
|
23
|
+
ad_user_data: e.adUserData,
|
|
24
|
+
ad_personalization: e.adPersonalization
|
|
25
|
+
}), a = (e) => Object.values(e).includes("denied"), o = () => typeof navigator < "u" && navigator.globalPrivacyControl === !0, s = () => window.dataLayer ??= [], c = () => Array.from(document.scripts).some((e) => URL.canParse(e.src) && new URL(e.src).pathname.endsWith("/gtag/js")), l = () => window.gtag ??= function() {
|
|
26
|
+
s().push(arguments);
|
|
27
|
+
}, u = ({ consent: e, waitForUpdate: t }) => {
|
|
28
|
+
l()("consent", "default", {
|
|
29
|
+
...i(e),
|
|
30
|
+
...a(e) ? { wait_for_update: t } : {}
|
|
31
|
+
});
|
|
32
|
+
}, d = (e) => {
|
|
33
|
+
l()("consent", "update", i(e));
|
|
34
|
+
}, f = (e, t) => {
|
|
35
|
+
let n = URL.canParse(e) ? new URL(e) : void 0;
|
|
36
|
+
if (n?.protocol !== "https:") throw Error(`[react-marketing-tools] ${t} must be an https:// URL (received ${JSON.stringify(e)}).`);
|
|
37
|
+
return n;
|
|
38
|
+
}, p = (e) => Array.from(document.scripts).some((t) => t.src === e), m = ({ src: e, nonce: t }) => {
|
|
39
|
+
let n = document.createElement("script");
|
|
40
|
+
n.async = !0, n.src = e, t && n.setAttribute("nonce", t), document.head.append(n);
|
|
41
|
+
}, h = /^G-[A-Z0-9]+$/, g = ({ measurementId: e, pageViews: t = "auto", loadScript: n = !0, serverContainerUrl: r, waitForUpdate: i = 500, nonce: a }) => {
|
|
42
|
+
if (!h.test(e)) throw Error(`[react-marketing-tools] ga4.measurementId must look like "G-XXXXXXX" (received ${JSON.stringify(e)}).`);
|
|
43
|
+
r !== void 0 && f(r, "ga4.serverContainerUrl");
|
|
44
|
+
let o = `https://www.googletagmanager.com/gtag/js?id=${e}`, s = (e, t) => r ? {
|
|
45
|
+
...e,
|
|
46
|
+
event_id: t
|
|
47
|
+
} : e;
|
|
48
|
+
return {
|
|
49
|
+
name: "ga4",
|
|
50
|
+
start({ consent: s }) {
|
|
51
|
+
u({
|
|
52
|
+
consent: s,
|
|
53
|
+
waitForUpdate: i
|
|
54
|
+
}), c() || (l()("js", /* @__PURE__ */ new Date()), n && m({
|
|
55
|
+
src: o,
|
|
56
|
+
nonce: a
|
|
57
|
+
})), l()("config", e, {
|
|
58
|
+
...t === "manual" ? { send_page_view: !1 } : {},
|
|
59
|
+
...r ? { server_container_url: r } : {}
|
|
60
|
+
});
|
|
61
|
+
},
|
|
62
|
+
track({ name: e, params: t, eventId: n }) {
|
|
63
|
+
l()("event", e, s(t, n));
|
|
64
|
+
},
|
|
65
|
+
consent(e) {
|
|
66
|
+
d(e);
|
|
67
|
+
},
|
|
68
|
+
page({ params: e, eventId: n }) {
|
|
69
|
+
t === "manual" && l()("event", "page_view", s(e, n));
|
|
70
|
+
},
|
|
71
|
+
identify({ userId: e }) {
|
|
72
|
+
l()("set", { user_id: e });
|
|
73
|
+
},
|
|
74
|
+
reset() {
|
|
75
|
+
l()("set", { user_id: null });
|
|
76
|
+
}
|
|
77
|
+
};
|
|
78
|
+
}, _ = /^GTM-[A-Z0-9]+$/, v = (e, t) => {
|
|
79
|
+
let n = f(t ?? "https://www.googletagmanager.com/gtm.js", "gtm.scriptUrl");
|
|
80
|
+
return n.searchParams.set("id", e), n.href;
|
|
81
|
+
}, y = ({ containerId: e, loadScript: t = !0, scriptUrl: n, waitForUpdate: r = 500, nonce: i }) => {
|
|
82
|
+
if (!_.test(e)) throw Error(`[react-marketing-tools] gtm.containerId must look like "GTM-XXXXXXX" (received ${JSON.stringify(e)}).`);
|
|
83
|
+
let a = v(e, n);
|
|
84
|
+
return {
|
|
85
|
+
name: "gtm",
|
|
86
|
+
start({ consent: e }) {
|
|
87
|
+
u({
|
|
88
|
+
consent: e,
|
|
89
|
+
waitForUpdate: r
|
|
90
|
+
}), t && !p(a) && (s().push({
|
|
91
|
+
"gtm.start": Date.now(),
|
|
92
|
+
event: "gtm.js"
|
|
93
|
+
}), m({
|
|
94
|
+
src: a,
|
|
95
|
+
nonce: i
|
|
96
|
+
}));
|
|
97
|
+
},
|
|
98
|
+
track({ name: e, params: t, eventId: n }) {
|
|
99
|
+
s().push({
|
|
100
|
+
...t,
|
|
101
|
+
event: e,
|
|
102
|
+
event_id: n
|
|
103
|
+
});
|
|
104
|
+
},
|
|
105
|
+
consent(e) {
|
|
106
|
+
d(e);
|
|
107
|
+
},
|
|
108
|
+
identify({ userId: e }) {
|
|
109
|
+
s().push({
|
|
110
|
+
event: "identify",
|
|
111
|
+
user_id: e
|
|
112
|
+
});
|
|
113
|
+
},
|
|
114
|
+
reset() {
|
|
115
|
+
s().push({
|
|
116
|
+
event: "reset",
|
|
117
|
+
user_id: void 0
|
|
118
|
+
});
|
|
119
|
+
}
|
|
120
|
+
};
|
|
121
|
+
}, b = class extends Error {
|
|
122
|
+
code;
|
|
123
|
+
constructor(e, t, n) {
|
|
124
|
+
super(`[react-marketing-tools] ${t}`, n), this.name = "AnalyticsError", this.code = e;
|
|
125
|
+
}
|
|
126
|
+
}, x = /^[A-Za-z][A-Za-z0-9_]{0,39}$/, S = [
|
|
127
|
+
"google_",
|
|
128
|
+
"ga_",
|
|
129
|
+
"firebase_"
|
|
130
|
+
], C = 25, w = 100, T = {
|
|
131
|
+
page_location: 1e3,
|
|
132
|
+
page_referrer: 420,
|
|
133
|
+
page_title: 300
|
|
134
|
+
}, E = [
|
|
135
|
+
"email",
|
|
136
|
+
"phone",
|
|
137
|
+
"first_name",
|
|
138
|
+
"last_name",
|
|
139
|
+
"address",
|
|
140
|
+
"password"
|
|
141
|
+
], D = String.raw`[\w.+-]+(?:@|%40)[\w-]+(?:\.[\w-]+)+`, O = "[redacted]", k = (e) => {
|
|
142
|
+
if (!x.test(e)) return "must start with a letter, contain only letters, digits and underscores, and be at most 40 characters";
|
|
143
|
+
let t = S.find((t) => e.toLowerCase().startsWith(t));
|
|
144
|
+
return t && `must not start with the reserved prefix "${t}"`;
|
|
145
|
+
}, A = (e) => {
|
|
146
|
+
let t = k(e);
|
|
147
|
+
return t && `event name "${e}" ${t}`;
|
|
148
|
+
}, j = (e) => {
|
|
149
|
+
let t = Object.keys(e), n = t.flatMap((t) => {
|
|
150
|
+
let n = k(t);
|
|
151
|
+
if (n) return [`param "${t}" ${n}`];
|
|
152
|
+
let r = e[t], i = T[t] ?? w;
|
|
153
|
+
return typeof r == "string" && r.length > i ? [`param "${t}" is longer than ${i} characters`] : [];
|
|
154
|
+
});
|
|
155
|
+
return t.length > C ? [`has ${t.length} params; the limit is ${C}`, ...n] : n;
|
|
156
|
+
}, M = (e) => new RegExp(D, "i").test(e), N = (e, t) => (typeof t == "string" || typeof t == "number") && E.some((t) => e.toLowerCase().includes(t)) ? O : typeof t == "string" ? t.replace(new RegExp(D, "gi"), O) : t, P = (e) => {
|
|
157
|
+
let t = Object.entries(e).map(([e, t]) => [
|
|
158
|
+
e,
|
|
159
|
+
t,
|
|
160
|
+
N(e, t)
|
|
161
|
+
]);
|
|
162
|
+
return {
|
|
163
|
+
params: Object.fromEntries(t.map(([e, , t]) => [e, t])),
|
|
164
|
+
redactedKeys: t.filter(([, e, t]) => t !== e).map(([e]) => e)
|
|
165
|
+
};
|
|
166
|
+
}, F = () => typeof window < "u", I = () => typeof crypto.randomUUID == "function" ? crypto.randomUUID() : "10000000-1000-4000-8000-100000000000".replace(/[018]/g, (e) => (Number(e) ^ crypto.getRandomValues(/* @__PURE__ */ new Uint8Array(1))[0] & 15 >> Number(e) / 4).toString(16)), L = (e) => {
|
|
167
|
+
if (e.consent !== "granted" && e.consent !== "denied") throw Error(`[react-marketing-tools] createAnalytics: "consent" must be 'granted' or 'denied' (received ${JSON.stringify(e.consent)}).`);
|
|
168
|
+
}, R = (i) => {
|
|
169
|
+
L(i);
|
|
170
|
+
let { debug: a = !1, onError: s = console.error, respectGpc: c = !0 } = i, l = n({
|
|
171
|
+
consent: i.consent,
|
|
172
|
+
gpc: c && o()
|
|
173
|
+
}), u = l, d = [
|
|
174
|
+
...i.gtm ? [y({
|
|
175
|
+
...i.gtm,
|
|
176
|
+
nonce: i.nonce
|
|
177
|
+
})] : [],
|
|
178
|
+
...i.ga4 ? [g({
|
|
179
|
+
...i.ga4,
|
|
180
|
+
nonce: i.nonce
|
|
181
|
+
})] : [],
|
|
182
|
+
...i.destinations ?? []
|
|
183
|
+
], f = [], p = !1, m = (e) => {
|
|
184
|
+
if (a) throw e;
|
|
185
|
+
s(e);
|
|
186
|
+
}, h = (e) => {
|
|
187
|
+
for (let t of d) try {
|
|
188
|
+
e(t);
|
|
189
|
+
} catch (e) {
|
|
190
|
+
s(new b("destination_failed", `destination "${t.name}" failed`, { cause: e }));
|
|
191
|
+
}
|
|
192
|
+
}, _ = (e) => {
|
|
193
|
+
p ? h(e) : f.push(e);
|
|
194
|
+
}, v = (e, t) => {
|
|
195
|
+
let n = A(e);
|
|
196
|
+
if (n) {
|
|
197
|
+
m(new b("invalid_event", n));
|
|
198
|
+
return;
|
|
199
|
+
}
|
|
200
|
+
for (let n of j(t)) m(new b("invalid_param", `event "${e}" ${n}`));
|
|
201
|
+
let { params: r, redactedKeys: i } = P(t);
|
|
202
|
+
return i.length > 0 && m(new b("pii_redacted", `event "${e}": personal data redacted from ${i.join(", ")}`)), {
|
|
203
|
+
name: e,
|
|
204
|
+
params: r,
|
|
205
|
+
eventId: I(),
|
|
206
|
+
timestamp: Date.now()
|
|
207
|
+
};
|
|
208
|
+
};
|
|
209
|
+
return {
|
|
210
|
+
start() {
|
|
211
|
+
!p && F() && (p = !0, h((e) => e.start({ consent: l })), f.splice(0).forEach(h));
|
|
212
|
+
},
|
|
213
|
+
track(e, t = {}) {
|
|
214
|
+
if (!F()) return;
|
|
215
|
+
let n = v(e, t);
|
|
216
|
+
n && _((e) => e.track(n));
|
|
217
|
+
},
|
|
218
|
+
page(e = {}) {
|
|
219
|
+
if (!F()) return;
|
|
220
|
+
let t = v("page_view", {
|
|
221
|
+
page_location: location.href,
|
|
222
|
+
page_title: document.title,
|
|
223
|
+
...e
|
|
224
|
+
});
|
|
225
|
+
t && _((e) => e.page ? e.page(t) : e.track(t));
|
|
226
|
+
},
|
|
227
|
+
identify(e, t = {}) {
|
|
228
|
+
if (F()) {
|
|
229
|
+
if (!e || M(e)) {
|
|
230
|
+
m(new b("invalid_user_id", "identify() needs a non-empty user id that is not personal data such as an email address"));
|
|
231
|
+
return;
|
|
232
|
+
}
|
|
233
|
+
_((n) => n.identify?.({
|
|
234
|
+
userId: e,
|
|
235
|
+
traits: t
|
|
236
|
+
}));
|
|
237
|
+
}
|
|
238
|
+
},
|
|
239
|
+
reset() {
|
|
240
|
+
F() && _((e) => e.reset?.());
|
|
241
|
+
},
|
|
242
|
+
consent: {
|
|
243
|
+
update(n) {
|
|
244
|
+
if (!F()) return;
|
|
245
|
+
let i = Object.entries(n).filter(([n, r]) => r !== void 0 && !(e.includes(n) && t(r)));
|
|
246
|
+
if (i.length > 0) {
|
|
247
|
+
m(new b("invalid_consent", `consent.update() ignored: ${i.map(([e, t]) => `${e}=${JSON.stringify(t)}`).join(", ")}. Use analytics, ads, adUserData or adPersonalization with 'granted' or 'denied'.`));
|
|
248
|
+
return;
|
|
249
|
+
}
|
|
250
|
+
let a = r(u, n);
|
|
251
|
+
u = a, _((e) => e.consent?.(a));
|
|
252
|
+
},
|
|
253
|
+
get: () => u
|
|
254
|
+
}
|
|
255
|
+
};
|
|
256
|
+
};
|
|
257
|
+
//#endregion
|
|
258
|
+
export { b as n, R as t };
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import type { ConsentState, ConsentStatus, ConsentUpdate } from './types.js';
|
|
2
|
+
export declare const CONSENT_KEYS: readonly (keyof ConsentState)[];
|
|
3
|
+
export declare const isConsentStatus: (value: unknown) => value is ConsentStatus;
|
|
4
|
+
/** Every signal follows `consent`, except that Global Privacy Control (an ad opt-out) denies the advertising signals. */
|
|
5
|
+
export declare const initialConsentState: ({ consent, gpc, }: {
|
|
6
|
+
consent: ConsentStatus;
|
|
7
|
+
gpc: boolean;
|
|
8
|
+
}) => ConsentState;
|
|
9
|
+
/** `ads` also sets `adUserData` and `adPersonalization`, unless the update gives them explicitly (e.g. TCF-style CMPs). */
|
|
10
|
+
export declare const applyConsentUpdate: (state: ConsentState, update: ConsentUpdate) => ConsentState;
|
|
11
|
+
/** Google Consent Mode v2 signals. */
|
|
12
|
+
export declare const toGoogleConsent: (state: ConsentState) => {
|
|
13
|
+
analytics_storage: ConsentStatus;
|
|
14
|
+
ad_storage: ConsentStatus;
|
|
15
|
+
ad_user_data: ConsentStatus;
|
|
16
|
+
ad_personalization: ConsentStatus;
|
|
17
|
+
};
|
|
18
|
+
export declare const hasDeniedSignal: (state: ConsentState) => boolean;
|
|
19
|
+
/** Global Privacy Control: a browser-level "do not sell or share" signal. Absent in browsers that don't support it. */
|
|
20
|
+
export declare const readGlobalPrivacyControl: () => boolean;
|
package/dist/core/errors.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export type AnalyticsErrorCode = 'invalid_event' | 'invalid_param' | 'invalid_user_id' | 'pii_redacted' | 'destination_failed';
|
|
1
|
+
export type AnalyticsErrorCode = 'invalid_event' | 'invalid_param' | 'invalid_user_id' | 'invalid_consent' | 'pii_redacted' | 'destination_failed';
|
|
2
2
|
/** Every problem the library reports to `onError`, or throws when `debug` is on. */
|
|
3
3
|
export declare class AnalyticsError extends Error {
|
|
4
4
|
readonly code: AnalyticsErrorCode;
|
package/dist/core/types.d.ts
CHANGED
|
@@ -1,5 +1,16 @@
|
|
|
1
1
|
import type { AnalyticsError } from './errors.js';
|
|
2
2
|
export type ConsentStatus = 'granted' | 'denied';
|
|
3
|
+
/**
|
|
4
|
+
* The visitor's consent, per purpose. `ads` covers advertising storage; `adUserData` (sending user data to ad platforms)
|
|
5
|
+
* and `adPersonalization` follow `ads` unless set explicitly.
|
|
6
|
+
*/
|
|
7
|
+
export type ConsentState = {
|
|
8
|
+
analytics: ConsentStatus;
|
|
9
|
+
ads: ConsentStatus;
|
|
10
|
+
adUserData: ConsentStatus;
|
|
11
|
+
adPersonalization: ConsentStatus;
|
|
12
|
+
};
|
|
13
|
+
export type ConsentUpdate = Partial<ConsentState>;
|
|
3
14
|
/** Event parameters, passed to every destination as-is. */
|
|
4
15
|
export type EventParams = Record<string, unknown>;
|
|
5
16
|
export type AnalyticsEvent = {
|
|
@@ -27,9 +38,13 @@ export type Identity = {
|
|
|
27
38
|
/** Somewhere events are sent. Built-in destinations are configured by key; custom ones go in `destinations`. */
|
|
28
39
|
export type Destination = {
|
|
29
40
|
name: string;
|
|
30
|
-
/** Called once, in the browser, by `analytics.start()
|
|
31
|
-
start(
|
|
41
|
+
/** Called once, in the browser, by `analytics.start()`, with the consent state the page started with. */
|
|
42
|
+
start(context: {
|
|
43
|
+
consent: ConsentState;
|
|
44
|
+
}): void;
|
|
32
45
|
track(event: AnalyticsEvent): void;
|
|
46
|
+
/** Receives every consent change, in order with events. */
|
|
47
|
+
consent?(state: ConsentState): void;
|
|
33
48
|
/** Receives `page_view` events. Destinations without it get them through `track`. */
|
|
34
49
|
page?(event: AnalyticsEvent): void;
|
|
35
50
|
identify?(identity: Identity): void;
|
|
@@ -41,11 +56,36 @@ export type GtmConfig = {
|
|
|
41
56
|
containerId: string;
|
|
42
57
|
/** Set to `false` when the page already includes the GTM snippet. Defaults to `true`. */
|
|
43
58
|
loadScript?: boolean;
|
|
59
|
+
/** Load `gtm.js` from this https URL instead of googletagmanager.com, e.g. your server-side GTM domain. */
|
|
60
|
+
scriptUrl?: string;
|
|
61
|
+
/** Milliseconds tags wait for a consent update when a Consent Mode signal starts denied. Defaults to 500. */
|
|
62
|
+
waitForUpdate?: number;
|
|
63
|
+
};
|
|
64
|
+
export type Ga4Config = {
|
|
65
|
+
/** Google Analytics 4 measurement ID, e.g. `G-XXXXXXX`. */
|
|
66
|
+
measurementId: string;
|
|
67
|
+
/**
|
|
68
|
+
* `'auto'` (default): GA4 records page views itself, including history changes when Enhanced Measurement is on.
|
|
69
|
+
* `'manual'`: only `analytics.page()` sends page views.
|
|
70
|
+
*/
|
|
71
|
+
pageViews?: 'auto' | 'manual';
|
|
72
|
+
/** Set to `false` when the page already loads gtag.js. Defaults to `true`. */
|
|
73
|
+
loadScript?: boolean;
|
|
74
|
+
/** Your server-side GTM URL (https). Hits go there instead of Google, and events carry `event_id`. */
|
|
75
|
+
serverContainerUrl?: string;
|
|
76
|
+
/** Milliseconds tags wait for a consent update when a Consent Mode signal starts denied. Defaults to 500. */
|
|
77
|
+
waitForUpdate?: number;
|
|
44
78
|
};
|
|
45
79
|
export type AnalyticsConfig = {
|
|
46
|
-
/** Initial consent
|
|
80
|
+
/** Initial consent for every purpose. Required, so every site makes an explicit choice. */
|
|
47
81
|
consent: ConsentStatus;
|
|
82
|
+
/**
|
|
83
|
+
* Honour the browser's Global Privacy Control signal by starting the advertising signals denied. Defaults to `true`.
|
|
84
|
+
* An explicit `analytics.consent.update()` still wins.
|
|
85
|
+
*/
|
|
86
|
+
respectGpc?: boolean;
|
|
48
87
|
gtm?: GtmConfig;
|
|
88
|
+
ga4?: Ga4Config;
|
|
49
89
|
/** Custom destinations, in addition to the built-in ones. */
|
|
50
90
|
destinations?: Destination[];
|
|
51
91
|
/** Content-Security-Policy nonce added to every script the library injects. */
|
|
@@ -66,4 +106,9 @@ export type Analytics = {
|
|
|
66
106
|
identify(userId: string, traits?: IdentityTraits): void;
|
|
67
107
|
/** Forgets the identified user, e.g. on logout. */
|
|
68
108
|
reset(): void;
|
|
109
|
+
consent: {
|
|
110
|
+
/** Records the visitor's choice, e.g. from your consent banner. Omitted purposes keep their current state. */
|
|
111
|
+
update(update: ConsentUpdate): void;
|
|
112
|
+
get(): ConsentState;
|
|
113
|
+
};
|
|
69
114
|
};
|
package/dist/core.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
export { createAnalytics } from './core/createAnalytics.js';
|
|
2
2
|
export { AnalyticsError } from './core/errors.js';
|
|
3
3
|
export type { AnalyticsErrorCode } from './core/errors.js';
|
|
4
|
-
export type { Analytics, AnalyticsConfig, AnalyticsEvent, ConsentStatus, Destination, EventParams, GtmConfig, Identity, IdentityTraits, } from './core/types.js';
|
|
4
|
+
export type { Analytics, AnalyticsConfig, AnalyticsEvent, ConsentState, ConsentStatus, ConsentUpdate, Destination, EventParams, Ga4Config, GtmConfig, Identity, IdentityTraits, } from './core/types.js';
|
package/dist/core.js
CHANGED
|
@@ -1,154 +1,2 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
let n = document.createElement("script");
|
|
4
|
-
n.async = !0, n.src = e, t && n.setAttribute("nonce", t), document.head.append(n);
|
|
5
|
-
}, i = ({ containerId: i, loadScript: a = !0, nonce: o }) => {
|
|
6
|
-
if (!e.test(i)) throw Error(`[react-marketing-tools] gtm.containerId must look like "GTM-XXXXXXX" (received ${JSON.stringify(i)}).`);
|
|
7
|
-
let s = `https://www.googletagmanager.com/gtm.js?id=${i}`;
|
|
8
|
-
return {
|
|
9
|
-
name: "gtm",
|
|
10
|
-
start() {
|
|
11
|
-
let e = t();
|
|
12
|
-
a && !n(s) && (e.push({
|
|
13
|
-
"gtm.start": Date.now(),
|
|
14
|
-
event: "gtm.js"
|
|
15
|
-
}), r({
|
|
16
|
-
src: s,
|
|
17
|
-
nonce: o
|
|
18
|
-
}));
|
|
19
|
-
},
|
|
20
|
-
track({ name: e, params: n, eventId: r }) {
|
|
21
|
-
t().push({
|
|
22
|
-
...n,
|
|
23
|
-
event: e,
|
|
24
|
-
event_id: r
|
|
25
|
-
});
|
|
26
|
-
},
|
|
27
|
-
identify({ userId: e }) {
|
|
28
|
-
t().push({
|
|
29
|
-
event: "identify",
|
|
30
|
-
user_id: e
|
|
31
|
-
});
|
|
32
|
-
},
|
|
33
|
-
reset() {
|
|
34
|
-
t().push({
|
|
35
|
-
event: "reset",
|
|
36
|
-
user_id: void 0
|
|
37
|
-
});
|
|
38
|
-
}
|
|
39
|
-
};
|
|
40
|
-
}, a = class extends Error {
|
|
41
|
-
code;
|
|
42
|
-
constructor(e, t, n) {
|
|
43
|
-
super(`[react-marketing-tools] ${t}`, n), this.name = "AnalyticsError", this.code = e;
|
|
44
|
-
}
|
|
45
|
-
}, o = /^[A-Za-z][A-Za-z0-9_]{0,39}$/, s = [
|
|
46
|
-
"google_",
|
|
47
|
-
"ga_",
|
|
48
|
-
"firebase_"
|
|
49
|
-
], c = 25, l = 100, u = {
|
|
50
|
-
page_location: 1e3,
|
|
51
|
-
page_referrer: 420,
|
|
52
|
-
page_title: 300
|
|
53
|
-
}, d = [
|
|
54
|
-
"email",
|
|
55
|
-
"phone",
|
|
56
|
-
"first_name",
|
|
57
|
-
"last_name",
|
|
58
|
-
"address",
|
|
59
|
-
"password"
|
|
60
|
-
], f = String.raw`[\w.+-]+(?:@|%40)[\w-]+(?:\.[\w-]+)+`, p = "[redacted]", m = (e) => {
|
|
61
|
-
if (!o.test(e)) return "must start with a letter, contain only letters, digits and underscores, and be at most 40 characters";
|
|
62
|
-
let t = s.find((t) => e.toLowerCase().startsWith(t));
|
|
63
|
-
return t && `must not start with the reserved prefix "${t}"`;
|
|
64
|
-
}, h = (e) => {
|
|
65
|
-
let t = m(e);
|
|
66
|
-
return t && `event name "${e}" ${t}`;
|
|
67
|
-
}, g = (e) => {
|
|
68
|
-
let t = Object.keys(e), n = t.flatMap((t) => {
|
|
69
|
-
let n = m(t);
|
|
70
|
-
if (n) return [`param "${t}" ${n}`];
|
|
71
|
-
let r = e[t], i = u[t] ?? l;
|
|
72
|
-
return typeof r == "string" && r.length > i ? [`param "${t}" is longer than ${i} characters`] : [];
|
|
73
|
-
});
|
|
74
|
-
return t.length > c ? [`has ${t.length} params; the limit is ${c}`, ...n] : n;
|
|
75
|
-
}, _ = (e) => new RegExp(f, "i").test(e), v = (e, t) => (typeof t == "string" || typeof t == "number") && d.some((t) => e.toLowerCase().includes(t)) ? p : typeof t == "string" ? t.replace(new RegExp(f, "gi"), p) : t, y = (e) => {
|
|
76
|
-
let t = Object.entries(e).map(([e, t]) => [
|
|
77
|
-
e,
|
|
78
|
-
t,
|
|
79
|
-
v(e, t)
|
|
80
|
-
]);
|
|
81
|
-
return {
|
|
82
|
-
params: Object.fromEntries(t.map(([e, , t]) => [e, t])),
|
|
83
|
-
redactedKeys: t.filter(([, e, t]) => t !== e).map(([e]) => e)
|
|
84
|
-
};
|
|
85
|
-
}, b = () => typeof window < "u", x = () => typeof crypto.randomUUID == "function" ? crypto.randomUUID() : "10000000-1000-4000-8000-100000000000".replace(/[018]/g, (e) => (Number(e) ^ crypto.getRandomValues(/* @__PURE__ */ new Uint8Array(1))[0] & 15 >> Number(e) / 4).toString(16)), S = (e) => {
|
|
86
|
-
if (e.consent !== "granted" && e.consent !== "denied") throw Error(`[react-marketing-tools] createAnalytics: "consent" must be 'granted' or 'denied' (received ${JSON.stringify(e.consent)}).`);
|
|
87
|
-
}, C = (e) => {
|
|
88
|
-
S(e);
|
|
89
|
-
let { debug: t = !1, onError: n = console.error } = e, r = [...e.gtm ? [i({
|
|
90
|
-
...e.gtm,
|
|
91
|
-
nonce: e.nonce
|
|
92
|
-
})] : [], ...e.destinations ?? []], o = [], s = !1, c = (e) => {
|
|
93
|
-
if (t) throw e;
|
|
94
|
-
n(e);
|
|
95
|
-
}, l = (e) => {
|
|
96
|
-
for (let t of r) try {
|
|
97
|
-
e(t);
|
|
98
|
-
} catch (e) {
|
|
99
|
-
n(new a("destination_failed", `destination "${t.name}" failed`, { cause: e }));
|
|
100
|
-
}
|
|
101
|
-
}, u = (e) => {
|
|
102
|
-
s ? l(e) : o.push(e);
|
|
103
|
-
}, d = (e, t) => {
|
|
104
|
-
let n = h(e);
|
|
105
|
-
if (n) {
|
|
106
|
-
c(new a("invalid_event", n));
|
|
107
|
-
return;
|
|
108
|
-
}
|
|
109
|
-
for (let n of g(t)) c(new a("invalid_param", `event "${e}" ${n}`));
|
|
110
|
-
let { params: r, redactedKeys: i } = y(t);
|
|
111
|
-
return i.length > 0 && c(new a("pii_redacted", `event "${e}": personal data redacted from ${i.join(", ")}`)), {
|
|
112
|
-
name: e,
|
|
113
|
-
params: r,
|
|
114
|
-
eventId: x(),
|
|
115
|
-
timestamp: Date.now()
|
|
116
|
-
};
|
|
117
|
-
};
|
|
118
|
-
return {
|
|
119
|
-
start() {
|
|
120
|
-
!s && b() && (s = !0, l((e) => e.start()), o.splice(0).forEach(l));
|
|
121
|
-
},
|
|
122
|
-
track(e, t = {}) {
|
|
123
|
-
if (!b()) return;
|
|
124
|
-
let n = d(e, t);
|
|
125
|
-
n && u((e) => e.track(n));
|
|
126
|
-
},
|
|
127
|
-
page(e = {}) {
|
|
128
|
-
if (!b()) return;
|
|
129
|
-
let t = d("page_view", {
|
|
130
|
-
page_location: location.href,
|
|
131
|
-
page_title: document.title,
|
|
132
|
-
...e
|
|
133
|
-
});
|
|
134
|
-
t && u((e) => e.page ? e.page(t) : e.track(t));
|
|
135
|
-
},
|
|
136
|
-
identify(e, t = {}) {
|
|
137
|
-
if (b()) {
|
|
138
|
-
if (!e || _(e)) {
|
|
139
|
-
c(new a("invalid_user_id", "identify() needs a non-empty user id that is not personal data such as an email address"));
|
|
140
|
-
return;
|
|
141
|
-
}
|
|
142
|
-
u((n) => n.identify?.({
|
|
143
|
-
userId: e,
|
|
144
|
-
traits: t
|
|
145
|
-
}));
|
|
146
|
-
}
|
|
147
|
-
},
|
|
148
|
-
reset() {
|
|
149
|
-
b() && u((e) => e.reset?.());
|
|
150
|
-
}
|
|
151
|
-
};
|
|
152
|
-
};
|
|
153
|
-
//#endregion
|
|
154
|
-
export { a as AnalyticsError, C as createAnalytics };
|
|
1
|
+
import { n as e, t } from "./chunks/core.js";
|
|
2
|
+
export { e as AnalyticsError, t as createAnalytics };
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import type { Destination, Ga4Config } from '../core/types.js';
|
|
2
|
+
type Ga4DestinationOptions = Ga4Config & {
|
|
3
|
+
nonce?: string;
|
|
4
|
+
};
|
|
5
|
+
export declare const createGa4Destination: ({ measurementId, pageViews, loadScript, serverContainerUrl, waitForUpdate, nonce, }: Ga4DestinationOptions) => Destination;
|
|
6
|
+
export {};
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import type { ConsentState } from '../core/types.js';
|
|
2
|
+
type Gtag = (...args: unknown[]) => void;
|
|
3
|
+
export declare const dataLayer: () => unknown[];
|
|
4
|
+
/**
|
|
5
|
+
* Whether the page already loads the gtag.js library (any stream, Google- or first-party-hosted). Checked by script rather
|
|
6
|
+
* than by `window.gtag`, because the Consent Mode default defines the gtag stub before any library loads.
|
|
7
|
+
*/
|
|
8
|
+
export declare const isGtagLibraryOnPage: () => boolean;
|
|
9
|
+
/** The page's `gtag()`, defining Google's official command-queue stub when the page doesn't have one yet. */
|
|
10
|
+
export declare const gtag: () => Gtag;
|
|
11
|
+
/**
|
|
12
|
+
* Consent Mode v2 default. It must be queued before any config or container load. When a signal starts denied, tags
|
|
13
|
+
* wait `waitForUpdate` ms for the visitor's choice before firing.
|
|
14
|
+
*/
|
|
15
|
+
export declare const setDefaultConsent: ({ consent, waitForUpdate, }: {
|
|
16
|
+
consent: ConsentState;
|
|
17
|
+
waitForUpdate: number;
|
|
18
|
+
}) => void;
|
|
19
|
+
export declare const updateConsent: (consent: ConsentState) => void;
|
|
20
|
+
/** Parses an option that must be an https:// URL, failing fast with the option's name. */
|
|
21
|
+
export declare const toHttpsUrl: (value: string, option: string) => URL;
|
|
22
|
+
export {};
|
|
@@ -2,5 +2,5 @@ import type { Destination, GtmConfig } from '../core/types.js';
|
|
|
2
2
|
type GtmDestinationOptions = GtmConfig & {
|
|
3
3
|
nonce?: string;
|
|
4
4
|
};
|
|
5
|
-
export declare const createGtmDestination: ({ containerId, loadScript, nonce, }: GtmDestinationOptions) => Destination;
|
|
5
|
+
export declare const createGtmDestination: ({ containerId, loadScript, scriptUrl, waitForUpdate, nonce, }: GtmDestinationOptions) => Destination;
|
|
6
6
|
export {};
|
package/dist/index.d.ts
CHANGED
|
@@ -1,26 +1,2 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
export type { Event, GooglePayload, EventNameInfo, AllowedTypes, TrackAnalyticsEventOptions, GlobalVars, DataLayer, ConsoleLogData, AnalyticsTrackerDataOptions, HandleDataLayerPushOptions, ServerLocationData, IpInfo, Ga4GoogleAnalyticsEventTracking, Platform, AnalyticsPlatform, Tokens, Config, BuildConfigOptions, ProviderStateProps, ProviderApiProps, AnalyticsEventActionPrefix, AnalyticsGlobalEventAction, } from './types/index.js';
|
|
4
|
-
export { config, analyticsPlatform, buildConfig, showMeBuildInAnalyticsPlatform, showMeBuildInGlobalEventActionList, showMeBuildInEventActionPrefixList, } from './buildConfig/index.js';
|
|
5
|
-
export { trackAnalyticsEvent } from './analytics/analyticsEventService.js';
|
|
6
|
-
export declare const ContextState: Context<ProviderStateProps>;
|
|
7
|
-
export declare const ContextApi: Context<ProviderApiProps>;
|
|
8
|
-
type ReactMarketingProviderProps = {
|
|
9
|
-
children: ReactNode;
|
|
10
|
-
};
|
|
11
|
-
export declare const ReactMarketingProvider: ({ children, }: ReactMarketingProviderProps) => ReactElement;
|
|
12
|
-
/**
|
|
13
|
-
* @property {string} appName
|
|
14
|
-
* @property {string} appSessionCookieName
|
|
15
|
-
* @property {AnalyticsPlatform} analyticsPlatform
|
|
16
|
-
* @property {AnalyticsEventActionPrefix} eventActionPrefixList
|
|
17
|
-
* @property {AnalyticsGlobalEventAction} analyticsGlobalEventActionList
|
|
18
|
-
*/
|
|
19
|
-
export declare const useMarketingState: () => ProviderStateProps;
|
|
20
|
-
/**
|
|
21
|
-
* @property {function} trackAnalyticsEvent(options)
|
|
22
|
-
* @property {function} showMeBuildInAnalyticsPlatform
|
|
23
|
-
* @property {function} showMeBuildInEventActionPrefixList
|
|
24
|
-
* @property {function} showMeBuildInGlobalEventActionList
|
|
25
|
-
*/
|
|
26
|
-
export declare const useMarketingApi: () => ProviderApiProps;
|
|
1
|
+
export * from './core.js';
|
|
2
|
+
export { AnalyticsProvider, useAnalytics } from './react/AnalyticsProvider.js';
|