@buildinams/core 0.3.1 → 0.4.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 +24 -1
- package/dist/analytics/google/index.d.ts +158 -0
- package/dist/analytics/google/index.js +274 -0
- package/dist/analytics/google/index.js.map +1 -0
- package/dist/analytics/index.d.ts +101 -0
- package/dist/analytics/index.js +384 -0
- package/dist/analytics/index.js.map +1 -0
- package/dist/analytics/shopify/index.d.ts +186 -0
- package/dist/analytics/shopify/index.js +175 -0
- package/dist/analytics/shopify/index.js.map +1 -0
- package/dist/{chunk-5KDGGGVO.js → chunk-FJ7SNQQI.js} +24 -6
- package/dist/chunk-FJ7SNQQI.js.map +1 -0
- package/dist/chunk-G2BKC4BU.js +8 -0
- package/dist/chunk-G2BKC4BU.js.map +1 -0
- package/dist/chunk-GQVN2ECX.js +14 -0
- package/dist/chunk-GQVN2ECX.js.map +1 -0
- package/dist/chunk-LL3VTFNY.js +22 -0
- package/dist/chunk-LL3VTFNY.js.map +1 -0
- package/dist/chunk-M3HNM5HC.js +48 -0
- package/dist/chunk-M3HNM5HC.js.map +1 -0
- package/dist/chunk-MHFGMH3G.js +11 -0
- package/dist/chunk-MHFGMH3G.js.map +1 -0
- package/dist/chunk-OS2T6P2T.js +50 -0
- package/dist/chunk-OS2T6P2T.js.map +1 -0
- package/dist/chunk-QIJGG2F4.js +51 -0
- package/dist/chunk-QIJGG2F4.js.map +1 -0
- package/dist/consent/cookiebot/index.d.ts +16 -0
- package/dist/consent/cookiebot/index.js +27 -0
- package/dist/consent/cookiebot/index.js.map +1 -0
- package/dist/consent/google/index.d.ts +23 -0
- package/dist/consent/google/index.js +48 -0
- package/dist/consent/google/index.js.map +1 -0
- package/dist/consent/index.d.ts +1 -0
- package/dist/consent/index.js +3 -0
- package/dist/consent/index.js.map +1 -0
- package/dist/consent/onetrust/index.d.ts +23 -0
- package/dist/consent/onetrust/index.js +39 -0
- package/dist/consent/onetrust/index.js.map +1 -0
- package/dist/consent/shopify/index.d.ts +44 -0
- package/dist/consent/shopify/index.js +84 -0
- package/dist/consent/shopify/index.js.map +1 -0
- package/dist/cookies/index.d.ts +230 -0
- package/dist/cookies/index.js +188 -0
- package/dist/cookies/index.js.map +1 -0
- package/dist/define-events-CxZjoUlG.d.ts +255 -0
- package/dist/destination-5KJ5PeR3.d.ts +122 -0
- package/dist/index-Zef1nnTP.d.ts +36 -0
- package/dist/index.d.ts +2 -2
- package/dist/index.js +64 -1
- package/dist/index.js.map +1 -1
- package/dist/listeners-DkU-vBPa.d.ts +15 -0
- package/dist/navigation/index.js +1 -1
- package/dist/schema/index.d.ts +2 -0
- package/dist/schema/index.js +1 -1
- package/dist/store/index.js +1 -1
- package/dist/tracking/cart/index.d.ts +74 -0
- package/dist/tracking/cart/index.js +118 -0
- package/dist/tracking/cart/index.js.map +1 -0
- package/dist/tracking/dom/index.d.ts +320 -0
- package/dist/tracking/dom/index.js +1196 -0
- package/dist/tracking/dom/index.js.map +1 -0
- package/docs/analytics.md +883 -0
- package/docs/cookies.md +354 -0
- package/docs/decisions/001-analytics-project-events.md +31 -0
- package/docs/decisions/002-tracking-dom-opt-in.md +29 -0
- package/docs/decisions/003-tracking-element-data.md +27 -0
- package/docs/decisions/004-cookies-explicit-store.md +26 -0
- package/docs/decisions/005-cookies-invalid-reads-fall-back.md +27 -0
- package/docs/decisions/006-cookies-server-only-types.md +19 -0
- package/docs/decisions/007-cookies-options-on-definition.md +25 -0
- package/docs/decisions/008-cookies-in-vertical-slices.md +24 -0
- package/docs/decisions/009-analytics-consent-targets.md +29 -0
- package/docs/decisions/010-analytics-destination-contract.md +29 -0
- package/docs/decisions/011-tracking-decoupled.md +30 -0
- package/docs/tracking.md +221 -0
- package/package.json +83 -2
- package/dist/chunk-5KDGGGVO.js.map +0 -1
- package/dist/chunk-SIU7SKJK.js +0 -139
- package/dist/chunk-SIU7SKJK.js.map +0 -1
|
@@ -0,0 +1,384 @@
|
|
|
1
|
+
export { defineEvents, shape } from '../chunk-G2BKC4BU.js';
|
|
2
|
+
import { shallowEqual } from '../chunk-LL3VTFNY.js';
|
|
3
|
+
import { hasConsent } from '../chunk-MHFGMH3G.js';
|
|
4
|
+
import { IS_DEVELOPMENT } from '../chunk-GQVN2ECX.js';
|
|
5
|
+
|
|
6
|
+
// src/analytics/destination.ts
|
|
7
|
+
var subscriptions = /* @__PURE__ */ new WeakMap();
|
|
8
|
+
var deliver = (destination, state, event, context, warnUnsubscribed = true) => {
|
|
9
|
+
if (state.failed) return;
|
|
10
|
+
if (state.pending) {
|
|
11
|
+
state.pending.push([event, context, warnUnsubscribed]);
|
|
12
|
+
return;
|
|
13
|
+
}
|
|
14
|
+
const callbacks = state.callbacks.get(event.name);
|
|
15
|
+
if (!callbacks?.size) {
|
|
16
|
+
if (warnUnsubscribed && IS_DEVELOPMENT && !state.warned.has(event.name)) {
|
|
17
|
+
state.warned.add(event.name);
|
|
18
|
+
console.warn(
|
|
19
|
+
`[analytics] ${destination.name}: a mapping sends "${event.name}", which it doesn't subscribe to.`
|
|
20
|
+
);
|
|
21
|
+
}
|
|
22
|
+
return;
|
|
23
|
+
}
|
|
24
|
+
for (const callback of callbacks) {
|
|
25
|
+
try {
|
|
26
|
+
callback(event.payload, context);
|
|
27
|
+
} catch (error) {
|
|
28
|
+
console.error(`[analytics] ${destination.name}.${event.name}`, error);
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
};
|
|
32
|
+
var setUp = (destination) => {
|
|
33
|
+
const existing = subscriptions.get(destination);
|
|
34
|
+
if (existing) return existing;
|
|
35
|
+
const state = {
|
|
36
|
+
callbacks: /* @__PURE__ */ new Map(),
|
|
37
|
+
failed: false,
|
|
38
|
+
warned: /* @__PURE__ */ new Set()
|
|
39
|
+
};
|
|
40
|
+
subscriptions.set(destination, state);
|
|
41
|
+
const fail = (error) => {
|
|
42
|
+
state.failed = true;
|
|
43
|
+
state.pending = void 0;
|
|
44
|
+
console.error(`[analytics] ${destination.name}.setup`, error);
|
|
45
|
+
};
|
|
46
|
+
try {
|
|
47
|
+
const result = destination.setup({
|
|
48
|
+
subscribe: (event, callback) => {
|
|
49
|
+
const callbacks = state.callbacks.get(event) ?? /* @__PURE__ */ new Set();
|
|
50
|
+
state.callbacks.set(event, callbacks.add(callback));
|
|
51
|
+
return () => callbacks.delete(callback);
|
|
52
|
+
}
|
|
53
|
+
});
|
|
54
|
+
if (result instanceof Promise) {
|
|
55
|
+
state.pending = [];
|
|
56
|
+
result.then(() => {
|
|
57
|
+
const pending = state.pending ?? [];
|
|
58
|
+
state.pending = void 0;
|
|
59
|
+
for (const [event, context, warnUnsubscribed] of pending) {
|
|
60
|
+
deliver(destination, state, event, context, warnUnsubscribed);
|
|
61
|
+
}
|
|
62
|
+
}, fail);
|
|
63
|
+
}
|
|
64
|
+
} catch (error) {
|
|
65
|
+
fail(error);
|
|
66
|
+
}
|
|
67
|
+
return state;
|
|
68
|
+
};
|
|
69
|
+
var map = (destination, mapping) => {
|
|
70
|
+
const entries = Object.entries(mapping).filter(
|
|
71
|
+
(entry) => !!entry[1]
|
|
72
|
+
);
|
|
73
|
+
const target = destination;
|
|
74
|
+
return {
|
|
75
|
+
name: destination.name,
|
|
76
|
+
events: entries.map(([name]) => name),
|
|
77
|
+
setup: ({ subscribe }) => {
|
|
78
|
+
const state = setUp(target);
|
|
79
|
+
for (const [name, entry] of entries) {
|
|
80
|
+
subscribe(name, (payload, context) => {
|
|
81
|
+
const result = entry(payload, {
|
|
82
|
+
id: context.id,
|
|
83
|
+
name,
|
|
84
|
+
context: context.context
|
|
85
|
+
});
|
|
86
|
+
if (!result) return;
|
|
87
|
+
const events = Array.isArray(result) ? result : [result];
|
|
88
|
+
for (const sent of events) deliver(target, state, sent, context);
|
|
89
|
+
});
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
};
|
|
93
|
+
};
|
|
94
|
+
var mapEvery = (events, toEvent) => {
|
|
95
|
+
const entries = {};
|
|
96
|
+
for (const name of Object.keys(events)) {
|
|
97
|
+
entries[name] = (payload, { id, context }) => toEvent({ id, name, payload, context });
|
|
98
|
+
}
|
|
99
|
+
return entries;
|
|
100
|
+
};
|
|
101
|
+
var sources = /* @__PURE__ */ new WeakMap();
|
|
102
|
+
var sourceOf = (destination) => sources.get(destination) ?? destination;
|
|
103
|
+
var withConsent = (destination, requires) => {
|
|
104
|
+
const consented = { ...destination, requires };
|
|
105
|
+
sources.set(consented, sourceOf(destination));
|
|
106
|
+
return consented;
|
|
107
|
+
};
|
|
108
|
+
var toConnection = (destination) => {
|
|
109
|
+
const { name, events, requires } = destination;
|
|
110
|
+
const source = sourceOf(destination);
|
|
111
|
+
const subscribed = events && new Set(events);
|
|
112
|
+
return {
|
|
113
|
+
name,
|
|
114
|
+
requires: requires === "none" ? void 0 : requires,
|
|
115
|
+
events,
|
|
116
|
+
setup: () => void setUp(source),
|
|
117
|
+
track: (event, consent) => {
|
|
118
|
+
const state = subscriptions.get(source);
|
|
119
|
+
if (!state || subscribed && !subscribed.has(event.name)) return;
|
|
120
|
+
const context = { id: event.id, context: event.context, consent };
|
|
121
|
+
deliver(source, state, event, context, false);
|
|
122
|
+
}
|
|
123
|
+
};
|
|
124
|
+
};
|
|
125
|
+
|
|
126
|
+
// src/analytics/create-analytics.ts
|
|
127
|
+
var connected = /* @__PURE__ */ new WeakMap();
|
|
128
|
+
var MAX_QUEUE_SIZE = 100;
|
|
129
|
+
var isBrowser = () => typeof window !== "undefined";
|
|
130
|
+
var isPrerendering = () => document.prerendering === true;
|
|
131
|
+
var MAX_FLUSH_DURATION = 5;
|
|
132
|
+
var MAX_PAINT_DELAY = 100;
|
|
133
|
+
var afterNextPaint = (callback) => {
|
|
134
|
+
let isDone = false;
|
|
135
|
+
const run = () => {
|
|
136
|
+
if (isDone) return;
|
|
137
|
+
isDone = true;
|
|
138
|
+
setTimeout(callback);
|
|
139
|
+
};
|
|
140
|
+
if (typeof requestAnimationFrame !== "undefined") {
|
|
141
|
+
requestAnimationFrame(run);
|
|
142
|
+
}
|
|
143
|
+
setTimeout(run, MAX_PAINT_DELAY);
|
|
144
|
+
};
|
|
145
|
+
var createId = () => globalThis.crypto?.randomUUID?.() ?? `${Date.now().toString(36)}-${Math.random().toString(36).slice(2)}`;
|
|
146
|
+
var getContext = () => ({
|
|
147
|
+
url: window.location.href,
|
|
148
|
+
title: document.title,
|
|
149
|
+
referrer: document.referrer
|
|
150
|
+
});
|
|
151
|
+
var safely = (label, callback) => {
|
|
152
|
+
try {
|
|
153
|
+
return callback();
|
|
154
|
+
} catch (error) {
|
|
155
|
+
console.error(`[analytics] ${label}`, error);
|
|
156
|
+
return void 0;
|
|
157
|
+
}
|
|
158
|
+
};
|
|
159
|
+
var createAnalytics = ({
|
|
160
|
+
events,
|
|
161
|
+
consent: consentSource,
|
|
162
|
+
consentTargets = [],
|
|
163
|
+
destinations: allDestinations = [],
|
|
164
|
+
sources: sources2 = [],
|
|
165
|
+
context: initialContext = {},
|
|
166
|
+
delivery = "deferred"
|
|
167
|
+
}) => {
|
|
168
|
+
const destinations = [...new Set(allDestinations)].map(
|
|
169
|
+
(destination) => toConnection(destination)
|
|
170
|
+
);
|
|
171
|
+
let consent = null;
|
|
172
|
+
let queue = [];
|
|
173
|
+
let context = safely("context", () => structuredClone(initialContext)) ?? {};
|
|
174
|
+
let syncing = false;
|
|
175
|
+
const active = /* @__PURE__ */ new Set();
|
|
176
|
+
const listeners = /* @__PURE__ */ new Set();
|
|
177
|
+
const outbox = [];
|
|
178
|
+
let head = 0;
|
|
179
|
+
let isFlushing = false;
|
|
180
|
+
let scheduled;
|
|
181
|
+
let tokens = 0;
|
|
182
|
+
let isLeaving = false;
|
|
183
|
+
const unknown = /* @__PURE__ */ new Set();
|
|
184
|
+
const label = (destination, method) => `${destination.name}.${method}`;
|
|
185
|
+
const targets = [...new Set(consentTargets)].filter(
|
|
186
|
+
(target) => target.name !== consentSource.name
|
|
187
|
+
);
|
|
188
|
+
const tell = (target, current) => {
|
|
189
|
+
const result = safely(
|
|
190
|
+
`${target.name}.apply`,
|
|
191
|
+
() => target.apply({ ...current })
|
|
192
|
+
);
|
|
193
|
+
Promise.resolve(result).catch((error) => {
|
|
194
|
+
console.error(`[analytics] ${target.name}.apply`, error);
|
|
195
|
+
});
|
|
196
|
+
};
|
|
197
|
+
const sync = (destination, current) => {
|
|
198
|
+
if (!destination.requires) return;
|
|
199
|
+
if (hasConsent(current, destination.requires)) {
|
|
200
|
+
active.add(destination);
|
|
201
|
+
} else {
|
|
202
|
+
active.delete(destination);
|
|
203
|
+
}
|
|
204
|
+
};
|
|
205
|
+
const deliver2 = (event, to = "all") => {
|
|
206
|
+
for (const destination of destinations) {
|
|
207
|
+
if (!active.has(destination)) continue;
|
|
208
|
+
if (to !== "all" && to === "gated" !== !!destination.requires) {
|
|
209
|
+
continue;
|
|
210
|
+
}
|
|
211
|
+
safely(
|
|
212
|
+
label(destination, "track"),
|
|
213
|
+
() => destination.track(event, consent)
|
|
214
|
+
);
|
|
215
|
+
}
|
|
216
|
+
};
|
|
217
|
+
const dispatch = (event) => {
|
|
218
|
+
if (consent === null || syncing) {
|
|
219
|
+
deliver2(event, "ungated");
|
|
220
|
+
queue.push(event);
|
|
221
|
+
if (queue.length > MAX_QUEUE_SIZE) queue.shift();
|
|
222
|
+
return;
|
|
223
|
+
}
|
|
224
|
+
deliver2(event);
|
|
225
|
+
};
|
|
226
|
+
const mustDeliverNow = () => delivery === "immediate" || isLeaving || document.visibilityState === "hidden";
|
|
227
|
+
const schedule = (wait) => {
|
|
228
|
+
const token = ++tokens;
|
|
229
|
+
scheduled = token;
|
|
230
|
+
wait(() => {
|
|
231
|
+
if (scheduled === token) flush();
|
|
232
|
+
});
|
|
233
|
+
};
|
|
234
|
+
const flush = (all = mustDeliverNow()) => {
|
|
235
|
+
scheduled = void 0;
|
|
236
|
+
if (isPrerendering()) return;
|
|
237
|
+
const wasFlushing = isFlushing;
|
|
238
|
+
isFlushing = true;
|
|
239
|
+
const start = performance.now();
|
|
240
|
+
try {
|
|
241
|
+
while (head < outbox.length) {
|
|
242
|
+
dispatch(outbox[head++]);
|
|
243
|
+
if (head < outbox.length && !all && performance.now() - start > MAX_FLUSH_DURATION) {
|
|
244
|
+
schedule((callback) => setTimeout(callback));
|
|
245
|
+
return;
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
} finally {
|
|
249
|
+
isFlushing = wasFlushing;
|
|
250
|
+
if (!wasFlushing) {
|
|
251
|
+
outbox.splice(0, head);
|
|
252
|
+
head = 0;
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
};
|
|
256
|
+
const apply = (next) => {
|
|
257
|
+
if (!isBrowser() || consent && shallowEqual(consent, next)) return;
|
|
258
|
+
flush(true);
|
|
259
|
+
consent = { ...next };
|
|
260
|
+
const current = consent;
|
|
261
|
+
syncing = true;
|
|
262
|
+
targets.forEach((target) => tell(target, current));
|
|
263
|
+
destinations.forEach((destination) => sync(destination, current));
|
|
264
|
+
syncing = false;
|
|
265
|
+
const queued = queue;
|
|
266
|
+
queue = [];
|
|
267
|
+
queued.forEach((event) => deliver2(event, "gated"));
|
|
268
|
+
};
|
|
269
|
+
if (isBrowser()) {
|
|
270
|
+
for (const destination of destinations) {
|
|
271
|
+
safely(label(destination, "setup"), () => destination.setup());
|
|
272
|
+
if (!destination.requires) active.add(destination);
|
|
273
|
+
}
|
|
274
|
+
document.addEventListener("visibilitychange", () => {
|
|
275
|
+
if (document.visibilityState === "hidden") flush();
|
|
276
|
+
});
|
|
277
|
+
window.addEventListener("pagehide", () => {
|
|
278
|
+
isLeaving = true;
|
|
279
|
+
flush();
|
|
280
|
+
});
|
|
281
|
+
window.addEventListener("pageshow", () => {
|
|
282
|
+
isLeaving = false;
|
|
283
|
+
});
|
|
284
|
+
document.addEventListener("prerenderingchange", () => flush());
|
|
285
|
+
window.navigation?.addEventListener(
|
|
286
|
+
"navigate",
|
|
287
|
+
() => flush(true)
|
|
288
|
+
);
|
|
289
|
+
}
|
|
290
|
+
const initial = consentSource.get();
|
|
291
|
+
if (initial) apply(initial);
|
|
292
|
+
consentSource.subscribe(apply);
|
|
293
|
+
const send = (name, payload) => {
|
|
294
|
+
if (!isBrowser()) return;
|
|
295
|
+
const snapshot = safely(
|
|
296
|
+
`track ${name}`,
|
|
297
|
+
() => structuredClone(payload ?? {})
|
|
298
|
+
);
|
|
299
|
+
if (!snapshot) return;
|
|
300
|
+
const event = {
|
|
301
|
+
id: createId(),
|
|
302
|
+
name,
|
|
303
|
+
payload: snapshot,
|
|
304
|
+
context: { ...getContext(), ...context }
|
|
305
|
+
};
|
|
306
|
+
listeners.forEach((listener) => safely("subscribe", () => listener(event)));
|
|
307
|
+
if (IS_DEVELOPMENT && !Object.hasOwn(events, name) && !unknown.has(name)) {
|
|
308
|
+
unknown.add(name);
|
|
309
|
+
console.warn(
|
|
310
|
+
`[analytics] "${name}" isn't in the events passed to createAnalytics(). Add it with defineEvents().`
|
|
311
|
+
);
|
|
312
|
+
}
|
|
313
|
+
outbox.push(event);
|
|
314
|
+
if (isFlushing) return;
|
|
315
|
+
if (mustDeliverNow()) {
|
|
316
|
+
flush();
|
|
317
|
+
} else if (scheduled === void 0) {
|
|
318
|
+
schedule(afterNextPaint);
|
|
319
|
+
}
|
|
320
|
+
};
|
|
321
|
+
const routeKeys = /* @__PURE__ */ new Map();
|
|
322
|
+
const destinationKeys = destinations.map((destination) => {
|
|
323
|
+
const { name } = destination;
|
|
324
|
+
const count = (routeKeys.get(name) ?? 0) + 1;
|
|
325
|
+
routeKeys.set(name, count);
|
|
326
|
+
return count === 1 ? name : `${name} (${count})`;
|
|
327
|
+
});
|
|
328
|
+
const subscribed = destinations.map(
|
|
329
|
+
(destination) => destination.events && new Set(destination.events)
|
|
330
|
+
);
|
|
331
|
+
const routes = () => {
|
|
332
|
+
const result = {};
|
|
333
|
+
for (const [name, { description }] of Object.entries(
|
|
334
|
+
events
|
|
335
|
+
)) {
|
|
336
|
+
const reached = {};
|
|
337
|
+
destinations.forEach((destination, index) => {
|
|
338
|
+
if (subscribed[index]?.has(name) === false) return;
|
|
339
|
+
reached[destinationKeys[index]] = {
|
|
340
|
+
requires: destination.requires ?? "none"
|
|
341
|
+
};
|
|
342
|
+
});
|
|
343
|
+
result[name] = { description, destinations: reached };
|
|
344
|
+
}
|
|
345
|
+
return result;
|
|
346
|
+
};
|
|
347
|
+
if (IS_DEVELOPMENT && isBrowser() && destinations.length > 0) {
|
|
348
|
+
const unrouted = Object.entries(routes()).filter(
|
|
349
|
+
([, { destinations: reached }]) => Object.keys(reached).length === 0
|
|
350
|
+
).map(([name]) => `"${name}"`);
|
|
351
|
+
if (unrouted.length > 0) {
|
|
352
|
+
console.warn(
|
|
353
|
+
`[analytics] ${unrouted.join(", ")} reach no destination. Add them to a mapping in map().`
|
|
354
|
+
);
|
|
355
|
+
}
|
|
356
|
+
}
|
|
357
|
+
if (isBrowser()) {
|
|
358
|
+
for (const source of new Set(sources2)) {
|
|
359
|
+
connected.get(source)?.();
|
|
360
|
+
connected.delete(source);
|
|
361
|
+
const unsubscribe = safely(
|
|
362
|
+
"sources",
|
|
363
|
+
() => source.subscribe(({ name, payload }) => send(name, payload))
|
|
364
|
+
);
|
|
365
|
+
if (unsubscribe) connected.set(source, unsubscribe);
|
|
366
|
+
}
|
|
367
|
+
}
|
|
368
|
+
return {
|
|
369
|
+
track: (name, ...[payload]) => send(name, payload),
|
|
370
|
+
setContext: (fields) => {
|
|
371
|
+
const copy = safely("setContext", () => structuredClone(fields));
|
|
372
|
+
if (copy) context = { ...context, ...copy };
|
|
373
|
+
},
|
|
374
|
+
subscribe: (listener) => {
|
|
375
|
+
listeners.add(listener);
|
|
376
|
+
return () => listeners.delete(listener);
|
|
377
|
+
},
|
|
378
|
+
routes
|
|
379
|
+
};
|
|
380
|
+
};
|
|
381
|
+
|
|
382
|
+
export { createAnalytics, map, mapEvery, withConsent };
|
|
383
|
+
//# sourceMappingURL=index.js.map
|
|
384
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/analytics/destination.ts","../../src/analytics/create-analytics.ts"],"names":["sources","deliver"],"mappings":"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","file":"index.js","sourcesContent":["import { IS_DEVELOPMENT } from \"../config\";\nimport type { ConsentCategory, ConsentState } from \"../consent/consent\";\nimport type { EventDefinition, EventsOf } from \"./define-events\";\nimport type {\n\tAnalyticsContext,\n\tAnalyticsEnvelope,\n\tAnalyticsEventName,\n\tEventMap,\n} from \"./events\";\n\n/** One event in a destination's own vocabulary, narrowed by `name`. */\nexport type DestinationEvent<Accepts extends object> = {\n\t[N in keyof Accepts & string]: { name: N; payload: Accepts[N] };\n}[keyof Accepts & string];\n\n/** What a destination's callbacks receive with each payload. */\nexport type DestinationEventContext<\n\tContext extends object = Record<never, never>,\n> = {\n\t/** Of the tracked event: unique per event, e.g. for Meta's `eventID`. */\n\tid: string;\n\t/** Of the tracked event, with the project's own fields too. */\n\tcontext: AnalyticsContext<Context>;\n\t/** The current answer, `null` until answered, e.g. to send personal data only with marketing consent. */\n\tconsent: ConsentState;\n};\n\n/** What a destination's `setup` receives. */\nexport type DestinationSetupContext<\n\tAccepts extends object,\n\tContext extends object = Record<never, never>,\n> = {\n\t/** Subscribes a callback to one of the destination's events. Returns a function that unsubscribes it. */\n\tsubscribe: <N extends keyof Accepts & string>(\n\t\tevent: N,\n\t\tcallback: (\n\t\t\tpayload: Accepts[N],\n\t\t\tcontext: DestinationEventContext<Context>,\n\t\t) => void,\n\t) => () => void;\n};\n\n/**\n * Forwards events to one provider (GTM, Meta, Shopify, …), in the provider's own vocabulary: `Accepts` holds the\n * minimum shape of every event it takes. Shaped like Hydrogen's `StorefrontAnalyticsDestination`. `map()` turns it\n * into one that accepts the project's events, which `withConsent()` hands to `createAnalytics()`. Telling a provider the\n * visitor's consent is a `ConsentTarget`'s job.\n */\nexport type AnalyticsDestination<\n\tAccepts extends object,\n\tContext extends object = Record<never, never>,\n> = {\n\t/** Prefixes this destination's errors and warnings in the console. */\n\tname: string;\n\t/**\n\t * The events it subscribes to, so `routes()` can list them without setting it up, e.g. on the server. Without it,\n\t * it counts as receiving every event.\n\t */\n\tevents?: readonly (keyof Accepts & string)[];\n\t/**\n\t * Runs once in the browser when analytics is created, before consent is answered: subscribe to events here, but\n\t * load the provider's script once the first event arrives. Events wait for an async setup. When it throws or\n\t * rejects, the error is logged and the destination receives nothing.\n\t */\n\tsetup: (\n\t\tcontext: DestinationSetupContext<Accepts, Context>,\n\t) => void | Promise<void>;\n};\n\n/** One event, several (e.g. one per item), or `null` to send nothing. */\nexport type MappingResult<Accepts extends object> =\n\t| DestinationEvent<Accepts>\n\t| readonly DestinationEvent<Accepts>[]\n\t| null;\n\n/** Per event of `Events`, what a destination receives. Events without an entry aren't sent. */\nexport type AnalyticsMapping<\n\tEvents extends EventMap,\n\tAccepts extends object,\n\tContext extends object = Record<never, never>,\n> = {\n\t[N in AnalyticsEventName<Events>]?: (\n\t\tpayload: Events[N],\n\t\tevent: { id: string; name: N; context: AnalyticsContext<Context> },\n\t) => MappingResult<Accepts>;\n};\n\n/**\n * Categories that must all be granted before a destination receives events; until consent is answered, they're queued\n * for it. `\"none\"` delivers every event straight away, even before consent is answered, for providers that gate on\n * consent themselves, e.g. GTM with Consent Mode.\n */\nexport type ConsentRequirement =\n\t| readonly [ConsentCategory, ...ConsentCategory[]]\n\t| \"none\";\n\n/** What `createAnalytics()` takes: a destination of the project's events with the consent it requires, from `withConsent()`. */\nexport type ConsentedDestination<\n\tEvents extends EventMap,\n\tContext extends object = Record<never, never>,\n> = AnalyticsDestination<Events, Context> & { requires: ConsentRequirement };\n\n/** How `createAnalytics()` delivers to a destination. */\nexport type Connection<\n\tEvents extends EventMap,\n\tContext extends object = Record<never, never>,\n> = {\n\tname: string;\n\t/** Absent for `\"none\"`. */\n\trequires?: readonly [ConsentCategory, ...ConsentCategory[]];\n\tevents?: readonly string[];\n\tsetup: () => void;\n\ttrack: (\n\t\tevent: AnalyticsEnvelope<Events, Context>,\n\t\tconsent: ConsentState,\n\t) => void;\n};\n\ntype Callback = (payload: unknown, context: DestinationEventContext) => void;\n\ntype SentEvent = { name: string; payload: unknown };\n\ntype Subscriptions = {\n\tcallbacks: Map<string, Set<Callback>>;\n\t/** Events sent while an async setup runs. */\n\tpending?: [SentEvent, DestinationEventContext, boolean][];\n\tfailed: boolean;\n\twarned: Set<string>;\n};\n\n// Per destination, so one mapped or connected twice is set up once.\nconst subscriptions = new WeakMap<object, Subscriptions>();\n\nconst deliver = (\n\tdestination: AnalyticsDestination<object>,\n\tstate: Subscriptions,\n\tevent: SentEvent,\n\tcontext: DestinationEventContext,\n\t// Only for a mapping's events: a connected destination needn't subscribe to every project event.\n\twarnUnsubscribed = true,\n) => {\n\tif (state.failed) return;\n\tif (state.pending) {\n\t\tstate.pending.push([event, context, warnUnsubscribed]);\n\t\treturn;\n\t}\n\tconst callbacks = state.callbacks.get(event.name);\n\tif (!callbacks?.size) {\n\t\tif (warnUnsubscribed && IS_DEVELOPMENT && !state.warned.has(event.name)) {\n\t\t\tstate.warned.add(event.name);\n\t\t\tconsole.warn(\n\t\t\t\t`[analytics] ${destination.name}: a mapping sends \"${event.name}\", which it doesn't subscribe to.`,\n\t\t\t);\n\t\t}\n\t\treturn;\n\t}\n\tfor (const callback of callbacks) {\n\t\ttry {\n\t\t\tcallback(event.payload, context);\n\t\t} catch (error) {\n\t\t\tconsole.error(`[analytics] ${destination.name}.${event.name}`, error);\n\t\t}\n\t}\n};\n\nconst setUp = (destination: AnalyticsDestination<object>) => {\n\tconst existing = subscriptions.get(destination);\n\tif (existing) return existing;\n\tconst state: Subscriptions = {\n\t\tcallbacks: new Map(),\n\t\tfailed: false,\n\t\twarned: new Set(),\n\t};\n\tsubscriptions.set(destination, state);\n\n\tconst fail = (error: unknown) => {\n\t\tstate.failed = true;\n\t\tstate.pending = undefined;\n\t\tconsole.error(`[analytics] ${destination.name}.setup`, error);\n\t};\n\n\ttry {\n\t\tconst result = destination.setup({\n\t\t\tsubscribe: (event, callback) => {\n\t\t\t\tconst callbacks = state.callbacks.get(event) ?? new Set();\n\t\t\t\tstate.callbacks.set(event, callbacks.add(callback as Callback));\n\t\t\t\treturn () => callbacks.delete(callback as Callback);\n\t\t\t},\n\t\t});\n\t\tif (result instanceof Promise) {\n\t\t\tstate.pending = [];\n\t\t\tresult.then(() => {\n\t\t\t\tconst pending = state.pending ?? [];\n\t\t\t\tstate.pending = undefined;\n\t\t\t\tfor (const [event, context, warnUnsubscribed] of pending) {\n\t\t\t\t\tdeliver(destination, state, event, context, warnUnsubscribed);\n\t\t\t\t}\n\t\t\t}, fail);\n\t\t}\n\t} catch (error) {\n\t\tfail(error);\n\t}\n\treturn state;\n};\n\ntype Entry = (\n\tpayload: unknown,\n\tevent: { id: string; name: string; context: AnalyticsContext },\n) => SentEvent | readonly SentEvent[] | null | undefined;\n\n/**\n * Maps the project's events onto a destination: per event of `createAnalytics<Events>()`, what the destination\n * receives, typed by what it accepts. Events without an entry aren't sent to it. Returns a destination that accepts the\n * project's events, for `withConsent()`. Inside `withConsent()`, it takes the events from there. See `docs/analytics.md`.\n *\n * @example\n * ```ts\n * map(metaDestination(), {\n * newsletterSignedUp: ({ source }) => ({ name: \"Lead\", payload: { content_name: source } }),\n * });\n * ```\n */\nexport const map = <\n\tAccepts extends object,\n\tEvents extends EventMap,\n\tContext extends object = Record<never, never>,\n>(\n\tdestination: AnalyticsDestination<Accepts>,\n\t// Inferred from where the result goes, e.g. `withConsent()`, so a misspelled event is rejected here.\n\tmapping: AnalyticsMapping<NoInfer<Events>, Accepts, NoInfer<Context>>,\n): AnalyticsDestination<Events, Context> => {\n\tconst entries = Object.entries(mapping).filter(\n\t\t(entry): entry is [string, Entry] => !!entry[1],\n\t);\n\tconst target = destination as AnalyticsDestination<object>;\n\n\treturn {\n\t\tname: destination.name,\n\t\tevents: entries.map(([name]) => name as AnalyticsEventName<Events>),\n\t\tsetup: ({ subscribe }) => {\n\t\t\tconst state = setUp(target);\n\t\t\tfor (const [name, entry] of entries) {\n\t\t\t\tsubscribe(name as AnalyticsEventName<Events>, (payload, context) => {\n\t\t\t\t\tconst result = entry(payload, {\n\t\t\t\t\t\tid: context.id,\n\t\t\t\t\t\tname,\n\t\t\t\t\t\tcontext: context.context,\n\t\t\t\t\t});\n\t\t\t\t\tif (!result) return;\n\t\t\t\t\tconst events: readonly SentEvent[] = Array.isArray(result)\n\t\t\t\t\t\t? result\n\t\t\t\t\t\t: [result];\n\t\t\t\t\tfor (const sent of events) deliver(target, state, sent, context);\n\t\t\t\t});\n\t\t\t}\n\t\t},\n\t};\n};\n\n/**\n * A mapping with an entry for every event of the catalog, e.g. to send the events without one of their own in a\n * generic format. Spread it first, so the entries after it override it.\n *\n * @example\n * ```ts\n * map(gtmDestination(), { ...mapEvery(events, toGa4CustomEvent), ...ga4Mapping<Events>({ item: toGa4Item }) });\n * ```\n */\nexport const mapEvery = <\n\tCatalog extends Record<string, EventDefinition>,\n\tResult extends\n\t\t| { name: string; payload: unknown }\n\t\t| readonly { name: string; payload: unknown }[]\n\t\t| null,\n\t// Inferred from an annotated `toEvent`, e.g. `(event: AnalyticsEnvelope<Events, Context>) => …`.\n\tContext extends object = Record<never, never>,\n>(\n\tevents: Catalog,\n\ttoEvent: (event: AnalyticsEnvelope<EventsOf<Catalog>, Context>) => Result,\n): {\n\t[N in keyof Catalog & string]: (\n\t\tpayload: EventsOf<Catalog>[N],\n\t\tevent: { id: string; name: N; context: AnalyticsContext<Context> },\n\t) => Result;\n} => {\n\tconst entries: Record<string, Entry> = {};\n\tfor (const name of Object.keys(events)) {\n\t\tentries[name] = (payload, { id, context }) =>\n\t\t\ttoEvent({ id, name, payload, context } as AnalyticsEnvelope<\n\t\t\t\tEventsOf<Catalog>,\n\t\t\t\tContext\n\t\t\t>);\n\t}\n\treturn entries as never;\n};\n\n// The destination `withConsent()` was given, so one given twice, e.g. with different consent, is set up once.\nconst sources = new WeakMap<object, AnalyticsDestination<object>>();\n\nconst sourceOf = (destination: object) =>\n\tsources.get(destination) ?? (destination as AnalyticsDestination<object>);\n\n/**\n * Gives a destination of the project's events, usually from `map()`, the consent it requires, for `createAnalytics()`.\n * Required, so consent is never left out by accident. Inside `createAnalytics()`, it takes the events from there.\n *\n * @example\n * ```ts\n * createAnalytics<Events>({\n * events,\n * consent,\n * destinations: [\n * withConsent(map(gtmDestination(), ga4Mapping<Events>({ item: toGa4Item })), \"none\"),\n * withConsent(\n * map(metaDestination(), {\n * newsletterSignedUp: ({ source }) => ({ name: \"Lead\", payload: { content_name: source } }),\n * }),\n * [\"marketing\"],\n * ),\n * ],\n * });\n * ```\n */\nexport const withConsent = <\n\tEvents extends EventMap,\n\tContext extends object = Record<never, never>,\n>(\n\tdestination: AnalyticsDestination<Events, Context>,\n\trequires: ConsentRequirement,\n): ConsentedDestination<Events, Context> => {\n\tconst consented = { ...destination, requires };\n\tsources.set(consented, sourceOf(destination));\n\treturn consented;\n};\n\nexport const toConnection = <\n\tEvents extends EventMap,\n\tContext extends object = Record<never, never>,\n>(\n\tdestination: ConsentedDestination<Events, Context>,\n): Connection<Events, Context> => {\n\tconst { name, events, requires } = destination;\n\tconst source = sourceOf(destination);\n\tconst subscribed = events && new Set<string>(events);\n\n\treturn {\n\t\tname,\n\t\trequires: requires === \"none\" ? undefined : requires,\n\t\tevents,\n\t\tsetup: () => void setUp(source),\n\t\ttrack: (event, consent) => {\n\t\t\tconst state = subscriptions.get(source);\n\t\t\tif (!state || (subscribed && !subscribed.has(event.name))) return;\n\t\t\tconst context = { id: event.id, context: event.context, consent };\n\t\t\tdeliver(source, state, event, context, false);\n\t\t},\n\t};\n};\n","import { IS_DEVELOPMENT } from \"../config\";\nimport {\n\ttype Consent,\n\ttype ConsentCategory,\n\ttype ConsentSource,\n\ttype ConsentState,\n\ttype ConsentTarget,\n\thasConsent,\n} from \"../consent/consent\";\nimport { shallowEqual } from \"../functions/shallow-equal\";\nimport type { EventCatalog } from \"./define-events\";\nimport {\n\ttype Connection,\n\ttype ConsentedDestination,\n\ttoConnection,\n} from \"./destination\";\nimport type {\n\tAnalyticsContextFields,\n\tAnalyticsEnvelope,\n\tAnalyticsEventName,\n\tAnalyticsPayloadArgs,\n\tEventMap,\n} from \"./events\";\n\ntype AnalyticsOptions<Events extends EventMap, Context extends object> = {\n\t/** Every event the project tracks, from `defineEvents()`. */\n\tevents: EventCatalog<Events>;\n\t/** E.g. `cookiebotConsent()` from `@buildinams/core/consent`. Until answered, events are queued. */\n\tconsent: ConsentSource;\n\t/** Told every answer, before destinations receive events under it, e.g. `googleConsentMode()`. */\n\tconsentTargets?: ConsentTarget[];\n\t/** Destinations with the consent they require, from `withConsent()`. */\n\tdestinations?: ConsentedDestination<Events, Context>[];\n\t/**\n\t * What reports events to track under their own names, e.g. `createDomWatcher()` and `createCartChanges()`, with their\n\t * `events` in the catalog. A source feeds one instance: a later `createAnalytics()` with it, e.g. after HMR, takes it\n\t * over.\n\t */\n\tsources?: NoInfer<AnalyticsSource<Events>>[];\n\t/** Initial `context` for every event, after `url`, `title` and `referrer`. Update it with `setContext()`. */\n\tcontext?: AnalyticsContextFields<Context>;\n\t/**\n\t * `\"deferred\"` delivers events to destinations after the next paint, so tracking in an event handler doesn't delay\n\t * its response (INP), or straight away while the page is hidden or unloading. `\"immediate\"` delivers them as they're\n\t * tracked, e.g. in tests. Subscribers always receive them as they're tracked. Either way, a prerendered page delivers\n\t * nothing until the visitor navigates to it.\n\t */\n\tdelivery?: \"deferred\" | \"immediate\";\n};\n\ndeclare const types: unique symbol;\n\n/** Reports events of the project, e.g. `createDomWatcher()` or `createCartChanges()`. */\nexport type AnalyticsSource<Events extends EventMap = EventMap> = {\n\tsubscribe: (\n\t\tlistener: (\n\t\t\tevent: {\n\t\t\t\t[N in AnalyticsEventName<Events>]: { name: N; payload: Events[N] };\n\t\t\t}[AnalyticsEventName<Events>],\n\t\t) => void,\n\t) => () => void;\n};\n\n// Each source's subscription, so the instance that connects it last is the one it feeds.\nconst connected = new WeakMap<object, () => void>();\n\n/** How a destination receives an event. */\nexport type AnalyticsRoute = {\n\t/** The consent categories it waits for, or `\"none\"`. */\n\trequires: readonly ConsentCategory[] | \"none\";\n};\n\n/** Per event of the catalog, its description and the destinations that receive it, by name. */\nexport type AnalyticsRoutes<Events extends EventMap> = {\n\t[N in AnalyticsEventName<Events>]: {\n\t\tdescription: string;\n\t\tdestinations: Record<string, AnalyticsRoute>;\n\t};\n};\n\nexport type Analytics<\n\tEvents extends EventMap,\n\tContext extends object = Record<never, never>,\n> = {\n\t/** Type only, so helpers taking the instance infer its events. */\n\treadonly [types]?: { events: Events; context: Context };\n\ttrack: <N extends AnalyticsEventName<Events>>(\n\t\tname: N,\n\t\t...args: AnalyticsPayloadArgs<Events, N>\n\t) => void;\n\t/**\n\t * Merges into the context of events tracked from now on, e.g. `{ user }` after login. Pass `undefined` to clear\n\t * a field.\n\t */\n\tsetContext: (fields: AnalyticsContextFields<Context>) => void;\n\t/** Every tracked event as it's tracked, before consent gating, e.g. to log it. Returns a function that unsubscribes. */\n\tsubscribe: (\n\t\tlistener: (event: AnalyticsEnvelope<Events, Context>) => void,\n\t) => () => void;\n\t/**\n\t * Which destinations receive each event, and how, e.g. to check in a test, or for an agent or analyst to read what\n\t * the project tracks where. Also on the server.\n\t */\n\troutes: () => AnalyticsRoutes<Events>;\n};\n\n// Keeps the queue bounded when the visitor never answers the consent banner.\nconst MAX_QUEUE_SIZE = 100;\n\n// Checked per call, not `IS_IN_BROWSER`, so a module-level instance stays inert during SSR.\nconst isBrowser = () => typeof window !== \"undefined\";\n\n// Rendered ahead by speculation rules, possibly for a link the visitor never follows.\nconst isPrerendering = () =>\n\t(document as { prerendering?: boolean }).prerendering === true;\n\n// Per task, so a burst of events, each running a provider's tags, doesn't block the next interaction.\nconst MAX_FLUSH_DURATION = 5;\n\n// Frames can be throttled while the page is visible, e.g. in an offscreen iframe.\nconst MAX_PAINT_DELAY = 100;\n\nconst afterNextPaint = (callback: () => void) => {\n\tlet isDone = false;\n\tconst run = () => {\n\t\tif (isDone) return;\n\t\tisDone = true;\n\t\tsetTimeout(callback);\n\t};\n\tif (typeof requestAnimationFrame !== \"undefined\") {\n\t\trequestAnimationFrame(run);\n\t}\n\tsetTimeout(run, MAX_PAINT_DELAY);\n};\n\n// `crypto.randomUUID` only exists in secure contexts.\nconst createId = () =>\n\tglobalThis.crypto?.randomUUID?.() ??\n\t`${Date.now().toString(36)}-${Math.random().toString(36).slice(2)}`;\n\nconst getContext = () => ({\n\turl: window.location.href,\n\ttitle: document.title,\n\treferrer: document.referrer,\n});\n\n// Tracking must never break the caller, nor one destination or listener the others.\nconst safely = <T>(label: string, callback: () => T): T | undefined => {\n\ttry {\n\t\treturn callback();\n\t} catch (error) {\n\t\tconsole.error(`[analytics] ${label}`, error);\n\t\treturn undefined;\n\t}\n};\n\n/**\n * Creates the analytics bus: one `track()`, typed by the project's `Events` from `defineEvents()`, that forwards to\n * every destination the visitor consented to, and tracks what its `sources` report. `Context` adds fields to the\n * built-in context. Never throws: invalid input and failing destinations\n * are logged instead. Calls on the server do nothing. See `docs/analytics.md`.\n *\n * @example\n * ```ts\n * const events = defineEvents({\n * ...domWatcher.events,\n * newsletterSignedUp: { description: \"The newsletter form was submitted.\", payload: shape<{ source: \"footer\" }>() },\n * });\n *\n * type Events = EventsOf<typeof events>;\n *\n * export const analytics = createAnalytics<Events>({\n * events,\n * consent,\n * sources: [domWatcher],\n * destinations: [withConsent(map(gtmDestination(), ga4Mapping<Events>()), \"none\")],\n * });\n *\n * analytics.track(\"newsletterSignedUp\", { source: \"footer\" });\n * ```\n */\nexport const createAnalytics = <\n\tEvents extends EventMap,\n\tContext extends object = Record<never, never>,\n>({\n\tevents,\n\tconsent: consentSource,\n\tconsentTargets = [],\n\tdestinations: allDestinations = [],\n\tsources = [],\n\tcontext: initialContext = {} as AnalyticsContextFields<Context>,\n\tdelivery = \"deferred\",\n}: AnalyticsOptions<Events, Context>): Analytics<Events, Context> => {\n\ttype Envelope = AnalyticsEnvelope<Events, Context>;\n\ttype Destination = Connection<Events, Context>;\n\n\tconst destinations = [...new Set(allDestinations)].map((destination) =>\n\t\ttoConnection(destination),\n\t);\n\t// Lags the source while its earlier subscribers run, so events they track are queued, not lost.\n\tlet consent: ConsentState = null;\n\t// Events waiting for consent, for destinations with `requires`.\n\tlet queue: Envelope[] = [];\n\tlet context =\n\t\tsafely(\"context\", () => structuredClone(initialContext)) ??\n\t\t({} as AnalyticsContextFields<Context>);\n\t// Queues events tracked from `apply`, e.g. by a consent target, so they flush after the events queued before them.\n\tlet syncing = false;\n\t// Allowed destinations.\n\tconst active = new Set<Destination>();\n\tconst listeners = new Set<(event: Envelope) => void>();\n\t// Tracked, waiting for the next paint. Delivered up to `head`, shared by nested flushes so none delivers twice.\n\tconst outbox: Envelope[] = [];\n\tlet head = 0;\n\tlet isFlushing = false;\n\t// The scheduled flush's token, so one scheduled before another flush doesn't deliver events tracked after it.\n\tlet scheduled: number | undefined;\n\tlet tokens = 0;\n\tlet isLeaving = false;\n\t// Names already warned about for not being in the catalog.\n\tconst unknown = new Set<string>();\n\n\tconst label = (destination: Destination, method: string) =>\n\t\t`${destination.name}.${method}`;\n\n\t// Not back to the platform the answer came from, which may hold more than `Consent` does.\n\tconst targets = [...new Set(consentTargets)].filter(\n\t\t(target) => target.name !== consentSource.name,\n\t);\n\n\tconst tell = (target: ConsentTarget, current: Consent) => {\n\t\tconst result = safely(`${target.name}.apply`, () =>\n\t\t\ttarget.apply({ ...current }),\n\t\t);\n\t\tPromise.resolve(result).catch((error: unknown) => {\n\t\t\tconsole.error(`[analytics] ${target.name}.apply`, error);\n\t\t});\n\t};\n\n\tconst sync = (destination: Destination, current: Consent) => {\n\t\tif (!destination.requires) return;\n\n\t\tif (hasConsent(current, destination.requires)) {\n\t\t\tactive.add(destination);\n\t\t} else {\n\t\t\tactive.delete(destination);\n\t\t}\n\t};\n\n\tconst deliver = (\n\t\tevent: Envelope,\n\t\tto: \"all\" | \"gated\" | \"ungated\" = \"all\",\n\t) => {\n\t\tfor (const destination of destinations) {\n\t\t\tif (!active.has(destination)) continue;\n\t\t\tif (to !== \"all\" && (to === \"gated\") !== !!destination.requires) {\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tsafely(label(destination, \"track\"), () =>\n\t\t\t\tdestination.track(event, consent),\n\t\t\t);\n\t\t}\n\t};\n\n\tconst dispatch = (event: Envelope) => {\n\t\tif (consent === null || syncing) {\n\t\t\tdeliver(event, \"ungated\");\n\t\t\tqueue.push(event);\n\t\t\tif (queue.length > MAX_QUEUE_SIZE) queue.shift();\n\t\t\treturn;\n\t\t}\n\t\tdeliver(event);\n\t};\n\n\tconst mustDeliverNow = () =>\n\t\tdelivery === \"immediate\" ||\n\t\tisLeaving ||\n\t\tdocument.visibilityState === \"hidden\";\n\n\tconst schedule = (wait: (callback: () => void) => void) => {\n\t\tconst token = ++tokens;\n\t\tscheduled = token;\n\t\twait(() => {\n\t\t\tif (scheduled === token) flush();\n\t\t});\n\t};\n\n\tconst flush = (all = mustDeliverNow()) => {\n\t\tscheduled = undefined;\n\t\t// Delivered on `prerenderingchange`, once the visitor navigates to the page.\n\t\tif (isPrerendering()) return;\n\t\tconst wasFlushing = isFlushing;\n\t\tisFlushing = true;\n\t\tconst start = performance.now();\n\t\ttry {\n\t\t\twhile (head < outbox.length) {\n\t\t\t\tdispatch(outbox[head++]!);\n\t\t\t\tif (\n\t\t\t\t\thead < outbox.length &&\n\t\t\t\t\t!all &&\n\t\t\t\t\tperformance.now() - start > MAX_FLUSH_DURATION\n\t\t\t\t) {\n\t\t\t\t\tschedule((callback) => setTimeout(callback));\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t}\n\t\t} finally {\n\t\t\tisFlushing = wasFlushing;\n\t\t\tif (!wasFlushing) {\n\t\t\t\toutbox.splice(0, head);\n\t\t\t\thead = 0;\n\t\t\t}\n\t\t}\n\t};\n\n\tconst apply = (next: Consent) => {\n\t\t// Sources may report the same answer more than once.\n\t\tif (!isBrowser() || (consent && shallowEqual(consent, next))) return;\n\t\t// Under the consent they were tracked with.\n\t\tflush(true);\n\t\tconsent = { ...next };\n\t\tconst current = consent;\n\n\t\tsyncing = true;\n\t\ttargets.forEach((target) => tell(target, current));\n\t\tdestinations.forEach((destination) => sync(destination, current));\n\t\tsyncing = false;\n\n\t\tconst queued = queue;\n\t\tqueue = [];\n\t\tqueued.forEach((event) => deliver(event, \"gated\"));\n\t};\n\n\tif (isBrowser()) {\n\t\tfor (const destination of destinations) {\n\t\t\tsafely(label(destination, \"setup\"), () => destination.setup());\n\t\t\tif (!destination.requires) active.add(destination);\n\t\t}\n\t\t// Scheduled callbacks may never run once the page is hidden, e.g. after a link to another site.\n\t\tdocument.addEventListener(\"visibilitychange\", () => {\n\t\t\tif (document.visibilityState === \"hidden\") flush();\n\t\t});\n\t\t// Not every browser hides the page before unloading it.\n\t\twindow.addEventListener(\"pagehide\", () => {\n\t\t\tisLeaving = true;\n\t\t\tflush();\n\t\t});\n\t\twindow.addEventListener(\"pageshow\", () => {\n\t\t\tisLeaving = false;\n\t\t});\n\t\tdocument.addEventListener(\"prerenderingchange\", () => flush());\n\t\t// Before a client-side navigation changes the URL, which providers read when they receive an event.\n\t\t(window as { navigation?: EventTarget }).navigation?.addEventListener(\n\t\t\t\"navigate\",\n\t\t\t() => flush(true),\n\t\t);\n\t}\n\tconst initial = consentSource.get();\n\tif (initial) apply(initial);\n\tconsentSource.subscribe(apply);\n\n\tconst send = (name: string, payload: object | undefined) => {\n\t\tif (!isBrowser()) return;\n\n\t\t// Snapshot, so later mutations by the caller don't change queued events.\n\t\tconst snapshot = safely(`track ${name}`, () =>\n\t\t\tstructuredClone(payload ?? {}),\n\t\t);\n\t\tif (!snapshot) return;\n\n\t\tconst event = {\n\t\t\tid: createId(),\n\t\t\tname,\n\t\t\tpayload: snapshot,\n\t\t\tcontext: { ...getContext(), ...context },\n\t\t} as unknown as Envelope;\n\n\t\tlisteners.forEach((listener) => safely(\"subscribe\", () => listener(event)));\n\n\t\tif (IS_DEVELOPMENT && !Object.hasOwn(events, name) && !unknown.has(name)) {\n\t\t\tunknown.add(name);\n\t\t\tconsole.warn(\n\t\t\t\t`[analytics] \"${name}\" isn't in the events passed to createAnalytics(). Add it with defineEvents().`,\n\t\t\t);\n\t\t}\n\n\t\toutbox.push(event);\n\t\t// Delivered by the running flush, after the events before it.\n\t\tif (isFlushing) return;\n\t\tif (mustDeliverNow()) {\n\t\t\tflush();\n\t\t} else if (scheduled === undefined) {\n\t\t\tschedule(afterNextPaint);\n\t\t}\n\t};\n\n\t// Numbered when several share a name, e.g. two GTM containers.\n\tconst routeKeys = new Map<string, number>();\n\tconst destinationKeys = destinations.map((destination) => {\n\t\tconst { name } = destination;\n\t\tconst count = (routeKeys.get(name) ?? 0) + 1;\n\t\trouteKeys.set(name, count);\n\t\treturn count === 1 ? name : `${name} (${count})`;\n\t});\n\n\tconst subscribed = destinations.map(\n\t\t(destination) => destination.events && new Set<string>(destination.events),\n\t);\n\n\tconst routes = () => {\n\t\tconst result: Record<\n\t\t\tstring,\n\t\t\t{ description: string; destinations: Record<string, AnalyticsRoute> }\n\t\t> = {};\n\t\tfor (const [name, { description }] of Object.entries(\n\t\t\tevents as EventCatalog,\n\t\t)) {\n\t\t\tconst reached: Record<string, AnalyticsRoute> = {};\n\t\t\tdestinations.forEach((destination, index) => {\n\t\t\t\tif (subscribed[index]?.has(name) === false) return;\n\t\t\t\treached[destinationKeys[index]!] = {\n\t\t\t\t\trequires: destination.requires ?? \"none\",\n\t\t\t\t};\n\t\t\t});\n\t\t\tresult[name] = { description, destinations: reached };\n\t\t}\n\t\treturn result as AnalyticsRoutes<Events>;\n\t};\n\n\tif (IS_DEVELOPMENT && isBrowser() && destinations.length > 0) {\n\t\tconst unrouted = Object.entries(routes())\n\t\t\t.filter(\n\t\t\t\t([, { destinations: reached }]) => Object.keys(reached).length === 0,\n\t\t\t)\n\t\t\t.map(([name]) => `\"${name}\"`);\n\t\tif (unrouted.length > 0) {\n\t\t\tconsole.warn(\n\t\t\t\t`[analytics] ${unrouted.join(\", \")} reach no destination. Add them to a mapping in map().`,\n\t\t\t);\n\t\t}\n\t}\n\n\tif (isBrowser()) {\n\t\tfor (const source of new Set(sources)) {\n\t\t\tconnected.get(source)?.();\n\t\t\tconnected.delete(source);\n\t\t\tconst unsubscribe = safely(\"sources\", () =>\n\t\t\t\tsource.subscribe(({ name, payload }) => send(name, payload)),\n\t\t\t);\n\t\t\tif (unsubscribe) connected.set(source, unsubscribe);\n\t\t}\n\t}\n\n\treturn {\n\t\ttrack: (name, ...[payload]) => send(name, payload),\n\n\t\tsetContext: (fields) => {\n\t\t\t// Copied, so later mutations by the caller don't change the context of tracked events.\n\t\t\tconst copy = safely(\"setContext\", () => structuredClone(fields));\n\t\t\tif (copy) context = { ...context, ...copy };\n\t\t},\n\n\t\tsubscribe: (listener) => {\n\t\t\tlisteners.add(listener);\n\t\t\treturn () => listeners.delete(listener);\n\t\t},\n\n\t\troutes,\n\t};\n};\n"]}
|
|
@@ -0,0 +1,186 @@
|
|
|
1
|
+
import { A as AnalyticsDestination, a as AnalyticsMapping } from '../../destination-5KJ5PeR3.js';
|
|
2
|
+
import { E as EventMap, k as ShapesOf, C as CartItemOf } from '../../define-events-CxZjoUlG.js';
|
|
3
|
+
import { CartSnapshot } from '../../tracking/cart/index.js';
|
|
4
|
+
import '../../index-Zef1nnTP.js';
|
|
5
|
+
import '../../listeners-DkU-vBPa.js';
|
|
6
|
+
|
|
7
|
+
/** Hydrogen's `ProductPayload`. */
|
|
8
|
+
type ShopifyProduct = {
|
|
9
|
+
id: string;
|
|
10
|
+
title: string;
|
|
11
|
+
price: {
|
|
12
|
+
amount: string;
|
|
13
|
+
currencyCode: string;
|
|
14
|
+
};
|
|
15
|
+
vendor: string;
|
|
16
|
+
variantId: string;
|
|
17
|
+
variantTitle: string;
|
|
18
|
+
quantity: number;
|
|
19
|
+
productType?: string;
|
|
20
|
+
};
|
|
21
|
+
type ShopifyCartLine = {
|
|
22
|
+
id: string;
|
|
23
|
+
quantity: number;
|
|
24
|
+
merchandise: {
|
|
25
|
+
id: string;
|
|
26
|
+
title: string;
|
|
27
|
+
price: ShopifyProduct["price"];
|
|
28
|
+
product: Pick<ShopifyProduct, "id" | "title" | "vendor" | "productType">;
|
|
29
|
+
};
|
|
30
|
+
};
|
|
31
|
+
/**
|
|
32
|
+
* The events of Hydrogen's analytics bus, with the fields Shopify's script needs. Hydrogen adds the shop itself.
|
|
33
|
+
*/
|
|
34
|
+
type ShopifyEvents = {
|
|
35
|
+
page_viewed: {
|
|
36
|
+
url: string;
|
|
37
|
+
};
|
|
38
|
+
search_viewed: {
|
|
39
|
+
url: string;
|
|
40
|
+
searchTerm: string;
|
|
41
|
+
};
|
|
42
|
+
product_viewed: {
|
|
43
|
+
url: string;
|
|
44
|
+
products: ShopifyProduct[];
|
|
45
|
+
};
|
|
46
|
+
collection_viewed: {
|
|
47
|
+
url: string;
|
|
48
|
+
collection: {
|
|
49
|
+
id: string;
|
|
50
|
+
handle: string;
|
|
51
|
+
};
|
|
52
|
+
};
|
|
53
|
+
product_added_to_cart: {
|
|
54
|
+
cart: {
|
|
55
|
+
id: string;
|
|
56
|
+
};
|
|
57
|
+
prevCart: null;
|
|
58
|
+
currentLine: ShopifyCartLine;
|
|
59
|
+
};
|
|
60
|
+
cart_viewed: {
|
|
61
|
+
url: string;
|
|
62
|
+
cart: {
|
|
63
|
+
id: string;
|
|
64
|
+
};
|
|
65
|
+
} & Record<string, unknown>;
|
|
66
|
+
cart_updated: {
|
|
67
|
+
cart: {
|
|
68
|
+
id: string;
|
|
69
|
+
};
|
|
70
|
+
} & Record<string, unknown>;
|
|
71
|
+
product_removed_from_cart: {
|
|
72
|
+
cart: {
|
|
73
|
+
id: string;
|
|
74
|
+
};
|
|
75
|
+
} & Record<string, unknown>;
|
|
76
|
+
};
|
|
77
|
+
type ShopifyMappingOptions<Item, CartItem> = {
|
|
78
|
+
/**
|
|
79
|
+
* For `itemViewed`: the project's item as Hydrogen's `ProductPayload`, or `null` to leave it out. Products without a
|
|
80
|
+
* field Shopify requires are left out too, with a warning in development.
|
|
81
|
+
*/
|
|
82
|
+
product?: (item: Item) => ShopifyProduct | null;
|
|
83
|
+
/** For `addedToCart`: the project's cart line item as a product. Price and quantity come from the line. */
|
|
84
|
+
cartProduct?: (item: CartItem) => Omit<ShopifyProduct, "price" | "quantity"> | null;
|
|
85
|
+
/**
|
|
86
|
+
* The current cart's GID, read when the event is delivered. `addedToCart` reaches Shopify only with it. Leave it
|
|
87
|
+
* out when Hydrogen's `useCartAnalytics()` tracks the cart, or adds to cart are counted twice.
|
|
88
|
+
*/
|
|
89
|
+
getCartId?: () => string | null | undefined;
|
|
90
|
+
};
|
|
91
|
+
/**
|
|
92
|
+
* Maps `pageViewed`, and with the adapters for the project's items `itemViewed` and `addedToCart`, to Shopify's events.
|
|
93
|
+
* Spread it to add others, e.g. `search_viewed` or `collection_viewed`.
|
|
94
|
+
*
|
|
95
|
+
* @example
|
|
96
|
+
* ```ts
|
|
97
|
+
* shopifyMapping<Events>({
|
|
98
|
+
* product: ({ product, variant }) => ({
|
|
99
|
+
* id: product.id,
|
|
100
|
+
* title: product.title,
|
|
101
|
+
* vendor: product.vendor,
|
|
102
|
+
* variantId: variant.id,
|
|
103
|
+
* variantTitle: variant.title,
|
|
104
|
+
* price: variant.price,
|
|
105
|
+
* quantity: 1,
|
|
106
|
+
* }),
|
|
107
|
+
* cartProduct: toCartProduct,
|
|
108
|
+
* getCartId: () => cartStore.get().id,
|
|
109
|
+
* });
|
|
110
|
+
* ```
|
|
111
|
+
*/
|
|
112
|
+
declare const shopifyMapping: <Events extends EventMap, Context extends object = Record<never, never>>({ product: toProduct, cartProduct: toCartProduct, getCartId, }?: ShopifyMappingOptions<ShapesOf<Events>["item"], CartItemOf<Events>>) => AnalyticsMapping<Events, ShopifyEvents, Context>;
|
|
113
|
+
/**
|
|
114
|
+
* Publishes events to Hydrogen's analytics bus (`window.Shopify.analytics`), which `<ShopifyScripts>` from
|
|
115
|
+
* `@shopify/hydrogen/react` sets up and forwards to Shopify Analytics. Map it with `shopifyMapping<Events>()` and give it
|
|
116
|
+
* `withConsent(…, ["analytics"])`, as Hydrogen's bus only delivers with analytics consent. Consent reaches Shopify through
|
|
117
|
+
* `shopifyCustomerPrivacy()` from `@buildinams/core/consent/shopify`, or comes from it with `shopifyConsent()`. See
|
|
118
|
+
* `docs/analytics.md`.
|
|
119
|
+
*
|
|
120
|
+
* @example
|
|
121
|
+
* ```ts
|
|
122
|
+
* export const analytics = createAnalytics<Events>({
|
|
123
|
+
* events,
|
|
124
|
+
* consent: cookiebotConsent(),
|
|
125
|
+
* consentTargets: [shopifyCustomerPrivacy()],
|
|
126
|
+
* destinations: [
|
|
127
|
+
* withConsent(map(shopifyDestination(), shopifyMapping<Events>({ product: toShopifyProduct })), ["analytics"]),
|
|
128
|
+
* ],
|
|
129
|
+
* });
|
|
130
|
+
* ```
|
|
131
|
+
*/
|
|
132
|
+
declare const shopifyDestination: () => AnalyticsDestination<ShopifyEvents>;
|
|
133
|
+
|
|
134
|
+
type Money = {
|
|
135
|
+
amount: string;
|
|
136
|
+
currencyCode: string;
|
|
137
|
+
};
|
|
138
|
+
/** A Storefront API cart line, with the fields `toStorefrontCartSnapshot()` reads. */
|
|
139
|
+
type ShopifyStorefrontCartLine = {
|
|
140
|
+
id: string;
|
|
141
|
+
quantity: number;
|
|
142
|
+
cost: {
|
|
143
|
+
amountPerQuantity: Money;
|
|
144
|
+
};
|
|
145
|
+
merchandise: {
|
|
146
|
+
id: string;
|
|
147
|
+
title: string;
|
|
148
|
+
sku?: string | null;
|
|
149
|
+
product: {
|
|
150
|
+
id: string;
|
|
151
|
+
title: string;
|
|
152
|
+
vendor: string;
|
|
153
|
+
productType?: string | null;
|
|
154
|
+
};
|
|
155
|
+
};
|
|
156
|
+
};
|
|
157
|
+
/** A Storefront API cart, with lines as `nodes` or `edges`. */
|
|
158
|
+
type ShopifyStorefrontCart = {
|
|
159
|
+
id: string;
|
|
160
|
+
cost?: {
|
|
161
|
+
totalAmount?: Money;
|
|
162
|
+
subtotalAmount?: Money;
|
|
163
|
+
};
|
|
164
|
+
lines: {
|
|
165
|
+
nodes?: ShopifyStorefrontCartLine[];
|
|
166
|
+
edges?: {
|
|
167
|
+
node: ShopifyStorefrontCartLine;
|
|
168
|
+
}[];
|
|
169
|
+
};
|
|
170
|
+
};
|
|
171
|
+
/**
|
|
172
|
+
* A Storefront API cart as a `CartSnapshot` for `createCartChanges()`, with each line as the item, so `ga4Mapping()`'s
|
|
173
|
+
* `cartItem` and `shopifyMapping()`'s `cartProduct` read it as it came from Shopify.
|
|
174
|
+
*/
|
|
175
|
+
declare const toStorefrontCartSnapshot: (cart: ShopifyStorefrontCart) => CartSnapshot<ShopifyStorefrontCartLine>;
|
|
176
|
+
/** For `shopifyMapping()`'s `cartProduct`, with carts from `toStorefrontCartSnapshot()`. */
|
|
177
|
+
declare const toStorefrontCartProduct: ({ merchandise, }: ShopifyStorefrontCartLine) => {
|
|
178
|
+
id: string;
|
|
179
|
+
title: string;
|
|
180
|
+
vendor: string;
|
|
181
|
+
variantId: string;
|
|
182
|
+
variantTitle: string;
|
|
183
|
+
productType: string | undefined;
|
|
184
|
+
};
|
|
185
|
+
|
|
186
|
+
export { type ShopifyEvents, type ShopifyMappingOptions, type ShopifyProduct, type ShopifyStorefrontCart, type ShopifyStorefrontCartLine, shopifyDestination, shopifyMapping, toStorefrontCartProduct, toStorefrontCartSnapshot };
|