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 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
- > **1.0 is in alpha** on the `next` tag. `npm install react-marketing-tools` still installs 0.4.x, whose API 1.0
10
- > replaces; see the [changelog](https://github.com/bronz3beard/react-marketing-tools/blob/main/docs/CHANGELOG.md).
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
- The same instance identifies users and records consent:
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 };