@hifilabs/pixel 0.1.5 → 0.1.7
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/browser.js +554 -0
- package/dist/index.js +616 -527
- package/dist/{index.esm.js → index.mjs} +4 -4
- package/package.json +10 -2
- package/dist/balance-pixel.js +0 -291
- package/dist/balance-pixel.min.js +0 -1
- /package/dist/{index.min.js → browser.min.js} +0 -0
package/dist/index.js
CHANGED
|
@@ -1,554 +1,643 @@
|
|
|
1
|
-
var
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
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 });
|
|
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
|
+
};
|
|
33
|
+
|
|
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) {
|
|
68
71
|
return;
|
|
69
72
|
}
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
const SESSION_DURATION = 60 * 60 * 1e3;
|
|
76
|
-
const STORAGE_PREFIX = "balance_";
|
|
77
|
-
const API_ENDPOINT = useEmulator ? `http://localhost:5001/artist-os-distro/us-central1/pixelEndpoint` : `https://us-central1-artist-os-distro.cloudfunctions.net/pixelEndpoint`;
|
|
78
|
-
let sessionId = null;
|
|
79
|
-
let fanIdHash = null;
|
|
80
|
-
let consent = null;
|
|
81
|
-
let attribution = {};
|
|
82
|
-
let eventQueue = [];
|
|
83
|
-
let flushTimer = null;
|
|
84
|
-
let currentStorageTier = "session";
|
|
85
|
-
let heartbeatTimer = null;
|
|
86
|
-
let pageStartTime = 0;
|
|
87
|
-
let activeTime = 0;
|
|
88
|
-
let lastActiveTimestamp = 0;
|
|
89
|
-
let isPageVisible = true;
|
|
90
|
-
const IDLE_TIMEOUT = 2 * 60 * 1e3;
|
|
91
|
-
let lastActivityTime = Date.now();
|
|
92
|
-
let isIdle = false;
|
|
93
|
-
const log = (...args) => {
|
|
94
|
-
if (debug)
|
|
95
|
-
console.log("[BALANCE Pixel]", ...args);
|
|
96
|
-
};
|
|
97
|
-
function getStorage() {
|
|
98
|
-
try {
|
|
99
|
-
return currentStorageTier === "local" ? localStorage : sessionStorage;
|
|
100
|
-
} catch {
|
|
101
|
-
return null;
|
|
102
|
-
}
|
|
103
|
-
}
|
|
104
|
-
function storageGet(key) {
|
|
105
|
-
const storage = getStorage();
|
|
106
|
-
if (!storage)
|
|
107
|
-
return null;
|
|
108
|
-
try {
|
|
109
|
-
const fullKey = STORAGE_PREFIX + key;
|
|
110
|
-
let value = storage.getItem(fullKey);
|
|
111
|
-
if (!value && currentStorageTier === "session") {
|
|
112
|
-
try {
|
|
113
|
-
value = localStorage.getItem(fullKey);
|
|
114
|
-
} catch {
|
|
115
|
-
}
|
|
116
|
-
}
|
|
117
|
-
return value;
|
|
118
|
-
} catch {
|
|
119
|
-
return null;
|
|
120
|
-
}
|
|
121
|
-
}
|
|
122
|
-
function storageSet(key, value) {
|
|
123
|
-
const storage = getStorage();
|
|
124
|
-
if (!storage)
|
|
125
|
-
return;
|
|
126
|
-
try {
|
|
127
|
-
storage.setItem(STORAGE_PREFIX + key, value);
|
|
128
|
-
} catch {
|
|
129
|
-
}
|
|
130
|
-
}
|
|
131
|
-
function upgradeStorageTier() {
|
|
132
|
-
if (currentStorageTier === "local")
|
|
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)");
|
|
133
78
|
return;
|
|
134
|
-
log("Upgrading storage tier: session -> local");
|
|
135
|
-
try {
|
|
136
|
-
const keysToMigrate = [];
|
|
137
|
-
for (let i = 0; i < sessionStorage.length; i++) {
|
|
138
|
-
const key = sessionStorage.key(i);
|
|
139
|
-
if (key?.startsWith(STORAGE_PREFIX)) {
|
|
140
|
-
keysToMigrate.push(key);
|
|
141
|
-
}
|
|
142
|
-
}
|
|
143
|
-
for (const key of keysToMigrate) {
|
|
144
|
-
const value = sessionStorage.getItem(key);
|
|
145
|
-
if (value) {
|
|
146
|
-
localStorage.setItem(key, value);
|
|
147
|
-
}
|
|
148
|
-
}
|
|
149
|
-
for (const key of keysToMigrate) {
|
|
150
|
-
sessionStorage.removeItem(key);
|
|
151
|
-
}
|
|
152
|
-
currentStorageTier = "local";
|
|
153
|
-
log(`Storage tier upgraded, migrated ${keysToMigrate.length} items`);
|
|
154
|
-
} catch (error) {
|
|
155
|
-
console.error("[BALANCE Pixel] Storage migration failed:", error);
|
|
156
79
|
}
|
|
157
80
|
}
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
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;
|
|
114
|
+
}
|
|
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")
|
|
128
|
+
return;
|
|
129
|
+
if (this.currentTier === newTier)
|
|
130
|
+
return;
|
|
131
|
+
const oldTier = this.currentTier;
|
|
132
|
+
if (oldTier === "session" && newTier === "local") {
|
|
133
|
+
this.migrateToLocal();
|
|
134
|
+
}
|
|
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();
|
|
150
|
+
}
|
|
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);
|
|
210
166
|
}
|
|
211
|
-
} catch (e) {
|
|
212
167
|
}
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
}
|
|
219
|
-
}
|
|
220
|
-
function loadConsent() {
|
|
221
|
-
try {
|
|
222
|
-
const stored = localStorage.getItem(CONSENT_STORAGE_KEY);
|
|
223
|
-
if (stored) {
|
|
224
|
-
const parsed = JSON.parse(stored);
|
|
225
|
-
consent = parsed.preferences || null;
|
|
226
|
-
log("Loaded consent:", consent);
|
|
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}`);
|
|
227
173
|
}
|
|
228
|
-
} catch (e) {
|
|
229
|
-
}
|
|
230
|
-
}
|
|
231
|
-
function setConsent(preferences) {
|
|
232
|
-
const previousConsent = consent;
|
|
233
|
-
consent = preferences;
|
|
234
|
-
try {
|
|
235
|
-
const storage = {
|
|
236
|
-
preferences,
|
|
237
|
-
method: "explicit",
|
|
238
|
-
version: 1
|
|
239
|
-
};
|
|
240
|
-
localStorage.setItem(CONSENT_STORAGE_KEY, JSON.stringify(storage));
|
|
241
|
-
log("Consent saved:", preferences);
|
|
242
|
-
} catch (e) {
|
|
243
|
-
console.error("[BALANCE Pixel] Could not save consent:", e);
|
|
244
174
|
}
|
|
245
|
-
|
|
246
|
-
|
|
175
|
+
for (const key of keysToMigrate) {
|
|
176
|
+
sessionStorage.removeItem(key);
|
|
247
177
|
}
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
178
|
+
console.log(`[StorageManager] Migration complete: ${keysToMigrate.length} items`);
|
|
179
|
+
} catch (error) {
|
|
180
|
+
console.error("[StorageManager] Migration failed:", error);
|
|
181
|
+
}
|
|
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);
|
|
254
195
|
}
|
|
255
|
-
});
|
|
256
|
-
enqueueEvent(event);
|
|
257
|
-
}
|
|
258
|
-
function getConsent() {
|
|
259
|
-
return consent;
|
|
260
|
-
}
|
|
261
|
-
function hasConsent(type) {
|
|
262
|
-
return consent?.[type] === true;
|
|
263
|
-
}
|
|
264
|
-
async function hashEmail(email) {
|
|
265
|
-
const normalized = email.toLowerCase().trim();
|
|
266
|
-
const encoder = new TextEncoder();
|
|
267
|
-
const data = encoder.encode(normalized);
|
|
268
|
-
const hashBuffer = await crypto.subtle.digest("SHA-256", data);
|
|
269
|
-
const hashArray = Array.from(new Uint8Array(hashBuffer));
|
|
270
|
-
return hashArray.map((b) => b.toString(16).padStart(2, "0")).join("");
|
|
271
|
-
}
|
|
272
|
-
function buildEvent(partial) {
|
|
273
|
-
const deviceInfo = getDeviceInfo();
|
|
274
|
-
const base = {
|
|
275
|
-
artist_id: artistId,
|
|
276
|
-
fan_session_id: sessionId,
|
|
277
|
-
fan_id_hash: fanIdHash || void 0,
|
|
278
|
-
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
279
|
-
source_url: window.location.href,
|
|
280
|
-
referrer_url: document.referrer || void 0,
|
|
281
|
-
user_agent: navigator.userAgent,
|
|
282
|
-
// Device info (parsed client-side)
|
|
283
|
-
device_type: deviceInfo.device_type,
|
|
284
|
-
browser: deviceInfo.browser,
|
|
285
|
-
os: deviceInfo.os,
|
|
286
|
-
// Tracking source (gtm, pixel, etc.) - enables filtering in artistHQ
|
|
287
|
-
tracking_source: trackingSource,
|
|
288
|
-
...partial,
|
|
289
|
-
...attribution
|
|
290
|
-
};
|
|
291
|
-
if (projectId && !partial.projectId) {
|
|
292
|
-
base.projectId = projectId;
|
|
293
196
|
}
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
function enqueueEvent(event) {
|
|
297
|
-
eventQueue.push(event);
|
|
298
|
-
log("Event queued:", event.event_name, "(queue:", eventQueue.length, ")");
|
|
299
|
-
if (eventQueue.length >= 10) {
|
|
300
|
-
flush();
|
|
197
|
+
for (const key of keysToRemove) {
|
|
198
|
+
localStorage.removeItem(key);
|
|
301
199
|
}
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
} catch (error) {
|
|
322
|
-
console.error("[BALANCE Pixel] Failed to send events:", error);
|
|
323
|
-
if (eventQueue.length < 50) {
|
|
324
|
-
eventQueue.push(...events);
|
|
200
|
+
console.log(`[StorageManager] Cleared ${keysToRemove.length} items from localStorage`);
|
|
201
|
+
} catch (error) {
|
|
202
|
+
console.error("[StorageManager] Clear failed:", error);
|
|
203
|
+
}
|
|
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 {
|
|
325
219
|
}
|
|
326
220
|
}
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
221
|
+
return value;
|
|
222
|
+
} catch {
|
|
223
|
+
return null;
|
|
224
|
+
}
|
|
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);
|
|
238
|
+
}
|
|
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 {
|
|
250
|
+
}
|
|
251
|
+
try {
|
|
252
|
+
localStorage.removeItem(fullKey);
|
|
253
|
+
} catch {
|
|
254
|
+
}
|
|
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;
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
/**
|
|
270
|
+
* Set JSON-stringified item
|
|
271
|
+
*/
|
|
272
|
+
setJSON(key, value) {
|
|
273
|
+
try {
|
|
274
|
+
this.setItem(key, JSON.stringify(value));
|
|
275
|
+
} catch {
|
|
276
|
+
}
|
|
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;
|
|
329
|
+
}
|
|
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;
|
|
351
|
+
}
|
|
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;
|
|
369
377
|
return;
|
|
370
378
|
}
|
|
371
|
-
if (
|
|
372
|
-
|
|
373
|
-
isIdle = true;
|
|
374
|
-
pauseActiveTimeTracking();
|
|
375
|
-
log("User idle - pausing heartbeat");
|
|
376
|
-
}
|
|
377
|
-
return;
|
|
379
|
+
if (storageManager) {
|
|
380
|
+
initStorageWithConsent(hasConsent2);
|
|
378
381
|
}
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
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;
|
|
398
|
+
}
|
|
388
399
|
}
|
|
389
|
-
});
|
|
390
|
-
enqueueEvent(event);
|
|
391
|
-
log("Heartbeat sent:", timeOnPageSeconds, "seconds active");
|
|
400
|
+
}, 500);
|
|
392
401
|
}
|
|
393
|
-
|
|
394
|
-
if (
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
}
|
|
398
|
-
if (heartbeatTimer)
|
|
399
|
-
clearInterval(heartbeatTimer);
|
|
400
|
-
startActiveTimeTracking();
|
|
401
|
-
heartbeatTimer = window.setInterval(() => {
|
|
402
|
-
sendHeartbeat();
|
|
403
|
-
}, heartbeatInterval);
|
|
404
|
-
log("Heartbeat started with interval:", heartbeatInterval, "ms");
|
|
405
|
-
}
|
|
406
|
-
function stopHeartbeat() {
|
|
407
|
-
if (heartbeatTimer) {
|
|
408
|
-
clearInterval(heartbeatTimer);
|
|
409
|
-
heartbeatTimer = null;
|
|
402
|
+
return () => {
|
|
403
|
+
if (pollIntervalRef.current) {
|
|
404
|
+
clearInterval(pollIntervalRef.current);
|
|
405
|
+
pollIntervalRef.current = null;
|
|
410
406
|
}
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
}
|
|
440
|
-
fanIdHash = await hashEmail(email);
|
|
441
|
-
if (consent?.analytics === true) {
|
|
442
|
-
upgradeStorageTier();
|
|
443
|
-
}
|
|
444
|
-
storageSet(FAN_ID_KEY, fanIdHash);
|
|
445
|
-
const emailParts = email.split("@");
|
|
446
|
-
const maskedEmail = emailParts[0].charAt(0) + "***@" + (emailParts[1] || "");
|
|
447
|
-
log("Fan identified:", {
|
|
448
|
-
name: traits.name || "(no name)",
|
|
449
|
-
email: maskedEmail,
|
|
450
|
-
hash: fanIdHash.substring(0, 16) + "...",
|
|
451
|
-
traits,
|
|
452
|
-
storageTier: currentStorageTier
|
|
453
|
-
});
|
|
454
|
-
const event = buildEvent({
|
|
455
|
-
event_name: "identify",
|
|
456
|
-
fan_id_hash: fanIdHash,
|
|
457
|
-
metadata: {
|
|
458
|
-
email_sha256: fanIdHash,
|
|
459
|
-
traits,
|
|
460
|
-
consent_preferences: consent || void 0,
|
|
461
|
-
storage_tier: currentStorageTier
|
|
462
|
-
}
|
|
463
|
-
});
|
|
464
|
-
enqueueEvent(event);
|
|
465
|
-
} catch (error) {
|
|
466
|
-
console.error("[BALANCE Pixel] Failed to identify:", error);
|
|
407
|
+
};
|
|
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
|
|
419
|
+
};
|
|
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, enabled = true } = 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);
|
|
467
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
|
+
};
|
|
468
447
|
}
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
currentStorageTier = "session";
|
|
489
|
-
log("Storage tier: session (privacy by default)");
|
|
490
|
-
}
|
|
491
|
-
sessionId = getOrCreateSession();
|
|
492
|
-
loadFanId();
|
|
493
|
-
if (!consent) {
|
|
494
|
-
consent = {
|
|
495
|
-
analytics: true,
|
|
496
|
-
marketing: true,
|
|
497
|
-
personalization: true,
|
|
498
|
-
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
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);
|
|
499
467
|
};
|
|
500
|
-
log("Default consent enabled (all tracking):", consent);
|
|
501
|
-
upgradeStorageTier();
|
|
502
468
|
}
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
}
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
startActiveTimeTracking();
|
|
532
|
-
}
|
|
533
|
-
});
|
|
534
|
-
}
|
|
535
|
-
window.balance = {
|
|
536
|
-
track,
|
|
537
|
-
identify,
|
|
538
|
-
page: trackPageView,
|
|
539
|
-
purchase,
|
|
540
|
-
getSessionId: () => sessionId,
|
|
541
|
-
getFanIdHash: () => fanIdHash,
|
|
542
|
-
getAttribution: () => attribution,
|
|
543
|
-
setConsent,
|
|
544
|
-
getConsent,
|
|
545
|
-
hasConsent
|
|
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)(() => {
|
|
483
|
+
if (typeof window === "undefined" || !enabled)
|
|
484
|
+
return;
|
|
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);
|
|
546
497
|
};
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
498
|
+
}, [syncConsent, pollInterval, log, enabled]);
|
|
499
|
+
return {
|
|
500
|
+
/**
|
|
501
|
+
* Manually trigger a consent sync
|
|
502
|
+
*/
|
|
503
|
+
syncConsent,
|
|
504
|
+
/**
|
|
505
|
+
* Get current consent config
|
|
506
|
+
*/
|
|
507
|
+
getConsentConfig: mapPixelConsentToGTM
|
|
508
|
+
};
|
|
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, enabled: !!resolvedGtmId });
|
|
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
|
+
}
|
|
551
559
|
}
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
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
|
+
};
|