@hifilabs/pixel 0.0.10 → 0.1.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.
@@ -19,6 +19,38 @@ export declare const setConsent: (preferences: {
19
19
  export declare const getConsent: () => any;
20
20
  export declare const hasConsent: (type: 'analytics' | 'marketing' | 'personalization') => boolean;
21
21
 
22
+ // GTM Integration Types
23
+ export type GTMConsentState = 'granted' | 'denied';
24
+
25
+ export interface GTMConsentConfig {
26
+ ad_storage: GTMConsentState;
27
+ ad_user_data: GTMConsentState;
28
+ ad_personalization: GTMConsentState;
29
+ analytics_storage: GTMConsentState;
30
+ }
31
+
32
+ export declare const DEFAULT_GTM_CONSENT: GTMConsentConfig;
33
+
34
+ // GTMProvider Component
35
+ export interface GTMProviderProps {
36
+ gtmId?: string;
37
+ children: React.ReactNode;
38
+ debug?: boolean;
39
+ }
40
+
41
+ export declare function GTMProvider(props: GTMProviderProps): JSX.Element;
42
+
43
+ // useGTMConsent Hook
44
+ export interface UseGTMConsentOptions {
45
+ pollInterval?: number;
46
+ debug?: boolean;
47
+ }
48
+
49
+ export declare function useGTMConsent(options?: UseGTMConsentOptions): {
50
+ syncConsent: () => void;
51
+ getConsentConfig: () => GTMConsentConfig;
52
+ };
53
+
22
54
  // useBalanceIdentify hook - consent-aware identify wrapper
23
55
  export declare function useBalanceIdentify(): {
24
56
  /**
package/dist/index.esm.js CHANGED
@@ -7,7 +7,7 @@ var __publicField = (obj, key, value) => {
7
7
  };
8
8
 
9
9
  // src/react/BalanceAnalytics.tsx
10
- import React, { useEffect, useRef, Suspense } from "react";
10
+ import React2, { useEffect, useRef, Suspense } from "react";
11
11
  import { usePathname, useSearchParams } from "next/navigation";
12
12
  function BalanceAnalyticsInner() {
13
13
  const pathname = usePathname();
@@ -37,7 +37,7 @@ function BalanceAnalyticsInner() {
37
37
  return null;
38
38
  }
39
39
  function BalanceAnalytics() {
40
- return /* @__PURE__ */ React.createElement(Suspense, { fallback: null }, /* @__PURE__ */ React.createElement(BalanceAnalyticsInner, null));
40
+ return /* @__PURE__ */ React2.createElement(Suspense, { fallback: null }, /* @__PURE__ */ React2.createElement(BalanceAnalyticsInner, null));
41
41
  }
42
42
 
43
43
  // src/react/useBalanceIdentify.ts
@@ -370,6 +370,154 @@ function useBalanceIdentify() {
370
370
  };
371
371
  }
372
372
 
373
+ // src/react/GTMProvider.tsx
374
+ import { useEffect as useEffect4 } from "react";
375
+ import Script from "next/script";
376
+
377
+ // src/react/useGTMConsent.ts
378
+ import { useEffect as useEffect3, useRef as useRef3, useCallback as useCallback2 } from "react";
379
+ function useGTMConsent(options = {}) {
380
+ const { pollInterval = 1e3, debug = false } = options;
381
+ const lastConsentRef = useRef3("");
382
+ const log = useCallback2(
383
+ (...args) => {
384
+ if (debug) {
385
+ console.log("[useGTMConsent]", ...args);
386
+ }
387
+ },
388
+ [debug]
389
+ );
390
+ const mapPixelConsentToGTM = useCallback2(() => {
391
+ if (typeof window === "undefined" || !window.balance) {
392
+ return {
393
+ ad_storage: "denied",
394
+ ad_user_data: "denied",
395
+ ad_personalization: "denied",
396
+ analytics_storage: "denied"
397
+ };
398
+ }
399
+ const hasAnalytics = window.balance.hasConsent("analytics");
400
+ const hasMarketing = window.balance.hasConsent("marketing");
401
+ const analyticsState = hasAnalytics ? "granted" : "denied";
402
+ const marketingState = hasMarketing ? "granted" : "denied";
403
+ return {
404
+ analytics_storage: analyticsState,
405
+ ad_storage: marketingState,
406
+ ad_user_data: marketingState,
407
+ ad_personalization: marketingState
408
+ };
409
+ }, []);
410
+ const updateGTMConsent = useCallback2(
411
+ (consentConfig) => {
412
+ if (typeof window === "undefined")
413
+ return;
414
+ window.dataLayer = window.dataLayer || [];
415
+ if (typeof window.gtag !== "function") {
416
+ window.gtag = function gtag(...args) {
417
+ window.dataLayer.push(args);
418
+ };
419
+ }
420
+ window.gtag("consent", "update", consentConfig);
421
+ log("Pushed consent update to GTM:", consentConfig);
422
+ },
423
+ [log]
424
+ );
425
+ const syncConsent = useCallback2(() => {
426
+ const currentConsent = mapPixelConsentToGTM();
427
+ const consentKey = JSON.stringify(currentConsent);
428
+ if (consentKey !== lastConsentRef.current) {
429
+ lastConsentRef.current = consentKey;
430
+ updateGTMConsent(currentConsent);
431
+ }
432
+ }, [mapPixelConsentToGTM, updateGTMConsent]);
433
+ useEffect3(() => {
434
+ if (typeof window === "undefined")
435
+ return;
436
+ syncConsent();
437
+ const intervalId = setInterval(syncConsent, pollInterval);
438
+ const handleStorageChange = (e) => {
439
+ if (e.key === "balance_consent") {
440
+ log("Consent changed in another tab, syncing...");
441
+ syncConsent();
442
+ }
443
+ };
444
+ window.addEventListener("storage", handleStorageChange);
445
+ return () => {
446
+ clearInterval(intervalId);
447
+ window.removeEventListener("storage", handleStorageChange);
448
+ };
449
+ }, [syncConsent, pollInterval, log]);
450
+ return {
451
+ /**
452
+ * Manually trigger a consent sync
453
+ */
454
+ syncConsent,
455
+ /**
456
+ * Get current consent config
457
+ */
458
+ getConsentConfig: mapPixelConsentToGTM
459
+ };
460
+ }
461
+
462
+ // src/types/gtm.ts
463
+ var DEFAULT_GTM_CONSENT = {
464
+ ad_storage: "denied",
465
+ ad_user_data: "denied",
466
+ ad_personalization: "denied",
467
+ analytics_storage: "denied"
468
+ };
469
+
470
+ // src/react/GTMProvider.tsx
471
+ function GTMProvider({ gtmId, children, debug = false }) {
472
+ const resolvedGtmId = gtmId || process.env.NEXT_PUBLIC_GTM_ID;
473
+ useGTMConsent({ debug });
474
+ useEffect4(() => {
475
+ if (typeof window === "undefined" || !resolvedGtmId)
476
+ return;
477
+ window.dataLayer = window.dataLayer || [];
478
+ if (typeof window.gtag !== "function") {
479
+ window.gtag = function gtag(...args) {
480
+ window.dataLayer.push(args);
481
+ };
482
+ }
483
+ window.gtag("consent", "default", DEFAULT_GTM_CONSENT);
484
+ if (debug) {
485
+ console.log("[GTMProvider] Initialized with default consent:", DEFAULT_GTM_CONSENT);
486
+ }
487
+ }, [resolvedGtmId, debug]);
488
+ if (!resolvedGtmId) {
489
+ if (debug) {
490
+ console.log("[GTMProvider] No GTM ID configured, skipping GTM initialization");
491
+ }
492
+ return /* @__PURE__ */ React.createElement(React.Fragment, null, children);
493
+ }
494
+ return /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement(
495
+ Script,
496
+ {
497
+ id: "gtm-script",
498
+ strategy: "afterInteractive",
499
+ dangerouslySetInnerHTML: {
500
+ __html: `
501
+ (function(w,d,s,l,i){w[l]=w[l]||[];w[l].push({'gtm.start':
502
+ new Date().getTime(),event:'gtm.js'});var f=d.getElementsByTagName(s)[0],
503
+ j=d.createElement(s),dl=l!='dataLayer'?'&l='+l:'';j.async=true;j.src=
504
+ 'https://www.googletagmanager.com/gtm.js?id='+i+dl;f.parentNode.insertBefore(j,f);
505
+ })(window,document,'script','dataLayer','${resolvedGtmId}');
506
+ `
507
+ }
508
+ }
509
+ ), /* @__PURE__ */ React.createElement("noscript", null, /* @__PURE__ */ React.createElement(
510
+ "iframe",
511
+ {
512
+ src: `https://www.googletagmanager.com/ns.html?id=${resolvedGtmId}`,
513
+ height: "0",
514
+ width: "0",
515
+ style: { display: "none", visibility: "hidden" },
516
+ title: "GTM"
517
+ }
518
+ )), children);
519
+ }
520
+
373
521
  // src/index.esm.ts
374
522
  var track = (eventName, properties) => {
375
523
  if (typeof window === "undefined")
@@ -444,6 +592,8 @@ var hasConsent = (type) => {
444
592
  };
445
593
  export {
446
594
  BalanceAnalytics,
595
+ DEFAULT_GTM_CONSENT,
596
+ GTMProvider,
447
597
  StorageManager,
448
598
  getAttribution,
449
599
  getConsent,
@@ -457,5 +607,6 @@ export {
457
607
  purchase,
458
608
  setConsent,
459
609
  track,
460
- useBalanceIdentify
610
+ useBalanceIdentify,
611
+ useGTMConsent
461
612
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hifilabs/pixel",
3
- "version": "0.0.10",
3
+ "version": "0.1.0",
4
4
  "description": "BALANCE Pixel - Lightweight browser tracking script for artist fan analytics",
5
5
  "main": "./dist/index.js",
6
6
  "module": "./dist/index.esm.js",