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,582 @@
|
|
|
1
|
+
import { isBrowser, normalizeIdentity, createDebugLog, createEventId, getWindowLocation, hasRequiredConsent, safeJsonParse } from './chunk-TVSZJLRC.js';
|
|
2
|
+
import { assertHttpsOrRelativeUrl } from './chunk-J3KUPF4R.js';
|
|
3
|
+
import { legacyStorageKeysFor } from './chunk-R6I7OYQB.js';
|
|
4
|
+
|
|
5
|
+
// src/core/storage.ts
|
|
6
|
+
function createLocalStorageConsentStorage(options) {
|
|
7
|
+
return {
|
|
8
|
+
read() {
|
|
9
|
+
return withStorage(
|
|
10
|
+
options,
|
|
11
|
+
(storage) => toRecord(safeJsonParse(readMigrating(storage, options))),
|
|
12
|
+
null
|
|
13
|
+
);
|
|
14
|
+
},
|
|
15
|
+
write(record) {
|
|
16
|
+
withStorage(
|
|
17
|
+
options,
|
|
18
|
+
(storage) => record ? storage.setItem(options.key, JSON.stringify(record)) : storage.removeItem(options.key),
|
|
19
|
+
void 0
|
|
20
|
+
);
|
|
21
|
+
},
|
|
22
|
+
subscribe(listener) {
|
|
23
|
+
const win = options.window ?? (isBrowser() ? window : void 0);
|
|
24
|
+
if (!win) return () => void 0;
|
|
25
|
+
const handleStorage = (event) => {
|
|
26
|
+
if (event.key === options.key) listener();
|
|
27
|
+
};
|
|
28
|
+
win.addEventListener("storage", handleStorage);
|
|
29
|
+
return () => win.removeEventListener("storage", handleStorage);
|
|
30
|
+
},
|
|
31
|
+
clear() {
|
|
32
|
+
withStorage(options, (storage) => storage.removeItem(options.key), void 0);
|
|
33
|
+
}
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
function createLocalStorageIdentityStorage(options) {
|
|
37
|
+
return {
|
|
38
|
+
read() {
|
|
39
|
+
return withStorage(
|
|
40
|
+
options,
|
|
41
|
+
(storage) => normalizeIdentity(safeJsonParse(readMigrating(storage, options)) ?? {}),
|
|
42
|
+
{}
|
|
43
|
+
);
|
|
44
|
+
},
|
|
45
|
+
write(identity) {
|
|
46
|
+
withStorage(
|
|
47
|
+
options,
|
|
48
|
+
(storage) => storage.setItem(options.key, JSON.stringify(normalizeIdentity(identity))),
|
|
49
|
+
void 0
|
|
50
|
+
);
|
|
51
|
+
},
|
|
52
|
+
clear() {
|
|
53
|
+
withStorage(
|
|
54
|
+
options,
|
|
55
|
+
(storage) => {
|
|
56
|
+
for (const key of [options.key, ...options.legacyKeys ?? legacyStorageKeysFor(options.key)]) {
|
|
57
|
+
storage.removeItem(key);
|
|
58
|
+
}
|
|
59
|
+
},
|
|
60
|
+
void 0
|
|
61
|
+
);
|
|
62
|
+
}
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
function createMemoryConsentStorage(initial = null) {
|
|
66
|
+
let value = toRecord(initial);
|
|
67
|
+
const listeners = /* @__PURE__ */ new Set();
|
|
68
|
+
return {
|
|
69
|
+
read() {
|
|
70
|
+
return value;
|
|
71
|
+
},
|
|
72
|
+
write(record) {
|
|
73
|
+
value = record;
|
|
74
|
+
listeners.forEach((listener) => listener());
|
|
75
|
+
},
|
|
76
|
+
subscribe(listener) {
|
|
77
|
+
listeners.add(listener);
|
|
78
|
+
return () => listeners.delete(listener);
|
|
79
|
+
},
|
|
80
|
+
clear() {
|
|
81
|
+
value = null;
|
|
82
|
+
listeners.forEach((listener) => listener());
|
|
83
|
+
}
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
function createMemoryIdentityStorage(initial = {}) {
|
|
87
|
+
let value = normalizeIdentity(initial);
|
|
88
|
+
return {
|
|
89
|
+
read() {
|
|
90
|
+
return value;
|
|
91
|
+
},
|
|
92
|
+
write(identity) {
|
|
93
|
+
value = normalizeIdentity(identity);
|
|
94
|
+
},
|
|
95
|
+
clear() {
|
|
96
|
+
value = {};
|
|
97
|
+
}
|
|
98
|
+
};
|
|
99
|
+
}
|
|
100
|
+
function toRecord(value) {
|
|
101
|
+
if (!value || typeof value !== "object") return null;
|
|
102
|
+
return "consent" in value && typeof value.consent === "object" ? value : { consent: value };
|
|
103
|
+
}
|
|
104
|
+
function readMigrating(storage, { key, legacyKeys = legacyStorageKeysFor(key) }) {
|
|
105
|
+
const value = storage.getItem(key);
|
|
106
|
+
if (value !== null) return value;
|
|
107
|
+
for (const legacyKey of legacyKeys) {
|
|
108
|
+
const legacyValue = storage.getItem(legacyKey);
|
|
109
|
+
if (legacyValue === null) continue;
|
|
110
|
+
storage.setItem(key, legacyValue);
|
|
111
|
+
storage.removeItem(legacyKey);
|
|
112
|
+
return legacyValue;
|
|
113
|
+
}
|
|
114
|
+
return null;
|
|
115
|
+
}
|
|
116
|
+
function withStorage(options, run, fallback) {
|
|
117
|
+
try {
|
|
118
|
+
const storage = options.storage ?? (isBrowser() ? window.localStorage : void 0);
|
|
119
|
+
return storage ? run(storage) : fallback;
|
|
120
|
+
} catch {
|
|
121
|
+
return fallback;
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
// src/core/analytics-manager.ts
|
|
126
|
+
var LIFECYCLE_HOOKS = /* @__PURE__ */ new Set(["setup", "consent", "load", "teardown"]);
|
|
127
|
+
var HOOK_TOPICS = {
|
|
128
|
+
setup: "providers",
|
|
129
|
+
consent: "providers",
|
|
130
|
+
load: "providers",
|
|
131
|
+
teardown: "providers",
|
|
132
|
+
page: "events",
|
|
133
|
+
track: "events",
|
|
134
|
+
identify: "events",
|
|
135
|
+
group: "events",
|
|
136
|
+
reset: "events",
|
|
137
|
+
error: "errors"
|
|
138
|
+
};
|
|
139
|
+
var INTERACTIONS = ["pointerdown", "keydown", "scroll", "touchstart"];
|
|
140
|
+
function whenDue(timing, signal) {
|
|
141
|
+
if (!isBrowser()) return Promise.resolve();
|
|
142
|
+
return new Promise((resolve) => {
|
|
143
|
+
if (typeof timing === "number") {
|
|
144
|
+
window.setTimeout(resolve, timing);
|
|
145
|
+
} else if (timing === "idle") {
|
|
146
|
+
if (typeof window.requestIdleCallback === "function") {
|
|
147
|
+
window.requestIdleCallback(() => resolve(), { timeout: 5e3 });
|
|
148
|
+
} else window.setTimeout(resolve, 1);
|
|
149
|
+
} else {
|
|
150
|
+
const done = () => {
|
|
151
|
+
for (const type of INTERACTIONS) window.removeEventListener(type, done);
|
|
152
|
+
resolve();
|
|
153
|
+
};
|
|
154
|
+
for (const type of INTERACTIONS) {
|
|
155
|
+
window.addEventListener(type, done, { once: true, passive: true, signal });
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
});
|
|
159
|
+
}
|
|
160
|
+
function createAnalyticsManager(options = {}) {
|
|
161
|
+
if (options.autoTracking !== void 0 && typeof options.autoTracking !== "function") {
|
|
162
|
+
throw new Error("[consentlayer] autoTracking must be autoTracking() from consentlayer/auto-tracking");
|
|
163
|
+
}
|
|
164
|
+
const debugLog = createDebugLog(options.debug);
|
|
165
|
+
const listeners = /* @__PURE__ */ new Set();
|
|
166
|
+
const providerStates = /* @__PURE__ */ new Map();
|
|
167
|
+
const identityStorage = options.identityStorage ?? createMemoryIdentityStorage();
|
|
168
|
+
let consent = options.consent ?? options.consentEngine?.getConsent() ?? null;
|
|
169
|
+
const stored = identityStorage.read();
|
|
170
|
+
let identity = stored.userId ? { userId: stored.userId } : {};
|
|
171
|
+
let storedContact = stored.email || stored.phone ? stored : void 0;
|
|
172
|
+
const settleStoredContact = () => {
|
|
173
|
+
if (!storedContact || !consent) return;
|
|
174
|
+
if (consent.marketing && identity.userId === storedContact.userId) {
|
|
175
|
+
identity = { ...storedContact, ...identity };
|
|
176
|
+
}
|
|
177
|
+
storedContact = void 0;
|
|
178
|
+
};
|
|
179
|
+
settleStoredContact();
|
|
180
|
+
const pending = [];
|
|
181
|
+
let booted = false;
|
|
182
|
+
let ready = false;
|
|
183
|
+
let identifyQueued = false;
|
|
184
|
+
let destroyed = false;
|
|
185
|
+
const lifetime = new AbortController();
|
|
186
|
+
let lifecycleQueue = Promise.resolve();
|
|
187
|
+
let unsubscribeConsent;
|
|
188
|
+
let pageSession;
|
|
189
|
+
const snapshot = () => ({
|
|
190
|
+
booted,
|
|
191
|
+
consent,
|
|
192
|
+
activeProviders: [...providerStates.values()].filter((state) => state.active).map((state) => state.plugin.id)
|
|
193
|
+
});
|
|
194
|
+
let currentSnapshot = snapshot();
|
|
195
|
+
const refreshSnapshot = () => {
|
|
196
|
+
currentSnapshot = snapshot();
|
|
197
|
+
};
|
|
198
|
+
const notify = () => {
|
|
199
|
+
refreshSnapshot();
|
|
200
|
+
listeners.forEach((listener) => listener(currentSnapshot));
|
|
201
|
+
};
|
|
202
|
+
const runtimeEnabled = () => {
|
|
203
|
+
if (typeof options.enabled === "function") return options.enabled();
|
|
204
|
+
if (typeof options.enabled === "boolean") return options.enabled;
|
|
205
|
+
return true;
|
|
206
|
+
};
|
|
207
|
+
const emitError = (error) => {
|
|
208
|
+
debugLog("errors", error.providerId, `[${error.scope}] ${error.message}`, error.cause);
|
|
209
|
+
try {
|
|
210
|
+
options.onError?.(error);
|
|
211
|
+
} catch (handlerError) {
|
|
212
|
+
debugLog("errors", void 0, "onError handler threw", handlerError);
|
|
213
|
+
}
|
|
214
|
+
};
|
|
215
|
+
const overflowed = /* @__PURE__ */ new WeakSet();
|
|
216
|
+
const enqueue = (queue, run, providerId) => {
|
|
217
|
+
if (queue.length < 100) {
|
|
218
|
+
queue.push(run);
|
|
219
|
+
} else if (!overflowed.has(queue)) {
|
|
220
|
+
overflowed.add(queue);
|
|
221
|
+
emitError({
|
|
222
|
+
scope: "queue-full",
|
|
223
|
+
providerId,
|
|
224
|
+
message: `100 calls are already waiting ${providerId ? `for ${providerId} to load` : "for boot"}; later ones are dropped`
|
|
225
|
+
});
|
|
226
|
+
}
|
|
227
|
+
};
|
|
228
|
+
const providerEmitError = (providerId) => (error) => emitError({ ...error, providerId: error.providerId ?? providerId });
|
|
229
|
+
let serverEndpoint = options.server?.endpoint ? assertHttpsOrRelativeUrl(options.server.endpoint, "server.endpoint") : void 0;
|
|
230
|
+
const consentEndpoint = options.server?.consentEndpoint ? assertHttpsOrRelativeUrl(options.server.consentEndpoint, "server.consentEndpoint") : void 0;
|
|
231
|
+
let restoring = false;
|
|
232
|
+
const restoreConsentCookie = () => {
|
|
233
|
+
if (!consentEndpoint || restoring) return;
|
|
234
|
+
restoring = true;
|
|
235
|
+
fetch(consentEndpoint, {
|
|
236
|
+
method: "POST",
|
|
237
|
+
headers: { "content-type": "application/json" },
|
|
238
|
+
body: JSON.stringify({ consent, restored: true })
|
|
239
|
+
}).then((response) => {
|
|
240
|
+
if (!response.ok) throw new Error(`HTTP ${response.status}`);
|
|
241
|
+
}).catch(
|
|
242
|
+
(cause) => emitError({
|
|
243
|
+
scope: "server-forward",
|
|
244
|
+
message: `Restoring the consent cookie at ${consentEndpoint} failed`,
|
|
245
|
+
cause
|
|
246
|
+
})
|
|
247
|
+
);
|
|
248
|
+
};
|
|
249
|
+
const forward = (event) => {
|
|
250
|
+
if (!serverEndpoint || !(consent?.analytics || consent?.marketing)) return;
|
|
251
|
+
const body = JSON.stringify({ ...event, consent, identity: sharedIdentity() });
|
|
252
|
+
const fail = (cause) => emitError({ scope: "server-forward", message: `Sending ${event.name} to ${serverEndpoint} failed`, cause });
|
|
253
|
+
if (typeof fetch !== "function") {
|
|
254
|
+
if (!navigator.sendBeacon(serverEndpoint, new Blob([body], { type: "application/json" }))) {
|
|
255
|
+
fail(void 0);
|
|
256
|
+
}
|
|
257
|
+
return;
|
|
258
|
+
}
|
|
259
|
+
fetch(serverEndpoint, {
|
|
260
|
+
method: "POST",
|
|
261
|
+
keepalive: true,
|
|
262
|
+
headers: { "content-type": "application/json" },
|
|
263
|
+
body
|
|
264
|
+
}).then((response) => {
|
|
265
|
+
if (!response.ok) throw new Error(`HTTP ${response.status}`);
|
|
266
|
+
if (response.headers.get("consentlayer-forwarding") === "off") serverEndpoint = void 0;
|
|
267
|
+
if (response.headers.get("consentlayer-consent") === "missing") restoreConsentCookie();
|
|
268
|
+
}).catch(fail);
|
|
269
|
+
};
|
|
270
|
+
const sharedIdentity = () => consent?.marketing ? identity : identity.userId ? { userId: identity.userId } : {};
|
|
271
|
+
const persistIdentity = () => {
|
|
272
|
+
const stored2 = sharedIdentity();
|
|
273
|
+
if (Object.keys(stored2).length && (consent?.analytics || consent?.marketing)) identityStorage.write(stored2);
|
|
274
|
+
else identityStorage.clear?.();
|
|
275
|
+
};
|
|
276
|
+
const context = (providerId) => ({
|
|
277
|
+
consent,
|
|
278
|
+
identity: sharedIdentity(),
|
|
279
|
+
debug: Boolean(options.debug),
|
|
280
|
+
log: (message, ...data) => debugLog("providers", providerId, message, ...data),
|
|
281
|
+
emitError: providerEmitError(providerId)
|
|
282
|
+
});
|
|
283
|
+
const call = async (state, hook, run, ...detail) => {
|
|
284
|
+
if (destroyed) return false;
|
|
285
|
+
const { plugin } = state;
|
|
286
|
+
if (state.backlog && !LIFECYCLE_HOOKS.has(hook)) {
|
|
287
|
+
enqueue(state.backlog, () => call(state, hook, run, ...detail), plugin.id);
|
|
288
|
+
return Boolean(plugin[hook]);
|
|
289
|
+
}
|
|
290
|
+
if (plugin[hook]) debugLog(HOOK_TOPICS[hook], plugin.id, `${plugin.id}.${hook}`, ...detail);
|
|
291
|
+
try {
|
|
292
|
+
await run(plugin, context(plugin.id));
|
|
293
|
+
} catch (error) {
|
|
294
|
+
emitError({
|
|
295
|
+
scope: hook === "reset" || hook === "group" ? "provider-identify" : `provider-${hook}`,
|
|
296
|
+
providerId: plugin.id,
|
|
297
|
+
message: `${plugin.id}.${hook} failed`,
|
|
298
|
+
cause: error
|
|
299
|
+
});
|
|
300
|
+
}
|
|
301
|
+
return Boolean(plugin[hook]);
|
|
302
|
+
};
|
|
303
|
+
const ensureProviderState = (plugin) => {
|
|
304
|
+
const existing = providerStates.get(plugin.id);
|
|
305
|
+
if (existing) {
|
|
306
|
+
if (existing.plugin !== plugin) {
|
|
307
|
+
emitError({
|
|
308
|
+
scope: "provider-setup",
|
|
309
|
+
providerId: plugin.id,
|
|
310
|
+
message: `Another provider already uses the id "${plugin.id}"; this one is ignored`
|
|
311
|
+
});
|
|
312
|
+
}
|
|
313
|
+
return existing;
|
|
314
|
+
}
|
|
315
|
+
const created = { plugin, setup: false, loaded: false, active: false };
|
|
316
|
+
providerStates.set(plugin.id, created);
|
|
317
|
+
return created;
|
|
318
|
+
};
|
|
319
|
+
const teardown = async (state) => {
|
|
320
|
+
if (!state.active) return;
|
|
321
|
+
state.active = false;
|
|
322
|
+
await call(state, "teardown", (plugin, ctx) => plugin.teardown?.(ctx));
|
|
323
|
+
};
|
|
324
|
+
const setupProvider = async (state) => {
|
|
325
|
+
if (state.setup) return;
|
|
326
|
+
state.setup = true;
|
|
327
|
+
await call(state, "setup", (p, ctx) => p.setup?.(ctx));
|
|
328
|
+
};
|
|
329
|
+
const evaluateProvider = async (state) => {
|
|
330
|
+
if (!booted || !runtimeEnabled()) return teardown(state);
|
|
331
|
+
const { plugin } = state;
|
|
332
|
+
const allowed = hasRequiredConsent(consent, plugin.requiredConsent);
|
|
333
|
+
await setupProvider(state);
|
|
334
|
+
await call(state, "consent", (p, ctx) => p.consent?.(ctx));
|
|
335
|
+
if (!state.loaded && (allowed || plugin.loadBeforeConsent)) {
|
|
336
|
+
state.loaded = true;
|
|
337
|
+
if (plugin.defer) deferLoad(state, plugin.defer);
|
|
338
|
+
else await call(state, "load", (p, ctx) => p.load?.(ctx));
|
|
339
|
+
}
|
|
340
|
+
if (!allowed) return teardown(state);
|
|
341
|
+
if (state.active) return;
|
|
342
|
+
state.active = true;
|
|
343
|
+
if (Object.keys(sharedIdentity()).length && !identifyQueued) {
|
|
344
|
+
await call(state, "identify", (p, ctx) => p.identify?.(sharedIdentity(), {}, ctx));
|
|
345
|
+
}
|
|
346
|
+
};
|
|
347
|
+
const deferLoad = (state, timing) => {
|
|
348
|
+
state.backlog = [];
|
|
349
|
+
void whenDue(timing, lifetime.signal).then(() => {
|
|
350
|
+
lifecycleQueue = lifecycleQueue.then(async () => {
|
|
351
|
+
const backlog = state.backlog ?? [];
|
|
352
|
+
state.backlog = void 0;
|
|
353
|
+
if (!state.active && !state.plugin.loadBeforeConsent) {
|
|
354
|
+
state.loaded = false;
|
|
355
|
+
return;
|
|
356
|
+
}
|
|
357
|
+
await call(state, "load", (p, ctx) => p.load?.(ctx));
|
|
358
|
+
for (const run of backlog) if (state.active) await run();
|
|
359
|
+
});
|
|
360
|
+
});
|
|
361
|
+
};
|
|
362
|
+
const runLifecycle = async () => {
|
|
363
|
+
const evaluated = consent;
|
|
364
|
+
for (const state of providerStates.values()) {
|
|
365
|
+
await evaluateProvider(state);
|
|
366
|
+
}
|
|
367
|
+
if (options.autoTracking && !pageSession && booted && evaluated?.analytics && isBrowser()) {
|
|
368
|
+
pageSession = () => {
|
|
369
|
+
};
|
|
370
|
+
try {
|
|
371
|
+
pageSession = options.autoTracking({ track: api.track, signal: lifetime.signal });
|
|
372
|
+
pageSession();
|
|
373
|
+
} catch (cause) {
|
|
374
|
+
emitError({ scope: "auto-tracking", message: "Auto-tracking failed to start", cause });
|
|
375
|
+
}
|
|
376
|
+
}
|
|
377
|
+
notify();
|
|
378
|
+
};
|
|
379
|
+
const queueLifecycle = async () => {
|
|
380
|
+
lifecycleQueue = lifecycleQueue.then(runLifecycle);
|
|
381
|
+
await lifecycleQueue;
|
|
382
|
+
};
|
|
383
|
+
const labelTopic = (label) => label === "captureError" ? "errors" : "events";
|
|
384
|
+
const trackAgainstProviders = async (label, selectedProviderIds, callback, serverEvent) => {
|
|
385
|
+
if (destroyed) return;
|
|
386
|
+
if (!runtimeEnabled()) return debugLog(labelTopic(label), void 0, `${label} dropped: enabled is false`);
|
|
387
|
+
if (!ready) {
|
|
388
|
+
debugLog(labelTopic(label), void 0, `${label} held until boot`);
|
|
389
|
+
enqueue(pending, () => deliver(label, selectedProviderIds, callback, serverEvent));
|
|
390
|
+
return;
|
|
391
|
+
}
|
|
392
|
+
await deliver(label, selectedProviderIds, callback, serverEvent);
|
|
393
|
+
};
|
|
394
|
+
const deliver = async (label, selectedProviderIds, callback, serverEvent) => {
|
|
395
|
+
if (destroyed) return;
|
|
396
|
+
const event = serverEvent?.();
|
|
397
|
+
if (event) forward(event);
|
|
398
|
+
let reached = 0;
|
|
399
|
+
for (const state of providerStates.values()) {
|
|
400
|
+
if (!state.active) continue;
|
|
401
|
+
if (!hasRequiredConsent(consent, state.plugin.requiredConsent)) continue;
|
|
402
|
+
if (selectedProviderIds && !selectedProviderIds.includes(state.plugin.id)) {
|
|
403
|
+
continue;
|
|
404
|
+
}
|
|
405
|
+
if (await callback(state)) reached += 1;
|
|
406
|
+
}
|
|
407
|
+
if (!reached) debugLog(labelTopic(label), void 0, `${label} reached no provider`);
|
|
408
|
+
};
|
|
409
|
+
const identifyProviders = (traits, only) => trackAgainstProviders(
|
|
410
|
+
"identify",
|
|
411
|
+
only,
|
|
412
|
+
(state) => call(state, "identify", (p, ctx) => p.identify?.(sharedIdentity(), traits, ctx), traits)
|
|
413
|
+
);
|
|
414
|
+
if (options.consentEngine) {
|
|
415
|
+
unsubscribeConsent = options.consentEngine.subscribe(({ consent: nextConsent }) => {
|
|
416
|
+
void api.updateConsent(nextConsent);
|
|
417
|
+
});
|
|
418
|
+
}
|
|
419
|
+
for (const provider of options.providers ?? []) {
|
|
420
|
+
const state = ensureProviderState(provider);
|
|
421
|
+
if (isBrowser() && runtimeEnabled()) void setupProvider(state);
|
|
422
|
+
}
|
|
423
|
+
const errorsEnabled = options.errors !== false;
|
|
424
|
+
const reportedErrors = /* @__PURE__ */ new WeakSet();
|
|
425
|
+
const onWindowError = (event) => void api.captureError(event.error ?? event.message, { source: "global", level: "fatal" });
|
|
426
|
+
const onRejection = (event) => void api.captureError(event.reason, { source: "global" });
|
|
427
|
+
const globalHandlers = errorsEnabled && isBrowser() && !(typeof options.errors === "object" && options.errors.globalHandlers === false);
|
|
428
|
+
const api = {
|
|
429
|
+
async boot() {
|
|
430
|
+
if (destroyed || booted) return;
|
|
431
|
+
booted = true;
|
|
432
|
+
debugLog("providers", void 0, "boot");
|
|
433
|
+
if (globalHandlers) {
|
|
434
|
+
window.addEventListener("error", onWindowError);
|
|
435
|
+
window.addEventListener("unhandledrejection", onRejection);
|
|
436
|
+
}
|
|
437
|
+
await queueLifecycle();
|
|
438
|
+
for (let run = pending.shift(); run; run = pending.shift()) await run();
|
|
439
|
+
ready = true;
|
|
440
|
+
identifyQueued = false;
|
|
441
|
+
},
|
|
442
|
+
registerProvider(provider) {
|
|
443
|
+
const state = ensureProviderState(provider);
|
|
444
|
+
if (booted) void queueLifecycle();
|
|
445
|
+
else notify();
|
|
446
|
+
return () => {
|
|
447
|
+
if (!providerStates.has(provider.id)) return;
|
|
448
|
+
void teardown(state);
|
|
449
|
+
providerStates.delete(provider.id);
|
|
450
|
+
notify();
|
|
451
|
+
};
|
|
452
|
+
},
|
|
453
|
+
async updateConsent(nextConsent) {
|
|
454
|
+
const hadMarketing = Boolean(consent?.marketing);
|
|
455
|
+
const hadContact = Boolean(identity.email || identity.phone);
|
|
456
|
+
const wasActive = [...providerStates.values()].filter((state) => state.active).map((state) => state.plugin.id);
|
|
457
|
+
consent = nextConsent;
|
|
458
|
+
if (!nextConsent) storedContact = void 0;
|
|
459
|
+
settleStoredContact();
|
|
460
|
+
if (nextConsent && !nextConsent.marketing) {
|
|
461
|
+
identity = identity.userId ? { userId: identity.userId } : {};
|
|
462
|
+
}
|
|
463
|
+
persistIdentity();
|
|
464
|
+
await queueLifecycle();
|
|
465
|
+
if (!hadContact || hadMarketing === Boolean(nextConsent?.marketing)) return;
|
|
466
|
+
if (hadMarketing) {
|
|
467
|
+
for (const state of providerStates.values()) {
|
|
468
|
+
if (state.active) await call(state, "identify", (p, ctx) => p.identify?.(sharedIdentity(), {}, ctx));
|
|
469
|
+
else if (state.setup) await call(state, "reset", (p, ctx) => p.reset?.(ctx));
|
|
470
|
+
}
|
|
471
|
+
} else if (wasActive.length) {
|
|
472
|
+
await identifyProviders({}, wasActive);
|
|
473
|
+
}
|
|
474
|
+
},
|
|
475
|
+
getConsent() {
|
|
476
|
+
return consent;
|
|
477
|
+
},
|
|
478
|
+
async page(payload = {}, { only, server } = {}) {
|
|
479
|
+
const page = { ...getWindowLocation(), ...payload, id: createEventId() };
|
|
480
|
+
await trackAgainstProviders(
|
|
481
|
+
"page",
|
|
482
|
+
only,
|
|
483
|
+
(state) => call(state, "page", (p, ctx) => p.page?.(page, ctx), page),
|
|
484
|
+
() => server === false ? void 0 : { id: page.id, name: "page_view", url: page.url, only: server ? void 0 : only }
|
|
485
|
+
);
|
|
486
|
+
},
|
|
487
|
+
async track(name, params, optionsInput = {}) {
|
|
488
|
+
const event = {
|
|
489
|
+
id: optionsInput.eventId ?? createEventId(),
|
|
490
|
+
name,
|
|
491
|
+
params: params ?? {},
|
|
492
|
+
options: optionsInput
|
|
493
|
+
};
|
|
494
|
+
const allowed = () => !(optionsInput.requireAnalyticsConsent && !consent?.analytics) && !(optionsInput.requireMarketingConsent && !consent?.marketing);
|
|
495
|
+
await trackAgainstProviders(
|
|
496
|
+
name,
|
|
497
|
+
optionsInput.only,
|
|
498
|
+
async (state) => allowed() && await call(state, "track", (p, ctx) => allowed() ? p.track?.(event, ctx) : void 0, name, event.params),
|
|
499
|
+
() => allowed() ? {
|
|
500
|
+
id: event.id,
|
|
501
|
+
name,
|
|
502
|
+
params: event.params,
|
|
503
|
+
url: getWindowLocation().url,
|
|
504
|
+
only: optionsInput.only,
|
|
505
|
+
providers: optionsInput.providers
|
|
506
|
+
} : void 0
|
|
507
|
+
);
|
|
508
|
+
},
|
|
509
|
+
async identify(userId, traits = {}) {
|
|
510
|
+
if (userId && userId !== identity.userId) identity = {};
|
|
511
|
+
identity = {
|
|
512
|
+
...identity,
|
|
513
|
+
...normalizeIdentity({ userId, email: traits.email, phone: traits.phone })
|
|
514
|
+
};
|
|
515
|
+
persistIdentity();
|
|
516
|
+
if (!ready) identifyQueued = true;
|
|
517
|
+
await identifyProviders(
|
|
518
|
+
Object.fromEntries(Object.entries(traits).filter(([key]) => key !== "email" && key !== "phone"))
|
|
519
|
+
);
|
|
520
|
+
},
|
|
521
|
+
async reset() {
|
|
522
|
+
identity = {};
|
|
523
|
+
storedContact = void 0;
|
|
524
|
+
identityStorage.clear?.();
|
|
525
|
+
for (const state of providerStates.values()) {
|
|
526
|
+
if (!state.setup) continue;
|
|
527
|
+
await call(state, "reset", (p, ctx) => p.reset?.(ctx));
|
|
528
|
+
}
|
|
529
|
+
},
|
|
530
|
+
async group(groupId, traits = {}) {
|
|
531
|
+
await trackAgainstProviders(
|
|
532
|
+
"group",
|
|
533
|
+
void 0,
|
|
534
|
+
(state) => call(state, "group", (p, ctx) => p.group?.(groupId, traits, ctx), groupId, traits)
|
|
535
|
+
);
|
|
536
|
+
},
|
|
537
|
+
async captureError(error, errorContext = {}) {
|
|
538
|
+
if (!errorsEnabled) return;
|
|
539
|
+
if (typeof error === "object" && error !== null) {
|
|
540
|
+
if (reportedErrors.has(error)) return;
|
|
541
|
+
reportedErrors.add(error);
|
|
542
|
+
}
|
|
543
|
+
const captured = { source: "manual", ...errorContext };
|
|
544
|
+
await trackAgainstProviders(
|
|
545
|
+
"captureError",
|
|
546
|
+
void 0,
|
|
547
|
+
(state) => call(state, "error", (p, ctx) => p.error?.(error, captured, ctx), error)
|
|
548
|
+
);
|
|
549
|
+
},
|
|
550
|
+
newPageSession() {
|
|
551
|
+
try {
|
|
552
|
+
pageSession?.();
|
|
553
|
+
} catch (cause) {
|
|
554
|
+
emitError({ scope: "auto-tracking", message: "Auto-tracking failed to start a page session", cause });
|
|
555
|
+
}
|
|
556
|
+
},
|
|
557
|
+
getSnapshot() {
|
|
558
|
+
return currentSnapshot;
|
|
559
|
+
},
|
|
560
|
+
subscribe(listener) {
|
|
561
|
+
listeners.add(listener);
|
|
562
|
+
return () => listeners.delete(listener);
|
|
563
|
+
},
|
|
564
|
+
destroy() {
|
|
565
|
+
for (const state of providerStates.values()) void teardown(state);
|
|
566
|
+
destroyed = true;
|
|
567
|
+
lifetime.abort();
|
|
568
|
+
unsubscribeConsent?.();
|
|
569
|
+
if (globalHandlers) {
|
|
570
|
+
window.removeEventListener("error", onWindowError);
|
|
571
|
+
window.removeEventListener("unhandledrejection", onRejection);
|
|
572
|
+
}
|
|
573
|
+
providerStates.clear();
|
|
574
|
+
listeners.clear();
|
|
575
|
+
}
|
|
576
|
+
};
|
|
577
|
+
return api;
|
|
578
|
+
}
|
|
579
|
+
|
|
580
|
+
export { createAnalyticsManager, createLocalStorageConsentStorage, createLocalStorageIdentityStorage, createMemoryConsentStorage, createMemoryIdentityStorage };
|
|
581
|
+
//# sourceMappingURL=chunk-LEAQJXJU.js.map
|
|
582
|
+
//# sourceMappingURL=chunk-LEAQJXJU.js.map
|