consentlayer 0.0.0-stage → 1.0.0-rc.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/LICENSE +21 -0
- package/README.md +88 -2
- package/dist/adapters/google-consent-mode.d.ts +20 -0
- package/dist/adapters/google-consent-mode.js +18 -0
- package/dist/adapters/google-consent-mode.js.map +1 -0
- package/dist/adapters/shopify.d.ts +49 -0
- package/dist/adapters/shopify.js +85 -0
- package/dist/adapters/shopify.js.map +1 -0
- package/dist/adapters/tcf.d.ts +20 -0
- package/dist/adapters/tcf.js +64 -0
- package/dist/adapters/tcf.js.map +1 -0
- package/dist/analytics-provider-07mxUgIK.d.ts +36 -0
- package/dist/angular.d.ts +56 -0
- package/dist/angular.js +120 -0
- package/dist/angular.js.map +1 -0
- package/dist/auto-tracking.d.ts +11 -0
- package/dist/auto-tracking.js +72 -0
- package/dist/auto-tracking.js.map +1 -0
- package/dist/chunk-7XLGT6U3.js +34 -0
- package/dist/chunk-7XLGT6U3.js.map +1 -0
- package/dist/chunk-BWPWDN4G.js +61 -0
- package/dist/chunk-BWPWDN4G.js.map +1 -0
- package/dist/chunk-DK45XPEY.js +77 -0
- package/dist/chunk-DK45XPEY.js.map +1 -0
- package/dist/chunk-ETU64ZV3.js +98 -0
- package/dist/chunk-ETU64ZV3.js.map +1 -0
- package/dist/chunk-FDJMIJFV.js +367 -0
- package/dist/chunk-FDJMIJFV.js.map +1 -0
- package/dist/chunk-HJGBVDPM.js +47 -0
- package/dist/chunk-HJGBVDPM.js.map +1 -0
- package/dist/chunk-J3KUPF4R.js +69 -0
- package/dist/chunk-J3KUPF4R.js.map +1 -0
- package/dist/chunk-LBEIEAJX.js +55 -0
- package/dist/chunk-LBEIEAJX.js.map +1 -0
- package/dist/chunk-LEAQJXJU.js +582 -0
- package/dist/chunk-LEAQJXJU.js.map +1 -0
- package/dist/chunk-R6I7OYQB.js +69 -0
- package/dist/chunk-R6I7OYQB.js.map +1 -0
- package/dist/chunk-RK2M57GW.js +54 -0
- package/dist/chunk-RK2M57GW.js.map +1 -0
- package/dist/chunk-RZ4TYRNY.js +128 -0
- package/dist/chunk-RZ4TYRNY.js.map +1 -0
- package/dist/chunk-TVSZJLRC.js +112 -0
- package/dist/chunk-TVSZJLRC.js.map +1 -0
- package/dist/chunk-TYOQYV45.js +79 -0
- package/dist/chunk-TYOQYV45.js.map +1 -0
- package/dist/chunk-UAIZU5WD.js +52 -0
- package/dist/chunk-UAIZU5WD.js.map +1 -0
- package/dist/chunk-VIIGBALD.js +14 -0
- package/dist/chunk-VIIGBALD.js.map +1 -0
- package/dist/consent-controller-DyHPNfyT.d.ts +47 -0
- package/dist/consentlayer-banner.global.js +20 -0
- package/dist/consentlayer.global.js +9 -0
- package/dist/create-analytics-Bzvm1UpE.d.ts +44 -0
- package/dist/ecommerce.d.ts +41 -0
- package/dist/ecommerce.js +43 -0
- package/dist/ecommerce.js.map +1 -0
- package/dist/index.d.ts +58 -0
- package/dist/index.js +42 -0
- package/dist/index.js.map +1 -0
- package/dist/next.d.ts +16 -0
- package/dist/next.js +27 -0
- package/dist/next.js.map +1 -0
- package/dist/nuxt/config.d.ts +7 -0
- package/dist/nuxt/config.js +6 -0
- package/dist/nuxt/config.js.map +1 -0
- package/dist/nuxt/index.d.ts +11 -0
- package/dist/nuxt/index.js +35 -0
- package/dist/nuxt/index.js.map +1 -0
- package/dist/nuxt/plugin.d.ts +5 -0
- package/dist/nuxt/plugin.js +24 -0
- package/dist/nuxt/plugin.js.map +1 -0
- package/dist/providers/contentsquare.d.ts +27 -0
- package/dist/providers/contentsquare.js +58 -0
- package/dist/providers/contentsquare.js.map +1 -0
- package/dist/providers/ga4.d.ts +46 -0
- package/dist/providers/ga4.js +3 -0
- package/dist/providers/ga4.js.map +1 -0
- package/dist/providers/gtm.d.ts +28 -0
- package/dist/providers/gtm.js +83 -0
- package/dist/providers/gtm.js.map +1 -0
- package/dist/providers/hotjar.d.ts +38 -0
- package/dist/providers/hotjar.js +68 -0
- package/dist/providers/hotjar.js.map +1 -0
- package/dist/providers/meta.d.ts +42 -0
- package/dist/providers/meta.js +65 -0
- package/dist/providers/meta.js.map +1 -0
- package/dist/providers/posthog.d.ts +42 -0
- package/dist/providers/posthog.js +91 -0
- package/dist/providers/posthog.js.map +1 -0
- package/dist/providers/sentry.d.ts +38 -0
- package/dist/providers/sentry.js +82 -0
- package/dist/providers/sentry.js.map +1 -0
- package/dist/providers/tiktok.d.ts +46 -0
- package/dist/providers/tiktok.js +72 -0
- package/dist/providers/tiktok.js.map +1 -0
- package/dist/providers/vercel.d.ts +14 -0
- package/dist/providers/vercel.js +17 -0
- package/dist/providers/vercel.js.map +1 -0
- package/dist/react/index.d.ts +78 -0
- package/dist/react/index.js +79 -0
- package/dist/react/index.js.map +1 -0
- package/dist/sentry/next.d.ts +1 -0
- package/dist/sentry/next.js +3 -0
- package/dist/sentry/next.js.map +1 -0
- package/dist/sentry/server.d.ts +24 -0
- package/dist/sentry/server.js +28 -0
- package/dist/sentry/server.js.map +1 -0
- package/dist/sentry-replay-HSFVEFCM.js +3 -0
- package/dist/sentry-replay-HSFVEFCM.js.map +1 -0
- package/dist/sentry-sdk-YQOGKGWD.js +3 -0
- package/dist/sentry-sdk-YQOGKGWD.js.map +1 -0
- package/dist/sentry-tracing-KWT7ITHF.js +3 -0
- package/dist/sentry-tracing-KWT7ITHF.js.map +1 -0
- package/dist/server/ga4.d.ts +15 -0
- package/dist/server/ga4.js +29 -0
- package/dist/server/ga4.js.map +1 -0
- package/dist/server/index.d.ts +116 -0
- package/dist/server/index.js +206 -0
- package/dist/server/index.js.map +1 -0
- package/dist/server/meta.d.ts +14 -0
- package/dist/server/meta.js +63 -0
- package/dist/server/meta.js.map +1 -0
- package/dist/server/sgtm.d.ts +18 -0
- package/dist/server/sgtm.js +34 -0
- package/dist/server/sgtm.js.map +1 -0
- package/dist/server/tiktok.d.ts +15 -0
- package/dist/server/tiktok.js +64 -0
- package/dist/server/tiktok.js.map +1 -0
- package/dist/solid.d.ts +59 -0
- package/dist/solid.js +3 -0
- package/dist/solid.js.map +1 -0
- package/dist/solidstart.d.ts +16 -0
- package/dist/solidstart.js +22 -0
- package/dist/solidstart.js.map +1 -0
- package/dist/svelte/GtmNoscript.svelte +18 -0
- package/dist/svelte/MetaPixelNoscript.svelte +18 -0
- package/dist/svelte.d.ts +55 -0
- package/dist/svelte.js +3 -0
- package/dist/svelte.js.map +1 -0
- package/dist/sveltekit.d.ts +18 -0
- package/dist/sveltekit.js +16 -0
- package/dist/sveltekit.js.map +1 -0
- package/dist/testing.d.ts +11 -0
- package/dist/testing.js +50 -0
- package/dist/testing.js.map +1 -0
- package/dist/types-BV_AFZKQ.d.ts +407 -0
- package/dist/vue.d.ts +92 -0
- package/dist/vue.js +97 -0
- package/dist/vue.js.map +1 -0
- package/package.json +281 -4
|
@@ -0,0 +1,367 @@
|
|
|
1
|
+
import { createLocalStorageConsentStorage, createAnalyticsManager, createLocalStorageIdentityStorage } from './chunk-LEAQJXJU.js';
|
|
2
|
+
import { createDebugLog, BUILT_IN_CATEGORIES, normalizeConsent, equalConsent, broadcastConsent } from './chunk-TVSZJLRC.js';
|
|
3
|
+
import { assertHttpsOrRelativeUrl, assertValidConsent } from './chunk-J3KUPF4R.js';
|
|
4
|
+
import { configureNames, withLegacy, getNames } from './chunk-R6I7OYQB.js';
|
|
5
|
+
|
|
6
|
+
// src/core/consent-engine.ts
|
|
7
|
+
function createConsentEngine(options = {}) {
|
|
8
|
+
const debugLog = createDebugLog(options.debug);
|
|
9
|
+
const categories = [...BUILT_IN_CATEGORIES, ...options.categories ?? []];
|
|
10
|
+
const normalize = (consent) => normalizeConsent(consent, categories);
|
|
11
|
+
let current = normalize(options.initialConsent);
|
|
12
|
+
const listeners = /* @__PURE__ */ new Set();
|
|
13
|
+
const cleanup = /* @__PURE__ */ new Set();
|
|
14
|
+
const adapters = options.adapters ?? [];
|
|
15
|
+
const storage = options.storage;
|
|
16
|
+
let destroyed = false;
|
|
17
|
+
let bootstrapping;
|
|
18
|
+
const policyVersion = options.policyVersion?.trim() || void 0;
|
|
19
|
+
const { maxAgeDays } = options;
|
|
20
|
+
if (maxAgeDays !== void 0 && !(maxAgeDays > 0 && Number.isFinite(maxAgeDays))) {
|
|
21
|
+
throw new Error(`[consentlayer] consent.maxAgeDays must be a positive number, received ${maxAgeDays}`);
|
|
22
|
+
}
|
|
23
|
+
const major = (version) => version?.split(".")[0];
|
|
24
|
+
const isStaleRecord = (record) => policyVersion !== void 0 && major(record.policyVersion) !== major(policyVersion) || maxAgeDays !== void 0 && !(typeof record.updatedAt === "number" && Date.now() - record.updatedAt <= maxAgeDays * 864e5);
|
|
25
|
+
let stale = false;
|
|
26
|
+
let changes = 0;
|
|
27
|
+
const notify = () => {
|
|
28
|
+
const snapshot = { consent: current };
|
|
29
|
+
listeners.forEach((listener) => listener(snapshot));
|
|
30
|
+
};
|
|
31
|
+
const commit = async (next, source, {
|
|
32
|
+
persist,
|
|
33
|
+
applyAdapters,
|
|
34
|
+
excludeAdapter
|
|
35
|
+
}) => {
|
|
36
|
+
if (destroyed || !next || equalConsent(current, next) && !(persist && stale)) return;
|
|
37
|
+
current = next;
|
|
38
|
+
changes++;
|
|
39
|
+
if (persist) {
|
|
40
|
+
stale = false;
|
|
41
|
+
storage?.write({ consent: next, updatedAt: Date.now(), policyVersion });
|
|
42
|
+
}
|
|
43
|
+
broadcastConsent(next, options.broadcast);
|
|
44
|
+
debugLog("consent", void 0, "consent updated", source, next);
|
|
45
|
+
notify();
|
|
46
|
+
if (!applyAdapters) return;
|
|
47
|
+
await Promise.all(
|
|
48
|
+
adapters.filter((adapter) => adapter.apply && adapter !== excludeAdapter).map(async (adapter) => {
|
|
49
|
+
try {
|
|
50
|
+
await adapter.apply?.(next);
|
|
51
|
+
} catch (cause) {
|
|
52
|
+
options.onError?.({
|
|
53
|
+
scope: "consent-adapter",
|
|
54
|
+
providerId: adapter.id,
|
|
55
|
+
message: `Consent adapter "${adapter.id}" failed to apply consent`,
|
|
56
|
+
cause
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
})
|
|
60
|
+
);
|
|
61
|
+
};
|
|
62
|
+
if (storage?.subscribe) {
|
|
63
|
+
cleanup.add(
|
|
64
|
+
storage.subscribe(() => {
|
|
65
|
+
const record = storage.read();
|
|
66
|
+
const next = normalize(record?.consent);
|
|
67
|
+
if (next && !isStaleRecord(record)) {
|
|
68
|
+
stale = false;
|
|
69
|
+
void commit(next, "storage", { persist: false, applyAdapters: false });
|
|
70
|
+
}
|
|
71
|
+
})
|
|
72
|
+
);
|
|
73
|
+
}
|
|
74
|
+
for (const adapter of adapters) {
|
|
75
|
+
if (!adapter.observe) continue;
|
|
76
|
+
const observe = (consent) => {
|
|
77
|
+
try {
|
|
78
|
+
adapter.observe?.(consent);
|
|
79
|
+
} catch (cause) {
|
|
80
|
+
options.onError?.({
|
|
81
|
+
scope: "consent-adapter",
|
|
82
|
+
providerId: adapter.id,
|
|
83
|
+
message: `Consent adapter "${adapter.id}" failed to observe consent`,
|
|
84
|
+
cause
|
|
85
|
+
});
|
|
86
|
+
}
|
|
87
|
+
};
|
|
88
|
+
observe(current);
|
|
89
|
+
listeners.add(({ consent }) => observe(consent));
|
|
90
|
+
}
|
|
91
|
+
for (const adapter of adapters) {
|
|
92
|
+
if (!adapter.subscribe) continue;
|
|
93
|
+
const unsubscribe = adapter.subscribe((next) => {
|
|
94
|
+
void commit(normalize({ ...current, ...next }), "adapter", {
|
|
95
|
+
persist: true,
|
|
96
|
+
applyAdapters: true,
|
|
97
|
+
excludeAdapter: adapter
|
|
98
|
+
});
|
|
99
|
+
});
|
|
100
|
+
if (typeof unsubscribe === "function") {
|
|
101
|
+
cleanup.add(unsubscribe);
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
const runBootstrap = async () => {
|
|
105
|
+
const record = storage?.read() ?? null;
|
|
106
|
+
const stored = normalize(record?.consent);
|
|
107
|
+
stale = stored !== null && isStaleRecord(record);
|
|
108
|
+
if (stale) debugLog("consent", void 0, "stored consent is stale", record);
|
|
109
|
+
const seen = changes;
|
|
110
|
+
const ask = async (adapter) => {
|
|
111
|
+
try {
|
|
112
|
+
return normalize(await adapter.bootstrap?.());
|
|
113
|
+
} catch (cause) {
|
|
114
|
+
options.onError?.({
|
|
115
|
+
scope: "consent-adapter",
|
|
116
|
+
providerId: adapter.id,
|
|
117
|
+
message: `Consent adapter "${adapter.id}" failed to read consent`,
|
|
118
|
+
cause
|
|
119
|
+
});
|
|
120
|
+
return null;
|
|
121
|
+
}
|
|
122
|
+
};
|
|
123
|
+
const ordered = [
|
|
124
|
+
...adapters.filter((adapter) => adapter.authoritative),
|
|
125
|
+
void 0,
|
|
126
|
+
...adapters.filter((adapter) => !adapter.authoritative)
|
|
127
|
+
];
|
|
128
|
+
for (const adapter of ordered) {
|
|
129
|
+
if (adapter && !adapter.bootstrap) continue;
|
|
130
|
+
const next = adapter ? await ask(adapter) : stored && (!stale || options.onStale === "keep") ? stored : null;
|
|
131
|
+
if (changes !== seen) return current;
|
|
132
|
+
if (!next) continue;
|
|
133
|
+
await commit(next, "bootstrap", { persist: adapter !== void 0, applyAdapters: false, excludeAdapter: adapter });
|
|
134
|
+
return current;
|
|
135
|
+
}
|
|
136
|
+
notify();
|
|
137
|
+
return current;
|
|
138
|
+
};
|
|
139
|
+
return {
|
|
140
|
+
categories,
|
|
141
|
+
gpc: options.gpc !== false && typeof navigator !== "undefined" && navigator.globalPrivacyControl === true,
|
|
142
|
+
bootstrap() {
|
|
143
|
+
bootstrapping ??= runBootstrap();
|
|
144
|
+
return bootstrapping;
|
|
145
|
+
},
|
|
146
|
+
getConsent() {
|
|
147
|
+
return current;
|
|
148
|
+
},
|
|
149
|
+
isStale() {
|
|
150
|
+
return stale;
|
|
151
|
+
},
|
|
152
|
+
async setConsent(consent, source = "imperative") {
|
|
153
|
+
assertValidConsent(consent, categories);
|
|
154
|
+
await commit(normalize(consent), source, {
|
|
155
|
+
persist: true,
|
|
156
|
+
applyAdapters: true
|
|
157
|
+
});
|
|
158
|
+
},
|
|
159
|
+
subscribe(listener) {
|
|
160
|
+
listeners.add(listener);
|
|
161
|
+
return () => listeners.delete(listener);
|
|
162
|
+
},
|
|
163
|
+
destroy() {
|
|
164
|
+
destroyed = true;
|
|
165
|
+
cleanup.forEach((fn) => fn());
|
|
166
|
+
cleanup.clear();
|
|
167
|
+
listeners.clear();
|
|
168
|
+
}
|
|
169
|
+
};
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
// src/core/create-analytics.ts
|
|
173
|
+
var latest;
|
|
174
|
+
var latestAnalytics = () => latest?.analytics;
|
|
175
|
+
function createAnalytics(options = {}) {
|
|
176
|
+
const { consent: consentOptions, categories, names, ...managerOptions } = options;
|
|
177
|
+
configureNames(names);
|
|
178
|
+
const consentEndpoint = options.server?.consentEndpoint ? assertHttpsOrRelativeUrl(options.server.consentEndpoint, "server.consentEndpoint") : void 0;
|
|
179
|
+
const recorder = {
|
|
180
|
+
id: "consent-endpoint",
|
|
181
|
+
async apply(consent) {
|
|
182
|
+
const response = await fetch(consentEndpoint, {
|
|
183
|
+
method: "POST",
|
|
184
|
+
headers: { "content-type": "application/json" },
|
|
185
|
+
body: JSON.stringify({ consent })
|
|
186
|
+
});
|
|
187
|
+
if (!response.ok) throw new Error(`HTTP ${response.status}`);
|
|
188
|
+
}
|
|
189
|
+
};
|
|
190
|
+
const engine = createConsentEngine({
|
|
191
|
+
storage: createLocalStorageConsentStorage({ key: getNames().consentStorageKey }),
|
|
192
|
+
broadcast: {
|
|
193
|
+
eventName: withLegacy((n) => n.consentEventName),
|
|
194
|
+
globalKey: withLegacy((n) => n.consentGlobalKey)
|
|
195
|
+
},
|
|
196
|
+
...consentOptions,
|
|
197
|
+
adapters: [...consentOptions?.adapters ?? [], ...consentEndpoint ? [recorder] : []],
|
|
198
|
+
categories,
|
|
199
|
+
debug: options.debug,
|
|
200
|
+
onError: options.onError
|
|
201
|
+
});
|
|
202
|
+
const manager = createAnalyticsManager({
|
|
203
|
+
identityStorage: createLocalStorageIdentityStorage({
|
|
204
|
+
key: getNames().identityStorageKey
|
|
205
|
+
}),
|
|
206
|
+
...managerOptions,
|
|
207
|
+
consentEngine: engine
|
|
208
|
+
});
|
|
209
|
+
const analytics = {
|
|
210
|
+
boot: manager.boot,
|
|
211
|
+
registerProvider: manager.registerProvider,
|
|
212
|
+
page: manager.page,
|
|
213
|
+
track: manager.track,
|
|
214
|
+
identify: manager.identify,
|
|
215
|
+
reset: manager.reset,
|
|
216
|
+
group: manager.group,
|
|
217
|
+
captureError: manager.captureError,
|
|
218
|
+
newPageSession: manager.newPageSession,
|
|
219
|
+
getSnapshot: manager.getSnapshot,
|
|
220
|
+
subscribe: manager.subscribe,
|
|
221
|
+
consent: {
|
|
222
|
+
categories: engine.categories,
|
|
223
|
+
gpc: engine.gpc,
|
|
224
|
+
bootstrap: engine.bootstrap,
|
|
225
|
+
getConsent: engine.getConsent,
|
|
226
|
+
isStale: engine.isStale,
|
|
227
|
+
setConsent: engine.setConsent,
|
|
228
|
+
subscribe: engine.subscribe,
|
|
229
|
+
async update(partial) {
|
|
230
|
+
await engine.bootstrap();
|
|
231
|
+
const denied = Object.fromEntries(engine.categories.map((category) => [category, false]));
|
|
232
|
+
await engine.setConsent({ ...denied, ...engine.getConsent(), ...partial });
|
|
233
|
+
}
|
|
234
|
+
},
|
|
235
|
+
destroy() {
|
|
236
|
+
manager.destroy();
|
|
237
|
+
engine.destroy();
|
|
238
|
+
if (latest?.analytics === analytics) latest = void 0;
|
|
239
|
+
}
|
|
240
|
+
};
|
|
241
|
+
const namesKey = JSON.stringify(names ?? {});
|
|
242
|
+
if (latest && latest.names !== namesKey) {
|
|
243
|
+
console.warn(
|
|
244
|
+
"[consentlayer] createAnalytics() was called again with different names. Storage keys, globals and events are per page, so the last names win: create one instance per page."
|
|
245
|
+
);
|
|
246
|
+
}
|
|
247
|
+
latest = { analytics, names: namesKey };
|
|
248
|
+
return analytics;
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
// src/core/consent-controller.ts
|
|
252
|
+
function scheduleBoot(analytics, boot) {
|
|
253
|
+
const mode = boot?.mode ?? "immediate";
|
|
254
|
+
if (mode === "manual") return () => void 0;
|
|
255
|
+
if (mode === "immediate" || typeof window === "undefined") {
|
|
256
|
+
void analytics.boot();
|
|
257
|
+
return () => void 0;
|
|
258
|
+
}
|
|
259
|
+
if ("requestIdleCallback" in window) {
|
|
260
|
+
const handle = window.requestIdleCallback(() => void analytics.boot(), { timeout: boot?.timeoutMs ?? 3e3 });
|
|
261
|
+
return () => window.cancelIdleCallback?.(handle);
|
|
262
|
+
}
|
|
263
|
+
const timeout = globalThis.setTimeout(() => void analytics.boot(), boot?.timeoutMs ?? 1500);
|
|
264
|
+
return () => globalThis.clearTimeout(timeout);
|
|
265
|
+
}
|
|
266
|
+
function createConsentController(analytics, { boot, autoShowBanner = true, openConsentGlobalKey } = {}) {
|
|
267
|
+
const engine = analytics.consent;
|
|
268
|
+
const all = (granted) => Object.fromEntries(
|
|
269
|
+
engine.categories.map((category) => [category, typeof granted === "function" ? granted(category) : granted])
|
|
270
|
+
);
|
|
271
|
+
let state = {
|
|
272
|
+
ready: false,
|
|
273
|
+
error: null,
|
|
274
|
+
bannerVisible: false,
|
|
275
|
+
dialogVisible: false,
|
|
276
|
+
categories: engine.categories,
|
|
277
|
+
gpc: engine.gpc,
|
|
278
|
+
prefs: all(false)
|
|
279
|
+
};
|
|
280
|
+
const listeners = /* @__PURE__ */ new Set();
|
|
281
|
+
const set = (partial) => {
|
|
282
|
+
state = { ...state, ...partial };
|
|
283
|
+
listeners.forEach((listener) => listener());
|
|
284
|
+
};
|
|
285
|
+
let routeKey = null;
|
|
286
|
+
let sessionRouteKey;
|
|
287
|
+
let page;
|
|
288
|
+
let paged = { routeKey: null, providers: /* @__PURE__ */ new Set(), forwarded: false };
|
|
289
|
+
const sendPageViews = () => {
|
|
290
|
+
const snapshot = analytics.getSnapshot();
|
|
291
|
+
if (!snapshot.booted || routeKey === null) return;
|
|
292
|
+
if (paged.routeKey !== routeKey) paged = { routeKey, providers: /* @__PURE__ */ new Set(), forwarded: false };
|
|
293
|
+
const fresh = snapshot.activeProviders.filter((id) => !paged.providers.has(id));
|
|
294
|
+
const server = !paged.forwarded && Boolean(snapshot.consent?.analytics || snapshot.consent?.marketing);
|
|
295
|
+
if (!fresh.length && !server) return;
|
|
296
|
+
fresh.forEach((id) => paged.providers.add(id));
|
|
297
|
+
if (server) paged.forwarded = true;
|
|
298
|
+
void analytics.page(page, { only: fresh, server });
|
|
299
|
+
};
|
|
300
|
+
const applyAndClose = async (consent) => {
|
|
301
|
+
await engine.setConsent(consent);
|
|
302
|
+
set({ bannerVisible: false, dialogVisible: false });
|
|
303
|
+
};
|
|
304
|
+
return {
|
|
305
|
+
getSnapshot: () => state,
|
|
306
|
+
subscribe(listener) {
|
|
307
|
+
listeners.add(listener);
|
|
308
|
+
return () => listeners.delete(listener);
|
|
309
|
+
},
|
|
310
|
+
start() {
|
|
311
|
+
let stopped = false;
|
|
312
|
+
void engine.bootstrap().then((consent) => {
|
|
313
|
+
if (stopped) return;
|
|
314
|
+
set({
|
|
315
|
+
...consent && { prefs: all((category) => consent[category]) },
|
|
316
|
+
bannerVisible: consent && !engine.isStale() ? false : autoShowBanner
|
|
317
|
+
});
|
|
318
|
+
}).catch((error) => {
|
|
319
|
+
if (!stopped) set({ error: String(error), bannerVisible: autoShowBanner });
|
|
320
|
+
}).finally(() => {
|
|
321
|
+
if (!stopped) set({ ready: true });
|
|
322
|
+
});
|
|
323
|
+
const cancelBoot = scheduleBoot(analytics, boot);
|
|
324
|
+
const syncPrefs = (consent) => {
|
|
325
|
+
if (consent) set({ prefs: all((category) => consent[category]) });
|
|
326
|
+
};
|
|
327
|
+
syncPrefs(engine.getConsent());
|
|
328
|
+
const unsubscribeConsent = engine.subscribe(({ consent }) => syncPrefs(consent));
|
|
329
|
+
const unsubscribeAnalytics = analytics.subscribe(sendPageViews);
|
|
330
|
+
sendPageViews();
|
|
331
|
+
const globals = typeof window === "undefined" ? {} : window;
|
|
332
|
+
const openConsent = () => set({ dialogVisible: true });
|
|
333
|
+
const keys = openConsentGlobalKey ? [openConsentGlobalKey] : withLegacy((names) => names.openConsentGlobalKey);
|
|
334
|
+
for (const key of keys) globals[key] = openConsent;
|
|
335
|
+
return () => {
|
|
336
|
+
stopped = true;
|
|
337
|
+
for (const key of keys) if (globals[key] === openConsent) delete globals[key];
|
|
338
|
+
cancelBoot();
|
|
339
|
+
unsubscribeConsent();
|
|
340
|
+
unsubscribeAnalytics();
|
|
341
|
+
};
|
|
342
|
+
},
|
|
343
|
+
setRoute(nextRouteKey, nextPage) {
|
|
344
|
+
routeKey = nextRouteKey;
|
|
345
|
+
page = nextPage;
|
|
346
|
+
sendPageViews();
|
|
347
|
+
if (nextRouteKey === null || sessionRouteKey === nextRouteKey) return;
|
|
348
|
+
const known = sessionRouteKey !== void 0 && sessionRouteKey !== null;
|
|
349
|
+
sessionRouteKey = nextRouteKey;
|
|
350
|
+
if (known) analytics.newPageSession();
|
|
351
|
+
},
|
|
352
|
+
actions: {
|
|
353
|
+
setPrefs: (prefs) => set({ prefs }),
|
|
354
|
+
showBanner: () => set({ bannerVisible: true }),
|
|
355
|
+
hideBanner: () => set({ bannerVisible: false }),
|
|
356
|
+
openDialog: () => set({ dialogVisible: true }),
|
|
357
|
+
closeDialog: () => set({ dialogVisible: false }),
|
|
358
|
+
acceptAll: () => applyAndClose(all((category) => !(engine.gpc && category === "marketing"))),
|
|
359
|
+
rejectNonEssential: () => applyAndClose(all(false)),
|
|
360
|
+
savePreferences: () => applyAndClose(all((category) => state.prefs[category]))
|
|
361
|
+
}
|
|
362
|
+
};
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
export { createAnalytics, createConsentController, latestAnalytics };
|
|
366
|
+
//# sourceMappingURL=chunk-FDJMIJFV.js.map
|
|
367
|
+
//# sourceMappingURL=chunk-FDJMIJFV.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/core/consent-engine.ts","../src/core/create-analytics.ts","../src/core/consent-controller.ts"],"names":[],"mappings":"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","file":"chunk-FDJMIJFV.js","sourcesContent":["import type {\n ConsentChangeSource,\n ConsentEngine,\n ConsentEngineOptions,\n ConsentEngineSnapshot,\n ConsentIntegrationAdapter,\n ConsentState,\n StoredConsent,\n} from './types'\nimport { BUILT_IN_CATEGORIES, broadcastConsent, createDebugLog, equalConsent, normalizeConsent } from './utils'\nimport { assertValidConsent } from './validation'\n\nexport function createConsentEngine(options: ConsentEngineOptions = {}): ConsentEngine {\n const debugLog = createDebugLog(options.debug)\n const categories = [...BUILT_IN_CATEGORIES, ...(options.categories ?? [])]\n const normalize = (consent: Partial<ConsentState> | null | undefined) => normalizeConsent(consent, categories)\n let current = normalize(options.initialConsent)\n const listeners = new Set<(snapshot: ConsentEngineSnapshot) => void>()\n const cleanup = new Set<() => void>()\n const adapters = options.adapters ?? []\n const storage = options.storage\n let destroyed = false\n let bootstrapping: Promise<ConsentState | null> | undefined\n const policyVersion = options.policyVersion?.trim() || undefined\n const { maxAgeDays } = options\n if (maxAgeDays !== undefined && !(maxAgeDays > 0 && Number.isFinite(maxAgeDays))) {\n throw new Error(`[consentlayer] consent.maxAgeDays must be a positive number, received ${maxAgeDays}`)\n }\n const major = (version: string | undefined) => version?.split('.')[0]\n const isStaleRecord = (record: StoredConsent) =>\n (policyVersion !== undefined && major(record.policyVersion) !== major(policyVersion)) ||\n (maxAgeDays !== undefined &&\n !(typeof record.updatedAt === 'number' && Date.now() - record.updatedAt <= maxAgeDays * 86_400_000))\n let stale = false\n let changes = 0\n\n const notify = () => {\n const snapshot = { consent: current }\n listeners.forEach((listener) => listener(snapshot))\n }\n\n const commit = async (\n next: ConsentState | null,\n source: ConsentChangeSource,\n {\n persist,\n applyAdapters,\n excludeAdapter,\n }: {\n persist: boolean\n applyAdapters: boolean\n excludeAdapter?: ConsentIntegrationAdapter\n },\n ) => {\n if (destroyed || !next || (equalConsent(current, next) && !(persist && stale))) return\n current = next\n changes++\n if (persist) {\n stale = false\n storage?.write({ consent: next, updatedAt: Date.now(), policyVersion })\n }\n broadcastConsent(next, options.broadcast)\n debugLog('consent', undefined, 'consent updated', source, next)\n notify()\n if (!applyAdapters) return\n await Promise.all(\n adapters\n .filter((adapter) => adapter.apply && adapter !== excludeAdapter)\n .map(async (adapter) => {\n try {\n await adapter.apply?.(next)\n } catch (cause) {\n options.onError?.({\n scope: 'consent-adapter',\n providerId: adapter.id,\n message: `Consent adapter \"${adapter.id}\" failed to apply consent`,\n cause,\n })\n }\n }),\n )\n }\n\n if (storage?.subscribe) {\n cleanup.add(\n storage.subscribe(() => {\n const record = storage.read()\n const next = normalize(record?.consent)\n if (next && !isStaleRecord(record!)) {\n stale = false\n void commit(next, 'storage', { persist: false, applyAdapters: false })\n }\n }),\n )\n }\n\n for (const adapter of adapters) {\n if (!adapter.observe) continue\n const observe = (consent: ConsentState | null) => {\n try {\n adapter.observe?.(consent)\n } catch (cause) {\n options.onError?.({\n scope: 'consent-adapter',\n providerId: adapter.id,\n message: `Consent adapter \"${adapter.id}\" failed to observe consent`,\n cause,\n })\n }\n }\n observe(current)\n listeners.add(({ consent }) => observe(consent))\n }\n\n for (const adapter of adapters) {\n if (!adapter.subscribe) continue\n const unsubscribe = adapter.subscribe((next) => {\n void commit(normalize({ ...current, ...next }), 'adapter', {\n persist: true,\n applyAdapters: true,\n excludeAdapter: adapter,\n })\n })\n if (typeof unsubscribe === 'function') {\n cleanup.add(unsubscribe)\n }\n }\n\n const runBootstrap = async () => {\n const record = storage?.read() ?? null\n const stored = normalize(record?.consent)\n stale = stored !== null && isStaleRecord(record!)\n if (stale) debugLog('consent', undefined, 'stored consent is stale', record)\n const seen = changes\n const ask = async (adapter: ConsentIntegrationAdapter) => {\n try {\n return normalize(await adapter.bootstrap?.())\n } catch (cause) {\n options.onError?.({\n scope: 'consent-adapter',\n providerId: adapter.id,\n message: `Consent adapter \"${adapter.id}\" failed to read consent`,\n cause,\n })\n return null\n }\n }\n const ordered = [\n ...adapters.filter((adapter) => adapter.authoritative),\n undefined,\n ...adapters.filter((adapter) => !adapter.authoritative),\n ]\n for (const adapter of ordered) {\n if (adapter && !adapter.bootstrap) continue\n const next = adapter ? await ask(adapter) : stored && (!stale || options.onStale === 'keep') ? stored : null\n if (changes !== seen) return current\n if (!next) continue\n await commit(next, 'bootstrap', { persist: adapter !== undefined, applyAdapters: false, excludeAdapter: adapter })\n return current\n }\n\n notify()\n return current\n }\n\n return {\n categories,\n gpc:\n options.gpc !== false &&\n typeof navigator !== 'undefined' &&\n (navigator as { globalPrivacyControl?: boolean }).globalPrivacyControl === true,\n bootstrap() {\n bootstrapping ??= runBootstrap()\n return bootstrapping\n },\n getConsent() {\n return current\n },\n isStale() {\n return stale\n },\n async setConsent(consent, source = 'imperative') {\n assertValidConsent(consent, categories)\n await commit(normalize(consent), source, {\n persist: true,\n applyAdapters: true,\n })\n },\n subscribe(listener) {\n listeners.add(listener)\n return () => listeners.delete(listener)\n },\n destroy() {\n destroyed = true\n cleanup.forEach((fn) => fn())\n cleanup.clear()\n listeners.clear()\n },\n }\n}\n","import { createAnalyticsManager } from './analytics-manager'\nimport { createConsentEngine } from './consent-engine'\nimport { configureNames, getNames, withLegacy, type NamesInput } from './names'\nimport { createLocalStorageConsentStorage, createLocalStorageIdentityStorage } from './storage'\nimport type {\n Analytics,\n AnalyticsManagerOptions,\n ConsentEngineOptions,\n ConsentIntegrationAdapter,\n ConsentState,\n} from './types'\nimport { assertHttpsOrRelativeUrl } from './validation'\n\n/** Options for `createAnalytics`. */\nexport type AnalyticsOptions = Omit<AnalyticsManagerOptions, 'consent' | 'consentEngine'> & {\n /** Storage, CMP integrations (e.g. Shopify Customer Privacy) and broadcast. Defaults to localStorage. */\n consent?: Omit<ConsentEngineOptions, 'debug' | 'categories' | 'onError'>\n /** Your own categories, declared in `ConsentCategories`. Stored, broadcast and sent to the server like the built-in ones. */\n categories?: ConsentEngineOptions['categories']\n /** Renames storage keys, globals and DOM events. */\n names?: NamesInput\n}\n\nlet latest: { analytics: Analytics; names: string } | undefined\n\n/** The most recent `createAnalytics()` instance, for hooks used outside `<AnalyticsProvider>`. */\nexport const latestAnalytics = (): Analytics | undefined => latest?.analytics\n\n/** Creates the analytics instance. Nothing loads until `boot()` (which `<AnalyticsProvider>` calls) and then only what consent allows. */\nexport function createAnalytics(options: AnalyticsOptions = {}): Analytics {\n const { consent: consentOptions, categories, names, ...managerOptions } = options\n configureNames(names)\n\n const consentEndpoint = options.server?.consentEndpoint\n ? assertHttpsOrRelativeUrl(options.server.consentEndpoint, 'server.consentEndpoint')\n : undefined\n const recorder: ConsentIntegrationAdapter = {\n id: 'consent-endpoint',\n async apply(consent) {\n const response = await fetch(consentEndpoint!, {\n method: 'POST',\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify({ consent }),\n })\n if (!response.ok) throw new Error(`HTTP ${response.status}`)\n },\n }\n\n const engine = createConsentEngine({\n storage: createLocalStorageConsentStorage({ key: getNames().consentStorageKey }),\n broadcast: {\n eventName: withLegacy((n) => n.consentEventName),\n globalKey: withLegacy((n) => n.consentGlobalKey),\n },\n ...consentOptions,\n adapters: [...(consentOptions?.adapters ?? []), ...(consentEndpoint ? [recorder] : [])],\n categories,\n debug: options.debug,\n onError: options.onError,\n })\n const manager = createAnalyticsManager({\n identityStorage: createLocalStorageIdentityStorage({\n key: getNames().identityStorageKey,\n }),\n ...managerOptions,\n consentEngine: engine,\n })\n\n const analytics: Analytics = {\n boot: manager.boot,\n registerProvider: manager.registerProvider,\n page: manager.page,\n track: manager.track,\n identify: manager.identify,\n reset: manager.reset,\n group: manager.group,\n captureError: manager.captureError,\n newPageSession: manager.newPageSession,\n getSnapshot: manager.getSnapshot,\n subscribe: manager.subscribe,\n consent: {\n categories: engine.categories,\n gpc: engine.gpc,\n bootstrap: engine.bootstrap,\n getConsent: engine.getConsent,\n isStale: engine.isStale,\n setConsent: engine.setConsent,\n subscribe: engine.subscribe,\n async update(partial) {\n await engine.bootstrap()\n const denied = Object.fromEntries(engine.categories.map((category) => [category, false]))\n await engine.setConsent({ ...denied, ...engine.getConsent(), ...partial } as ConsentState)\n },\n },\n destroy() {\n manager.destroy()\n engine.destroy()\n if (latest?.analytics === analytics) latest = undefined\n },\n }\n const namesKey = JSON.stringify(names ?? {})\n if (latest && latest.names !== namesKey) {\n console.warn(\n '[consentlayer] createAnalytics() was called again with different names. Storage keys, globals and events are per page, so the last names win: create one instance per page.',\n )\n }\n latest = { analytics, names: namesKey }\n return analytics\n}\n","import { withLegacy } from './names'\nimport type { Analytics, ConsentCategory, ConsentState, LazyBootConfig, PageViewPayload } from './types'\n\ntype Prefs = Record<ConsentCategory, boolean>\n\n/** The state your consent UI renders. */\nexport type ConsentControllerState = {\n ready: boolean\n error: string | null\n bannerVisible: boolean\n dialogVisible: boolean\n /** The categories the dialog shows, from `analytics.consent.categories`. */\n categories: readonly ConsentCategory[]\n /** Whether the browser's Global Privacy Control signal is honored: `acceptAll` then leaves marketing off. */\n gpc: boolean\n /** The dialog's toggles, one per category in `analytics.consent.categories`. */\n prefs: Prefs\n}\n\n/** Actions for your consent UI. */\nexport type ConsentControllerActions = {\n setPrefs: (next: Prefs) => void\n showBanner: () => void\n hideBanner: () => void\n openDialog: () => void\n closeDialog: () => void\n acceptAll: () => Promise<void>\n rejectNonEssential: () => Promise<void>\n savePreferences: () => Promise<void>\n}\n\nexport type ConsentControllerOptions = {\n /** When providers load. Defaults to `{ mode: 'immediate' }`. */\n boot?: LazyBootConfig\n autoShowBanner?: boolean\n /** Defaults to the configured name (plus `ACM_openConsent` with `names.legacy`). */\n openConsentGlobalKey?: string\n}\n\nexport type ConsentController = {\n actions: ConsentControllerActions\n getSnapshot: () => ConsentControllerState\n subscribe: (listener: () => void) => () => void\n /** Bootstraps consent, schedules boot and starts sending page views. Returns a function that stops it. */\n start: () => () => void\n /** Changes on every navigation; a page view is sent once per key. `null` holds page views until the route is known. */\n setRoute: (routeKey: string | null, page?: PageViewPayload) => void\n}\n\nfunction scheduleBoot(analytics: Analytics, boot: LazyBootConfig | undefined): () => void {\n const mode = boot?.mode ?? 'immediate'\n if (mode === 'manual') return () => undefined\n if (mode === 'immediate' || typeof window === 'undefined') {\n void analytics.boot()\n return () => undefined\n }\n if ('requestIdleCallback' in window) {\n const handle = window.requestIdleCallback(() => void analytics.boot(), { timeout: boot?.timeoutMs ?? 3000 })\n return () => window.cancelIdleCallback?.(handle)\n }\n const timeout = globalThis.setTimeout(() => void analytics.boot(), boot?.timeoutMs ?? 1500)\n return () => globalThis.clearTimeout(timeout)\n}\n\n/** Boots `analytics`, sends a page view per route and holds the state of a consent banner and settings dialog. */\nexport function createConsentController(\n analytics: Analytics,\n { boot, autoShowBanner = true, openConsentGlobalKey }: ConsentControllerOptions = {},\n): ConsentController {\n const engine = analytics.consent\n const all = (granted: boolean | ((category: ConsentCategory) => boolean)) =>\n Object.fromEntries(\n engine.categories.map((category) => [category, typeof granted === 'function' ? granted(category) : granted]),\n ) as Prefs\n let state: ConsentControllerState = {\n ready: false,\n error: null,\n bannerVisible: false,\n dialogVisible: false,\n categories: engine.categories,\n gpc: engine.gpc,\n prefs: all(false),\n }\n const listeners = new Set<() => void>()\n const set = (partial: Partial<ConsentControllerState>) => {\n state = { ...state, ...partial }\n listeners.forEach((listener) => listener())\n }\n\n let routeKey: string | null = null\n let sessionRouteKey: string | null | undefined\n let page: PageViewPayload | undefined\n let paged = { routeKey: null as string | null, providers: new Set<string>(), forwarded: false }\n const sendPageViews = () => {\n const snapshot = analytics.getSnapshot()\n if (!snapshot.booted || routeKey === null) return\n if (paged.routeKey !== routeKey) paged = { routeKey, providers: new Set(), forwarded: false }\n const fresh = snapshot.activeProviders.filter((id) => !paged.providers.has(id))\n const server = !paged.forwarded && Boolean(snapshot.consent?.analytics || snapshot.consent?.marketing)\n if (!fresh.length && !server) return\n fresh.forEach((id) => paged.providers.add(id))\n if (server) paged.forwarded = true\n void analytics.page(page, { only: fresh, server })\n }\n\n const applyAndClose = async (consent: ConsentState) => {\n await engine.setConsent(consent)\n set({ bannerVisible: false, dialogVisible: false })\n }\n\n return {\n getSnapshot: () => state,\n subscribe(listener) {\n listeners.add(listener)\n return () => listeners.delete(listener)\n },\n start() {\n let stopped = false\n void engine\n .bootstrap()\n .then((consent) => {\n if (stopped) return\n set({\n ...(consent && { prefs: all((category) => consent[category]) }),\n bannerVisible: consent && !engine.isStale() ? false : autoShowBanner,\n })\n })\n .catch((error) => {\n if (!stopped) set({ error: String(error), bannerVisible: autoShowBanner })\n })\n .finally(() => {\n if (!stopped) set({ ready: true })\n })\n const cancelBoot = scheduleBoot(analytics, boot)\n const syncPrefs = (consent: ConsentState | null) => {\n if (consent) set({ prefs: all((category) => consent[category]) })\n }\n syncPrefs(engine.getConsent())\n const unsubscribeConsent = engine.subscribe(({ consent }) => syncPrefs(consent))\n const unsubscribeAnalytics = analytics.subscribe(sendPageViews)\n sendPageViews()\n const globals = typeof window === 'undefined' ? {} : (window as unknown as Record<string, unknown>)\n const openConsent = () => set({ dialogVisible: true })\n const keys = openConsentGlobalKey ? [openConsentGlobalKey] : withLegacy((names) => names.openConsentGlobalKey)\n for (const key of keys) globals[key] = openConsent\n return () => {\n stopped = true\n for (const key of keys) if (globals[key] === openConsent) delete globals[key]\n cancelBoot()\n unsubscribeConsent()\n unsubscribeAnalytics()\n }\n },\n setRoute(nextRouteKey, nextPage) {\n routeKey = nextRouteKey\n page = nextPage\n sendPageViews()\n if (nextRouteKey === null || sessionRouteKey === nextRouteKey) return\n const known = sessionRouteKey !== undefined && sessionRouteKey !== null\n sessionRouteKey = nextRouteKey\n if (known) analytics.newPageSession()\n },\n actions: {\n setPrefs: (prefs) => set({ prefs }),\n showBanner: () => set({ bannerVisible: true }),\n hideBanner: () => set({ bannerVisible: false }),\n openDialog: () => set({ dialogVisible: true }),\n closeDialog: () => set({ dialogVisible: false }),\n acceptAll: () => applyAndClose(all((category) => !(engine.gpc && category === 'marketing'))),\n rejectNonEssential: () => applyAndClose(all(false)),\n savePreferences: () => applyAndClose(all((category) => state.prefs[category])),\n },\n }\n}\n"]}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { sha256 } from './chunk-DK45XPEY.js';
|
|
2
|
+
import { ga4EventName } from './chunk-ETU64ZV3.js';
|
|
3
|
+
import { isValidPhoneE164, isValidEmail } from './chunk-J3KUPF4R.js';
|
|
4
|
+
|
|
5
|
+
// src/server/measurement-protocol.ts
|
|
6
|
+
function clientIdFromCookie(cookie) {
|
|
7
|
+
const parts = cookie?.split(".");
|
|
8
|
+
return parts && parts.length >= 4 ? parts.slice(-2).join(".") : void 0;
|
|
9
|
+
}
|
|
10
|
+
var consentValue = (granted) => granted ? "GRANTED" : "DENIED";
|
|
11
|
+
async function sendMeasurementProtocol({ url, sender, receiver }, event, request, fetchImpl) {
|
|
12
|
+
const clientId = event.clientId ?? clientIdFromCookie(request?.cookies._ga);
|
|
13
|
+
if (!clientId) throw new Error(`${sender} needs event.clientId or the _ga cookie`);
|
|
14
|
+
const marketing = event.consent.marketing;
|
|
15
|
+
const email = event.identity?.email?.trim().toLowerCase().replace(
|
|
16
|
+
/^(.+)@(gmail|googlemail)\.com$/,
|
|
17
|
+
(_, local, domain) => `${local.replace(/\./g, "")}@${domain}.com`
|
|
18
|
+
);
|
|
19
|
+
const phone = event.identity?.phone && `+${event.identity.phone.replace(/\D/g, "")}`;
|
|
20
|
+
const userData = marketing ? Object.fromEntries(
|
|
21
|
+
Object.entries({
|
|
22
|
+
sha256_email_address: email && isValidEmail(email) ? await sha256(email) : void 0,
|
|
23
|
+
sha256_phone_number: phone && isValidPhoneE164(phone) ? await sha256(phone) : void 0
|
|
24
|
+
}).filter(([, value]) => value)
|
|
25
|
+
) : {};
|
|
26
|
+
const response = await fetchImpl(url, {
|
|
27
|
+
method: "POST",
|
|
28
|
+
headers: { "Content-Type": "application/json" },
|
|
29
|
+
body: JSON.stringify({
|
|
30
|
+
client_id: clientId,
|
|
31
|
+
user_id: event.identity?.userId,
|
|
32
|
+
user_data: Object.keys(userData).length ? userData : void 0,
|
|
33
|
+
consent: { ad_user_data: consentValue(marketing), ad_personalization: consentValue(marketing) },
|
|
34
|
+
events: [
|
|
35
|
+
{
|
|
36
|
+
name: ga4EventName(event.name),
|
|
37
|
+
params: { ...event.url && { page_location: event.url }, ...event.params, ...event.providers?.ga4 }
|
|
38
|
+
}
|
|
39
|
+
]
|
|
40
|
+
})
|
|
41
|
+
});
|
|
42
|
+
if (!response.ok) throw new Error(`${receiver} ${response.status}: ${await response.text()}`);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
export { sendMeasurementProtocol };
|
|
46
|
+
//# sourceMappingURL=chunk-HJGBVDPM.js.map
|
|
47
|
+
//# sourceMappingURL=chunk-HJGBVDPM.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/server/measurement-protocol.ts"],"names":[],"mappings":"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","file":"chunk-HJGBVDPM.js","sourcesContent":["import { isValidEmail, isValidPhoneE164 } from '../core/validation'\nimport { ga4EventName } from '../providers/ga4'\nimport type { ServerEvent, ServerRequestInfo } from './handler'\nimport { sha256 } from './http'\n\n/** `GA1.1.123.456` → `123.456` */\nfunction clientIdFromCookie(cookie: string | undefined): string | undefined {\n const parts = cookie?.split('.')\n return parts && parts.length >= 4 ? parts.slice(-2).join('.') : undefined\n}\n\nconst consentValue = (granted: boolean) => (granted ? 'GRANTED' : 'DENIED')\n\n/** Posts `event` as a GA4 Measurement Protocol request; `sender` and `receiver` name both ends in errors. */\nexport async function sendMeasurementProtocol(\n { url, sender, receiver }: { url: string; sender: string; receiver: string },\n event: ServerEvent,\n request: ServerRequestInfo | undefined,\n fetchImpl: typeof fetch,\n) {\n const clientId = event.clientId ?? clientIdFromCookie(request?.cookies._ga)\n if (!clientId) throw new Error(`${sender} needs event.clientId or the _ga cookie`)\n const marketing = event.consent.marketing\n const email = event.identity?.email\n ?.trim()\n .toLowerCase()\n .replace(\n /^(.+)@(gmail|googlemail)\\.com$/,\n (_, local: string, domain: string) => `${local.replace(/\\./g, '')}@${domain}.com`,\n )\n const phone = event.identity?.phone && `+${event.identity.phone.replace(/\\D/g, '')}`\n const userData = marketing\n ? Object.fromEntries(\n Object.entries({\n sha256_email_address: email && isValidEmail(email) ? await sha256(email) : undefined,\n sha256_phone_number: phone && isValidPhoneE164(phone) ? await sha256(phone) : undefined,\n }).filter(([, value]) => value),\n )\n : {}\n const response = await fetchImpl(url, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({\n client_id: clientId,\n user_id: event.identity?.userId,\n user_data: Object.keys(userData).length ? userData : undefined,\n consent: { ad_user_data: consentValue(marketing), ad_personalization: consentValue(marketing) },\n events: [\n {\n name: ga4EventName(event.name),\n params: { ...(event.url && { page_location: event.url }), ...event.params, ...event.providers?.ga4 },\n },\n ],\n }),\n })\n if (!response.ok) throw new Error(`${receiver} ${response.status}: ${await response.text()}`)\n}\n"]}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
// src/core/validation.ts
|
|
2
|
+
var GA4_MEASUREMENT_ID = /^G-[A-Z0-9]{4,16}$/;
|
|
3
|
+
var META_PIXEL_ID = /^\d{8,20}$/;
|
|
4
|
+
var EMAIL = /^[^\s@]+@[^\s@.]+(?:\.[^\s@.]+)+$/;
|
|
5
|
+
var PHONE_E164 = /^\+?[1-9]\d{6,14}$/;
|
|
6
|
+
function isValidGa4MeasurementId(value) {
|
|
7
|
+
return typeof value === "string" && GA4_MEASUREMENT_ID.test(value);
|
|
8
|
+
}
|
|
9
|
+
function assertValidGa4MeasurementId(value) {
|
|
10
|
+
if (!isValidGa4MeasurementId(value)) {
|
|
11
|
+
throw new Error(
|
|
12
|
+
`[consentlayer] Invalid GA4 measurement ID: expected format "G-XXXXXXXX", received ${describe(value)}.`
|
|
13
|
+
);
|
|
14
|
+
}
|
|
15
|
+
return value;
|
|
16
|
+
}
|
|
17
|
+
function isValidMetaPixelId(value) {
|
|
18
|
+
return typeof value === "string" && META_PIXEL_ID.test(value);
|
|
19
|
+
}
|
|
20
|
+
function assertValidMetaPixelId(value) {
|
|
21
|
+
if (!isValidMetaPixelId(value)) {
|
|
22
|
+
throw new Error(
|
|
23
|
+
`[consentlayer] Invalid Meta Pixel ID: expected 8-20 digit numeric string, received ${describe(value)}.`
|
|
24
|
+
);
|
|
25
|
+
}
|
|
26
|
+
return value;
|
|
27
|
+
}
|
|
28
|
+
function isHttpsOrRelativeUrl(value) {
|
|
29
|
+
if (typeof value !== "string" || value.length === 0) return false;
|
|
30
|
+
if (value.startsWith("/")) return !value.startsWith("//");
|
|
31
|
+
try {
|
|
32
|
+
return new URL(value).protocol === "https:";
|
|
33
|
+
} catch {
|
|
34
|
+
return false;
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
function assertHttpsOrRelativeUrl(value, label) {
|
|
38
|
+
if (!isHttpsOrRelativeUrl(value)) {
|
|
39
|
+
throw new Error(
|
|
40
|
+
`[consentlayer] ${label} must be an HTTPS URL or a relative path starting with "/", received ${describe(value)}.`
|
|
41
|
+
);
|
|
42
|
+
}
|
|
43
|
+
return value;
|
|
44
|
+
}
|
|
45
|
+
function isValidEmail(value) {
|
|
46
|
+
return typeof value === "string" && EMAIL.test(value);
|
|
47
|
+
}
|
|
48
|
+
function isValidPhoneE164(value) {
|
|
49
|
+
return typeof value === "string" && PHONE_E164.test(value);
|
|
50
|
+
}
|
|
51
|
+
function assertValidConsent(value, categories = ["preferences", "analytics", "marketing"]) {
|
|
52
|
+
const consent = value;
|
|
53
|
+
const valid = typeof consent === "object" && consent !== null && categories.every((key) => typeof consent[key] === "boolean") && (consent.sale_of_data === void 0 || typeof consent.sale_of_data === "boolean");
|
|
54
|
+
if (!valid) {
|
|
55
|
+
throw new TypeError(
|
|
56
|
+
`[consentlayer] consent must have boolean ${categories.slice(0, -1).join(", ")} and ${categories.at(-1)}, received ${JSON.stringify(value) ?? "undefined"}.`
|
|
57
|
+
);
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
function describe(value) {
|
|
61
|
+
if (value === null) return "null";
|
|
62
|
+
if (value === void 0) return "undefined";
|
|
63
|
+
if (typeof value === "string") return value.length > 32 ? `string(length=${value.length})` : `"${value}"`;
|
|
64
|
+
return typeof value;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
export { assertHttpsOrRelativeUrl, assertValidConsent, assertValidGa4MeasurementId, assertValidMetaPixelId, isHttpsOrRelativeUrl, isValidEmail, isValidPhoneE164 };
|
|
68
|
+
//# sourceMappingURL=chunk-J3KUPF4R.js.map
|
|
69
|
+
//# sourceMappingURL=chunk-J3KUPF4R.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/core/validation.ts"],"names":[],"mappings":"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","file":"chunk-J3KUPF4R.js","sourcesContent":["const GA4_MEASUREMENT_ID = /^G-[A-Z0-9]{4,16}$/\nconst META_PIXEL_ID = /^\\d{8,20}$/\nconst EMAIL = /^[^\\s@]+@[^\\s@.]+(?:\\.[^\\s@.]+)+$/\nconst PHONE_E164 = /^\\+?[1-9]\\d{6,14}$/\n\nfunction isValidGa4MeasurementId(value: unknown): value is string {\n return typeof value === 'string' && GA4_MEASUREMENT_ID.test(value)\n}\n\nexport function assertValidGa4MeasurementId(value: unknown): string {\n if (!isValidGa4MeasurementId(value)) {\n throw new Error(\n `[consentlayer] Invalid GA4 measurement ID: expected format \"G-XXXXXXXX\", received ${describe(value)}.`,\n )\n }\n return value\n}\n\nfunction isValidMetaPixelId(value: unknown): value is string {\n return typeof value === 'string' && META_PIXEL_ID.test(value)\n}\n\nexport function assertValidMetaPixelId(value: unknown): string {\n if (!isValidMetaPixelId(value)) {\n throw new Error(\n `[consentlayer] Invalid Meta Pixel ID: expected 8-20 digit numeric string, received ${describe(value)}.`,\n )\n }\n return value\n}\n\nexport function isHttpsOrRelativeUrl(value: unknown): value is string {\n if (typeof value !== 'string' || value.length === 0) return false\n if (value.startsWith('/')) return !value.startsWith('//')\n try {\n return new URL(value).protocol === 'https:'\n } catch {\n return false\n }\n}\n\nexport function assertHttpsOrRelativeUrl(value: unknown, label: string): string {\n if (!isHttpsOrRelativeUrl(value)) {\n throw new Error(\n `[consentlayer] ${label} must be an HTTPS URL or a relative path starting with \"/\", received ${describe(value)}.`,\n )\n }\n return value\n}\n\nexport function isValidEmail(value: unknown): value is string {\n return typeof value === 'string' && EMAIL.test(value)\n}\n\nexport function isValidPhoneE164(value: unknown): value is string {\n return typeof value === 'string' && PHONE_E164.test(value)\n}\n\nexport function assertValidConsent(\n value: unknown,\n categories: readonly string[] = ['preferences', 'analytics', 'marketing'],\n): void {\n const consent = value as Record<string, unknown> | null\n const valid =\n typeof consent === 'object' &&\n consent !== null &&\n categories.every((key) => typeof consent[key] === 'boolean') &&\n (consent.sale_of_data === undefined || typeof consent.sale_of_data === 'boolean')\n if (!valid) {\n throw new TypeError(\n `[consentlayer] consent must have boolean ${categories.slice(0, -1).join(', ')} and ${categories.at(-1)}, received ${JSON.stringify(value) ?? 'undefined'}.`,\n )\n }\n}\n\nfunction describe(value: unknown): string {\n if (value === null) return 'null'\n if (value === undefined) return 'undefined'\n if (typeof value === 'string') return value.length > 32 ? `string(length=${value.length})` : `\"${value}\"`\n return typeof value\n}\n"]}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
// src/core/meta-events.ts
|
|
2
|
+
var META_EVENT_NAMES = {
|
|
3
|
+
page_view: "PageView",
|
|
4
|
+
view_item: "ViewContent",
|
|
5
|
+
add_to_cart: "AddToCart",
|
|
6
|
+
add_to_wishlist: "AddToWishlist",
|
|
7
|
+
begin_checkout: "InitiateCheckout",
|
|
8
|
+
add_payment_info: "AddPaymentInfo",
|
|
9
|
+
purchase: "Purchase",
|
|
10
|
+
sign_up: "CompleteRegistration",
|
|
11
|
+
lead: "Lead",
|
|
12
|
+
search: "Search",
|
|
13
|
+
contact: "Contact",
|
|
14
|
+
trial_started: "StartTrial",
|
|
15
|
+
subscription_started: "Subscribe",
|
|
16
|
+
customize_product: "CustomizeProduct",
|
|
17
|
+
schedule: "Schedule",
|
|
18
|
+
submit_application: "SubmitApplication",
|
|
19
|
+
find_location: "FindLocation",
|
|
20
|
+
donate: "Donate"
|
|
21
|
+
};
|
|
22
|
+
var META_STANDARD = /* @__PURE__ */ new Set([...Object.values(META_EVENT_NAMES), "PageView"]);
|
|
23
|
+
function toMetaEvent(event) {
|
|
24
|
+
const standard = META_EVENT_NAMES[event.name] ?? (META_STANDARD.has(event.name) ? event.name : void 0);
|
|
25
|
+
const { items, search_term, ...rest } = event.params;
|
|
26
|
+
const params = { ...rest };
|
|
27
|
+
if (Array.isArray(items) && items.length) {
|
|
28
|
+
const id = (item) => item.item_variant ?? item.item_id;
|
|
29
|
+
params.content_ids = items.map(id);
|
|
30
|
+
params.contents = items.map((item) => ({
|
|
31
|
+
id: id(item),
|
|
32
|
+
quantity: item.quantity ?? 1,
|
|
33
|
+
item_price: item.price
|
|
34
|
+
}));
|
|
35
|
+
params.content_type = "product";
|
|
36
|
+
params.num_items = items.reduce((sum, item) => sum + (item.quantity ?? 1), 0);
|
|
37
|
+
}
|
|
38
|
+
if (search_term !== void 0) params.search_string = search_term;
|
|
39
|
+
return {
|
|
40
|
+
method: standard ? "track" : "trackCustom",
|
|
41
|
+
name: standard ?? event.name,
|
|
42
|
+
params: { ...params, ...event.options.providers?.meta }
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
function metaUserData(identity) {
|
|
46
|
+
const data = {};
|
|
47
|
+
if (identity.email) data.em = identity.email;
|
|
48
|
+
if (identity.phone) data.ph = identity.phone.replace(/\D/g, "");
|
|
49
|
+
if (identity.userId) data.external_id = identity.userId;
|
|
50
|
+
return data;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
export { metaUserData, toMetaEvent };
|
|
54
|
+
//# sourceMappingURL=chunk-LBEIEAJX.js.map
|
|
55
|
+
//# sourceMappingURL=chunk-LBEIEAJX.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/core/meta-events.ts"],"names":[],"mappings":"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","file":"chunk-LBEIEAJX.js","sourcesContent":["import type { AnalyticsEventPayload, EcommerceItem, UserIdentity } from './types'\n\nconst META_EVENT_NAMES: Record<string, string> = {\n page_view: 'PageView',\n view_item: 'ViewContent',\n add_to_cart: 'AddToCart',\n add_to_wishlist: 'AddToWishlist',\n begin_checkout: 'InitiateCheckout',\n add_payment_info: 'AddPaymentInfo',\n purchase: 'Purchase',\n sign_up: 'CompleteRegistration',\n lead: 'Lead',\n search: 'Search',\n contact: 'Contact',\n trial_started: 'StartTrial',\n subscription_started: 'Subscribe',\n customize_product: 'CustomizeProduct',\n schedule: 'Schedule',\n submit_application: 'SubmitApplication',\n find_location: 'FindLocation',\n donate: 'Donate',\n}\nconst META_STANDARD = new Set([...Object.values(META_EVENT_NAMES), 'PageView'])\n\n/** Translates a standard event into a Meta standard event, or a custom one. */\nexport function toMetaEvent(event: AnalyticsEventPayload): {\n method: 'track' | 'trackCustom'\n name: string\n params: Record<string, unknown>\n} {\n const standard = META_EVENT_NAMES[event.name] ?? (META_STANDARD.has(event.name) ? event.name : undefined)\n const { items, search_term, ...rest } = event.params as {\n items?: EcommerceItem[]\n search_term?: string\n } & Record<string, unknown>\n const params: Record<string, unknown> = { ...rest }\n if (Array.isArray(items) && items.length) {\n const id = (item: EcommerceItem) => item.item_variant ?? item.item_id\n params.content_ids = items.map(id)\n params.contents = items.map((item) => ({\n id: id(item),\n quantity: item.quantity ?? 1,\n item_price: item.price,\n }))\n params.content_type = 'product'\n params.num_items = items.reduce((sum, item) => sum + (item.quantity ?? 1), 0)\n }\n if (search_term !== undefined) params.search_string = search_term\n return {\n method: standard ? 'track' : 'trackCustom',\n name: standard ?? event.name,\n params: { ...params, ...event.options.providers?.meta },\n }\n}\n\n/** Advanced matching; Meta accepts it only in `fbq('init')`. */\nexport function metaUserData(identity: UserIdentity): Record<string, string> {\n const data: Record<string, string> = {}\n if (identity.email) data.em = identity.email\n if (identity.phone) data.ph = identity.phone.replace(/\\D/g, '')\n if (identity.userId) data.external_id = identity.userId\n return data\n}\n"]}
|