react-marketing-tools 1.0.0-alpha.5 → 1.0.0-beta.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/README.md +76 -4
- package/dist/autocapture/clicks.d.ts +11 -0
- package/dist/chunks/cookies.js +49 -0
- package/dist/chunks/core.js +337 -205
- package/dist/core/errors.d.ts +1 -1
- package/dist/core/types.d.ts +47 -2
- package/dist/core.d.ts +1 -1
- package/dist/destinations/serverRelay.d.ts +34 -0
- package/dist/fingerprintjs.d.ts +8 -0
- package/dist/fingerprintjs.js +7 -0
- package/dist/identity/visitorId.d.ts +14 -0
- package/dist/journeys/journey.d.ts +8 -0
- package/dist/server/conversionsApi.d.ts +38 -0
- package/dist/server/createTrackHandler.d.ts +20 -0
- package/dist/server/ga4Cookies.d.ts +11 -0
- package/dist/server/measurementProtocol.d.ts +34 -0
- package/dist/server/normalize.d.ts +19 -0
- package/dist/server.d.ts +8 -0
- package/dist/server.js +229 -0
- package/dist/shared/hash.d.ts +4 -0
- package/dist/shared/uuid.d.ts +1 -0
- package/dist/webVitals.d.ts +8 -0
- package/dist/webVitals.js +16 -0
- package/package.json +31 -4
package/README.md
CHANGED
|
@@ -6,8 +6,12 @@
|
|
|
6
6
|
One `track()` call for Google Tag Manager, Google Analytics 4 and the Meta Pixel, with Consent Mode v2, UTM attribution
|
|
7
7
|
and personal-data redaction built in.
|
|
8
8
|
|
|
9
|
-
|
|
10
|
-
|
|
9
|
+
**[Try it in the playground](https://bronz3beard.github.io/react-marketing-tools/)**: see what each vendor receives for
|
|
10
|
+
every event, without sending anything.
|
|
11
|
+
|
|
12
|
+
> **1.0 is in beta** on the `next` tag. `npm install react-marketing-tools` still installs 0.4.x, whose API 1.0
|
|
13
|
+
> replaces; see the [migration guide](https://github.com/bronz3beard/react-marketing-tools/blob/main/docs/migration-v1.md)
|
|
14
|
+
> and the [changelog](https://github.com/bronz3beard/react-marketing-tools/blob/main/docs/CHANGELOG.md).
|
|
11
15
|
|
|
12
16
|
## Install
|
|
13
17
|
|
|
@@ -60,28 +64,96 @@ export const SignUpButton = () => {
|
|
|
60
64
|
}
|
|
61
65
|
```
|
|
62
66
|
|
|
63
|
-
|
|
67
|
+
Or without code, with `autocapture: { clicks: true }` in the config:
|
|
68
|
+
|
|
69
|
+
```html
|
|
70
|
+
<button data-analytics-event="cta_click" data-analytics-param-location="hero">Start</button>
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
Follow a multi-step flow as a funnel:
|
|
74
|
+
|
|
75
|
+
```ts
|
|
76
|
+
const checkout = analytics.journey('checkout')
|
|
77
|
+
checkout.step('shipping') // journey_start, then journey_step
|
|
78
|
+
checkout.complete({ value: 42, currency: 'USD' }) // journey_complete
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
Report Core Web Vitals (LCP, INP, CLS) to GA4 and Tag Manager, after `npm install web-vitals@^6`:
|
|
82
|
+
|
|
83
|
+
```ts
|
|
84
|
+
import { trackWebVitals } from 'react-marketing-tools/web-vitals'
|
|
85
|
+
|
|
86
|
+
void trackWebVitals(analytics)
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
The same instance identifies users, records consent and gives a consenting visitor a stable ID:
|
|
64
90
|
|
|
65
91
|
```ts
|
|
66
92
|
analytics.identify('user-42', { email: 'ada@example.com' }) // user id for GTM and GA4, advanced matching for Meta
|
|
67
93
|
analytics.consent.update({ analytics: 'granted', ads: 'granted' }) // Google Consent Mode v2 and Meta consent
|
|
94
|
+
const visitorId = await analytics.getVisitorId() // random by default, or a fingerprint; never sent to GA4
|
|
68
95
|
```
|
|
69
96
|
|
|
70
97
|
Configure only the destinations you use. Without React, import `createAnalytics` from `react-marketing-tools/core` and
|
|
71
98
|
call `analytics.start()` yourself.
|
|
72
99
|
|
|
100
|
+
Send events that happen on your server, such as a purchase confirmed by a payment webhook, from
|
|
101
|
+
`react-marketing-tools/server`:
|
|
102
|
+
|
|
103
|
+
```ts
|
|
104
|
+
import { sendMeasurementProtocolEvent } from 'react-marketing-tools/server'
|
|
105
|
+
|
|
106
|
+
await sendMeasurementProtocolEvent({
|
|
107
|
+
measurementId: 'G-XXXXXXX',
|
|
108
|
+
apiSecret: process.env.GA4_API_SECRET!,
|
|
109
|
+
clientId: order.ga4ClientId, // saved at checkout with readGa4Cookies()
|
|
110
|
+
events: [{ name: 'purchase', params: { transaction_id: order.id, value: 42, currency: 'USD' } }],
|
|
111
|
+
})
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
`sendConversionsApiEvent` does the same for Meta, hashing customer information as Meta requires. To have Meta receive
|
|
115
|
+
the events the browser Pixel misses, counted once, relay them through your server:
|
|
116
|
+
|
|
117
|
+
```ts
|
|
118
|
+
// analytics.ts
|
|
119
|
+
createAnalytics({ /* …as above */ server: { endpoint: '/api/track' } })
|
|
120
|
+
|
|
121
|
+
// app/api/track/route.ts (Next.js; any Request → Response server works)
|
|
122
|
+
import { createTrackHandler } from 'react-marketing-tools/server'
|
|
123
|
+
|
|
124
|
+
export const POST = createTrackHandler({
|
|
125
|
+
allowedOrigins: ['https://shop.example.com'],
|
|
126
|
+
meta: { pixelId: '1234567890123456', accessToken: process.env.META_CAPI_TOKEN! },
|
|
127
|
+
})
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
## What each destination receives
|
|
131
|
+
|
|
132
|
+
| | Google Tag Manager | Google Analytics 4 | Meta Pixel | Conversions API relay |
|
|
133
|
+
| --- | --- | --- | --- | --- |
|
|
134
|
+
| `track()` | dataLayer push with `event_id` | gtag.js event | standard or custom event with `eventID` | the same event from your server |
|
|
135
|
+
| `identify()` | `user_id` | `user_id` | advanced matching | user data, hashed on your server |
|
|
136
|
+
| `consent.update()` | Consent Mode v2 | Consent Mode v2 | `grant` / `revoke` | only with `adUserData` |
|
|
137
|
+
| Attribution | `attribution` (last touch) | read from the page URL | `fbc` | `fbc`, `fbp` |
|
|
138
|
+
|
|
73
139
|
## Documentation
|
|
74
140
|
|
|
141
|
+
- [All docs](https://github.com/bronz3beard/react-marketing-tools/blob/main/docs/README.md)
|
|
75
142
|
- [Getting started](https://github.com/bronz3beard/react-marketing-tools/blob/main/docs/getting-started.md)
|
|
76
143
|
- [React](https://github.com/bronz3beard/react-marketing-tools/blob/main/docs/react.md): provider, hook, Next.js App Router, single-page apps
|
|
77
|
-
- [Tracking events](https://github.com/bronz3beard/react-marketing-tools/blob/main/docs/tracking-events.md): naming rules, page views, users, personal data, errors
|
|
144
|
+
- [Tracking events](https://github.com/bronz3beard/react-marketing-tools/blob/main/docs/tracking-events.md): naming rules, page views, journeys, click autocapture, Web Vitals, users, personal data, errors
|
|
78
145
|
- [Configuration](https://github.com/bronz3beard/react-marketing-tools/blob/main/docs/configuration.md)
|
|
79
146
|
- [Consent](https://github.com/bronz3beard/react-marketing-tools/blob/main/docs/consent.md): Consent Mode v2 and Global Privacy Control
|
|
80
147
|
- [Attribution](https://github.com/bronz3beard/react-marketing-tools/blob/main/docs/attribution-utm.md): UTM params and ad click IDs
|
|
148
|
+
- [Visitor ID](https://github.com/bronz3beard/react-marketing-tools/blob/main/docs/visitor-id.md): a stable ID for consenting visitors, random or fingerprint
|
|
81
149
|
- [Google Tag Manager](https://github.com/bronz3beard/react-marketing-tools/blob/main/docs/google-tag-manager.md)
|
|
82
150
|
- [Google Analytics 4](https://github.com/bronz3beard/react-marketing-tools/blob/main/docs/google-analytics-4.md)
|
|
83
151
|
- [Meta Pixel](https://github.com/bronz3beard/react-marketing-tools/blob/main/docs/meta-pixel.md)
|
|
84
152
|
- [Server-side tagging](https://github.com/bronz3beard/react-marketing-tools/blob/main/docs/server-side-tagging.md)
|
|
153
|
+
- [GA4 Measurement Protocol](https://github.com/bronz3beard/react-marketing-tools/blob/main/docs/measurement-protocol.md): GA4 events from your server
|
|
154
|
+
- [Meta Conversions API](https://github.com/bronz3beard/react-marketing-tools/blob/main/docs/meta-conversions-api.md): Meta events from your server
|
|
155
|
+
- [Debugging](https://github.com/bronz3beard/react-marketing-tools/blob/main/docs/debugging.md): see what's sent, and fix common problems
|
|
156
|
+
- [Migrating from 0.4](https://github.com/bronz3beard/react-marketing-tools/blob/main/docs/migration-v1.md)
|
|
85
157
|
- [Changelog](https://github.com/bronz3beard/react-marketing-tools/blob/main/docs/CHANGELOG.md)
|
|
86
158
|
|
|
87
159
|
## License
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { EventParams } from '../core/types.js';
|
|
2
|
+
/**
|
|
3
|
+
* The event a click asks for: the nearest element around the click target with `data-analytics-event`, and its
|
|
4
|
+
* `data-analytics-param-*` attributes as params (`data-analytics-param-button-text` → `button_text`).
|
|
5
|
+
*/
|
|
6
|
+
export declare const readClickEvent: (target: EventTarget | null) => {
|
|
7
|
+
name: string;
|
|
8
|
+
params: EventParams;
|
|
9
|
+
} | undefined;
|
|
10
|
+
/** Tracks clicks on marked elements through one listener on the document, so elements added later are covered too. */
|
|
11
|
+
export declare const captureClicks: (track: (name: string, params: EventParams) => void) => void;
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
//#region lib/core/validate.ts
|
|
2
|
+
var e = /^[A-Za-z][A-Za-z0-9_]{0,39}$/, t = [
|
|
3
|
+
"google_",
|
|
4
|
+
"ga_",
|
|
5
|
+
"firebase_"
|
|
6
|
+
], n = 25, r = 100, i = {
|
|
7
|
+
page_location: 1e3,
|
|
8
|
+
page_referrer: 420,
|
|
9
|
+
page_title: 300
|
|
10
|
+
}, a = [
|
|
11
|
+
"email",
|
|
12
|
+
"phone",
|
|
13
|
+
"first_name",
|
|
14
|
+
"last_name",
|
|
15
|
+
"address",
|
|
16
|
+
"password"
|
|
17
|
+
], o = String.raw`[\w.+-]+(?:@|%40)[\w-]+(?:\.[\w-]+)+`, s = "[redacted]", c = (n) => {
|
|
18
|
+
if (!e.test(n)) return "must start with a letter, contain only letters, digits and underscores, and be at most 40 characters";
|
|
19
|
+
let r = t.find((e) => n.toLowerCase().startsWith(e));
|
|
20
|
+
return r && `must not start with the reserved prefix "${r}"`;
|
|
21
|
+
}, l = (e) => {
|
|
22
|
+
let t = c(e);
|
|
23
|
+
return t && `event name "${e}" ${t}`;
|
|
24
|
+
}, u = (e) => {
|
|
25
|
+
let t = Object.keys(e), a = t.flatMap((t) => {
|
|
26
|
+
let n = c(t);
|
|
27
|
+
if (n) return [`param "${t}" ${n}`];
|
|
28
|
+
let a = e[t], o = i[t] ?? r;
|
|
29
|
+
return typeof a == "string" && a.length > o ? [`param "${t}" is longer than ${o} characters`] : [];
|
|
30
|
+
});
|
|
31
|
+
return t.length > n ? [`has ${t.length} params; the limit is ${n}`, ...a] : a;
|
|
32
|
+
}, d = (e) => new RegExp(o, "i").test(e), f = (e, t) => (typeof t == "string" || typeof t == "number") && a.some((t) => e.toLowerCase().includes(t)) ? s : typeof t == "string" ? t.replace(new RegExp(o, "gi"), s) : t, p = (e) => {
|
|
33
|
+
let t = Object.entries(e).map(([e, t]) => [
|
|
34
|
+
e,
|
|
35
|
+
t,
|
|
36
|
+
f(e, t)
|
|
37
|
+
]);
|
|
38
|
+
return {
|
|
39
|
+
params: Object.fromEntries(t.map(([e, , t]) => [e, t])),
|
|
40
|
+
redactedKeys: t.filter(([, e, t]) => t !== e).map(([e]) => e)
|
|
41
|
+
};
|
|
42
|
+
}, m = (e, t) => {
|
|
43
|
+
for (let n of e.split(";")) {
|
|
44
|
+
let e = n.indexOf("=");
|
|
45
|
+
if (e !== -1 && n.slice(0, e).trim() === t) return n.slice(e + 1).trim();
|
|
46
|
+
}
|
|
47
|
+
};
|
|
48
|
+
//#endregion
|
|
49
|
+
export { p as a, u as i, d as n, l as r, m as t };
|