@hifilabs/pixel 0.1.4 → 0.1.6

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/dist/index.js CHANGED
@@ -1,711 +1,643 @@
1
- var BalancePixel = (() => {
2
- var __create = Object.create;
3
- var __defProp = Object.defineProperty;
4
- var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
- var __getOwnPropNames = Object.getOwnPropertyNames;
6
- var __getProtoOf = Object.getPrototypeOf;
7
- var __hasOwnProp = Object.prototype.hasOwnProperty;
8
- var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
9
- get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
10
- }) : x)(function(x) {
11
- if (typeof require !== "undefined")
12
- return require.apply(this, arguments);
13
- throw Error('Dynamic require of "' + x + '" is not supported');
14
- });
15
- var __export = (target, all) => {
16
- for (var name in all)
17
- __defProp(target, name, { get: all[name], enumerable: true });
18
- };
19
- var __copyProps = (to, from, except, desc) => {
20
- if (from && typeof from === "object" || typeof from === "function") {
21
- for (let key of __getOwnPropNames(from))
22
- if (!__hasOwnProp.call(to, key) && key !== except)
23
- __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
24
- }
25
- return to;
26
- };
27
- var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
28
- // If the importer is in node compatibility mode or this is not an ESM
29
- // file that has been converted to a CommonJS file using a Babel-
30
- // compatible transform (i.e. "__esModule" has not been set), then set
31
- // "default" to the CommonJS "module.exports" for node compatibility.
32
- isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
33
- mod
34
- ));
35
- var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
36
-
37
- // src/index.ts
38
- var src_exports = {};
39
- __export(src_exports, {
40
- BalanceAnalytics: () => BalanceAnalytics,
41
- getAttribution: () => getAttribution,
42
- getConsent: () => getConsent,
43
- getFanIdHash: () => getFanIdHash,
44
- getSessionId: () => getSessionId,
45
- hasConsent: () => hasConsent,
46
- identify: () => identify,
47
- page: () => page,
48
- purchase: () => purchase,
49
- setConsent: () => setConsent,
50
- track: () => track
51
- });
52
-
53
- // src/react/BalanceAnalytics.tsx
54
- var import_react = __toESM(__require("react"));
55
- var import_navigation = __require("next/navigation");
56
- function BalanceAnalyticsInner() {
57
- const pathname = (0, import_navigation.usePathname)();
58
- const searchParams = (0, import_navigation.useSearchParams)();
59
- const isFirstRender = (0, import_react.useRef)(true);
60
- const lastTrackedPath = (0, import_react.useRef)(null);
61
- (0, import_react.useEffect)(() => {
62
- if (typeof window === "undefined" || !window.balance)
63
- return;
64
- const currentPath = pathname + (searchParams?.toString() || "");
65
- if (lastTrackedPath.current === currentPath) {
66
- return;
67
- }
68
- if (isFirstRender.current) {
69
- isFirstRender.current = false;
70
- if (window._balanceInitialPageviewFired) {
71
- lastTrackedPath.current = currentPath;
72
- console.log("[BalanceAnalytics] Skipping initial pageview (script already fired)");
73
- return;
74
- }
75
- }
76
- const url = window.location.href;
77
- const title = document.title;
78
- lastTrackedPath.current = currentPath;
79
- window.balance.page({ url, title });
80
- }, [pathname, searchParams]);
81
- return null;
82
- }
83
- function BalanceAnalytics() {
84
- return /* @__PURE__ */ import_react.default.createElement(import_react.Suspense, { fallback: null }, /* @__PURE__ */ import_react.default.createElement(BalanceAnalyticsInner, null));
1
+ var __create = Object.create;
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __getProtoOf = Object.getPrototypeOf;
6
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
7
+ var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
8
+ var __export = (target, all) => {
9
+ for (var name in all)
10
+ __defProp(target, name, { get: all[name], enumerable: true });
11
+ };
12
+ var __copyProps = (to, from, except, desc) => {
13
+ if (from && typeof from === "object" || typeof from === "function") {
14
+ for (let key of __getOwnPropNames(from))
15
+ if (!__hasOwnProp.call(to, key) && key !== except)
16
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
85
17
  }
18
+ return to;
19
+ };
20
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
21
+ // If the importer is in node compatibility mode or this is not an ESM
22
+ // file that has been converted to a CommonJS file using a Babel-
23
+ // compatible transform (i.e. "__esModule" has not been set), then set
24
+ // "default" to the CommonJS "module.exports" for node compatibility.
25
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
26
+ mod
27
+ ));
28
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
29
+ var __publicField = (obj, key, value) => {
30
+ __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
31
+ return value;
32
+ };
86
33
 
87
- // src/index.ts
88
- (function() {
89
- function parseUserAgent(ua) {
90
- let device_type = "desktop";
91
- if (/ipad|tablet|android(?!.*mobile)/i.test(ua))
92
- device_type = "tablet";
93
- else if (/mobile|iphone|android.*mobile|blackberry|iemobile/i.test(ua))
94
- device_type = "mobile";
95
- let browser = "Unknown";
96
- if (/edg/i.test(ua))
97
- browser = "Edge";
98
- else if (/opr|opera/i.test(ua))
99
- browser = "Opera";
100
- else if (/firefox/i.test(ua))
101
- browser = "Firefox";
102
- else if (/chrome/i.test(ua))
103
- browser = "Chrome";
104
- else if (/safari/i.test(ua))
105
- browser = "Safari";
106
- let os = "Unknown";
107
- if (/iphone|ipad/i.test(ua))
108
- os = "iOS";
109
- else if (/android/i.test(ua))
110
- os = "Android";
111
- else if (/windows/i.test(ua))
112
- os = "Windows";
113
- else if (/mac os/i.test(ua))
114
- os = "macOS";
115
- else if (/linux/i.test(ua))
116
- os = "Linux";
117
- else if (/cros/i.test(ua))
118
- os = "ChromeOS";
119
- return { device_type, browser, os };
34
+ // src/index.ts
35
+ var src_exports = {};
36
+ __export(src_exports, {
37
+ BalanceAnalytics: () => BalanceAnalytics,
38
+ DEFAULT_GTM_CONSENT: () => DEFAULT_GTM_CONSENT,
39
+ GTMProvider: () => GTMProvider,
40
+ StorageManager: () => StorageManager,
41
+ getAttribution: () => getAttribution,
42
+ getConsent: () => getConsent,
43
+ getFanIdHash: () => getFanIdHash,
44
+ getSessionId: () => getSessionId,
45
+ getStorageManager: () => getStorageManager,
46
+ hasConsent: () => hasConsent,
47
+ identify: () => identify,
48
+ initStorageWithConsent: () => initStorageWithConsent,
49
+ page: () => page,
50
+ purchase: () => purchase,
51
+ setConsent: () => setConsent,
52
+ track: () => track,
53
+ useBalanceIdentify: () => useBalanceIdentify,
54
+ useGTMConsent: () => useGTMConsent
55
+ });
56
+ module.exports = __toCommonJS(src_exports);
57
+
58
+ // src/react/BalanceAnalytics.tsx
59
+ var import_react = __toESM(require("react"));
60
+ var import_navigation = require("next/navigation");
61
+ function BalanceAnalyticsInner() {
62
+ const pathname = (0, import_navigation.usePathname)();
63
+ const searchParams = (0, import_navigation.useSearchParams)();
64
+ const isFirstRender = (0, import_react.useRef)(true);
65
+ const lastTrackedPath = (0, import_react.useRef)(null);
66
+ (0, import_react.useEffect)(() => {
67
+ if (typeof window === "undefined" || !window.balance)
68
+ return;
69
+ const currentPath = pathname + (searchParams?.toString() || "");
70
+ if (lastTrackedPath.current === currentPath) {
71
+ return;
120
72
  }
121
- let cachedDeviceInfo = null;
122
- function getDeviceInfo() {
123
- if (!cachedDeviceInfo) {
124
- try {
125
- cachedDeviceInfo = parseUserAgent(navigator.userAgent);
126
- } catch {
127
- cachedDeviceInfo = { device_type: "desktop", browser: "Unknown", os: "Unknown" };
128
- }
73
+ if (isFirstRender.current) {
74
+ isFirstRender.current = false;
75
+ if (window._balanceInitialPageviewFired) {
76
+ lastTrackedPath.current = currentPath;
77
+ console.log("[BalanceAnalytics] Skipping initial pageview (script already fired)");
78
+ return;
129
79
  }
130
- return cachedDeviceInfo;
131
80
  }
132
- const currentScript = document.currentScript;
133
- const artistId = currentScript?.dataset.artistId;
134
- const projectId = currentScript?.dataset.projectId;
135
- const useEmulator = currentScript?.dataset.emulator === "true";
136
- const debug = currentScript?.dataset.debug === "true";
137
- const heartbeatInterval = parseInt(currentScript?.dataset.heartbeatInterval || "30000", 10);
138
- const heartbeatEnabled = currentScript?.dataset.heartbeat !== "false";
139
- const explicitSource = currentScript?.dataset.source;
140
- function detectTrackingSource() {
141
- if (explicitSource)
142
- return explicitSource;
143
- const hasDataLayer = typeof window.dataLayer !== "undefined" && Array.isArray(window.dataLayer);
144
- const hasGtag = typeof window.gtag === "function";
145
- if (hasDataLayer && hasGtag) {
146
- return "gtm";
147
- }
148
- return "pixel";
81
+ const url = window.location.href;
82
+ const title = document.title;
83
+ lastTrackedPath.current = currentPath;
84
+ window.balance.page({ url, title });
85
+ }, [pathname, searchParams]);
86
+ return null;
87
+ }
88
+ function BalanceAnalytics() {
89
+ return /* @__PURE__ */ import_react.default.createElement(import_react.Suspense, { fallback: null }, /* @__PURE__ */ import_react.default.createElement(BalanceAnalyticsInner, null));
90
+ }
91
+
92
+ // src/react/useBalanceIdentify.ts
93
+ var import_react2 = require("react");
94
+
95
+ // src/storage/StorageManager.ts
96
+ var DEFAULT_PREFIX = "balance_";
97
+ var StorageManager = class {
98
+ constructor(config) {
99
+ __publicField(this, "prefix");
100
+ __publicField(this, "currentTier");
101
+ this.prefix = config?.prefix ?? DEFAULT_PREFIX;
102
+ this.currentTier = config?.tier ?? "session";
103
+ }
104
+ /**
105
+ * Get the appropriate storage based on current tier
106
+ */
107
+ getStorage() {
108
+ if (typeof window === "undefined")
109
+ return null;
110
+ try {
111
+ return this.currentTier === "local" ? localStorage : sessionStorage;
112
+ } catch {
113
+ return null;
149
114
  }
150
- let trackingSource = "pixel";
151
- if (!artistId) {
152
- console.error("[BALANCE Pixel] Error: data-artist-id attribute is required");
115
+ }
116
+ /**
117
+ * Get current storage tier
118
+ */
119
+ getTier() {
120
+ return this.currentTier;
121
+ }
122
+ /**
123
+ * Upgrade storage tier (e.g., when user gives consent)
124
+ * Automatically migrates data from sessionStorage to localStorage
125
+ */
126
+ upgradeTier(newTier) {
127
+ if (typeof window === "undefined")
153
128
  return;
129
+ if (this.currentTier === newTier)
130
+ return;
131
+ const oldTier = this.currentTier;
132
+ if (oldTier === "session" && newTier === "local") {
133
+ this.migrateToLocal();
154
134
  }
155
- const SESSION_KEY = "session_id";
156
- const SESSION_TIMESTAMP_KEY = "session_timestamp";
157
- const ATTRIBUTION_KEY = "attribution";
158
- const FAN_ID_KEY = "fan_id_hash";
159
- const CONSENT_STORAGE_KEY = "balance_consent";
160
- const SESSION_DURATION = 60 * 60 * 1e3;
161
- const STORAGE_PREFIX = "balance_";
162
- const API_ENDPOINT = useEmulator ? `http://localhost:5001/artist-os-distro/us-central1/pixelEndpoint` : `https://us-central1-artist-os-distro.cloudfunctions.net/pixelEndpoint`;
163
- let sessionId = null;
164
- let fanIdHash = null;
165
- let consent = null;
166
- let attribution = {};
167
- let eventQueue = [];
168
- let flushTimer = null;
169
- let currentStorageTier = "session";
170
- let heartbeatTimer = null;
171
- let pageStartTime = 0;
172
- let activeTime = 0;
173
- let lastActiveTimestamp = 0;
174
- let isPageVisible = true;
175
- const IDLE_TIMEOUT = 2 * 60 * 1e3;
176
- let lastActivityTime = Date.now();
177
- let isIdle = false;
178
- const log = (...args) => {
179
- if (debug)
180
- console.log("[BALANCE Pixel]", ...args);
181
- };
182
- function getStorage() {
183
- try {
184
- return currentStorageTier === "local" ? localStorage : sessionStorage;
185
- } catch {
186
- return null;
187
- }
135
+ this.currentTier = newTier;
136
+ console.log(`[StorageManager] Upgraded tier: ${oldTier} -> ${newTier}`);
137
+ }
138
+ /**
139
+ * Downgrade storage tier (e.g., when user revokes consent)
140
+ * Clears localStorage data and falls back to sessionStorage
141
+ */
142
+ downgradeTier(newTier) {
143
+ if (typeof window === "undefined")
144
+ return;
145
+ if (this.currentTier === newTier)
146
+ return;
147
+ const oldTier = this.currentTier;
148
+ if (oldTier === "local" && newTier === "session") {
149
+ this.clearLocalStorage();
188
150
  }
189
- function storageGet(key) {
190
- const storage = getStorage();
191
- if (!storage)
192
- return null;
193
- try {
194
- const fullKey = STORAGE_PREFIX + key;
195
- let value = storage.getItem(fullKey);
196
- if (!value && currentStorageTier === "session") {
197
- try {
198
- value = localStorage.getItem(fullKey);
199
- } catch {
200
- }
151
+ this.currentTier = newTier;
152
+ console.log(`[StorageManager] Downgraded tier: ${oldTier} -> ${newTier}`);
153
+ }
154
+ /**
155
+ * Migrate all prefixed data from sessionStorage to localStorage
156
+ */
157
+ migrateToLocal() {
158
+ if (typeof window === "undefined")
159
+ return;
160
+ try {
161
+ const keysToMigrate = [];
162
+ for (let i = 0; i < sessionStorage.length; i++) {
163
+ const key = sessionStorage.key(i);
164
+ if (key?.startsWith(this.prefix)) {
165
+ keysToMigrate.push(key);
201
166
  }
202
- return value;
203
- } catch {
204
- return null;
205
167
  }
206
- }
207
- function storageSet(key, value) {
208
- const storage = getStorage();
209
- if (!storage)
210
- return;
211
- try {
212
- storage.setItem(STORAGE_PREFIX + key, value);
213
- } catch {
214
- }
215
- }
216
- function upgradeStorageTier() {
217
- if (currentStorageTier === "local")
218
- return;
219
- log("Upgrading storage tier: session -> local");
220
- try {
221
- const keysToMigrate = [];
222
- for (let i = 0; i < sessionStorage.length; i++) {
223
- const key = sessionStorage.key(i);
224
- if (key?.startsWith(STORAGE_PREFIX)) {
225
- keysToMigrate.push(key);
226
- }
227
- }
228
- for (const key of keysToMigrate) {
229
- const value = sessionStorage.getItem(key);
230
- if (value) {
231
- localStorage.setItem(key, value);
232
- }
168
+ for (const key of keysToMigrate) {
169
+ const value = sessionStorage.getItem(key);
170
+ if (value) {
171
+ localStorage.setItem(key, value);
172
+ console.log(`[StorageManager] Migrated: ${key}`);
233
173
  }
234
- for (const key of keysToMigrate) {
235
- sessionStorage.removeItem(key);
236
- }
237
- currentStorageTier = "local";
238
- log(`Storage tier upgraded, migrated ${keysToMigrate.length} items`);
239
- } catch (error) {
240
- console.error("[BALANCE Pixel] Storage migration failed:", error);
241
- }
242
- }
243
- function generateUUID() {
244
- if (crypto && crypto.randomUUID) {
245
- return crypto.randomUUID();
246
174
  }
247
- return "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g, (c) => {
248
- const r = Math.random() * 16 | 0;
249
- const v = c === "x" ? r : r & 3 | 8;
250
- return v.toString(16);
251
- });
252
- }
253
- function getOrCreateSession() {
254
- try {
255
- const stored = storageGet(SESSION_KEY);
256
- const timestamp = storageGet(SESSION_TIMESTAMP_KEY);
257
- if (stored && timestamp) {
258
- const age = Date.now() - parseInt(timestamp, 10);
259
- if (age < SESSION_DURATION) {
260
- storageSet(SESSION_TIMESTAMP_KEY, Date.now().toString());
261
- return stored;
262
- }
263
- }
264
- const newId = generateUUID();
265
- storageSet(SESSION_KEY, newId);
266
- storageSet(SESSION_TIMESTAMP_KEY, Date.now().toString());
267
- return newId;
268
- } catch (e) {
269
- return generateUUID();
175
+ for (const key of keysToMigrate) {
176
+ sessionStorage.removeItem(key);
270
177
  }
178
+ console.log(`[StorageManager] Migration complete: ${keysToMigrate.length} items`);
179
+ } catch (error) {
180
+ console.error("[StorageManager] Migration failed:", error);
271
181
  }
272
- function extractUTM() {
273
- const params = new URLSearchParams(window.location.search);
274
- const utm = {};
275
- ["source", "medium", "campaign", "content", "term"].forEach((param) => {
276
- const value = params.get(`utm_${param}`);
277
- if (value)
278
- utm[`utm_${param}`] = value;
279
- });
280
- return utm;
281
- }
282
- function captureAttribution() {
283
- try {
284
- const stored = storageGet(ATTRIBUTION_KEY);
285
- if (stored) {
286
- attribution = JSON.parse(stored);
287
- log("Loaded attribution:", attribution);
288
- return;
289
- }
290
- const utm = extractUTM();
291
- if (Object.keys(utm).length > 0) {
292
- attribution = utm;
293
- storageSet(ATTRIBUTION_KEY, JSON.stringify(utm));
294
- log("Captured attribution:", attribution);
182
+ }
183
+ /**
184
+ * Clear all prefixed data from localStorage
185
+ */
186
+ clearLocalStorage() {
187
+ if (typeof window === "undefined")
188
+ return;
189
+ try {
190
+ const keysToRemove = [];
191
+ for (let i = 0; i < localStorage.length; i++) {
192
+ const key = localStorage.key(i);
193
+ if (key?.startsWith(this.prefix)) {
194
+ keysToRemove.push(key);
295
195
  }
296
- } catch (e) {
297
- }
298
- }
299
- function loadFanId() {
300
- try {
301
- fanIdHash = storageGet(FAN_ID_KEY);
302
- } catch (e) {
303
196
  }
304
- }
305
- function loadConsent() {
306
- try {
307
- const stored = localStorage.getItem(CONSENT_STORAGE_KEY);
308
- if (stored) {
309
- const parsed = JSON.parse(stored);
310
- consent = parsed.preferences || null;
311
- log("Loaded consent:", consent);
312
- }
313
- } catch (e) {
197
+ for (const key of keysToRemove) {
198
+ localStorage.removeItem(key);
314
199
  }
200
+ console.log(`[StorageManager] Cleared ${keysToRemove.length} items from localStorage`);
201
+ } catch (error) {
202
+ console.error("[StorageManager] Clear failed:", error);
315
203
  }
316
- function setConsent2(preferences) {
317
- const previousConsent = consent;
318
- consent = preferences;
319
- try {
320
- const storage = {
321
- preferences,
322
- method: "explicit",
323
- version: 1
324
- };
325
- localStorage.setItem(CONSENT_STORAGE_KEY, JSON.stringify(storage));
326
- log("Consent saved:", preferences);
327
- } catch (e) {
328
- console.error("[BALANCE Pixel] Could not save consent:", e);
329
- }
330
- if (preferences.analytics === true) {
331
- upgradeStorageTier();
332
- }
333
- const event = buildEvent({
334
- event_name: "consent_updated",
335
- metadata: {
336
- consent_preferences: preferences,
337
- consent_method: "explicit",
338
- previous_consent: previousConsent || void 0
204
+ }
205
+ /**
206
+ * Get item from current storage tier
207
+ */
208
+ getItem(key) {
209
+ const storage = this.getStorage();
210
+ if (!storage)
211
+ return null;
212
+ try {
213
+ const fullKey = this.prefix + key;
214
+ let value = storage.getItem(fullKey);
215
+ if (!value && this.currentTier === "session") {
216
+ try {
217
+ value = localStorage.getItem(fullKey);
218
+ } catch {
339
219
  }
340
- });
341
- enqueueEvent(event);
342
- }
343
- function getConsent2() {
344
- return consent;
345
- }
346
- function hasConsent2(type) {
347
- return consent?.[type] === true;
348
- }
349
- async function hashEmail(email) {
350
- const normalized = email.toLowerCase().trim();
351
- const encoder = new TextEncoder();
352
- const data = encoder.encode(normalized);
353
- const hashBuffer = await crypto.subtle.digest("SHA-256", data);
354
- const hashArray = Array.from(new Uint8Array(hashBuffer));
355
- return hashArray.map((b) => b.toString(16).padStart(2, "0")).join("");
356
- }
357
- function buildEvent(partial) {
358
- const deviceInfo = getDeviceInfo();
359
- const base = {
360
- artist_id: artistId,
361
- fan_session_id: sessionId,
362
- fan_id_hash: fanIdHash || void 0,
363
- timestamp: (/* @__PURE__ */ new Date()).toISOString(),
364
- source_url: window.location.href,
365
- referrer_url: document.referrer || void 0,
366
- user_agent: navigator.userAgent,
367
- // Device info (parsed client-side)
368
- device_type: deviceInfo.device_type,
369
- browser: deviceInfo.browser,
370
- os: deviceInfo.os,
371
- // Tracking source (gtm, pixel, etc.) - enables filtering in artistHQ
372
- tracking_source: trackingSource,
373
- ...partial,
374
- ...attribution
375
- };
376
- if (projectId && !partial.projectId) {
377
- base.projectId = projectId;
378
- }
379
- return base;
380
- }
381
- function enqueueEvent(event) {
382
- eventQueue.push(event);
383
- log("Event queued:", event.event_name, "(queue:", eventQueue.length, ")");
384
- if (eventQueue.length >= 10) {
385
- flush();
386
220
  }
221
+ return value;
222
+ } catch {
223
+ return null;
387
224
  }
388
- async function flush() {
389
- if (eventQueue.length === 0)
390
- return;
391
- const events = [...eventQueue];
392
- eventQueue = [];
393
- log("Flushing", events.length, "events to", API_ENDPOINT);
394
- try {
395
- const response = await fetch(API_ENDPOINT, {
396
- method: "POST",
397
- headers: { "Content-Type": "application/json" },
398
- body: JSON.stringify({ events }),
399
- keepalive: true
400
- // Send even if page is closing
401
- });
402
- if (!response.ok) {
403
- throw new Error(`HTTP ${response.status}`);
404
- }
405
- log("Events sent successfully");
406
- } catch (error) {
407
- console.error("[BALANCE Pixel] Failed to send events:", error);
408
- if (eventQueue.length < 50) {
409
- eventQueue.push(...events);
410
- }
411
- }
225
+ }
226
+ /**
227
+ * Set item in current storage tier
228
+ */
229
+ setItem(key, value) {
230
+ const storage = this.getStorage();
231
+ if (!storage)
232
+ return;
233
+ try {
234
+ const fullKey = this.prefix + key;
235
+ storage.setItem(fullKey, value);
236
+ } catch (error) {
237
+ console.error("[StorageManager] setItem failed:", error);
412
238
  }
413
- function startFlushTimer() {
414
- if (flushTimer)
415
- clearInterval(flushTimer);
416
- flushTimer = window.setInterval(() => {
417
- if (eventQueue.length > 0)
418
- flush();
419
- }, 5e3);
239
+ }
240
+ /**
241
+ * Remove item from all storage tiers
242
+ */
243
+ removeItem(key) {
244
+ if (typeof window === "undefined")
245
+ return;
246
+ const fullKey = this.prefix + key;
247
+ try {
248
+ sessionStorage.removeItem(fullKey);
249
+ } catch {
420
250
  }
421
- function startActiveTimeTracking() {
422
- if (!lastActiveTimestamp) {
423
- pageStartTime = Date.now();
424
- }
425
- lastActiveTimestamp = Date.now();
426
- isPageVisible = true;
427
- log("Active time tracking started/resumed");
251
+ try {
252
+ localStorage.removeItem(fullKey);
253
+ } catch {
428
254
  }
429
- function pauseActiveTimeTracking() {
430
- if (lastActiveTimestamp && isPageVisible) {
431
- activeTime += Date.now() - lastActiveTimestamp;
432
- }
433
- isPageVisible = false;
434
- log("Active time tracking paused, accumulated:", activeTime, "ms");
255
+ }
256
+ /**
257
+ * Get JSON-parsed item
258
+ */
259
+ getJSON(key) {
260
+ const value = this.getItem(key);
261
+ if (!value)
262
+ return null;
263
+ try {
264
+ return JSON.parse(value);
265
+ } catch {
266
+ return null;
435
267
  }
436
- function getCumulativeActiveTime() {
437
- let total = activeTime;
438
- if (isPageVisible && lastActiveTimestamp) {
439
- total += Date.now() - lastActiveTimestamp;
440
- }
441
- return total;
268
+ }
269
+ /**
270
+ * Set JSON-stringified item
271
+ */
272
+ setJSON(key, value) {
273
+ try {
274
+ this.setItem(key, JSON.stringify(value));
275
+ } catch {
442
276
  }
443
- function resetIdleTimer() {
444
- lastActivityTime = Date.now();
445
- if (isIdle) {
446
- isIdle = false;
447
- startActiveTimeTracking();
448
- log("User returned from idle");
449
- }
277
+ }
278
+ };
279
+ var storageManagerInstance = null;
280
+ function getStorageManager() {
281
+ if (!storageManagerInstance) {
282
+ storageManagerInstance = new StorageManager();
283
+ }
284
+ return storageManagerInstance;
285
+ }
286
+ function initStorageWithConsent(hasAnalyticsConsent) {
287
+ const manager = getStorageManager();
288
+ if (hasAnalyticsConsent) {
289
+ manager.upgradeTier("local");
290
+ } else {
291
+ manager.downgradeTier("session");
292
+ }
293
+ return manager;
294
+ }
295
+
296
+ // src/react/useBalanceIdentify.ts
297
+ var PENDING_IDENTIFY_KEY = "pending_identify";
298
+ var MAX_PENDING_AGE_MS = 30 * 60 * 1e3;
299
+ function checkAnalyticsConsent() {
300
+ if (typeof window === "undefined")
301
+ return false;
302
+ try {
303
+ if (window.balance?.hasConsent) {
304
+ return window.balance.hasConsent("analytics");
305
+ }
306
+ const stored = localStorage.getItem("balance_consent");
307
+ if (stored) {
308
+ const consent = JSON.parse(stored);
309
+ return consent?.preferences?.analytics !== false;
310
+ }
311
+ return true;
312
+ } catch {
313
+ return true;
314
+ }
315
+ }
316
+ function useBalanceIdentify() {
317
+ const pollIntervalRef = (0, import_react2.useRef)(null);
318
+ const hasProcessedPending = (0, import_react2.useRef)(false);
319
+ const storageManager = typeof window !== "undefined" ? getStorageManager() : null;
320
+ const getPendingIdentify = (0, import_react2.useCallback)(() => {
321
+ if (!storageManager)
322
+ return null;
323
+ const pending = storageManager.getJSON(PENDING_IDENTIFY_KEY);
324
+ if (!pending)
325
+ return null;
326
+ if (Date.now() - pending.timestamp > MAX_PENDING_AGE_MS) {
327
+ storageManager.removeItem(PENDING_IDENTIFY_KEY);
328
+ return null;
450
329
  }
451
- function sendHeartbeat() {
452
- if (document.visibilityState === "hidden") {
453
- log("Skipping heartbeat - tab hidden");
454
- return;
455
- }
456
- if (Date.now() - lastActivityTime > IDLE_TIMEOUT) {
457
- if (!isIdle) {
458
- isIdle = true;
459
- pauseActiveTimeTracking();
460
- log("User idle - pausing heartbeat");
461
- }
462
- return;
463
- }
464
- const timeOnPage = getCumulativeActiveTime();
465
- const timeOnPageSeconds = Math.round(timeOnPage / 1e3);
466
- const event = buildEvent({
467
- event_name: "engagement_heartbeat",
468
- metadata: {
469
- time_on_page_seconds: timeOnPageSeconds,
470
- time_on_page_ms: timeOnPage,
471
- heartbeat_interval_ms: heartbeatInterval,
472
- is_active: isPageVisible && !isIdle
473
- }
474
- });
475
- enqueueEvent(event);
476
- log("Heartbeat sent:", timeOnPageSeconds, "seconds active");
330
+ return pending;
331
+ }, [storageManager]);
332
+ const setPendingIdentify = (0, import_react2.useCallback)((email, traits) => {
333
+ if (!storageManager)
334
+ return;
335
+ storageManager.setJSON(PENDING_IDENTIFY_KEY, {
336
+ email,
337
+ traits,
338
+ timestamp: Date.now()
339
+ });
340
+ }, [storageManager]);
341
+ const clearPendingIdentify = (0, import_react2.useCallback)(() => {
342
+ if (!storageManager)
343
+ return;
344
+ storageManager.removeItem(PENDING_IDENTIFY_KEY);
345
+ }, [storageManager]);
346
+ const identify2 = (0, import_react2.useCallback)((email, traits) => {
347
+ const hasConsent2 = checkAnalyticsConsent();
348
+ if (!hasConsent2) {
349
+ console.log("[useBalanceIdentify] Skipping identify - user declined analytics consent");
350
+ return false;
477
351
  }
478
- function startHeartbeat() {
479
- if (!heartbeatEnabled) {
480
- log('Heartbeat disabled via data-heartbeat="false"');
352
+ if (storageManager) {
353
+ initStorageWithConsent(hasConsent2);
354
+ }
355
+ if (typeof window !== "undefined" && window.balance?.identify) {
356
+ window.balance.identify(email, traits);
357
+ console.log("[useBalanceIdentify] User identified:", email.split("@")[0] + "***");
358
+ clearPendingIdentify();
359
+ return true;
360
+ }
361
+ console.log("[useBalanceIdentify] Pixel not ready, queueing identify");
362
+ setPendingIdentify(email, traits);
363
+ return false;
364
+ }, [storageManager, clearPendingIdentify, setPendingIdentify]);
365
+ const processPendingIdentify = (0, import_react2.useCallback)(() => {
366
+ if (hasProcessedPending.current)
367
+ return;
368
+ const pending = getPendingIdentify();
369
+ if (!pending)
370
+ return;
371
+ if (typeof window !== "undefined" && window.balance?.identify) {
372
+ const hasConsent2 = checkAnalyticsConsent();
373
+ if (!hasConsent2) {
374
+ console.log("[useBalanceIdentify] Clearing pending identify - user declined analytics consent");
375
+ clearPendingIdentify();
376
+ hasProcessedPending.current = true;
481
377
  return;
482
378
  }
483
- if (heartbeatTimer)
484
- clearInterval(heartbeatTimer);
485
- startActiveTimeTracking();
486
- heartbeatTimer = window.setInterval(() => {
487
- sendHeartbeat();
488
- }, heartbeatInterval);
489
- log("Heartbeat started with interval:", heartbeatInterval, "ms");
490
- }
491
- function stopHeartbeat() {
492
- if (heartbeatTimer) {
493
- clearInterval(heartbeatTimer);
494
- heartbeatTimer = null;
495
- }
496
- if (heartbeatEnabled) {
497
- sendHeartbeat();
498
- log("Heartbeat stopped, final time sent");
379
+ if (storageManager) {
380
+ initStorageWithConsent(hasConsent2);
499
381
  }
500
- }
501
- function trackPageView(options = {}) {
502
- const event = buildEvent({
503
- event_name: "pageview",
504
- page_title: options.title || document.title,
505
- source_url: options.url || window.location.href
506
- });
507
- enqueueEvent(event);
508
- }
509
- function track2(eventName, properties = {}) {
510
- const event = buildEvent({
511
- event_name: "custom",
512
- metadata: {
513
- event_type: eventName,
514
- ...properties
515
- }
516
- });
517
- enqueueEvent(event);
518
- }
519
- async function identify2(email, traits = {}) {
520
- try {
521
- if (consent && consent.analytics === false) {
522
- log("Identify skipped - user declined analytics consent");
523
- return;
524
- }
525
- fanIdHash = await hashEmail(email);
526
- if (consent?.analytics === true) {
527
- upgradeStorageTier();
528
- }
529
- storageSet(FAN_ID_KEY, fanIdHash);
530
- const emailParts = email.split("@");
531
- const maskedEmail = emailParts[0].charAt(0) + "***@" + (emailParts[1] || "");
532
- log("Fan identified:", {
533
- name: traits.name || "(no name)",
534
- email: maskedEmail,
535
- hash: fanIdHash.substring(0, 16) + "...",
536
- traits,
537
- storageTier: currentStorageTier
538
- });
539
- const event = buildEvent({
540
- event_name: "identify",
541
- fan_id_hash: fanIdHash,
542
- metadata: {
543
- email_sha256: fanIdHash,
544
- traits,
545
- consent_preferences: consent || void 0,
546
- storage_tier: currentStorageTier
382
+ console.log("[useBalanceIdentify] Processing pending identify");
383
+ window.balance.identify(pending.email, pending.traits);
384
+ clearPendingIdentify();
385
+ hasProcessedPending.current = true;
386
+ }
387
+ }, [getPendingIdentify, clearPendingIdentify, storageManager]);
388
+ (0, import_react2.useEffect)(() => {
389
+ processPendingIdentify();
390
+ const pending = getPendingIdentify();
391
+ if (pending && !hasProcessedPending.current) {
392
+ pollIntervalRef.current = setInterval(() => {
393
+ processPendingIdentify();
394
+ if (hasProcessedPending.current || !getPendingIdentify()) {
395
+ if (pollIntervalRef.current) {
396
+ clearInterval(pollIntervalRef.current);
397
+ pollIntervalRef.current = null;
547
398
  }
548
- });
549
- enqueueEvent(event);
550
- } catch (error) {
551
- console.error("[BALANCE Pixel] Failed to identify:", error);
552
- }
553
- }
554
- function purchase2(revenue, currency = "USD", properties = {}) {
555
- const event = buildEvent({
556
- event_name: "purchase",
557
- metadata: {
558
- revenue,
559
- currency,
560
- ...properties
561
399
  }
562
- });
563
- enqueueEvent(event);
400
+ }, 500);
564
401
  }
565
- function init() {
566
- trackingSource = detectTrackingSource();
567
- log("Tracking source detected:", trackingSource);
568
- loadConsent();
569
- if (consent?.analytics === true) {
570
- currentStorageTier = "local";
571
- log("Storage tier: local (analytics consent granted)");
572
- } else {
573
- currentStorageTier = "session";
574
- log("Storage tier: session (privacy by default)");
402
+ return () => {
403
+ if (pollIntervalRef.current) {
404
+ clearInterval(pollIntervalRef.current);
405
+ pollIntervalRef.current = null;
575
406
  }
576
- sessionId = getOrCreateSession();
577
- loadFanId();
578
- if (!consent) {
579
- consent = {
580
- analytics: true,
581
- marketing: true,
582
- personalization: true,
583
- timestamp: (/* @__PURE__ */ new Date()).toISOString()
584
- };
585
- log("Default consent enabled (all tracking):", consent);
586
- upgradeStorageTier();
587
- }
588
- captureAttribution();
589
- startFlushTimer();
590
- log("Initialized", {
591
- artistId,
592
- projectId: projectId || "(none - will track to all projects)",
593
- sessionId,
594
- fanIdHash,
595
- consent,
596
- storageTier: currentStorageTier,
597
- trackingSource,
598
- useEmulator,
599
- endpoint: API_ENDPOINT
600
- });
601
- window._balanceInitialPageviewFired = true;
602
- trackPageView();
603
- startHeartbeat();
604
- ["mousemove", "keydown", "scroll", "touchstart"].forEach((eventType) => {
605
- document.addEventListener(eventType, resetIdleTimer, { passive: true });
606
- });
607
- window.addEventListener("beforeunload", () => {
608
- stopHeartbeat();
609
- flush();
610
- });
611
- document.addEventListener("visibilitychange", () => {
612
- if (document.visibilityState === "hidden") {
613
- pauseActiveTimeTracking();
614
- flush();
615
- } else {
616
- startActiveTimeTracking();
617
- }
618
- });
619
- }
620
- window.balance = {
621
- track: track2,
622
- identify: identify2,
623
- page: trackPageView,
624
- purchase: purchase2,
625
- getSessionId: () => sessionId,
626
- getFanIdHash: () => fanIdHash,
627
- getAttribution: () => attribution,
628
- setConsent: setConsent2,
629
- getConsent: getConsent2,
630
- hasConsent: hasConsent2
631
407
  };
632
- if (document.readyState === "loading") {
633
- document.addEventListener("DOMContentLoaded", init);
634
- } else {
635
- init();
636
- }
637
- log("Pixel script loaded");
638
- })();
639
- var track = (eventName, properties) => {
640
- if (typeof window === "undefined")
641
- return;
642
- if (window.balance) {
643
- window.balance.track(eventName, properties);
644
- } else {
645
- console.warn("[Balance Pixel] track() called before pixel initialized:", eventName);
646
- }
647
- };
648
- var identify = (email, traits) => {
649
- if (typeof window === "undefined")
650
- return Promise.resolve();
651
- if (window.balance) {
652
- return window.balance.identify(email, traits);
653
- } else {
654
- console.warn("[Balance Pixel] identify() called before pixel initialized");
655
- return Promise.resolve();
656
- }
408
+ }, [processPendingIdentify, getPendingIdentify]);
409
+ return {
410
+ /**
411
+ * Identify a user with BALANCE pixel.
412
+ * Handles consent checks, queuing, and storage tier automatically.
413
+ */
414
+ identify: identify2,
415
+ /**
416
+ * Check if analytics consent is currently granted
417
+ */
418
+ hasAnalyticsConsent: checkAnalyticsConsent
657
419
  };
658
- var page = (options) => {
659
- if (typeof window === "undefined")
660
- return;
661
- if (window.balance) {
662
- window.balance.page(options);
663
- } else {
664
- console.warn("[Balance Pixel] page() called before pixel initialized");
420
+ }
421
+
422
+ // src/react/GTMProvider.tsx
423
+ var import_react4 = __toESM(require("react"));
424
+ var import_script = __toESM(require("next/script"));
425
+
426
+ // src/react/useGTMConsent.ts
427
+ var import_react3 = require("react");
428
+ function useGTMConsent(options = {}) {
429
+ const { pollInterval = 1e3, debug = false } = options;
430
+ const lastConsentRef = (0, import_react3.useRef)("");
431
+ const log = (0, import_react3.useCallback)(
432
+ (...args) => {
433
+ if (debug) {
434
+ console.log("[useGTMConsent]", ...args);
435
+ }
436
+ },
437
+ [debug]
438
+ );
439
+ const mapPixelConsentToGTM = (0, import_react3.useCallback)(() => {
440
+ if (typeof window === "undefined" || !window.balance) {
441
+ return {
442
+ ad_storage: "denied",
443
+ ad_user_data: "denied",
444
+ ad_personalization: "denied",
445
+ analytics_storage: "denied"
446
+ };
665
447
  }
666
- };
667
- var purchase = (revenue, currency, properties) => {
448
+ const hasAnalytics = window.balance.hasConsent("analytics");
449
+ const hasMarketing = window.balance.hasConsent("marketing");
450
+ const analyticsState = hasAnalytics ? "granted" : "denied";
451
+ const marketingState = hasMarketing ? "granted" : "denied";
452
+ return {
453
+ analytics_storage: analyticsState,
454
+ ad_storage: marketingState,
455
+ ad_user_data: marketingState,
456
+ ad_personalization: marketingState
457
+ };
458
+ }, []);
459
+ const updateGTMConsent = (0, import_react3.useCallback)(
460
+ (consentConfig) => {
461
+ if (typeof window === "undefined")
462
+ return;
463
+ window.dataLayer = window.dataLayer || [];
464
+ if (typeof window.gtag !== "function") {
465
+ window.gtag = function gtag(...args) {
466
+ window.dataLayer.push(args);
467
+ };
468
+ }
469
+ window.gtag("consent", "update", consentConfig);
470
+ log("Pushed consent update to GTM:", consentConfig);
471
+ },
472
+ [log]
473
+ );
474
+ const syncConsent = (0, import_react3.useCallback)(() => {
475
+ const currentConsent = mapPixelConsentToGTM();
476
+ const consentKey = JSON.stringify(currentConsent);
477
+ if (consentKey !== lastConsentRef.current) {
478
+ lastConsentRef.current = consentKey;
479
+ updateGTMConsent(currentConsent);
480
+ }
481
+ }, [mapPixelConsentToGTM, updateGTMConsent]);
482
+ (0, import_react3.useEffect)(() => {
668
483
  if (typeof window === "undefined")
669
484
  return;
670
- if (window.balance) {
671
- window.balance.purchase(revenue, currency, properties);
672
- } else {
673
- console.warn("[Balance Pixel] purchase() called before pixel initialized");
674
- }
675
- };
676
- var getSessionId = () => {
677
- if (typeof window === "undefined")
678
- return null;
679
- return window.balance?.getSessionId() ?? null;
680
- };
681
- var getFanIdHash = () => {
682
- if (typeof window === "undefined")
683
- return null;
684
- return window.balance?.getFanIdHash() ?? null;
685
- };
686
- var getAttribution = () => {
687
- if (typeof window === "undefined")
688
- return {};
689
- return window.balance?.getAttribution() ?? {};
485
+ syncConsent();
486
+ const intervalId = setInterval(syncConsent, pollInterval);
487
+ const handleStorageChange = (e) => {
488
+ if (e.key === "balance_consent") {
489
+ log("Consent changed in another tab, syncing...");
490
+ syncConsent();
491
+ }
492
+ };
493
+ window.addEventListener("storage", handleStorageChange);
494
+ return () => {
495
+ clearInterval(intervalId);
496
+ window.removeEventListener("storage", handleStorageChange);
497
+ };
498
+ }, [syncConsent, pollInterval, log]);
499
+ return {
500
+ /**
501
+ * Manually trigger a consent sync
502
+ */
503
+ syncConsent,
504
+ /**
505
+ * Get current consent config
506
+ */
507
+ getConsentConfig: mapPixelConsentToGTM
690
508
  };
691
- var setConsent = (preferences) => {
692
- if (typeof window === "undefined")
693
- return;
694
- if (window.balance) {
695
- window.balance.setConsent(preferences);
696
- } else {
697
- console.warn("[Balance Pixel] setConsent() called before pixel initialized");
509
+ }
510
+
511
+ // src/types/gtm.ts
512
+ var DEFAULT_GTM_CONSENT = {
513
+ ad_storage: "denied",
514
+ ad_user_data: "denied",
515
+ ad_personalization: "denied",
516
+ analytics_storage: "denied"
517
+ };
518
+
519
+ // src/react/GTMProvider.tsx
520
+ function GTMProvider({ gtmId, children, debug = false }) {
521
+ const resolvedGtmId = gtmId || process.env.NEXT_PUBLIC_GTM_ID;
522
+ useGTMConsent({ debug });
523
+ if (!resolvedGtmId) {
524
+ if (debug) {
525
+ console.log("[GTMProvider] No GTM ID configured, skipping GTM initialization");
526
+ }
527
+ return /* @__PURE__ */ import_react4.default.createElement(import_react4.default.Fragment, null, children);
528
+ }
529
+ const consentJson = JSON.stringify(DEFAULT_GTM_CONSENT);
530
+ const consentScript = `
531
+ window.dataLayer = window.dataLayer || [];
532
+ function gtag(){dataLayer.push(arguments);}
533
+ window.gtag = gtag;
534
+ gtag('consent', 'default', ${consentJson});
535
+ gtag('set', 'wait_for_update', 500);
536
+ ${debug ? "console.log('[GTM] Default consent initialized (denied)');" : ""}
537
+ `;
538
+ return /* @__PURE__ */ import_react4.default.createElement(import_react4.default.Fragment, null, /* @__PURE__ */ import_react4.default.createElement(
539
+ import_script.default,
540
+ {
541
+ id: "gtm-consent-default",
542
+ strategy: "beforeInteractive",
543
+ dangerouslySetInnerHTML: { __html: consentScript }
544
+ }
545
+ ), /* @__PURE__ */ import_react4.default.createElement(
546
+ import_script.default,
547
+ {
548
+ id: "gtm-script",
549
+ strategy: "afterInteractive",
550
+ dangerouslySetInnerHTML: {
551
+ __html: `
552
+ (function(w,d,s,l,i){w[l]=w[l]||[];w[l].push({'gtm.start':
553
+ new Date().getTime(),event:'gtm.js'});var f=d.getElementsByTagName(s)[0],
554
+ j=d.createElement(s),dl=l!='dataLayer'?'&l='+l:'';j.async=true;j.src=
555
+ 'https://www.googletagmanager.com/gtm.js?id='+i+dl;f.parentNode.insertBefore(j,f);
556
+ })(window,document,'script','dataLayer','${resolvedGtmId}');
557
+ `
558
+ }
698
559
  }
699
- };
700
- var getConsent = () => {
701
- if (typeof window === "undefined")
702
- return null;
703
- return window.balance?.getConsent() ?? null;
704
- };
705
- var hasConsent = (type) => {
706
- if (typeof window === "undefined")
707
- return false;
708
- return window.balance?.hasConsent(type) ?? false;
709
- };
710
- return __toCommonJS(src_exports);
711
- })();
560
+ ), /* @__PURE__ */ import_react4.default.createElement("noscript", null, /* @__PURE__ */ import_react4.default.createElement(
561
+ "iframe",
562
+ {
563
+ src: `https://www.googletagmanager.com/ns.html?id=${resolvedGtmId}`,
564
+ height: "0",
565
+ width: "0",
566
+ style: { display: "none", visibility: "hidden" },
567
+ title: "GTM"
568
+ }
569
+ )), children);
570
+ }
571
+
572
+ // src/index.esm.ts
573
+ var track = (eventName, properties) => {
574
+ if (typeof window === "undefined")
575
+ return;
576
+ if (window.balance) {
577
+ window.balance.track(eventName, properties);
578
+ } else {
579
+ console.warn("[Balance Pixel] track() called before pixel initialized:", eventName);
580
+ }
581
+ };
582
+ var identify = (email, traits) => {
583
+ if (typeof window === "undefined")
584
+ return Promise.resolve();
585
+ if (window.balance) {
586
+ return window.balance.identify(email, traits);
587
+ } else {
588
+ console.warn("[Balance Pixel] identify() called before pixel initialized");
589
+ return Promise.resolve();
590
+ }
591
+ };
592
+ var page = (options) => {
593
+ if (typeof window === "undefined")
594
+ return;
595
+ if (window.balance) {
596
+ window.balance.page(options);
597
+ } else {
598
+ console.warn("[Balance Pixel] page() called before pixel initialized");
599
+ }
600
+ };
601
+ var purchase = (revenue, currency, properties) => {
602
+ if (typeof window === "undefined")
603
+ return;
604
+ if (window.balance) {
605
+ window.balance.purchase(revenue, currency, properties);
606
+ } else {
607
+ console.warn("[Balance Pixel] purchase() called before pixel initialized");
608
+ }
609
+ };
610
+ var getSessionId = () => {
611
+ if (typeof window === "undefined")
612
+ return null;
613
+ return window.balance?.getSessionId() ?? null;
614
+ };
615
+ var getFanIdHash = () => {
616
+ if (typeof window === "undefined")
617
+ return null;
618
+ return window.balance?.getFanIdHash() ?? null;
619
+ };
620
+ var getAttribution = () => {
621
+ if (typeof window === "undefined")
622
+ return {};
623
+ return window.balance?.getAttribution() ?? {};
624
+ };
625
+ var setConsent = (preferences) => {
626
+ if (typeof window === "undefined")
627
+ return;
628
+ if (window.balance) {
629
+ window.balance.setConsent(preferences);
630
+ } else {
631
+ console.warn("[Balance Pixel] setConsent() called before pixel initialized");
632
+ }
633
+ };
634
+ var getConsent = () => {
635
+ if (typeof window === "undefined")
636
+ return null;
637
+ return window.balance?.getConsent() ?? null;
638
+ };
639
+ var hasConsent = (type) => {
640
+ if (typeof window === "undefined")
641
+ return false;
642
+ return window.balance?.hasConsent(type) ?? false;
643
+ };