@jwiedeman/gtm-kit 1.1.2 → 1.1.3

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/README.md CHANGED
@@ -1,12 +1,12 @@
1
- # @react-gtm-kit/core
1
+ # @jwiedeman/gtm-kit
2
2
 
3
3
  [![CI](https://github.com/jwiedeman/react-gtm-kit/actions/workflows/ci.yml/badge.svg)](https://github.com/jwiedeman/react-gtm-kit/actions/workflows/ci.yml)
4
4
  [![Coverage](https://codecov.io/gh/jwiedeman/react-gtm-kit/graph/badge.svg?flag=core)](https://codecov.io/gh/jwiedeman/react-gtm-kit)
5
- [![npm version](https://img.shields.io/npm/v/@react-gtm-kit/core.svg)](https://www.npmjs.com/package/@react-gtm-kit/core)
6
- [![Bundle Size](https://img.shields.io/bundlephobia/minzip/@react-gtm-kit/core)](https://bundlephobia.com/package/@react-gtm-kit/core)
5
+ [![npm version](https://img.shields.io/npm/v/@jwiedeman/gtm-kit.svg)](https://www.npmjs.com/package/@jwiedeman/gtm-kit)
6
+ [![Bundle Size](https://img.shields.io/bundlephobia/minzip/@jwiedeman/gtm-kit)](https://bundlephobia.com/package/@jwiedeman/gtm-kit)
7
7
  [![TypeScript](https://img.shields.io/badge/TypeScript-Ready-blue.svg)](https://www.typescriptlang.org/)
8
8
  [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
9
- [![Zero Dependencies](https://img.shields.io/badge/dependencies-0-brightgreen.svg)](https://www.npmjs.com/package/@react-gtm-kit/core)
9
+ [![Zero Dependencies](https://img.shields.io/badge/dependencies-0-brightgreen.svg)](https://www.npmjs.com/package/@jwiedeman/gtm-kit)
10
10
 
11
11
  **Framework-agnostic Google Tag Manager client. Zero dependencies. 3.7KB gzipped.**
12
12
 
@@ -17,15 +17,15 @@ The foundation of GTM Kit - works with any JavaScript project, framework, or bui
17
17
  ## Installation
18
18
 
19
19
  ```bash
20
- npm install @react-gtm-kit/core
20
+ npm install @jwiedeman/gtm-kit
21
21
  ```
22
22
 
23
23
  ```bash
24
- yarn add @react-gtm-kit/core
24
+ yarn add @jwiedeman/gtm-kit
25
25
  ```
26
26
 
27
27
  ```bash
28
- pnpm add @react-gtm-kit/core
28
+ pnpm add @jwiedeman/gtm-kit
29
29
  ```
30
30
 
31
31
  ---
@@ -33,7 +33,7 @@ pnpm add @react-gtm-kit/core
33
33
  ## Quick Start
34
34
 
35
35
  ```ts
36
- import { createGtmClient, pushEvent } from '@react-gtm-kit/core';
36
+ import { createGtmClient, pushEvent } from '@jwiedeman/gtm-kit';
37
37
 
38
38
  // Create and initialize
39
39
  const gtm = createGtmClient({ containers: 'GTM-XXXXXX' });
@@ -91,7 +91,7 @@ await client.whenReady(); // Wait for scripts to load
91
91
  ### Event Helpers
92
92
 
93
93
  ```ts
94
- import { pushEvent, pushEcommerce } from '@react-gtm-kit/core';
94
+ import { pushEvent, pushEcommerce } from '@jwiedeman/gtm-kit';
95
95
 
96
96
  // Generic event
97
97
  pushEvent(client, 'button_click', { button_id: 'cta-main' });
@@ -108,7 +108,7 @@ pushEcommerce(client, 'purchase', {
108
108
  ### Consent Mode v2
109
109
 
110
110
  ```ts
111
- import { consentPresets } from '@react-gtm-kit/core';
111
+ import { consentPresets } from '@jwiedeman/gtm-kit';
112
112
 
113
113
  // Set defaults BEFORE init (required by Google)
114
114
  client.setConsentDefaults(consentPresets.eeaDefault, { region: ['EEA'] });
@@ -165,7 +165,7 @@ const client = createGtmClient({
165
165
  ## SSR / Server-Side Rendering
166
166
 
167
167
  ```ts
168
- import { generateNoscriptHtml } from '@react-gtm-kit/core';
168
+ import { generateNoscriptHtml } from '@jwiedeman/gtm-kit';
169
169
 
170
170
  // Generate noscript HTML for server-side rendering
171
171
  const noscriptHtml = generateNoscriptHtml('GTM-XXXXXX');
@@ -179,7 +179,7 @@ const noscriptHtml = generateNoscriptHtml('GTM-XXXXXX');
179
179
  Automatically buffer events that fire before GTM loads, then replay them in order:
180
180
 
181
181
  ```ts
182
- import { installAutoQueue, createGtmClient } from '@react-gtm-kit/core';
182
+ import { installAutoQueue, createGtmClient } from '@jwiedeman/gtm-kit';
183
183
 
184
184
  // Install FIRST - captures all dataLayer pushes
185
185
  installAutoQueue();
@@ -195,7 +195,7 @@ client.init(); // Buffer replays, GTM processes all events
195
195
  **For earliest possible protection**, embed in your HTML `<head>`:
196
196
 
197
197
  ```ts
198
- import { createAutoQueueScript } from '@react-gtm-kit/core';
198
+ import { createAutoQueueScript } from '@jwiedeman/gtm-kit';
199
199
 
200
200
  // Returns minified inline script for SSR
201
201
  const script = createAutoQueueScript();
@@ -218,15 +218,15 @@ installAutoQueue({
218
218
 
219
219
  ## Framework Adapters
220
220
 
221
- While `@react-gtm-kit/core` works standalone, we provide framework-specific adapters for better ergonomics:
221
+ While `@jwiedeman/gtm-kit` works standalone, we provide framework-specific adapters for better ergonomics:
222
222
 
223
- | Framework | Package | Install |
224
- | ------------- | ----------------------------- | ------------------------------------------------------------- |
225
- | React (hooks) | `@react-gtm-kit/react-modern` | `npm install @react-gtm-kit/core @react-gtm-kit/react-modern` |
226
- | React (class) | `@react-gtm-kit/react-legacy` | `npm install @react-gtm-kit/core @react-gtm-kit/react-legacy` |
227
- | Vue 3 | `@react-gtm-kit/vue` | `npm install @react-gtm-kit/core @react-gtm-kit/vue` |
228
- | Nuxt 3 | `@react-gtm-kit/nuxt` | `npm install @react-gtm-kit/core @react-gtm-kit/nuxt` |
229
- | Next.js | `@react-gtm-kit/next` | `npm install @react-gtm-kit/core @react-gtm-kit/next` |
223
+ | Framework | Package | Install |
224
+ | ------------- | --------------------------------- | ---------------------------------------------------------------- |
225
+ | React (hooks) | `@jwiedeman/gtm-kit-react` | `npm install @jwiedeman/gtm-kit @jwiedeman/gtm-kit-react` |
226
+ | React (class) | `@jwiedeman/gtm-kit-react-legacy` | `npm install @jwiedeman/gtm-kit @jwiedeman/gtm-kit-react-legacy` |
227
+ | Vue 3 | `@jwiedeman/gtm-kit-vue` | `npm install @jwiedeman/gtm-kit @jwiedeman/gtm-kit-vue` |
228
+ | Nuxt 3 | `@jwiedeman/gtm-kit-nuxt` | `npm install @jwiedeman/gtm-kit @jwiedeman/gtm-kit-nuxt` |
229
+ | Next.js | `@jwiedeman/gtm-kit-next` | `npm install @jwiedeman/gtm-kit @jwiedeman/gtm-kit-next` |
230
230
 
231
231
  ---
232
232
 
package/dist/index.cjs CHANGED
@@ -1,25 +1,33 @@
1
1
  'use strict';
2
2
 
3
- var C="https://www.googletagmanager.com",g="dataLayer";var k="consent",I="default",V="update",ne=["ad_storage","analytics_storage","ad_user_data","ad_personalization"],re=e=>ne.includes(e),ae=e=>e==="granted"||e==="denied",oe=e=>{if(!Array.isArray(e))throw new Error("Consent region list must be an array of ISO region codes.");for(let t of e)if(typeof t!="string"||t.trim().length===0)throw new Error("Consent region codes must be non-empty strings.")},ie=e=>{if(!Number.isFinite(e)||e<0)throw new Error("waitForUpdate must be a non-negative finite number.")},M=e=>{let t=Object.entries(e!=null?e:{}).map(([r,a])=>{if(!re(r))throw new Error(`Invalid consent key: ${r}`);if(!ae(a))throw new Error(`Invalid consent value for key "${r}". Expected "granted" or "denied".`);return [r,a]});if(!t.length)throw new Error("At least one consent key/value pair is required.");let n={};for(let[r,a]of t)n[r]=a;return Object.freeze(n)},se=e=>{if(!e)return;let t={};return e.region&&(oe(e.region),e.region.length&&(t.region=[...e.region])),typeof e.waitForUpdate=="number"&&(ie(e.waitForUpdate),t.wait_for_update=e.waitForUpdate),Object.keys(t).length?t:void 0},w=({command:e,state:t,options:n})=>{if(e!==I&&e!==V)throw new Error(`Unsupported consent command: ${e}`);let r=M(t),a=se(n);return a?[k,e,r,a]:[k,e,r]},S=e=>[...w(e)],j=(e,t)=>S({command:I,state:e,options:t}),z=(e,t)=>S({command:V,state:e,options:t}),de={buildConsentCommand:w,createConsentDefaultsCommand:j,createConsentUpdateCommand:z,normalizeConsentState:M};var v={eeaDefault:Object.freeze({ad_storage:"denied",analytics_storage:"denied",ad_user_data:"denied",ad_personalization:"denied"}),allGranted:Object.freeze({ad_storage:"granted",analytics_storage:"granted",ad_user_data:"granted",ad_personalization:"granted"}),analyticsOnly:Object.freeze({ad_storage:"denied",analytics_storage:"granted",ad_user_data:"denied",ad_personalization:"denied"})},ue=e=>({...v[e]}),ce=v.eeaDefault,le=v.allGranted,pe=v.analyticsOnly;var b=e=>Array.isArray(e),G=e=>{let t=globalThis,n=t[e],r=b(n)?[...n]:void 0;return b(n)||(t[e]=[]),{name:e,dataLayer:t[e],created:!b(n),restore(){if(!b(n)){delete t[e];return}let a=r?[...r]:[];t[e]=a;},snapshot:r}},U=(e,t)=>{e.dataLayer.push(t);};var me=["debug","info","warn","error"],fe=()=>{},T=e=>{let t={};for(let n of me){let r=e==null?void 0:e[n];if(typeof r=="function"){t[n]=r.bind(e);continue}t[n]=fe;}return t};var $="data-gtm-container-id",ge="data-gtm-kit-instance",q=()=>{let e=!1,t,n=new Promise(r=>{t=r;});return {get settled(){return e},promise:n,resolve:r=>{e||(e=!0,t(r));}}},ye=e=>e?Object.entries(e).reduce((t,[n,r])=>(t[n]=String(r),t),{}):{},he=(e,t,n)=>{let r=e.endsWith("/")?e.slice(0,-1):e,a=new URLSearchParams({id:t}),o=ye(n);for(let[s,d]of Object.entries(o))s!=="id"&&a.set(s,d);return `${r}/gtm.js?${a.toString()}`},Ce=e=>{if(typeof document=="undefined")return null;let t=`script[${$}="${e}"]`,n=document.querySelector(t);return n||Array.from(document.getElementsByTagName("script")).find(a=>a.src.includes(`id=${encodeURIComponent(e)}`))||null},Se=e=>{if(e instanceof ErrorEvent){if(e.error)return String(e.error);if(e.message)return e.message}return "Failed to load GTM script."},F,Q,A=class{constructor(t){this.options=t;this.logger=T(this.options.logger);this.host=(F=this.options.host)!=null?F:C;this.dataLayerName=(Q=this.options.dataLayerName)!=null?Q:g;this.defaultQueryParams=this.options.defaultQueryParams;this.scriptAttributes=this.options.scriptAttributes;this.insertedScripts=new Set;this.readyCallbacks=new Set;this.readiness=q();this.loadStates=new Map;this.pendingContainers=new Set;}whenReady(){return this.readiness.promise}onReady(t){return this.readyCallbacks.add(t),this.readiness.settled&&t(Array.from(this.loadStates.values())),()=>{this.readyCallbacks.delete(t);}}notifyReady(){let t=Array.from(this.loadStates.values());this.readiness.settled||this.readiness.resolve(t);for(let n of this.readyCallbacks)n(t);}maybeNotifyReady(){this.pendingContainers.size===0&&this.notifyReady();}recordState(t){this.loadStates.set(t.containerId,t);}resetReadiness(){this.pendingContainers.clear(),this.loadStates.clear(),this.readiness=q();}ensure(t){var a;if(typeof document=="undefined"){this.logger.warn("No document available \u2013 skipping script injection.");for(let o of t)o.id&&this.recordState({containerId:o.id,status:"skipped",error:"Document unavailable for script injection."});return this.maybeNotifyReady(),{inserted:[]}}let n=[],r=document.head||document.body;if(!r){this.logger.error("Unable to find document.head or document.body for script injection.");for(let o of t)o.id&&this.recordState({containerId:o.id,status:"skipped",error:"Missing document.head and document.body for GTM script injection."});return this.maybeNotifyReady(),{inserted:[]}}for(let o of t){if(!o.id){this.logger.warn("Skipping container with missing id.",{container:o});continue}let s=Ce(o.id);if(s){this.logger.debug("Container script already present, skipping injection.",{containerId:o.id}),this.recordState({containerId:o.id,src:s.src,status:"loaded",fromCache:!0});continue}let d={...this.defaultQueryParams,...o.queryParams};this.dataLayerName!==g&&d.l===void 0&&(d.l=this.dataLayerName);let i=document.createElement("script"),l=he(this.host,o.id,d);i.src=l,i.setAttribute($,o.id),i.setAttribute(ge,this.options.instanceId);let f=(a=this.scriptAttributes)!=null?a:{};f.async!==void 0?i.async=f.async:i.async=!0,f.defer!==void 0&&(i.defer=f.defer);for(let[p,u]of Object.entries(f)){if(p==="async"||p==="defer"||u==null)continue;let c=String(u);p==="nonce"&&(i.nonce=c),i.setAttribute(p,c);}this.pendingContainers.add(o.id);let m=(p,u)=>{if(!this.pendingContainers.has(o.id))return;this.pendingContainers.delete(o.id);let c={containerId:o.id,src:l,status:p,fromCache:!1};p==="failed"&&u&&(c.error=Se(u),this.logger.error("Failed to load GTM container script.",{containerId:o.id,src:l,error:c.error})),this.recordState(c),this.maybeNotifyReady();};i.addEventListener("load",()=>m("loaded")),i.addEventListener("error",p=>m("failed",p)),r.appendChild(i),this.insertedScripts.add(i),n.push(i),this.logger.info("Injected GTM container script.",{containerId:o.id,src:l});}return this.maybeNotifyReady(),{inserted:n}}teardown(){if(typeof document!="undefined"){for(let t of this.insertedScripts)t.parentNode&&t.parentNode.removeChild(t);this.insertedScripts.clear(),this.resetReadiness();}}};var ve=e=>typeof e=="string",B=e=>ve(e)?{id:e}:e,x=e=>{if(typeof e!="object"||e===null)return !1;let t=Object.getPrototypeOf(e);return t===Object.prototype||t===null},D=e=>{if(e===null||typeof e=="boolean"||typeof e=="number"||typeof e=="string")return JSON.stringify(e);if(Array.isArray(e)){let t=[];for(let n of e){let r=D(n);if(r===null)return null;t.push(r);}return `[${t.join(",")}]`}if(x(e)){let t=Object.keys(e).sort(),n=[];for(let r of t){let a=D(e[r]);if(a===null)return null;n.push(`${JSON.stringify(r)}:${a}`);}return `{${n.join(",")}}`}return null},N=e=>Array.isArray(e)||x(e)?D(e):null,E=e=>Array.isArray(e)&&e.length>=3&&e[0]==="consent"&&(e[1]==="default"||e[1]==="update"),H=e=>x(e)?e.event==="gtm.js":!1,Ee=0,K,R=class{constructor(t,n){this.options=t;this.instanceId=n;this.logger=T(this.options.logger);this.resolvedDataLayerName=(K=this.options.dataLayerName)!=null?K:g;this.containers=Array.isArray(this.options.containers)?this.options.containers.map(B):[B(this.options.containers)];this.queue=[];this.queuedConsentSignatures=new Set;this.deliveredConsentSignatures=new Set;this.snapshotSignatures=null;this.scriptManager=new A({instanceId:this.instanceId,host:this.options.host,dataLayerName:this.resolvedDataLayerName,defaultQueryParams:this.options.defaultQueryParams,scriptAttributes:this.options.scriptAttributes,logger:this.options.logger});this.dataLayerState=null;this.initialized=!1;this.startTimestamp=Date.now();if(!this.containers.length)throw new Error("At least one GTM container ID is required to initialize the client.")}init(){if(this.initialized){this.logger.debug("GTM client already initialized; skipping.");return}this.logger.info("Initializing GTM client.",{containers:this.containers.map(t=>t.id),dataLayerName:this.resolvedDataLayerName}),this.dataLayerState=G(this.resolvedDataLayerName),this.captureSnapshotSignatures(),this.pushStartEvent(),this.flushQueue(),this.scriptManager.ensure(this.containers),this.initialized=!0;}push(t){if(t==null){this.logger.warn("Ignoring falsy dataLayer push.",{value:t});return}this.deliverToDataLayer(t)?this.logger.debug("Pushed value to dataLayer.",{immediate:!0}):this.logger.debug("Queued dataLayer value (pre-init).",{queueLength:this.queue.length});}setConsentDefaults(t,n){let r=S({command:"default",state:t,options:n}),a=this.deliverToDataLayer(r);this.logger.info("Applied consent defaults.",{immediate:a,state:t,options:n});}updateConsent(t,n){let r=S({command:"update",state:t,options:n}),a=this.deliverToDataLayer(r);this.logger.info("Updated consent state.",{immediate:a,state:t,options:n});}teardown(){this.logger.info("Tearing down GTM client instance.",{dataLayerName:this.resolvedDataLayerName}),this.scriptManager.teardown(),this.dataLayerState&&this.dataLayerState.restore(),this.queue.length=0,this.queuedConsentSignatures.clear(),this.deliveredConsentSignatures.clear(),this.snapshotSignatures=null,this.initialized=!1,this.dataLayerState=null;}isInitialized(){return this.initialized}whenReady(){return this.scriptManager.whenReady()}onReady(t){return this.scriptManager.onReady(t)}get dataLayerName(){return this.resolvedDataLayerName}flushQueue(){if(this.dataLayerState)for(;this.queue.length;){let t=this.queue.shift();t&&(this.pushValueToDataLayer(t.value,t.signature),t.signature&&this.queuedConsentSignatures.delete(t.signature));}}deliverToDataLayer(t){return this.initialized&&this.dataLayerState?(this.pushValueToDataLayer(t),!0):(this.queueValue(t),!1)}pushStartEvent(){if(!this.dataLayerState)return;if(this.hasExistingStartEvent()){this.logger.debug("Detected existing gtm.js event; skipping duplicate start push.");return}let t={"gtm.start":this.startTimestamp,event:"gtm.js"};this.pushValueToDataLayer(t);}captureSnapshotSignatures(){var n;if(!this.dataLayerState)return;let t=(n=this.dataLayerState.snapshot)!=null?n:[];this.snapshotSignatures=new Set;for(let r of t){let a=N(r);a&&(this.snapshotSignatures.add(a),E(r)&&this.deliveredConsentSignatures.add(a));}}queueValue(t){let n=E(t)?N(t):null;if(n&&this.queuedConsentSignatures.has(n)){this.logger.debug("Skipping duplicate queued dataLayer value.",{value:t});return}let r={value:t,signature:n};if(E(t)){let a=this.queue.findIndex(o=>!E(o.value));a===-1?this.queue.push(r):this.queue.splice(a,0,r);}else this.queue.push(r);n&&this.queuedConsentSignatures.add(n);}pushValueToDataLayer(t,n){var d;if(!this.dataLayerState)return;let r=n!=null?n:N(t),a=E(t),o=r?(d=this.snapshotSignatures)==null?void 0:d.has(r):!1,s=a&&r?this.deliveredConsentSignatures.has(r):!1;if(r&&o){this.logger.debug("Skipping duplicate dataLayer value detected during hydration.",{value:t}),a&&this.deliveredConsentSignatures.add(r);return}if(a&&s){this.logger.debug("Skipping duplicate consent command.",{value:t});return}U(this.dataLayerState,t),a&&r&&this.deliveredConsentSignatures.add(r);}hasExistingStartEvent(){var n;return this.dataLayerState?((n=this.dataLayerState.snapshot)!=null?n:[]).some(H)?!0:this.dataLayerState.dataLayer.some(H):!1}},Le=e=>{let t=`gtm-kit-${++Ee}`;return new R(e,t)};var P=e=>typeof e=="object"&&e!==null,be=e=>e&&{...e},O=(e,t,n)=>{var a;if(!t)throw new Error("An event name is required when pushing to the dataLayer.");if(n!==void 0&&!P(n))throw new Error("Event payloads must be plain objects when pushing to the dataLayer.");let r={event:t,...(a=be(n))!=null?a:{}};return e.push(r),r},Y=(e,t,n,r)=>{var s;if(!P(n))throw new Error("Ecommerce payload must be an object.");let a=(s=r==null?void 0:r.extras)!=null?s:{};if(!P(a))throw new Error("Ecommerce extras must be an object when provided.");let o={...a,ecommerce:n};return O(e,t,o)};var W={height:"0",width:"0",style:"display:none;visibility:hidden",title:"Google Tag Manager"},Te=e=>typeof e=="string",J=e=>Te(e)?{id:e}:e,Ae=e=>e?Object.entries(e).reduce((t,[n,r])=>(t[n]=String(r),t),{}):{},X=e=>e.replace(/&/g,"&amp;").replace(/"/g,"&quot;").replace(/</g,"&lt;").replace(/>/g,"&gt;"),De=(e,t,n)=>{let r=e.endsWith("/")?e.slice(0,-1):e,a=new URLSearchParams({id:t}),o=Ae(n);for(let[s,d]of Object.entries(o))s!=="id"&&a.set(s,d);return `${r}/ns.html?${a.toString()}`},we=e=>{if(!e)return "";let t=Object.entries(e);return t.length?t.map(([n,r])=>`${n}="${X(String(r))}"`).join(" "):""},Ne=(e,t)=>{var i;if(!e.id)throw new Error("Container id is required to build noscript markup.");let n=(i=t.host)!=null?i:C,r={...t.defaultQueryParams,...e.queryParams},a=De(n,e.id,r),o={...W,...t.iframeAttributes},s=we(o),d=s?` ${s}`:"";return `<noscript><iframe src="${X(a)}"${d}></iframe></noscript>`},Re=(e,t={})=>{let n=Array.isArray(e)?e.map(J):[J(e)];if(!n.length)throw new Error("At least one container is required to build noscript markup.");return n.map(r=>Ne(r,t)).join("")},xe={...W};var Pe=e=>e.replace(/\\/g,"\\\\").replace(/'/g,"\\'").replace(/"/g,'\\"').replace(/\n/g,"\\n").replace(/\r/g,"\\r").replace(/</g,"\\x3c").replace(/>/g,"\\x3e").replace(/\u2028/g,"\\u2028").replace(/\u2029/g,"\\u2029");function Z(e={}){let{dataLayerName:t=g,pollInterval:n=50,timeout:r=3e4,maxBufferSize:a=1e3,onReplay:o,onTimeout:s}=e;if(typeof globalThis=="undefined"||typeof globalThis.document=="undefined")return ke();let d=globalThis;Array.isArray(d[t])||(d[t]=[]);let i=d[t],l=[],f=i.push.bind(i),m=!0,p=!1,u=null,c=null,_=()=>i.some(y=>y!==null&&typeof y=="object"&&!Array.isArray(y)&&y.event==="gtm.js"),L=()=>{if(!m)return;m=!1,p=!0,u&&(clearInterval(u),u=null),c&&(clearTimeout(c),c=null),i.push=f;let y=l.length;for(let h of l)f(h.value);l.length=0,o==null||o(y);},ee=()=>{m&&(m=!1,u&&(clearInterval(u),u=null),c&&(clearTimeout(c),c=null),i.push=f,l.length=0);},te=function(...y){for(let h of y)f(h),m&&l.length<a&&l.push({value:h,timestamp:Date.now()}),m&&h!==null&&typeof h=="object"&&!Array.isArray(h)&&h.event==="gtm.js"&&setTimeout(L,0);return i.length};return i.push=te,_()?setTimeout(L,0):(u=setInterval(()=>{_()&&L();},n),r>0&&(c=setTimeout(()=>{m&&(s==null||s(l.length));},r))),{get active(){return m},get bufferedCount(){return l.length},get gtmReady(){return p},replay:L,uninstall:ee}}function Oe(e=g){return `(function(w,n){w[n]=w[n]||[];var q=[],o=w[n].push.bind(w[n]);w[n].push=function(){for(var i=0;i<arguments.length;i++){q.push({v:arguments[i],t:Date.now()});o(arguments[i])}return w[n].length};w.__gtmkit_buffer={q:q,o:o,n:n}})(window,'${Pe(e)}');`}function _e(e={}){if(typeof globalThis=="undefined")return null;let t=globalThis,n=t.__gtmkit_buffer;if(!n)return null;let{n:r}=n;return delete t.__gtmkit_buffer,Z({...e,dataLayerName:r})}function ke(){let e=()=>{};return {active:!1,bufferedCount:0,gtmReady:!1,replay:e,uninstall:e}}
3
+ var C="https://www.googletagmanager.com",p="dataLayer";var z="consent",G="default",U="update",de=["ad_storage","analytics_storage","ad_user_data","ad_personalization"],ue=e=>de.includes(e),ce=e=>e==="granted"||e==="denied",le=e=>{if(!Array.isArray(e))throw new Error("Consent region list must be an array of ISO region codes.");for(let t of e)if(typeof t!="string"||t.trim().length===0)throw new Error("Consent region codes must be non-empty strings.")},pe=e=>{if(!Number.isFinite(e)||e<0)throw new Error("waitForUpdate must be a non-negative finite number.")},j=e=>{let t=Object.entries(e!=null?e:{}).map(([r,a])=>{if(!ue(r))throw new Error(`Invalid consent key: ${r}`);if(!ce(a))throw new Error(`Invalid consent value for key "${r}". Expected "granted" or "denied".`);return [r,a]});if(!t.length)throw new Error("At least one consent key/value pair is required.");let n={};for(let[r,a]of t)n[r]=a;return Object.freeze(n)},me=e=>{if(!e)return;let t={};return e.region&&(le(e.region),e.region.length&&(t.region=[...e.region])),typeof e.waitForUpdate=="number"&&(pe(e.waitForUpdate),t.wait_for_update=e.waitForUpdate),Object.keys(t).length?t:void 0},x=({command:e,state:t,options:n})=>{if(e!==G&&e!==U)throw new Error(`Unsupported consent command: ${e}`);let r=j(t),a=me(n);return a?[z,e,r,a]:[z,e,r]},v=e=>[...x(e)],q=(e,t)=>v({command:G,state:e,options:t}),F=(e,t)=>v({command:U,state:e,options:t}),fe={buildConsentCommand:x,createConsentDefaultsCommand:q,createConsentUpdateCommand:F,normalizeConsentState:j};var E={eeaDefault:Object.freeze({ad_storage:"denied",analytics_storage:"denied",ad_user_data:"denied",ad_personalization:"denied"}),allGranted:Object.freeze({ad_storage:"granted",analytics_storage:"granted",ad_user_data:"granted",ad_personalization:"granted"}),analyticsOnly:Object.freeze({ad_storage:"denied",analytics_storage:"granted",ad_user_data:"denied",ad_personalization:"denied"})},ge=e=>({...E[e]}),ye=E.eeaDefault,he=E.allGranted,Ce=E.analyticsOnly;var T=e=>Array.isArray(e),Q=e=>{let t=globalThis,n=t[e],r=T(n)?[...n]:void 0;return T(n)||(t[e]=[]),{name:e,dataLayer:t[e],created:!T(n),restore(){if(!T(n)){delete t[e];return}let a=r?[...r]:[];t[e]=a;},snapshot:r}},$=(e,t)=>{e.dataLayer.push(t);};var Se=["debug","info","warn","error"],ve=()=>{},A=e=>{let t={};for(let n of Se){let r=e==null?void 0:e[n];if(typeof r=="function"){t[n]=r.bind(e);continue}t[n]=ve;}return t};var H=e=>typeof e=="string",S=e=>H(e)?{id:e}:e,Ee=e=>Array.isArray(e)?e.map(S):[S(e)],B=e=>e?Object.entries(e).reduce((t,[n,r])=>(t[n]=String(r),t),{}):{},K=e=>e.endsWith("/")?e.slice(0,-1):e,Y=(e,t,n,r,a=p)=>{let o=K(t),s=new URLSearchParams({id:n}),u=B(r);a!==p&&u.l===void 0&&(u.l=a);for(let[d,m]of Object.entries(u))d!=="id"&&s.set(d,m);return `${o}/${e==="gtm"?"gtm.js":"ns.html"}?${s.toString()}`},R=(e,t,n,r=p)=>Y("gtm",e,t,n,r),_=(e,t,n,r=p)=>Y("ns",e,t,n,r),D=e=>e.replace(/&/g,"&amp;").replace(/"/g,"&quot;").replace(/</g,"&lt;").replace(/>/g,"&gt;");var Z="data-gtm-container-id",Le="data-gtm-kit-instance",J=()=>{let e=!1,t,n=new Promise(r=>{t=r;});return {get settled(){return e},promise:n,resolve:r=>{e||(e=!0,t(r));}}},be=e=>{if(typeof document=="undefined")return null;let t=`script[${Z}="${e}"]`,n=document.querySelector(t);return n||Array.from(document.getElementsByTagName("script")).find(a=>a.src.includes(`id=${encodeURIComponent(e)}`))||null},Te=e=>{if(e instanceof ErrorEvent){if(e.error)return String(e.error);if(e.message)return e.message}return "Failed to load GTM script."},W,X,w=class{constructor(t){this.options=t;this.logger=A(this.options.logger);this.host=(W=this.options.host)!=null?W:C;this.dataLayerName=(X=this.options.dataLayerName)!=null?X:p;this.defaultQueryParams=this.options.defaultQueryParams;this.scriptAttributes=this.options.scriptAttributes;this.insertedScripts=new Set;this.readyCallbacks=new Set;this.readiness=J();this.loadStates=new Map;this.pendingContainers=new Set;}whenReady(){return this.readiness.promise}onReady(t){return this.readyCallbacks.add(t),this.readiness.settled&&t(Array.from(this.loadStates.values())),()=>{this.readyCallbacks.delete(t);}}notifyReady(){let t=Array.from(this.loadStates.values());this.readiness.settled||this.readiness.resolve(t);for(let n of this.readyCallbacks)n(t);}maybeNotifyReady(){this.pendingContainers.size===0&&this.notifyReady();}recordState(t){this.loadStates.set(t.containerId,t);}resetReadiness(){this.pendingContainers.clear(),this.loadStates.clear(),this.readiness=J();}ensure(t){var a;if(typeof document=="undefined"){this.logger.warn("No document available \u2013 skipping script injection.");for(let o of t)o.id&&this.recordState({containerId:o.id,status:"skipped",error:"Document unavailable for script injection."});return this.maybeNotifyReady(),{inserted:[]}}let n=[],r=document.head||document.body;if(!r){this.logger.error("Unable to find document.head or document.body for script injection.");for(let o of t)o.id&&this.recordState({containerId:o.id,status:"skipped",error:"Missing document.head and document.body for GTM script injection."});return this.maybeNotifyReady(),{inserted:[]}}for(let o of t){if(!o.id){this.logger.warn("Skipping container with missing id.",{container:o});continue}let s=be(o.id);if(s){this.logger.debug("Container script already present, skipping injection.",{containerId:o.id}),this.recordState({containerId:o.id,src:s.src,status:"loaded",fromCache:!0});continue}let u={...this.defaultQueryParams,...o.queryParams},i=document.createElement("script"),d=R(this.host,o.id,u,this.dataLayerName);i.src=d,i.setAttribute(Z,o.id),i.setAttribute(Le,this.options.instanceId);let m=(a=this.scriptAttributes)!=null?a:{};m.async!==void 0?i.async=m.async:i.async=!0,m.defer!==void 0&&(i.defer=m.defer);for(let[f,c]of Object.entries(m)){if(f==="async"||f==="defer"||c==null)continue;let l=String(c);f==="nonce"&&(i.nonce=l),i.setAttribute(f,l);}this.pendingContainers.add(o.id);let g=(f,c)=>{if(!this.pendingContainers.has(o.id))return;this.pendingContainers.delete(o.id);let l={containerId:o.id,src:d,status:f,fromCache:!1};f==="failed"&&c&&(l.error=Te(c),this.logger.error("Failed to load GTM container script.",{containerId:o.id,src:d,error:l.error})),this.recordState(l),this.maybeNotifyReady();};i.addEventListener("load",()=>g("loaded")),i.addEventListener("error",f=>g("failed",f)),r.appendChild(i),this.insertedScripts.add(i),n.push(i),this.logger.info("Injected GTM container script.",{containerId:o.id,src:d});}return this.maybeNotifyReady(),{inserted:n}}teardown(){if(typeof document!="undefined"){for(let t of this.insertedScripts)t.parentNode&&t.parentNode.removeChild(t);this.insertedScripts.clear(),this.resetReadiness();}}};var Ae=e=>typeof e=="string",ee=e=>Ae(e)?{id:e}:e,I=e=>{if(typeof e!="object"||e===null)return !1;let t=Object.getPrototypeOf(e);return t===Object.prototype||t===null},N=e=>{if(e===null||typeof e=="boolean"||typeof e=="number"||typeof e=="string")return JSON.stringify(e);if(Array.isArray(e)){let t=[];for(let n of e){let r=N(n);if(r===null)return null;t.push(r);}return `[${t.join(",")}]`}if(I(e)){let t=Object.keys(e).sort(),n=[];for(let r of t){let a=N(e[r]);if(a===null)return null;n.push(`${JSON.stringify(r)}:${a}`);}return `{${n.join(",")}}`}return null},O=e=>Array.isArray(e)||I(e)?N(e):null,L=e=>Array.isArray(e)&&e.length>=3&&e[0]==="consent"&&(e[1]==="default"||e[1]==="update"),te=e=>I(e)?e.event==="gtm.js":!1,De=0,ne,P=class{constructor(t,n){this.options=t;this.instanceId=n;this.logger=A(this.options.logger);this.resolvedDataLayerName=(ne=this.options.dataLayerName)!=null?ne:p;this.containers=Array.isArray(this.options.containers)?this.options.containers.map(ee):[ee(this.options.containers)];this.queue=[];this.queuedConsentSignatures=new Set;this.deliveredConsentSignatures=new Set;this.snapshotSignatures=null;this.scriptManager=new w({instanceId:this.instanceId,host:this.options.host,dataLayerName:this.resolvedDataLayerName,defaultQueryParams:this.options.defaultQueryParams,scriptAttributes:this.options.scriptAttributes,logger:this.options.logger});this.dataLayerState=null;this.initialized=!1;this.startTimestamp=Date.now();if(!this.containers.length)throw new Error("At least one GTM container ID is required to initialize the client.")}init(){if(this.initialized){this.logger.debug("GTM client already initialized; skipping.");return}this.logger.info("Initializing GTM client.",{containers:this.containers.map(t=>t.id),dataLayerName:this.resolvedDataLayerName}),this.dataLayerState=Q(this.resolvedDataLayerName),this.captureSnapshotSignatures(),this.pushStartEvent(),this.flushQueue(),this.scriptManager.ensure(this.containers),this.initialized=!0;}push(t){if(t==null){this.logger.warn("Ignoring falsy dataLayer push.",{value:t});return}this.deliverToDataLayer(t)?this.logger.debug("Pushed value to dataLayer.",{immediate:!0}):this.logger.debug("Queued dataLayer value (pre-init).",{queueLength:this.queue.length});}setConsentDefaults(t,n){let r=v({command:"default",state:t,options:n}),a=this.deliverToDataLayer(r);this.logger.info("Applied consent defaults.",{immediate:a,state:t,options:n});}updateConsent(t,n){let r=v({command:"update",state:t,options:n}),a=this.deliverToDataLayer(r);this.logger.info("Updated consent state.",{immediate:a,state:t,options:n});}teardown(){this.logger.info("Tearing down GTM client instance.",{dataLayerName:this.resolvedDataLayerName}),this.scriptManager.teardown(),this.dataLayerState&&this.dataLayerState.restore(),this.queue.length=0,this.queuedConsentSignatures.clear(),this.deliveredConsentSignatures.clear(),this.snapshotSignatures=null,this.initialized=!1,this.dataLayerState=null;}isInitialized(){return this.initialized}whenReady(){return this.scriptManager.whenReady()}onReady(t){return this.scriptManager.onReady(t)}get dataLayerName(){return this.resolvedDataLayerName}flushQueue(){if(this.dataLayerState)for(;this.queue.length;){let t=this.queue.shift();t&&(this.pushValueToDataLayer(t.value,t.signature),t.signature&&this.queuedConsentSignatures.delete(t.signature));}}deliverToDataLayer(t){return this.initialized&&this.dataLayerState?(this.pushValueToDataLayer(t),!0):(this.queueValue(t),!1)}pushStartEvent(){if(!this.dataLayerState)return;if(this.hasExistingStartEvent()){this.logger.debug("Detected existing gtm.js event; skipping duplicate start push.");return}let t={"gtm.start":this.startTimestamp,event:"gtm.js"};this.pushValueToDataLayer(t);}captureSnapshotSignatures(){var n;if(!this.dataLayerState)return;let t=(n=this.dataLayerState.snapshot)!=null?n:[];this.snapshotSignatures=new Set;for(let r of t){let a=O(r);a&&(this.snapshotSignatures.add(a),L(r)&&this.deliveredConsentSignatures.add(a));}}queueValue(t){let n=L(t)?O(t):null;if(n&&this.queuedConsentSignatures.has(n)){this.logger.debug("Skipping duplicate queued dataLayer value.",{value:t});return}let r={value:t,signature:n};if(L(t)){let a=this.queue.findIndex(o=>!L(o.value));a===-1?this.queue.push(r):this.queue.splice(a,0,r);}else this.queue.push(r);n&&this.queuedConsentSignatures.add(n);}pushValueToDataLayer(t,n){var u;if(!this.dataLayerState)return;let r=n!=null?n:O(t),a=L(t),o=r?(u=this.snapshotSignatures)==null?void 0:u.has(r):!1,s=a&&r?this.deliveredConsentSignatures.has(r):!1;if(r&&o){this.logger.debug("Skipping duplicate dataLayer value detected during hydration.",{value:t}),a&&this.deliveredConsentSignatures.add(r);return}if(a&&s){this.logger.debug("Skipping duplicate consent command.",{value:t});return}$(this.dataLayerState,t),a&&r&&this.deliveredConsentSignatures.add(r);}hasExistingStartEvent(){var n;return this.dataLayerState?((n=this.dataLayerState.snapshot)!=null?n:[]).some(te)?!0:this.dataLayerState.dataLayer.some(te):!1}},we=e=>{let t=`gtm-kit-${++De}`;return new P(e,t)};var k=e=>typeof e=="object"&&e!==null,Ne=e=>e&&{...e},V=(e,t,n)=>{var a;if(!t)throw new Error("An event name is required when pushing to the dataLayer.");if(n!==void 0&&!k(n))throw new Error("Event payloads must be plain objects when pushing to the dataLayer.");let r={event:t,...(a=Ne(n))!=null?a:{}};return e.push(r),r},re=(e,t,n,r)=>{var s;if(!k(n))throw new Error("Ecommerce payload must be an object.");let a=(s=r==null?void 0:r.extras)!=null?s:{};if(!k(a))throw new Error("Ecommerce extras must be an object when provided.");let o={...a,ecommerce:n};return V(e,t,o)};var ae={height:"0",width:"0",style:"display:none;visibility:hidden",title:"Google Tag Manager"},xe=e=>{if(!e)return "";let t=Object.entries(e);return t.length?t.map(([n,r])=>`${n}="${D(String(r))}"`).join(" "):""},Re=(e,t)=>{var i;if(!e.id)throw new Error("Container id is required to build noscript markup.");let n=(i=t.host)!=null?i:C,r={...t.defaultQueryParams,...e.queryParams},a=_(n,e.id,r),o={...ae,...t.iframeAttributes},s=xe(o),u=s?` ${s}`:"";return `<noscript><iframe src="${D(a)}"${u}></iframe></noscript>`},_e=(e,t={})=>{let n=Array.isArray(e)?e.map(S):[S(e)];if(!n.length)throw new Error("At least one container is required to build noscript markup.");return n.map(r=>Re(r,t)).join("")},Oe={...ae};var Pe=e=>e.replace(/\\/g,"\\\\").replace(/'/g,"\\'").replace(/"/g,'\\"').replace(/\n/g,"\\n").replace(/\r/g,"\\r").replace(/</g,"\\x3c").replace(/>/g,"\\x3e").replace(/\u2028/g,"\\u2028").replace(/\u2029/g,"\\u2029");function oe(e={}){let{dataLayerName:t=p,pollInterval:n=50,timeout:r=3e4,maxBufferSize:a=1e3,onReplay:o,onTimeout:s}=e;if(typeof globalThis=="undefined"||typeof globalThis.document=="undefined")return Ve();let u=globalThis;Array.isArray(u[t])||(u[t]=[]);let i=u[t],d=[],m=i.push.bind(i),g=!0,f=!1,c=null,l=null,M=()=>i.some(y=>y!==null&&typeof y=="object"&&!Array.isArray(y)&&y.event==="gtm.js"),b=()=>{if(!g)return;g=!1,f=!0,c&&(clearInterval(c),c=null),l&&(clearTimeout(l),l=null),i.push=m;let y=d.length;for(let h of d)m(h.value);d.length=0,o==null||o(y);},ie=()=>{g&&(g=!1,c&&(clearInterval(c),c=null),l&&(clearTimeout(l),l=null),i.push=m,d.length=0);},se=function(...y){for(let h of y)m(h),g&&d.length<a&&d.push({value:h,timestamp:Date.now()}),g&&h!==null&&typeof h=="object"&&!Array.isArray(h)&&h.event==="gtm.js"&&setTimeout(b,0);return i.length};return i.push=se,M()?setTimeout(b,0):(c=setInterval(()=>{M()&&b();},n),r>0&&(l=setTimeout(()=>{g&&(s==null||s(d.length));},r))),{get active(){return g},get bufferedCount(){return d.length},get gtmReady(){return f},replay:b,uninstall:ie}}function Ie(e=p){return `(function(w,n){w[n]=w[n]||[];var q=[],o=w[n].push.bind(w[n]);w[n].push=function(){for(var i=0;i<arguments.length;i++){q.push({v:arguments[i],t:Date.now()});o(arguments[i])}return w[n].length};w.__gtmkit_buffer={q:q,o:o,n:n}})(window,'${Pe(e)}');`}function ke(e={}){if(typeof globalThis=="undefined")return null;let t=globalThis,n=t.__gtmkit_buffer;if(!n)return null;let{n:r}=n;return delete t.__gtmkit_buffer,oe({...e,dataLayerName:r})}function Ve(){let e=()=>{};return {active:!1,bufferedCount:0,gtmReady:!1,replay:e,uninstall:e}}
4
4
 
5
- exports.DEFAULT_DATA_LAYER_NAME = g;
5
+ exports.DEFAULT_DATA_LAYER_NAME = p;
6
6
  exports.DEFAULT_GTM_HOST = C;
7
- exports.DEFAULT_NOSCRIPT_IFRAME_ATTRIBUTES = xe;
8
- exports.allGranted = le;
9
- exports.analyticsOnly = pe;
10
- exports.attachToInlineBuffer = _e;
11
- exports.buildConsentCommand = w;
12
- exports.consent = de;
13
- exports.consentPresets = v;
14
- exports.createAutoQueueScript = Oe;
15
- exports.createConsentDefaultsCommand = j;
16
- exports.createConsentUpdateCommand = z;
17
- exports.createGtmClient = Le;
18
- exports.createNoscriptMarkup = Re;
19
- exports.eeaDefault = ce;
20
- exports.getConsentPreset = ue;
21
- exports.installAutoQueue = Z;
22
- exports.pushEcommerce = Y;
23
- exports.pushEvent = O;
7
+ exports.DEFAULT_NOSCRIPT_IFRAME_ATTRIBUTES = Oe;
8
+ exports.allGranted = he;
9
+ exports.analyticsOnly = Ce;
10
+ exports.attachToInlineBuffer = ke;
11
+ exports.buildConsentCommand = x;
12
+ exports.buildGtmNoscriptUrl = _;
13
+ exports.buildGtmScriptUrl = R;
14
+ exports.consent = fe;
15
+ exports.consentPresets = E;
16
+ exports.createAutoQueueScript = Ie;
17
+ exports.createConsentDefaultsCommand = q;
18
+ exports.createConsentUpdateCommand = F;
19
+ exports.createGtmClient = we;
20
+ exports.createNoscriptMarkup = _e;
21
+ exports.eeaDefault = ye;
22
+ exports.escapeAttributeValue = D;
23
+ exports.getConsentPreset = ge;
24
+ exports.installAutoQueue = oe;
25
+ exports.isString = H;
26
+ exports.normalizeContainer = S;
27
+ exports.normalizeContainers = Ee;
28
+ exports.normalizeHost = K;
29
+ exports.pushEcommerce = re;
30
+ exports.pushEvent = V;
31
+ exports.toRecord = B;
24
32
  //# sourceMappingURL=out.js.map
25
33
  //# sourceMappingURL=index.cjs.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/constants.ts","../src/consent.ts","../src/consent/presets.ts","../src/data-layer.ts","../src/logger.ts","../src/script-manager.ts","../src/client.ts","../src/events/push.ts","../src/noscript.ts","../src/auto-queue.ts"],"names":["DEFAULT_GTM_HOST","DEFAULT_DATA_LAYER_NAME","CONSENT_COMMAND","CONSENT_DEFAULT","CONSENT_UPDATE","CONSENT_KEYS","isConsentKey","value","isConsentDecision","assertValidRegions","regions","region","assertValidWaitForUpdate","waitForUpdate","normalizeConsentState","state","normalizedEntries","key","normalizedState","normalizeOptions","options","payload","buildConsentCommand","command","normalizedOptions","createConsentCommandValue","input","createConsentDefaultsCommand","createConsentUpdateCommand","consent","consentPresets","getConsentPreset","name","eeaDefault","allGranted","analyticsOnly","isArray","ensureDataLayer","globalScope","existing","snapshot","clone","pushToDataLayer","levels","noop","createLogger","logger","safeLogger","level","provided","CONTAINER_ATTR","INSTANCE_ATTR","createDeferred","resolved","resolver","promise","resolve","toRecord","params","acc","buildScriptUrl","host","containerId","queryParams","normalizedHost","searchParams","findExistingScript","attrSelector","existingWithAttr","script","formatErrorMessage","event","_a","_b","ScriptManager","callback","containers","container","inserted","targetParent","url","attributes","stringValue","settle","status","isString","normalizeContainer","isPlainObject","prototype","serializeUnknown","parts","entry","serialized","keys","serializeDataLayerValue","isConsentCommandValue","isStartEvent","instanceCounter","GtmClientImpl","instanceId","immediate","startEvent","signature","firstNonConsentIndex","queued","existingSignature","isConsentCommand","seenInSnapshot","alreadyDeliveredConsent","createGtmClient","isRecord","clonePayload","pushEvent","client","pushEcommerce","ecommerce","extras","DEFAULT_IFRAME_ATTRIBUTES","escapeAttributeValue","buildNoscriptUrl","buildAttributeString","entries","buildNoscriptForContainer","src","iframeAttributes","attributeString","attrs","createNoscriptMarkup","normalizedContainers","DEFAULT_NOSCRIPT_IFRAME_ATTRIBUTES","escapeJsString","installAutoQueue","dataLayerName","pollInterval","timeout","maxBufferSize","onReplay","onTimeout","createNoopState","dataLayer","buffer","originalPush","active","gtmReady","pollTimer","timeoutTimer","isGtmLoaded","replay","count","uninstall","interceptedPush","args","createAutoQueueScript","attachToInlineBuffer","inlineBuffer"],"mappings":"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","sourcesContent":["export const DEFAULT_GTM_HOST = 'https://www.googletagmanager.com';\nexport const DEFAULT_DATA_LAYER_NAME = 'dataLayer';\n","import type { DataLayerValue } from './types';\n\nconst CONSENT_COMMAND = 'consent' as const;\nconst CONSENT_DEFAULT = 'default' as const;\nconst CONSENT_UPDATE = 'update' as const;\n\n/**\n * The four consent categories tracked by Google Consent Mode v2.\n */\nconst CONSENT_KEYS = ['ad_storage', 'analytics_storage', 'ad_user_data', 'ad_personalization'] as const;\n\n/**\n * A consent category key: 'ad_storage' | 'analytics_storage' | 'ad_user_data' | 'ad_personalization'\n */\nexport type ConsentKey = (typeof CONSENT_KEYS)[number];\n\n/**\n * A consent decision: 'granted' or 'denied'\n */\nexport type ConsentDecision = 'granted' | 'denied';\n\n/**\n * Consent state object for one or more categories.\n *\n * This is a **partial** record - you only need to specify the categories you want to set.\n * Unspecified categories retain their previous state when using `updateConsent()`.\n *\n * @example\n * ```ts\n * // All four categories\n * const fullState: ConsentState = {\n * ad_storage: 'granted',\n * analytics_storage: 'granted',\n * ad_user_data: 'granted',\n * ad_personalization: 'granted'\n * };\n *\n * // Single category (partial update)\n * const partialState: ConsentState = {\n * analytics_storage: 'granted'\n * };\n *\n * // Multiple specific categories\n * const mixedState: ConsentState = {\n * analytics_storage: 'granted',\n * ad_storage: 'denied'\n * };\n * ```\n */\nexport type ConsentState = Partial<Record<ConsentKey, ConsentDecision>>;\n\nexport interface ConsentRegionOptions {\n /**\n * ISO 3166-2 region codes (e.g., `US-CA`, `EEA`) that the consent command applies to.\n */\n region?: readonly string[];\n /**\n * Milliseconds to wait for an explicit update before firing tags when using the default command.\n */\n waitForUpdate?: number;\n}\n\nexport type ConsentCommand = typeof CONSENT_DEFAULT | typeof CONSENT_UPDATE;\n\nexport interface ConsentCommandInput {\n command: ConsentCommand;\n state: ConsentState;\n options?: ConsentRegionOptions;\n}\n\nexport type ConsentCommandValue =\n | [typeof CONSENT_COMMAND, ConsentCommand, ConsentState]\n | [typeof CONSENT_COMMAND, ConsentCommand, ConsentState, Record<string, unknown>];\n\nconst isConsentKey = (value: string): value is ConsentKey => (CONSENT_KEYS as readonly string[]).includes(value);\n\nconst isConsentDecision = (value: unknown): value is ConsentDecision => value === 'granted' || value === 'denied';\n\nconst assertValidRegions = (regions: readonly string[]) => {\n if (!Array.isArray(regions)) {\n throw new Error('Consent region list must be an array of ISO region codes.');\n }\n\n for (const region of regions) {\n if (typeof region !== 'string' || region.trim().length === 0) {\n throw new Error('Consent region codes must be non-empty strings.');\n }\n }\n};\n\nconst assertValidWaitForUpdate = (waitForUpdate: number) => {\n if (!Number.isFinite(waitForUpdate) || waitForUpdate < 0) {\n throw new Error('waitForUpdate must be a non-negative finite number.');\n }\n};\n\nexport const normalizeConsentState = (state: ConsentState): ConsentState => {\n const normalizedEntries = Object.entries(state ?? {}).map(([key, value]) => {\n if (!isConsentKey(key)) {\n throw new Error(`Invalid consent key: ${key}`);\n }\n\n if (!isConsentDecision(value)) {\n throw new Error(`Invalid consent value for key \"${key}\". Expected \"granted\" or \"denied\".`);\n }\n\n return [key, value] as const;\n });\n\n if (!normalizedEntries.length) {\n throw new Error('At least one consent key/value pair is required.');\n }\n\n const normalizedState = {} as ConsentState;\n for (const [key, value] of normalizedEntries) {\n normalizedState[key as ConsentKey] = value;\n }\n\n return Object.freeze(normalizedState);\n};\n\nconst normalizeOptions = (options?: ConsentRegionOptions): Record<string, unknown> | undefined => {\n if (!options) {\n return undefined;\n }\n\n const payload: Record<string, unknown> = {};\n\n if (options.region) {\n assertValidRegions(options.region);\n if (options.region.length) {\n payload.region = [...options.region];\n }\n }\n\n if (typeof options.waitForUpdate === 'number') {\n assertValidWaitForUpdate(options.waitForUpdate);\n payload.wait_for_update = options.waitForUpdate;\n }\n\n return Object.keys(payload).length ? payload : undefined;\n};\n\nexport const buildConsentCommand = ({ command, state, options }: ConsentCommandInput): ConsentCommandValue => {\n if (command !== CONSENT_DEFAULT && command !== CONSENT_UPDATE) {\n throw new Error(`Unsupported consent command: ${command}`);\n }\n\n const normalizedState = normalizeConsentState(state);\n const normalizedOptions = normalizeOptions(options);\n\n if (normalizedOptions) {\n return [CONSENT_COMMAND, command, normalizedState, normalizedOptions];\n }\n\n return [CONSENT_COMMAND, command, normalizedState];\n};\n\nexport const createConsentCommandValue = (input: ConsentCommandInput): DataLayerValue => {\n // Spread the tuple to convert it to a plain array for DataLayerValue compatibility\n return [...buildConsentCommand(input)];\n};\n\nexport const createConsentDefaultsCommand = (state: ConsentState, options?: ConsentRegionOptions): DataLayerValue =>\n createConsentCommandValue({ command: CONSENT_DEFAULT, state, options });\n\nexport const createConsentUpdateCommand = (state: ConsentState, options?: ConsentRegionOptions): DataLayerValue =>\n createConsentCommandValue({ command: CONSENT_UPDATE, state, options });\n\nexport const consent = {\n buildConsentCommand,\n createConsentDefaultsCommand,\n createConsentUpdateCommand,\n normalizeConsentState\n};\n","import type { ConsentState } from '../consent';\n\n/**\n * Pre-configured consent state presets for common scenarios.\n *\n * These presets cover the most common consent patterns:\n * - `eeaDefault`: All denied (GDPR compliant default)\n * - `allGranted`: All granted (user accepts everything)\n * - `analyticsOnly`: Mixed state (analytics only, no ads)\n *\n * For custom combinations, pass a partial `ConsentState` object to `updateConsent()`.\n * You only need to specify the categories you want to update.\n *\n * @example\n * ```ts\n * // Use a preset\n * client.updateConsent(consentPresets.allGranted);\n *\n * // Or create a custom state\n * client.updateConsent({\n * analytics_storage: 'granted',\n * ad_storage: 'denied'\n * });\n *\n * // Partial updates (only update specific categories)\n * client.updateConsent({ analytics_storage: 'granted' });\n * ```\n */\nexport const consentPresets = {\n /**\n * All categories denied - GDPR/EEA compliant default.\n *\n * Use as the initial state for regions requiring explicit opt-in consent.\n * Tags will be blocked until the user grants specific permissions.\n *\n * | Category | State |\n * |----------|-------|\n * | ad_storage | denied |\n * | analytics_storage | denied |\n * | ad_user_data | denied |\n * | ad_personalization | denied |\n */\n eeaDefault: Object.freeze({\n ad_storage: 'denied',\n analytics_storage: 'denied',\n ad_user_data: 'denied',\n ad_personalization: 'denied'\n } satisfies ConsentState),\n\n /**\n * All categories granted - user accepts all tracking.\n *\n * Use when the user clicks \"Accept All\" or in regions where consent is implied.\n *\n * | Category | State |\n * |----------|-------|\n * | ad_storage | granted |\n * | analytics_storage | granted |\n * | ad_user_data | granted |\n * | ad_personalization | granted |\n */\n allGranted: Object.freeze({\n ad_storage: 'granted',\n analytics_storage: 'granted',\n ad_user_data: 'granted',\n ad_personalization: 'granted'\n } satisfies ConsentState),\n\n /**\n * Analytics allowed, advertising denied - mixed consent state.\n *\n * Use when the user accepts analytics/statistics but rejects advertising cookies.\n * This is a common \"essential + analytics\" consent pattern.\n *\n * | Category | State |\n * |----------|-------|\n * | ad_storage | denied |\n * | analytics_storage | granted |\n * | ad_user_data | denied |\n * | ad_personalization | denied |\n */\n analyticsOnly: Object.freeze({\n ad_storage: 'denied',\n analytics_storage: 'granted',\n ad_user_data: 'denied',\n ad_personalization: 'denied'\n } satisfies ConsentState)\n} as const;\n\nexport type ConsentPresetName = keyof typeof consentPresets;\n\nexport const getConsentPreset = (name: ConsentPresetName): ConsentState => ({\n ...consentPresets[name]\n});\n\n/**\n * Convenience export for the EEA default consent state.\n * All categories denied - GDPR/EEA compliant default.\n */\nexport const eeaDefault = consentPresets.eeaDefault;\n\n/**\n * Convenience export for the all-granted consent state.\n * All categories granted - user accepts all tracking.\n */\nexport const allGranted = consentPresets.allGranted;\n\n/**\n * Convenience export for the analytics-only consent state.\n * Analytics allowed, advertising denied.\n */\nexport const analyticsOnly = consentPresets.analyticsOnly;\n","import type { DataLayerState, DataLayerValue } from './types';\n\ninterface EnsureDataLayerResult extends DataLayerState {\n snapshot: DataLayerValue[] | undefined;\n}\n\nconst isArray = (value: unknown): value is DataLayerValue[] => Array.isArray(value);\n\nexport const ensureDataLayer = (name: string): EnsureDataLayerResult => {\n const globalScope = globalThis as Record<string, unknown>;\n const existing = globalScope[name];\n const snapshot = isArray(existing) ? [...existing] : undefined;\n\n if (!isArray(existing)) {\n globalScope[name] = [] as DataLayerValue[];\n }\n\n return {\n name,\n dataLayer: globalScope[name] as DataLayerValue[],\n created: !isArray(existing),\n restore() {\n if (!isArray(existing)) {\n delete globalScope[name];\n return;\n }\n\n const clone = snapshot ? [...snapshot] : [];\n globalScope[name] = clone;\n },\n snapshot\n };\n};\n\nexport const pushToDataLayer = (state: DataLayerState, value: DataLayerValue) => {\n state.dataLayer.push(value);\n};\n","import type { Logger, PartialLogger } from './types';\n\ntype LogLevel = keyof Logger;\n\nconst levels: LogLevel[] = ['debug', 'info', 'warn', 'error'];\n\nconst noop = () => {\n /* intentionally empty */\n};\n\nexport const createLogger = (logger?: PartialLogger): Logger => {\n const safeLogger: Partial<Record<LogLevel, (message: string, details?: Record<string, unknown>) => void>> = {};\n\n for (const level of levels) {\n const provided = logger?.[level];\n if (typeof provided === 'function') {\n safeLogger[level] = provided.bind(logger);\n continue;\n }\n safeLogger[level] = noop;\n }\n\n return safeLogger as Logger;\n};\n\nexport type LoggerFacade = ReturnType<typeof createLogger>;\n","import { DEFAULT_DATA_LAYER_NAME, DEFAULT_GTM_HOST } from './constants';\nimport { createLogger } from './logger';\nimport type {\n ContainerDescriptor,\n CreateGtmClientOptions,\n ScriptAttributes,\n ScriptLoadState,\n ScriptLoadStatus\n} from './types';\n\nconst CONTAINER_ATTR = 'data-gtm-container-id';\nconst INSTANCE_ATTR = 'data-gtm-kit-instance';\n\ninterface Deferred<T> {\n promise: Promise<T>;\n resolve: (value: T) => void;\n settled: boolean;\n}\n\nconst createDeferred = <T>(): Deferred<T> => {\n let resolved = false;\n let resolver: (value: T) => void;\n\n const promise = new Promise<T>((resolve) => {\n resolver = resolve;\n });\n\n return {\n get settled() {\n return resolved;\n },\n promise,\n resolve: (value: T) => {\n if (resolved) {\n return;\n }\n\n resolved = true;\n resolver(value);\n }\n } as Deferred<T>;\n};\n\nexport interface NormalizedContainer extends ContainerDescriptor {\n queryParams?: Record<string, string | number | boolean>;\n}\n\nexport interface ScriptManagerOptions {\n instanceId: string;\n host?: string;\n dataLayerName?: string;\n scriptAttributes?: ScriptAttributes;\n defaultQueryParams?: Record<string, string | number | boolean>;\n logger?: CreateGtmClientOptions['logger'];\n}\n\nexport interface EnsureResult {\n inserted: HTMLScriptElement[];\n}\n\nconst toRecord = (\n params?: Record<string, string | number | boolean>\n): Record<string, string> => {\n if (!params) {\n return {};\n }\n\n return Object.entries(params).reduce<Record<string, string>>((acc, [key, value]) => {\n acc[key] = String(value);\n return acc;\n }, {});\n};\n\nconst buildScriptUrl = (\n host: string,\n containerId: string,\n queryParams?: Record<string, string | number | boolean>\n): string => {\n const normalizedHost = host.endsWith('/') ? host.slice(0, -1) : host;\n const searchParams = new URLSearchParams({ id: containerId });\n\n const params = toRecord(queryParams);\n for (const [key, value] of Object.entries(params)) {\n if (key === 'id') {\n continue;\n }\n searchParams.set(key, value);\n }\n\n return `${normalizedHost}/gtm.js?${searchParams.toString()}`;\n};\n\nconst findExistingScript = (containerId: string): HTMLScriptElement | null => {\n if (typeof document === 'undefined') {\n return null;\n }\n\n const attrSelector = `script[${CONTAINER_ATTR}=\"${containerId}\"]`;\n const existingWithAttr = document.querySelector(attrSelector);\n if (existingWithAttr) {\n return existingWithAttr as HTMLScriptElement;\n }\n\n const scripts = Array.from(document.getElementsByTagName('script'));\n return (\n scripts.find((script) => script.src.includes(`id=${encodeURIComponent(containerId)}`)) || null\n );\n};\n\nconst formatErrorMessage = (event: Event): string => {\n if (event instanceof ErrorEvent) {\n if (event.error) {\n return String(event.error);\n }\n\n if (event.message) {\n return event.message;\n }\n }\n\n return 'Failed to load GTM script.';\n};\n\nexport class ScriptManager {\n private readonly logger = createLogger(this.options.logger);\n private readonly host = this.options.host ?? DEFAULT_GTM_HOST;\n private readonly dataLayerName = this.options.dataLayerName ?? DEFAULT_DATA_LAYER_NAME;\n private readonly defaultQueryParams = this.options.defaultQueryParams;\n private readonly scriptAttributes = this.options.scriptAttributes;\n private readonly insertedScripts = new Set<HTMLScriptElement>();\n private readonly readyCallbacks = new Set<(state: ScriptLoadState[]) => void>();\n private readiness = createDeferred<ScriptLoadState[]>();\n private readonly loadStates = new Map<string, ScriptLoadState>();\n private readonly pendingContainers = new Set<string>();\n\n constructor(private readonly options: ScriptManagerOptions) {}\n\n whenReady(): Promise<ScriptLoadState[]> {\n return this.readiness.promise;\n }\n\n onReady(callback: (state: ScriptLoadState[]) => void): () => void {\n this.readyCallbacks.add(callback);\n\n if (this.readiness.settled) {\n callback(Array.from(this.loadStates.values()));\n }\n\n return () => {\n this.readyCallbacks.delete(callback);\n };\n }\n\n private notifyReady(): void {\n const snapshot = Array.from(this.loadStates.values());\n\n if (!this.readiness.settled) {\n this.readiness.resolve(snapshot);\n }\n\n for (const callback of this.readyCallbacks) {\n callback(snapshot);\n }\n }\n\n private maybeNotifyReady(): void {\n if (this.pendingContainers.size === 0) {\n this.notifyReady();\n }\n }\n\n private recordState(state: ScriptLoadState): void {\n this.loadStates.set(state.containerId, state);\n }\n\n private resetReadiness(): void {\n this.pendingContainers.clear();\n this.loadStates.clear();\n this.readiness = createDeferred<ScriptLoadState[]>();\n }\n\n ensure(containers: NormalizedContainer[]): EnsureResult {\n if (typeof document === 'undefined') {\n this.logger.warn('No document available – skipping script injection.');\n\n for (const container of containers) {\n if (!container.id) {\n continue;\n }\n\n this.recordState({\n containerId: container.id,\n status: 'skipped',\n error: 'Document unavailable for script injection.'\n });\n }\n\n this.maybeNotifyReady();\n return { inserted: [] };\n }\n\n const inserted: HTMLScriptElement[] = [];\n const targetParent = document.head || document.body;\n if (!targetParent) {\n this.logger.error('Unable to find document.head or document.body for script injection.');\n\n for (const container of containers) {\n if (!container.id) {\n continue;\n }\n\n this.recordState({\n containerId: container.id,\n status: 'skipped',\n error: 'Missing document.head and document.body for GTM script injection.'\n });\n }\n\n this.maybeNotifyReady();\n return { inserted: [] };\n }\n\n for (const container of containers) {\n if (!container.id) {\n this.logger.warn('Skipping container with missing id.', { container });\n continue;\n }\n\n const existing = findExistingScript(container.id);\n if (existing) {\n this.logger.debug('Container script already present, skipping injection.', {\n containerId: container.id\n });\n\n this.recordState({\n containerId: container.id,\n src: existing.src,\n status: 'loaded',\n fromCache: true\n });\n continue;\n }\n\n const params = {\n ...this.defaultQueryParams,\n ...container.queryParams\n };\n\n if (this.dataLayerName !== DEFAULT_DATA_LAYER_NAME && params.l === undefined) {\n params.l = this.dataLayerName;\n }\n\n const script = document.createElement('script');\n const url = buildScriptUrl(this.host, container.id, params);\n script.src = url;\n script.setAttribute(CONTAINER_ATTR, container.id);\n script.setAttribute(INSTANCE_ATTR, this.options.instanceId);\n\n const attributes = this.scriptAttributes ?? {};\n if (attributes.async !== undefined) {\n script.async = attributes.async;\n } else {\n script.async = true;\n }\n if (attributes.defer !== undefined) {\n script.defer = attributes.defer;\n }\n\n for (const [key, value] of Object.entries(attributes)) {\n if (key === 'async' || key === 'defer') {\n continue;\n }\n\n if (value === undefined || value === null) {\n continue;\n }\n\n const stringValue = String(value);\n\n if (key === 'nonce') {\n script.nonce = stringValue;\n }\n\n script.setAttribute(key, stringValue);\n }\n\n this.pendingContainers.add(container.id);\n\n const settle = (status: ScriptLoadStatus, event?: Event): void => {\n if (!this.pendingContainers.has(container.id)) {\n return;\n }\n\n this.pendingContainers.delete(container.id);\n\n const state: ScriptLoadState = {\n containerId: container.id,\n src: url,\n status,\n fromCache: false\n };\n\n if (status === 'failed' && event) {\n state.error = formatErrorMessage(event);\n this.logger.error('Failed to load GTM container script.', {\n containerId: container.id,\n src: url,\n error: state.error\n });\n }\n\n this.recordState(state);\n this.maybeNotifyReady();\n };\n\n script.addEventListener('load', () => settle('loaded'));\n script.addEventListener('error', (event) => settle('failed', event));\n\n targetParent.appendChild(script);\n this.insertedScripts.add(script);\n inserted.push(script);\n this.logger.info('Injected GTM container script.', { containerId: container.id, src: url });\n }\n\n this.maybeNotifyReady();\n return { inserted };\n }\n\n teardown() {\n if (typeof document === 'undefined') {\n return;\n }\n\n for (const script of this.insertedScripts) {\n if (script.parentNode) {\n script.parentNode.removeChild(script);\n }\n }\n\n this.insertedScripts.clear();\n this.resetReadiness();\n }\n}\n","import { DEFAULT_DATA_LAYER_NAME } from './constants';\nimport { ensureDataLayer, pushToDataLayer } from './data-layer';\nimport { createConsentCommandValue } from './consent';\nimport type { ConsentRegionOptions, ConsentState } from './consent';\nimport { createLogger } from './logger';\nimport { ScriptManager } from './script-manager';\nimport type {\n ContainerConfigInput,\n ContainerDescriptor,\n CreateGtmClientOptions,\n DataLayerValue,\n GtmClient,\n ScriptLoadState\n} from './types';\n\nconst isString = (value: unknown): value is string => typeof value === 'string';\n\nconst normalizeContainer = (input: ContainerConfigInput): ContainerDescriptor => {\n if (isString(input)) {\n return { id: input };\n }\n\n return input;\n};\n\nconst isPlainObject = (value: unknown): value is Record<string, unknown> => {\n if (typeof value !== 'object' || value === null) {\n return false;\n }\n\n const prototype = Object.getPrototypeOf(value);\n return prototype === Object.prototype || prototype === null;\n};\n\nconst serializeUnknown = (value: unknown): string | null => {\n if (value === null || typeof value === 'boolean' || typeof value === 'number') {\n return JSON.stringify(value);\n }\n\n if (typeof value === 'string') {\n return JSON.stringify(value);\n }\n\n if (Array.isArray(value)) {\n const parts: string[] = [];\n for (const entry of value) {\n const serialized = serializeUnknown(entry);\n if (serialized === null) {\n return null;\n }\n parts.push(serialized);\n }\n return `[${parts.join(',')}]`;\n }\n\n if (isPlainObject(value)) {\n const keys = Object.keys(value).sort();\n const parts: string[] = [];\n for (const key of keys) {\n const serialized = serializeUnknown((value as Record<string, unknown>)[key]);\n if (serialized === null) {\n return null;\n }\n parts.push(`${JSON.stringify(key)}:${serialized}`);\n }\n return `{${parts.join(',')}}`;\n }\n\n return null;\n};\n\nconst serializeDataLayerValue = (value: DataLayerValue): string | null => {\n if (Array.isArray(value)) {\n return serializeUnknown(value);\n }\n\n if (isPlainObject(value)) {\n return serializeUnknown(value);\n }\n\n return null;\n};\n\nconst isConsentCommandValue = (value: DataLayerValue): value is unknown[] =>\n Array.isArray(value) &&\n value.length >= 3 &&\n value[0] === 'consent' &&\n (value[1] === 'default' || value[1] === 'update');\n\nconst isStartEvent = (value: DataLayerValue): boolean => {\n if (!isPlainObject(value)) {\n return false;\n }\n\n return (value.event as unknown) === 'gtm.js';\n};\n\ninterface QueuedEntry {\n value: DataLayerValue;\n signature: string | null;\n}\n\nlet instanceCounter = 0;\n\nexport class GtmClientImpl implements GtmClient {\n private readonly logger = createLogger(this.options.logger);\n private readonly resolvedDataLayerName = this.options.dataLayerName ?? DEFAULT_DATA_LAYER_NAME;\n private readonly containers = Array.isArray(this.options.containers)\n ? this.options.containers.map(normalizeContainer)\n : [normalizeContainer(this.options.containers)];\n private readonly queue: QueuedEntry[] = [];\n private readonly queuedConsentSignatures = new Set<string>();\n private readonly deliveredConsentSignatures = new Set<string>();\n private snapshotSignatures: Set<string> | null = null;\n private readonly scriptManager = new ScriptManager({\n instanceId: this.instanceId,\n host: this.options.host,\n dataLayerName: this.resolvedDataLayerName,\n defaultQueryParams: this.options.defaultQueryParams,\n scriptAttributes: this.options.scriptAttributes,\n logger: this.options.logger\n });\n private dataLayerState: ReturnType<typeof ensureDataLayer> | null = null;\n private initialized = false;\n private readonly startTimestamp = Date.now();\n\n constructor(\n private readonly options: CreateGtmClientOptions,\n private readonly instanceId: string\n ) {\n if (!this.containers.length) {\n throw new Error('At least one GTM container ID is required to initialize the client.');\n }\n }\n\n init(): void {\n if (this.initialized) {\n this.logger.debug('GTM client already initialized; skipping.');\n return;\n }\n\n this.logger.info('Initializing GTM client.', {\n containers: this.containers.map((container) => container.id),\n dataLayerName: this.resolvedDataLayerName\n });\n\n this.dataLayerState = ensureDataLayer(this.resolvedDataLayerName);\n this.captureSnapshotSignatures();\n this.pushStartEvent();\n this.flushQueue();\n this.scriptManager.ensure(this.containers);\n\n this.initialized = true;\n }\n\n push(value: DataLayerValue): void {\n if (value === undefined || value === null) {\n this.logger.warn('Ignoring falsy dataLayer push.', { value });\n return;\n }\n\n const immediate = this.deliverToDataLayer(value);\n\n if (immediate) {\n this.logger.debug('Pushed value to dataLayer.', { immediate: true });\n } else {\n this.logger.debug('Queued dataLayer value (pre-init).', { queueLength: this.queue.length });\n }\n }\n\n setConsentDefaults(state: ConsentState, options?: ConsentRegionOptions): void {\n const value = createConsentCommandValue({ command: 'default', state, options });\n const immediate = this.deliverToDataLayer(value);\n\n this.logger.info('Applied consent defaults.', {\n immediate,\n state,\n options\n });\n }\n\n updateConsent(state: ConsentState, options?: ConsentRegionOptions): void {\n const value = createConsentCommandValue({ command: 'update', state, options });\n const immediate = this.deliverToDataLayer(value);\n\n this.logger.info('Updated consent state.', {\n immediate,\n state,\n options\n });\n }\n\n teardown(): void {\n this.logger.info('Tearing down GTM client instance.', { dataLayerName: this.resolvedDataLayerName });\n this.scriptManager.teardown();\n if (this.dataLayerState) {\n this.dataLayerState.restore();\n }\n this.queue.length = 0;\n this.queuedConsentSignatures.clear();\n this.deliveredConsentSignatures.clear();\n this.snapshotSignatures = null;\n this.initialized = false;\n this.dataLayerState = null;\n }\n\n isInitialized(): boolean {\n return this.initialized;\n }\n\n whenReady(): Promise<ScriptLoadState[]> {\n return this.scriptManager.whenReady();\n }\n\n onReady(callback: (state: ScriptLoadState[]) => void): () => void {\n return this.scriptManager.onReady(callback);\n }\n\n get dataLayerName(): string {\n return this.resolvedDataLayerName;\n }\n\n private flushQueue(): void {\n if (!this.dataLayerState) {\n return;\n }\n\n while (this.queue.length) {\n const entry = this.queue.shift();\n if (!entry) {\n continue;\n }\n\n this.pushValueToDataLayer(entry.value, entry.signature);\n\n if (entry.signature) {\n this.queuedConsentSignatures.delete(entry.signature);\n }\n }\n }\n\n private deliverToDataLayer(value: DataLayerValue): boolean {\n if (this.initialized && this.dataLayerState) {\n this.pushValueToDataLayer(value);\n return true;\n }\n\n this.queueValue(value);\n return false;\n }\n\n private pushStartEvent(): void {\n if (!this.dataLayerState) {\n return;\n }\n\n if (this.hasExistingStartEvent()) {\n this.logger.debug('Detected existing gtm.js event; skipping duplicate start push.');\n return;\n }\n\n const startEvent = { 'gtm.start': this.startTimestamp, event: 'gtm.js' } as const;\n this.pushValueToDataLayer(startEvent);\n }\n\n private captureSnapshotSignatures(): void {\n if (!this.dataLayerState) {\n return;\n }\n\n const snapshot = this.dataLayerState.snapshot ?? [];\n this.snapshotSignatures = new Set<string>();\n\n for (const value of snapshot) {\n const signature = serializeDataLayerValue(value);\n if (signature) {\n this.snapshotSignatures.add(signature);\n if (isConsentCommandValue(value)) {\n this.deliveredConsentSignatures.add(signature);\n }\n }\n }\n }\n\n private queueValue(value: DataLayerValue): void {\n const signature = isConsentCommandValue(value) ? serializeDataLayerValue(value) : null;\n\n if (signature && this.queuedConsentSignatures.has(signature)) {\n this.logger.debug('Skipping duplicate queued dataLayer value.', { value });\n return;\n }\n\n const entry: QueuedEntry = { value, signature };\n\n if (isConsentCommandValue(value)) {\n const firstNonConsentIndex = this.queue.findIndex((queued) => !isConsentCommandValue(queued.value));\n\n if (firstNonConsentIndex === -1) {\n this.queue.push(entry);\n } else {\n this.queue.splice(firstNonConsentIndex, 0, entry);\n }\n } else {\n this.queue.push(entry);\n }\n\n if (signature) {\n this.queuedConsentSignatures.add(signature);\n }\n }\n\n private pushValueToDataLayer(value: DataLayerValue, existingSignature?: string | null): void {\n if (!this.dataLayerState) {\n return;\n }\n\n const signature = existingSignature ?? serializeDataLayerValue(value);\n const isConsentCommand = isConsentCommandValue(value);\n const seenInSnapshot = signature ? this.snapshotSignatures?.has(signature) : false;\n const alreadyDeliveredConsent =\n isConsentCommand && signature ? this.deliveredConsentSignatures.has(signature) : false;\n\n if (signature && seenInSnapshot) {\n this.logger.debug('Skipping duplicate dataLayer value detected during hydration.', { value });\n if (isConsentCommand) {\n this.deliveredConsentSignatures.add(signature);\n }\n return;\n }\n\n if (isConsentCommand && alreadyDeliveredConsent) {\n this.logger.debug('Skipping duplicate consent command.', { value });\n return;\n }\n\n pushToDataLayer(this.dataLayerState, value);\n\n if (isConsentCommand && signature) {\n this.deliveredConsentSignatures.add(signature);\n }\n }\n\n private hasExistingStartEvent(): boolean {\n if (!this.dataLayerState) {\n return false;\n }\n\n const snapshot = this.dataLayerState.snapshot ?? [];\n if (snapshot.some(isStartEvent)) {\n return true;\n }\n\n return this.dataLayerState.dataLayer.some(isStartEvent);\n }\n}\n\nexport const createGtmClient = (options: CreateGtmClientOptions): GtmClient => {\n const instanceId = `gtm-kit-${++instanceCounter}`;\n return new GtmClientImpl(options, instanceId);\n};\n","import type { GtmClient } from '../types';\nimport type {\n EcommerceEvent,\n EcommerceEventName,\n EcommercePayload,\n EventForName,\n EventPayload,\n GtmEvent\n} from './types';\n\nconst isRecord = (value: unknown): value is Record<string, unknown> => typeof value === 'object' && value !== null;\n\nconst clonePayload = <TPayload extends EventPayload | undefined>(payload: TPayload): TPayload => {\n if (!payload) {\n return payload;\n }\n\n return { ...payload } as TPayload;\n};\n\nexport const pushEvent = <TName extends string, TPayload extends EventPayload = EventPayload>(\n client: Pick<GtmClient, 'push'>,\n name: TName,\n payload?: TPayload\n): EventForName<TName> => {\n if (!name) {\n throw new Error('An event name is required when pushing to the dataLayer.');\n }\n\n if (payload !== undefined && !isRecord(payload)) {\n throw new Error('Event payloads must be plain objects when pushing to the dataLayer.');\n }\n\n const event = {\n event: name,\n ...(clonePayload(payload) ?? {})\n } as GtmEvent<TName, TPayload>;\n\n client.push(event);\n return event as EventForName<TName>;\n};\n\nexport interface PushEcommerceOptions<TExtras extends EventPayload = EventPayload> {\n extras?: TExtras;\n}\n\nexport const pushEcommerce = <TName extends EcommerceEventName, TExtras extends EventPayload = EventPayload>(\n client: Pick<GtmClient, 'push'>,\n name: TName,\n ecommerce: EcommercePayload,\n options?: PushEcommerceOptions<TExtras>\n): EcommerceEvent<TName, TExtras> => {\n if (!isRecord(ecommerce)) {\n throw new Error('Ecommerce payload must be an object.');\n }\n\n const extras = options?.extras ?? {};\n\n if (!isRecord(extras)) {\n throw new Error('Ecommerce extras must be an object when provided.');\n }\n\n const payload = { ...extras, ecommerce } as { ecommerce: EcommercePayload } & TExtras;\n return pushEvent(client, name, payload) as EcommerceEvent<TName, TExtras>;\n};\n","import type { ContainerConfigInput, ContainerDescriptor } from './types';\nimport { DEFAULT_GTM_HOST } from './constants';\nconst DEFAULT_IFRAME_ATTRIBUTES: Record<string, string> = {\n height: '0',\n width: '0',\n style: 'display:none;visibility:hidden',\n title: 'Google Tag Manager'\n};\n\nconst isString = (value: unknown): value is string => typeof value === 'string';\n\nconst normalizeContainer = (input: ContainerConfigInput): ContainerDescriptor => {\n if (isString(input)) {\n return { id: input };\n }\n\n return input;\n};\n\nconst toRecord = (params?: Record<string, string | number | boolean>): Record<string, string> => {\n if (!params) {\n return {};\n }\n\n return Object.entries(params).reduce<Record<string, string>>((acc, [key, value]) => {\n acc[key] = String(value);\n return acc;\n }, {});\n};\n\nconst escapeAttributeValue = (value: string): string =>\n value.replace(/&/g, '&amp;').replace(/\"/g, '&quot;').replace(/</g, '&lt;').replace(/>/g, '&gt;');\n\nconst buildNoscriptUrl = (\n host: string,\n containerId: string,\n queryParams?: Record<string, string | number | boolean>\n): string => {\n const normalizedHost = host.endsWith('/') ? host.slice(0, -1) : host;\n const searchParams = new URLSearchParams({ id: containerId });\n\n const params = toRecord(queryParams);\n for (const [key, value] of Object.entries(params)) {\n if (key === 'id') {\n continue;\n }\n searchParams.set(key, value);\n }\n\n return `${normalizedHost}/ns.html?${searchParams.toString()}`;\n};\n\nconst buildAttributeString = (attributes: Record<string, string | number | boolean> | undefined): string => {\n if (!attributes) {\n return '';\n }\n\n const entries = Object.entries(attributes);\n if (!entries.length) {\n return '';\n }\n\n return entries.map(([key, value]) => `${key}=\"${escapeAttributeValue(String(value))}\"`).join(' ');\n};\n\nexport interface NoscriptOptions {\n host?: string;\n defaultQueryParams?: Record<string, string | number | boolean>;\n iframeAttributes?: Record<string, string | number | boolean>;\n}\n\nconst buildNoscriptForContainer = (container: ContainerDescriptor, options: NoscriptOptions): string => {\n if (!container.id) {\n throw new Error('Container id is required to build noscript markup.');\n }\n\n const host = options.host ?? DEFAULT_GTM_HOST;\n const params = {\n ...options.defaultQueryParams,\n ...container.queryParams\n };\n\n const src = buildNoscriptUrl(host, container.id, params);\n const iframeAttributes = {\n ...DEFAULT_IFRAME_ATTRIBUTES,\n ...options.iframeAttributes\n };\n const attributeString = buildAttributeString(iframeAttributes);\n\n const attrs = attributeString ? ` ${attributeString}` : '';\n return `<noscript><iframe src=\"${escapeAttributeValue(src)}\"${attrs}></iframe></noscript>`;\n};\n\nexport const createNoscriptMarkup = (\n containers: ContainerConfigInput[] | ContainerConfigInput,\n options: NoscriptOptions = {}\n): string => {\n const normalizedContainers = Array.isArray(containers)\n ? containers.map(normalizeContainer)\n : [normalizeContainer(containers)];\n\n if (!normalizedContainers.length) {\n throw new Error('At least one container is required to build noscript markup.');\n }\n\n return normalizedContainers.map((container) => buildNoscriptForContainer(container, options)).join('');\n};\n\nexport const DEFAULT_NOSCRIPT_IFRAME_ATTRIBUTES = { ...DEFAULT_IFRAME_ATTRIBUTES };\n","/**\n * Auto-queue: Automatic dataLayer buffering for race condition elimination.\n *\n * This module provides automatic buffering of dataLayer pushes that occur before\n * GTM.js loads. Events are captured, stored in order, and replayed once GTM is ready.\n *\n * @example\n * ```ts\n * // Call as early as possible (ideally inline in <head>)\n * import { installAutoQueue } from '@react-gtm-kit/core';\n *\n * installAutoQueue(); // Start buffering immediately\n *\n * // Later, events pushed before GTM loads are automatically queued\n * window.dataLayer.push({ event: 'early_event' }); // Buffered!\n *\n * // When GTM loads, all buffered events replay in order\n * ```\n *\n * @example\n * ```html\n * <!-- Inline script for earliest possible buffering -->\n * <script>\n * // Minimal inline version for <head>\n * (function(w,d,n){\n * w[n]=w[n]||[];var q=[],o=w[n].push.bind(w[n]);\n * w[n].push=function(){q.push(arguments);return o.apply(this,arguments)};\n * w.__gtmkit_buffer=q;\n * })(window,document,'dataLayer');\n * </script>\n * ```\n */\n\nimport { DEFAULT_DATA_LAYER_NAME } from './constants';\nimport type { DataLayerValue } from './types';\n\n/**\n * Escape a string for safe use in JavaScript string literals.\n * Prevents XSS when interpolating values into inline scripts.\n */\nconst escapeJsString = (value: string): string =>\n value\n .replace(/\\\\/g, '\\\\\\\\')\n .replace(/'/g, \"\\\\'\")\n .replace(/\"/g, '\\\\\"')\n .replace(/\\n/g, '\\\\n')\n .replace(/\\r/g, '\\\\r')\n .replace(/</g, '\\\\x3c')\n .replace(/>/g, '\\\\x3e')\n .replace(/\\u2028/g, '\\\\u2028')\n .replace(/\\u2029/g, '\\\\u2029');\n\n/** Options for configuring the auto-queue behavior */\nexport interface AutoQueueOptions {\n /**\n * Name of the dataLayer array. Defaults to 'dataLayer'.\n */\n dataLayerName?: string;\n\n /**\n * Interval in milliseconds to check if GTM has loaded.\n * Lower values = faster detection, higher CPU usage.\n * @default 50\n */\n pollInterval?: number;\n\n /**\n * Maximum time in milliseconds to wait for GTM before giving up.\n * Set to 0 for unlimited waiting.\n * @default 30000 (30 seconds)\n */\n timeout?: number;\n\n /**\n * Maximum number of events to buffer.\n * Prevents memory issues if GTM never loads.\n * @default 1000\n */\n maxBufferSize?: number;\n\n /**\n * Callback fired when the buffer is replayed.\n */\n onReplay?: (bufferedCount: number) => void;\n\n /**\n * Callback fired if timeout is reached before GTM loads.\n */\n onTimeout?: (bufferedCount: number) => void;\n}\n\n/** State of the auto-queue system */\nexport interface AutoQueueState {\n /** Whether the auto-queue is currently active */\n active: boolean;\n /** Number of events currently buffered */\n bufferedCount: number;\n /** Whether GTM has been detected as ready */\n gtmReady: boolean;\n /** Manually trigger replay (useful for testing) */\n replay: () => void;\n /** Uninstall the auto-queue and restore original push */\n uninstall: () => void;\n}\n\ninterface BufferedEntry {\n value: DataLayerValue;\n timestamp: number;\n}\n\n/**\n * Installs automatic dataLayer buffering that captures events before GTM loads.\n *\n * Call this as early as possible in your application lifecycle, ideally before\n * any other scripts that might push to the dataLayer.\n *\n * The auto-queue:\n * 1. Creates the dataLayer if it doesn't exist\n * 2. Intercepts all pushes to capture them in a buffer\n * 3. Detects when GTM.js loads by watching for the 'gtm.js' event\n * 4. Replays all buffered events in order once GTM is ready\n * 5. Removes itself, allowing normal dataLayer operation\n *\n * @param options - Configuration options\n * @returns State object with control methods\n *\n * @example\n * ```ts\n * const queue = installAutoQueue({\n * onReplay: (count) => console.log(`Replayed ${count} buffered events`),\n * onTimeout: (count) => console.warn(`GTM didn't load, ${count} events buffered`)\n * });\n *\n * // Check state\n * console.log(queue.bufferedCount); // Number of events waiting\n *\n * // Manual control (usually not needed)\n * queue.replay(); // Force replay now\n * queue.uninstall(); // Remove the interceptor\n * ```\n */\nexport function installAutoQueue(options: AutoQueueOptions = {}): AutoQueueState {\n const {\n dataLayerName = DEFAULT_DATA_LAYER_NAME,\n pollInterval = 50,\n timeout = 30000,\n maxBufferSize = 1000,\n onReplay,\n onTimeout\n } = options;\n\n // Skip in non-browser environments\n if (typeof globalThis === 'undefined' || typeof globalThis.document === 'undefined') {\n return createNoopState();\n }\n\n const globalScope = globalThis as Record<string, unknown>;\n\n // Create dataLayer if it doesn't exist\n if (!Array.isArray(globalScope[dataLayerName])) {\n globalScope[dataLayerName] = [];\n }\n\n const dataLayer = globalScope[dataLayerName] as DataLayerValue[];\n const buffer: BufferedEntry[] = [];\n const originalPush = dataLayer.push.bind(dataLayer);\n\n let active = true;\n let gtmReady = false;\n let pollTimer: ReturnType<typeof setInterval> | null = null;\n let timeoutTimer: ReturnType<typeof setTimeout> | null = null;\n\n // Check if GTM.js event is present (indicating GTM has loaded)\n const isGtmLoaded = (): boolean => {\n return dataLayer.some(\n (entry) =>\n entry !== null &&\n typeof entry === 'object' &&\n !Array.isArray(entry) &&\n (entry as Record<string, unknown>).event === 'gtm.js'\n );\n };\n\n // Replay all buffered events to the dataLayer\n const replay = (): void => {\n if (!active) return;\n\n active = false;\n gtmReady = true;\n\n // Clear timers\n if (pollTimer) {\n clearInterval(pollTimer);\n pollTimer = null;\n }\n if (timeoutTimer) {\n clearTimeout(timeoutTimer);\n timeoutTimer = null;\n }\n\n // Restore original push\n dataLayer.push = originalPush;\n\n // Replay buffered events in order\n const count = buffer.length;\n for (const entry of buffer) {\n originalPush(entry.value);\n }\n buffer.length = 0;\n\n onReplay?.(count);\n };\n\n // Uninstall without replaying\n const uninstall = (): void => {\n if (!active) return;\n\n active = false;\n\n if (pollTimer) {\n clearInterval(pollTimer);\n pollTimer = null;\n }\n if (timeoutTimer) {\n clearTimeout(timeoutTimer);\n timeoutTimer = null;\n }\n\n dataLayer.push = originalPush;\n buffer.length = 0;\n };\n\n // Create intercepted push function\n const interceptedPush = function (this: DataLayerValue[], ...args: DataLayerValue[]): number {\n for (const value of args) {\n // Always push to actual dataLayer (GTM may already be listening)\n originalPush(value);\n\n // Buffer the value for potential replay\n if (active && buffer.length < maxBufferSize) {\n buffer.push({\n value,\n timestamp: Date.now()\n });\n }\n\n // Check if this push indicates GTM is ready\n if (\n active &&\n value !== null &&\n typeof value === 'object' &&\n !Array.isArray(value) &&\n (value as Record<string, unknown>).event === 'gtm.js'\n ) {\n // GTM just loaded! Trigger replay on next tick to ensure\n // this event is fully processed first\n setTimeout(replay, 0);\n }\n }\n\n return dataLayer.length;\n };\n\n // Install the interceptor\n dataLayer.push = interceptedPush;\n\n // Check if GTM was already loaded before we installed\n if (isGtmLoaded()) {\n // GTM already present, replay immediately\n setTimeout(replay, 0);\n } else {\n // Poll for GTM readiness as backup detection\n pollTimer = setInterval(() => {\n if (isGtmLoaded()) {\n replay();\n }\n }, pollInterval);\n\n // Set timeout if configured\n if (timeout > 0) {\n timeoutTimer = setTimeout(() => {\n if (active) {\n onTimeout?.(buffer.length);\n // Don't uninstall on timeout - keep buffering in case GTM loads late\n }\n }, timeout);\n }\n }\n\n // Return state object\n return {\n get active() {\n return active;\n },\n get bufferedCount() {\n return buffer.length;\n },\n get gtmReady() {\n return gtmReady;\n },\n replay,\n uninstall\n };\n}\n\n/**\n * Creates a minimal inline script for earliest possible buffering.\n *\n * This returns a script that can be embedded directly in the HTML `<head>`\n * before any other scripts. It's a minimal version of installAutoQueue()\n * that captures events until the full GTM Kit is loaded.\n *\n * @param dataLayerName - Name of the dataLayer array\n * @returns Inline script string to embed in HTML\n *\n * @example\n * ```ts\n * // In your SSR template\n * const inlineScript = createAutoQueueScript();\n * // Output: <script>{inlineScript}</script> in <head>\n * ```\n */\nexport function createAutoQueueScript(dataLayerName: string = DEFAULT_DATA_LAYER_NAME): string {\n // Minified inline script that:\n // 1. Creates dataLayer if missing\n // 2. Overrides push to capture events\n // 3. Stores buffer in __gtmkit_buffer for later retrieval\n // SECURITY: Escape the dataLayerName to prevent XSS via malicious input\n const safeName = escapeJsString(dataLayerName);\n return `(function(w,n){w[n]=w[n]||[];var q=[],o=w[n].push.bind(w[n]);w[n].push=function(){for(var i=0;i<arguments.length;i++){q.push({v:arguments[i],t:Date.now()});o(arguments[i])}return w[n].length};w.__gtmkit_buffer={q:q,o:o,n:n}})(window,'${safeName}');`;\n}\n\n/**\n * Attaches to an existing inline buffer created by createAutoQueueScript().\n *\n * If you used the inline script in your HTML head, call this when the full\n * GTM Kit loads to take over buffer management and enable replay.\n *\n * @param options - Configuration options\n * @returns State object, or null if no inline buffer exists\n *\n * @example\n * ```ts\n * // After GTM Kit bundle loads\n * const queue = attachToInlineBuffer({\n * onReplay: (count) => console.log(`Replayed ${count} events`)\n * });\n *\n * if (queue) {\n * console.log(`Taking over ${queue.bufferedCount} buffered events`);\n * }\n * ```\n */\nexport function attachToInlineBuffer(options: Omit<AutoQueueOptions, 'dataLayerName'> = {}): AutoQueueState | null {\n if (typeof globalThis === 'undefined') {\n return null;\n }\n\n const globalScope = globalThis as Record<string, unknown>;\n const inlineBuffer = globalScope.__gtmkit_buffer as\n | {\n q: { v: DataLayerValue; t: number }[];\n o: (...args: DataLayerValue[]) => number;\n n: string;\n }\n | undefined;\n\n if (!inlineBuffer) {\n return null;\n }\n\n const { n: dataLayerName } = inlineBuffer;\n\n // Clean up the global reference\n delete globalScope.__gtmkit_buffer;\n\n // Install full auto-queue with the same dataLayer name\n // The buffer from the inline script is already in the dataLayer,\n // so we just need to continue monitoring from here\n return installAutoQueue({\n ...options,\n dataLayerName\n });\n}\n\n/** Creates a no-op state for SSR environments */\nfunction createNoopState(): AutoQueueState {\n // No-op functions for SSR - these do nothing intentionally\n const noop = (): void => {\n /* no-op for SSR */\n };\n return {\n active: false,\n bufferedCount: 0,\n gtmReady: false,\n replay: noop,\n uninstall: noop\n };\n}\n"]}
1
+ {"version":3,"sources":["../src/constants.ts","../src/consent.ts","../src/consent/presets.ts","../src/data-layer.ts","../src/logger.ts","../src/url-utils.ts","../src/script-manager.ts","../src/client.ts","../src/events/push.ts","../src/noscript.ts","../src/auto-queue.ts"],"names":["DEFAULT_GTM_HOST","DEFAULT_DATA_LAYER_NAME","CONSENT_COMMAND","CONSENT_DEFAULT","CONSENT_UPDATE","CONSENT_KEYS","isConsentKey","value","isConsentDecision","assertValidRegions","regions","region","assertValidWaitForUpdate","waitForUpdate","normalizeConsentState","state","normalizedEntries","key","normalizedState","normalizeOptions","options","payload","buildConsentCommand","command","normalizedOptions","createConsentCommandValue","input","createConsentDefaultsCommand","createConsentUpdateCommand","consent","consentPresets","getConsentPreset","name","eeaDefault","allGranted","analyticsOnly","isArray","ensureDataLayer","globalScope","existing","snapshot","clone","pushToDataLayer","levels","noop","createLogger","logger","safeLogger","level","provided","isString","normalizeContainer","normalizeContainers","containers","toRecord","params","acc","normalizeHost","host","buildUrl","kind","containerId","queryParams","dataLayerName","normalizedHost","searchParams","buildGtmScriptUrl","buildGtmNoscriptUrl","escapeAttributeValue","CONTAINER_ATTR","INSTANCE_ATTR","createDeferred","resolved","resolver","promise","resolve","findExistingScript","attrSelector","existingWithAttr","script","formatErrorMessage","event","_a","_b","ScriptManager","callback","container","inserted","targetParent","url","attributes","stringValue","settle","status","isPlainObject","prototype","serializeUnknown","parts","entry","serialized","keys","serializeDataLayerValue","isConsentCommandValue","isStartEvent","instanceCounter","GtmClientImpl","instanceId","immediate","startEvent","signature","firstNonConsentIndex","queued","existingSignature","isConsentCommand","seenInSnapshot","alreadyDeliveredConsent","createGtmClient","isRecord","clonePayload","pushEvent","client","pushEcommerce","ecommerce","extras","DEFAULT_IFRAME_ATTRIBUTES","buildAttributeString","entries","buildNoscriptForContainer","src","iframeAttributes","attributeString","attrs","createNoscriptMarkup","normalizedContainers","DEFAULT_NOSCRIPT_IFRAME_ATTRIBUTES","escapeJsString","installAutoQueue","pollInterval","timeout","maxBufferSize","onReplay","onTimeout","createNoopState","dataLayer","buffer","originalPush","active","gtmReady","pollTimer","timeoutTimer","isGtmLoaded","replay","count","uninstall","interceptedPush","args","createAutoQueueScript","attachToInlineBuffer","inlineBuffer"],"mappings":"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","sourcesContent":["export const DEFAULT_GTM_HOST = 'https://www.googletagmanager.com';\nexport const DEFAULT_DATA_LAYER_NAME = 'dataLayer';\n","import type { DataLayerValue } from './types';\n\nconst CONSENT_COMMAND = 'consent' as const;\nconst CONSENT_DEFAULT = 'default' as const;\nconst CONSENT_UPDATE = 'update' as const;\n\n/**\n * The four consent categories tracked by Google Consent Mode v2.\n */\nconst CONSENT_KEYS = ['ad_storage', 'analytics_storage', 'ad_user_data', 'ad_personalization'] as const;\n\n/**\n * A consent category key: 'ad_storage' | 'analytics_storage' | 'ad_user_data' | 'ad_personalization'\n */\nexport type ConsentKey = (typeof CONSENT_KEYS)[number];\n\n/**\n * A consent decision: 'granted' or 'denied'\n */\nexport type ConsentDecision = 'granted' | 'denied';\n\n/**\n * Consent state object for one or more categories.\n *\n * This is a **partial** record - you only need to specify the categories you want to set.\n * Unspecified categories retain their previous state when using `updateConsent()`.\n *\n * @example\n * ```ts\n * // All four categories\n * const fullState: ConsentState = {\n * ad_storage: 'granted',\n * analytics_storage: 'granted',\n * ad_user_data: 'granted',\n * ad_personalization: 'granted'\n * };\n *\n * // Single category (partial update)\n * const partialState: ConsentState = {\n * analytics_storage: 'granted'\n * };\n *\n * // Multiple specific categories\n * const mixedState: ConsentState = {\n * analytics_storage: 'granted',\n * ad_storage: 'denied'\n * };\n * ```\n */\nexport type ConsentState = Partial<Record<ConsentKey, ConsentDecision>>;\n\nexport interface ConsentRegionOptions {\n /**\n * ISO 3166-2 region codes (e.g., `US-CA`, `EEA`) that the consent command applies to.\n */\n region?: readonly string[];\n /**\n * Milliseconds to wait for an explicit update before firing tags when using the default command.\n */\n waitForUpdate?: number;\n}\n\nexport type ConsentCommand = typeof CONSENT_DEFAULT | typeof CONSENT_UPDATE;\n\nexport interface ConsentCommandInput {\n command: ConsentCommand;\n state: ConsentState;\n options?: ConsentRegionOptions;\n}\n\nexport type ConsentCommandValue =\n | [typeof CONSENT_COMMAND, ConsentCommand, ConsentState]\n | [typeof CONSENT_COMMAND, ConsentCommand, ConsentState, Record<string, unknown>];\n\nconst isConsentKey = (value: string): value is ConsentKey => (CONSENT_KEYS as readonly string[]).includes(value);\n\nconst isConsentDecision = (value: unknown): value is ConsentDecision => value === 'granted' || value === 'denied';\n\nconst assertValidRegions = (regions: readonly string[]) => {\n if (!Array.isArray(regions)) {\n throw new Error('Consent region list must be an array of ISO region codes.');\n }\n\n for (const region of regions) {\n if (typeof region !== 'string' || region.trim().length === 0) {\n throw new Error('Consent region codes must be non-empty strings.');\n }\n }\n};\n\nconst assertValidWaitForUpdate = (waitForUpdate: number) => {\n if (!Number.isFinite(waitForUpdate) || waitForUpdate < 0) {\n throw new Error('waitForUpdate must be a non-negative finite number.');\n }\n};\n\nexport const normalizeConsentState = (state: ConsentState): ConsentState => {\n const normalizedEntries = Object.entries(state ?? {}).map(([key, value]) => {\n if (!isConsentKey(key)) {\n throw new Error(`Invalid consent key: ${key}`);\n }\n\n if (!isConsentDecision(value)) {\n throw new Error(`Invalid consent value for key \"${key}\". Expected \"granted\" or \"denied\".`);\n }\n\n return [key, value] as const;\n });\n\n if (!normalizedEntries.length) {\n throw new Error('At least one consent key/value pair is required.');\n }\n\n const normalizedState = {} as ConsentState;\n for (const [key, value] of normalizedEntries) {\n normalizedState[key as ConsentKey] = value;\n }\n\n return Object.freeze(normalizedState);\n};\n\nconst normalizeOptions = (options?: ConsentRegionOptions): Record<string, unknown> | undefined => {\n if (!options) {\n return undefined;\n }\n\n const payload: Record<string, unknown> = {};\n\n if (options.region) {\n assertValidRegions(options.region);\n if (options.region.length) {\n payload.region = [...options.region];\n }\n }\n\n if (typeof options.waitForUpdate === 'number') {\n assertValidWaitForUpdate(options.waitForUpdate);\n payload.wait_for_update = options.waitForUpdate;\n }\n\n return Object.keys(payload).length ? payload : undefined;\n};\n\nexport const buildConsentCommand = ({ command, state, options }: ConsentCommandInput): ConsentCommandValue => {\n if (command !== CONSENT_DEFAULT && command !== CONSENT_UPDATE) {\n throw new Error(`Unsupported consent command: ${command}`);\n }\n\n const normalizedState = normalizeConsentState(state);\n const normalizedOptions = normalizeOptions(options);\n\n if (normalizedOptions) {\n return [CONSENT_COMMAND, command, normalizedState, normalizedOptions];\n }\n\n return [CONSENT_COMMAND, command, normalizedState];\n};\n\nexport const createConsentCommandValue = (input: ConsentCommandInput): DataLayerValue => {\n // Spread the tuple to convert it to a plain array for DataLayerValue compatibility\n return [...buildConsentCommand(input)];\n};\n\nexport const createConsentDefaultsCommand = (state: ConsentState, options?: ConsentRegionOptions): DataLayerValue =>\n createConsentCommandValue({ command: CONSENT_DEFAULT, state, options });\n\nexport const createConsentUpdateCommand = (state: ConsentState, options?: ConsentRegionOptions): DataLayerValue =>\n createConsentCommandValue({ command: CONSENT_UPDATE, state, options });\n\nexport const consent = {\n buildConsentCommand,\n createConsentDefaultsCommand,\n createConsentUpdateCommand,\n normalizeConsentState\n};\n","import type { ConsentState } from '../consent';\n\n/**\n * Pre-configured consent state presets for common scenarios.\n *\n * These presets cover the most common consent patterns:\n * - `eeaDefault`: All denied (GDPR compliant default)\n * - `allGranted`: All granted (user accepts everything)\n * - `analyticsOnly`: Mixed state (analytics only, no ads)\n *\n * For custom combinations, pass a partial `ConsentState` object to `updateConsent()`.\n * You only need to specify the categories you want to update.\n *\n * @example\n * ```ts\n * // Use a preset\n * client.updateConsent(consentPresets.allGranted);\n *\n * // Or create a custom state\n * client.updateConsent({\n * analytics_storage: 'granted',\n * ad_storage: 'denied'\n * });\n *\n * // Partial updates (only update specific categories)\n * client.updateConsent({ analytics_storage: 'granted' });\n * ```\n */\nexport const consentPresets = {\n /**\n * All categories denied - GDPR/EEA compliant default.\n *\n * Use as the initial state for regions requiring explicit opt-in consent.\n * Tags will be blocked until the user grants specific permissions.\n *\n * | Category | State |\n * |----------|-------|\n * | ad_storage | denied |\n * | analytics_storage | denied |\n * | ad_user_data | denied |\n * | ad_personalization | denied |\n */\n eeaDefault: Object.freeze({\n ad_storage: 'denied',\n analytics_storage: 'denied',\n ad_user_data: 'denied',\n ad_personalization: 'denied'\n } satisfies ConsentState),\n\n /**\n * All categories granted - user accepts all tracking.\n *\n * Use when the user clicks \"Accept All\" or in regions where consent is implied.\n *\n * | Category | State |\n * |----------|-------|\n * | ad_storage | granted |\n * | analytics_storage | granted |\n * | ad_user_data | granted |\n * | ad_personalization | granted |\n */\n allGranted: Object.freeze({\n ad_storage: 'granted',\n analytics_storage: 'granted',\n ad_user_data: 'granted',\n ad_personalization: 'granted'\n } satisfies ConsentState),\n\n /**\n * Analytics allowed, advertising denied - mixed consent state.\n *\n * Use when the user accepts analytics/statistics but rejects advertising cookies.\n * This is a common \"essential + analytics\" consent pattern.\n *\n * | Category | State |\n * |----------|-------|\n * | ad_storage | denied |\n * | analytics_storage | granted |\n * | ad_user_data | denied |\n * | ad_personalization | denied |\n */\n analyticsOnly: Object.freeze({\n ad_storage: 'denied',\n analytics_storage: 'granted',\n ad_user_data: 'denied',\n ad_personalization: 'denied'\n } satisfies ConsentState)\n} as const;\n\nexport type ConsentPresetName = keyof typeof consentPresets;\n\nexport const getConsentPreset = (name: ConsentPresetName): ConsentState => ({\n ...consentPresets[name]\n});\n\n/**\n * Convenience export for the EEA default consent state.\n * All categories denied - GDPR/EEA compliant default.\n */\nexport const eeaDefault = consentPresets.eeaDefault;\n\n/**\n * Convenience export for the all-granted consent state.\n * All categories granted - user accepts all tracking.\n */\nexport const allGranted = consentPresets.allGranted;\n\n/**\n * Convenience export for the analytics-only consent state.\n * Analytics allowed, advertising denied.\n */\nexport const analyticsOnly = consentPresets.analyticsOnly;\n","import type { DataLayerState, DataLayerValue } from './types';\n\ninterface EnsureDataLayerResult extends DataLayerState {\n snapshot: DataLayerValue[] | undefined;\n}\n\nconst isArray = (value: unknown): value is DataLayerValue[] => Array.isArray(value);\n\nexport const ensureDataLayer = (name: string): EnsureDataLayerResult => {\n const globalScope = globalThis as Record<string, unknown>;\n const existing = globalScope[name];\n const snapshot = isArray(existing) ? [...existing] : undefined;\n\n if (!isArray(existing)) {\n globalScope[name] = [] as DataLayerValue[];\n }\n\n return {\n name,\n dataLayer: globalScope[name] as DataLayerValue[],\n created: !isArray(existing),\n restore() {\n if (!isArray(existing)) {\n delete globalScope[name];\n return;\n }\n\n const clone = snapshot ? [...snapshot] : [];\n globalScope[name] = clone;\n },\n snapshot\n };\n};\n\nexport const pushToDataLayer = (state: DataLayerState, value: DataLayerValue) => {\n state.dataLayer.push(value);\n};\n","import type { Logger, PartialLogger } from './types';\n\ntype LogLevel = keyof Logger;\n\nconst levels: LogLevel[] = ['debug', 'info', 'warn', 'error'];\n\nconst noop = () => {\n /* intentionally empty */\n};\n\nexport const createLogger = (logger?: PartialLogger): Logger => {\n const safeLogger: Partial<Record<LogLevel, (message: string, details?: Record<string, unknown>) => void>> = {};\n\n for (const level of levels) {\n const provided = logger?.[level];\n if (typeof provided === 'function') {\n safeLogger[level] = provided.bind(logger);\n continue;\n }\n safeLogger[level] = noop;\n }\n\n return safeLogger as Logger;\n};\n\nexport type LoggerFacade = ReturnType<typeof createLogger>;\n","import { DEFAULT_DATA_LAYER_NAME, DEFAULT_GTM_HOST } from './constants';\nimport type { ContainerConfigInput, ContainerDescriptor } from './types';\n\n/**\n * Type guard to check if a value is a string.\n */\nexport const isString = (value: unknown): value is string => typeof value === 'string';\n\n/**\n * Normalize a container input to a ContainerDescriptor.\n */\nexport const normalizeContainer = (input: ContainerConfigInput): ContainerDescriptor => {\n if (isString(input)) {\n return { id: input };\n }\n return input;\n};\n\n/**\n * Normalize container inputs to an array of ContainerDescriptors.\n */\nexport const normalizeContainers = (\n containers: ContainerConfigInput | ContainerConfigInput[]\n): ContainerDescriptor[] => {\n if (Array.isArray(containers)) {\n return containers.map(normalizeContainer);\n }\n return [normalizeContainer(containers)];\n};\n\n/**\n * Convert query params to a Record<string, string>.\n */\nexport const toRecord = (params?: Record<string, string | number | boolean>): Record<string, string> => {\n if (!params) {\n return {};\n }\n return Object.entries(params).reduce<Record<string, string>>((acc, [key, value]) => {\n acc[key] = String(value);\n return acc;\n }, {});\n};\n\n/**\n * Normalize a host URL by removing trailing slashes.\n */\nexport const normalizeHost = (host: string): string => (host.endsWith('/') ? host.slice(0, -1) : host);\n\ntype UrlKind = 'gtm' | 'ns';\n\nconst buildUrl = (\n kind: UrlKind,\n host: string,\n containerId: string,\n queryParams?: Record<string, string | number | boolean>,\n dataLayerName: string = DEFAULT_DATA_LAYER_NAME\n): string => {\n const normalizedHost = normalizeHost(host);\n const searchParams = new URLSearchParams({ id: containerId });\n\n const params = toRecord(queryParams);\n\n // Add dataLayer name parameter if using custom name\n if (dataLayerName !== DEFAULT_DATA_LAYER_NAME && params.l === undefined) {\n params.l = dataLayerName;\n }\n\n for (const [key, value] of Object.entries(params)) {\n if (key === 'id') {\n continue;\n }\n searchParams.set(key, value);\n }\n\n const suffix = kind === 'gtm' ? 'gtm.js' : 'ns.html';\n return `${normalizedHost}/${suffix}?${searchParams.toString()}`;\n};\n\n/**\n * Build a GTM script URL.\n */\nexport const buildGtmScriptUrl = (\n host: string,\n containerId: string,\n queryParams?: Record<string, string | number | boolean>,\n dataLayerName: string = DEFAULT_DATA_LAYER_NAME\n): string => buildUrl('gtm', host, containerId, queryParams, dataLayerName);\n\n/**\n * Build a GTM noscript iframe URL.\n */\nexport const buildGtmNoscriptUrl = (\n host: string,\n containerId: string,\n queryParams?: Record<string, string | number | boolean>,\n dataLayerName: string = DEFAULT_DATA_LAYER_NAME\n): string => buildUrl('ns', host, containerId, queryParams, dataLayerName);\n\n/**\n * Escape a string for use in HTML attributes.\n */\nexport const escapeAttributeValue = (value: string): string =>\n value.replace(/&/g, '&amp;').replace(/\"/g, '&quot;').replace(/</g, '&lt;').replace(/>/g, '&gt;');\n\n// Re-export default host for convenience\nexport { DEFAULT_GTM_HOST };\n","import { DEFAULT_DATA_LAYER_NAME, DEFAULT_GTM_HOST } from './constants';\nimport { createLogger } from './logger';\nimport type {\n ContainerDescriptor,\n CreateGtmClientOptions,\n ScriptAttributes,\n ScriptLoadState,\n ScriptLoadStatus\n} from './types';\nimport { buildGtmScriptUrl } from './url-utils';\n\nconst CONTAINER_ATTR = 'data-gtm-container-id';\nconst INSTANCE_ATTR = 'data-gtm-kit-instance';\n\ninterface Deferred<T> {\n promise: Promise<T>;\n resolve: (value: T) => void;\n settled: boolean;\n}\n\nconst createDeferred = <T>(): Deferred<T> => {\n let resolved = false;\n let resolver: (value: T) => void;\n\n const promise = new Promise<T>((resolve) => {\n resolver = resolve;\n });\n\n return {\n get settled() {\n return resolved;\n },\n promise,\n resolve: (value: T) => {\n if (resolved) {\n return;\n }\n\n resolved = true;\n resolver(value);\n }\n } as Deferred<T>;\n};\n\nexport interface NormalizedContainer extends ContainerDescriptor {\n queryParams?: Record<string, string | number | boolean>;\n}\n\nexport interface ScriptManagerOptions {\n instanceId: string;\n host?: string;\n dataLayerName?: string;\n scriptAttributes?: ScriptAttributes;\n defaultQueryParams?: Record<string, string | number | boolean>;\n logger?: CreateGtmClientOptions['logger'];\n}\n\nexport interface EnsureResult {\n inserted: HTMLScriptElement[];\n}\n\nconst findExistingScript = (containerId: string): HTMLScriptElement | null => {\n if (typeof document === 'undefined') {\n return null;\n }\n\n const attrSelector = `script[${CONTAINER_ATTR}=\"${containerId}\"]`;\n const existingWithAttr = document.querySelector(attrSelector);\n if (existingWithAttr) {\n return existingWithAttr as HTMLScriptElement;\n }\n\n const scripts = Array.from(document.getElementsByTagName('script'));\n return scripts.find((script) => script.src.includes(`id=${encodeURIComponent(containerId)}`)) || null;\n};\n\nconst formatErrorMessage = (event: Event): string => {\n if (event instanceof ErrorEvent) {\n if (event.error) {\n return String(event.error);\n }\n\n if (event.message) {\n return event.message;\n }\n }\n\n return 'Failed to load GTM script.';\n};\n\nexport class ScriptManager {\n private readonly logger = createLogger(this.options.logger);\n private readonly host = this.options.host ?? DEFAULT_GTM_HOST;\n private readonly dataLayerName = this.options.dataLayerName ?? DEFAULT_DATA_LAYER_NAME;\n private readonly defaultQueryParams = this.options.defaultQueryParams;\n private readonly scriptAttributes = this.options.scriptAttributes;\n private readonly insertedScripts = new Set<HTMLScriptElement>();\n private readonly readyCallbacks = new Set<(state: ScriptLoadState[]) => void>();\n private readiness = createDeferred<ScriptLoadState[]>();\n private readonly loadStates = new Map<string, ScriptLoadState>();\n private readonly pendingContainers = new Set<string>();\n\n constructor(private readonly options: ScriptManagerOptions) {}\n\n whenReady(): Promise<ScriptLoadState[]> {\n return this.readiness.promise;\n }\n\n onReady(callback: (state: ScriptLoadState[]) => void): () => void {\n this.readyCallbacks.add(callback);\n\n if (this.readiness.settled) {\n callback(Array.from(this.loadStates.values()));\n }\n\n return () => {\n this.readyCallbacks.delete(callback);\n };\n }\n\n private notifyReady(): void {\n const snapshot = Array.from(this.loadStates.values());\n\n if (!this.readiness.settled) {\n this.readiness.resolve(snapshot);\n }\n\n for (const callback of this.readyCallbacks) {\n callback(snapshot);\n }\n }\n\n private maybeNotifyReady(): void {\n if (this.pendingContainers.size === 0) {\n this.notifyReady();\n }\n }\n\n private recordState(state: ScriptLoadState): void {\n this.loadStates.set(state.containerId, state);\n }\n\n private resetReadiness(): void {\n this.pendingContainers.clear();\n this.loadStates.clear();\n this.readiness = createDeferred<ScriptLoadState[]>();\n }\n\n ensure(containers: NormalizedContainer[]): EnsureResult {\n if (typeof document === 'undefined') {\n this.logger.warn('No document available – skipping script injection.');\n\n for (const container of containers) {\n if (!container.id) {\n continue;\n }\n\n this.recordState({\n containerId: container.id,\n status: 'skipped',\n error: 'Document unavailable for script injection.'\n });\n }\n\n this.maybeNotifyReady();\n return { inserted: [] };\n }\n\n const inserted: HTMLScriptElement[] = [];\n const targetParent = document.head || document.body;\n if (!targetParent) {\n this.logger.error('Unable to find document.head or document.body for script injection.');\n\n for (const container of containers) {\n if (!container.id) {\n continue;\n }\n\n this.recordState({\n containerId: container.id,\n status: 'skipped',\n error: 'Missing document.head and document.body for GTM script injection.'\n });\n }\n\n this.maybeNotifyReady();\n return { inserted: [] };\n }\n\n for (const container of containers) {\n if (!container.id) {\n this.logger.warn('Skipping container with missing id.', { container });\n continue;\n }\n\n const existing = findExistingScript(container.id);\n if (existing) {\n this.logger.debug('Container script already present, skipping injection.', {\n containerId: container.id\n });\n\n this.recordState({\n containerId: container.id,\n src: existing.src,\n status: 'loaded',\n fromCache: true\n });\n continue;\n }\n\n const params = {\n ...this.defaultQueryParams,\n ...container.queryParams\n };\n\n const script = document.createElement('script');\n const url = buildGtmScriptUrl(this.host, container.id, params, this.dataLayerName);\n script.src = url;\n script.setAttribute(CONTAINER_ATTR, container.id);\n script.setAttribute(INSTANCE_ATTR, this.options.instanceId);\n\n const attributes = this.scriptAttributes ?? {};\n if (attributes.async !== undefined) {\n script.async = attributes.async;\n } else {\n script.async = true;\n }\n if (attributes.defer !== undefined) {\n script.defer = attributes.defer;\n }\n\n for (const [key, value] of Object.entries(attributes)) {\n if (key === 'async' || key === 'defer') {\n continue;\n }\n\n if (value === undefined || value === null) {\n continue;\n }\n\n const stringValue = String(value);\n\n if (key === 'nonce') {\n script.nonce = stringValue;\n }\n\n script.setAttribute(key, stringValue);\n }\n\n this.pendingContainers.add(container.id);\n\n const settle = (status: ScriptLoadStatus, event?: Event): void => {\n if (!this.pendingContainers.has(container.id)) {\n return;\n }\n\n this.pendingContainers.delete(container.id);\n\n const state: ScriptLoadState = {\n containerId: container.id,\n src: url,\n status,\n fromCache: false\n };\n\n if (status === 'failed' && event) {\n state.error = formatErrorMessage(event);\n this.logger.error('Failed to load GTM container script.', {\n containerId: container.id,\n src: url,\n error: state.error\n });\n }\n\n this.recordState(state);\n this.maybeNotifyReady();\n };\n\n script.addEventListener('load', () => settle('loaded'));\n script.addEventListener('error', (event) => settle('failed', event));\n\n targetParent.appendChild(script);\n this.insertedScripts.add(script);\n inserted.push(script);\n this.logger.info('Injected GTM container script.', { containerId: container.id, src: url });\n }\n\n this.maybeNotifyReady();\n return { inserted };\n }\n\n teardown() {\n if (typeof document === 'undefined') {\n return;\n }\n\n for (const script of this.insertedScripts) {\n if (script.parentNode) {\n script.parentNode.removeChild(script);\n }\n }\n\n this.insertedScripts.clear();\n this.resetReadiness();\n }\n}\n","import { DEFAULT_DATA_LAYER_NAME } from './constants';\nimport { ensureDataLayer, pushToDataLayer } from './data-layer';\nimport { createConsentCommandValue } from './consent';\nimport type { ConsentRegionOptions, ConsentState } from './consent';\nimport { createLogger } from './logger';\nimport { ScriptManager } from './script-manager';\nimport type {\n ContainerConfigInput,\n ContainerDescriptor,\n CreateGtmClientOptions,\n DataLayerValue,\n GtmClient,\n ScriptLoadState\n} from './types';\n\nconst isString = (value: unknown): value is string => typeof value === 'string';\n\nconst normalizeContainer = (input: ContainerConfigInput): ContainerDescriptor => {\n if (isString(input)) {\n return { id: input };\n }\n\n return input;\n};\n\nconst isPlainObject = (value: unknown): value is Record<string, unknown> => {\n if (typeof value !== 'object' || value === null) {\n return false;\n }\n\n const prototype = Object.getPrototypeOf(value);\n return prototype === Object.prototype || prototype === null;\n};\n\nconst serializeUnknown = (value: unknown): string | null => {\n if (value === null || typeof value === 'boolean' || typeof value === 'number') {\n return JSON.stringify(value);\n }\n\n if (typeof value === 'string') {\n return JSON.stringify(value);\n }\n\n if (Array.isArray(value)) {\n const parts: string[] = [];\n for (const entry of value) {\n const serialized = serializeUnknown(entry);\n if (serialized === null) {\n return null;\n }\n parts.push(serialized);\n }\n return `[${parts.join(',')}]`;\n }\n\n if (isPlainObject(value)) {\n const keys = Object.keys(value).sort();\n const parts: string[] = [];\n for (const key of keys) {\n const serialized = serializeUnknown((value as Record<string, unknown>)[key]);\n if (serialized === null) {\n return null;\n }\n parts.push(`${JSON.stringify(key)}:${serialized}`);\n }\n return `{${parts.join(',')}}`;\n }\n\n return null;\n};\n\nconst serializeDataLayerValue = (value: DataLayerValue): string | null => {\n if (Array.isArray(value)) {\n return serializeUnknown(value);\n }\n\n if (isPlainObject(value)) {\n return serializeUnknown(value);\n }\n\n return null;\n};\n\nconst isConsentCommandValue = (value: DataLayerValue): value is unknown[] =>\n Array.isArray(value) &&\n value.length >= 3 &&\n value[0] === 'consent' &&\n (value[1] === 'default' || value[1] === 'update');\n\nconst isStartEvent = (value: DataLayerValue): boolean => {\n if (!isPlainObject(value)) {\n return false;\n }\n\n return (value.event as unknown) === 'gtm.js';\n};\n\ninterface QueuedEntry {\n value: DataLayerValue;\n signature: string | null;\n}\n\nlet instanceCounter = 0;\n\nexport class GtmClientImpl implements GtmClient {\n private readonly logger = createLogger(this.options.logger);\n private readonly resolvedDataLayerName = this.options.dataLayerName ?? DEFAULT_DATA_LAYER_NAME;\n private readonly containers = Array.isArray(this.options.containers)\n ? this.options.containers.map(normalizeContainer)\n : [normalizeContainer(this.options.containers)];\n private readonly queue: QueuedEntry[] = [];\n private readonly queuedConsentSignatures = new Set<string>();\n private readonly deliveredConsentSignatures = new Set<string>();\n private snapshotSignatures: Set<string> | null = null;\n private readonly scriptManager = new ScriptManager({\n instanceId: this.instanceId,\n host: this.options.host,\n dataLayerName: this.resolvedDataLayerName,\n defaultQueryParams: this.options.defaultQueryParams,\n scriptAttributes: this.options.scriptAttributes,\n logger: this.options.logger\n });\n private dataLayerState: ReturnType<typeof ensureDataLayer> | null = null;\n private initialized = false;\n private readonly startTimestamp = Date.now();\n\n constructor(\n private readonly options: CreateGtmClientOptions,\n private readonly instanceId: string\n ) {\n if (!this.containers.length) {\n throw new Error('At least one GTM container ID is required to initialize the client.');\n }\n }\n\n init(): void {\n if (this.initialized) {\n this.logger.debug('GTM client already initialized; skipping.');\n return;\n }\n\n this.logger.info('Initializing GTM client.', {\n containers: this.containers.map((container) => container.id),\n dataLayerName: this.resolvedDataLayerName\n });\n\n this.dataLayerState = ensureDataLayer(this.resolvedDataLayerName);\n this.captureSnapshotSignatures();\n this.pushStartEvent();\n this.flushQueue();\n this.scriptManager.ensure(this.containers);\n\n this.initialized = true;\n }\n\n push(value: DataLayerValue): void {\n if (value === undefined || value === null) {\n this.logger.warn('Ignoring falsy dataLayer push.', { value });\n return;\n }\n\n const immediate = this.deliverToDataLayer(value);\n\n if (immediate) {\n this.logger.debug('Pushed value to dataLayer.', { immediate: true });\n } else {\n this.logger.debug('Queued dataLayer value (pre-init).', { queueLength: this.queue.length });\n }\n }\n\n setConsentDefaults(state: ConsentState, options?: ConsentRegionOptions): void {\n const value = createConsentCommandValue({ command: 'default', state, options });\n const immediate = this.deliverToDataLayer(value);\n\n this.logger.info('Applied consent defaults.', {\n immediate,\n state,\n options\n });\n }\n\n updateConsent(state: ConsentState, options?: ConsentRegionOptions): void {\n const value = createConsentCommandValue({ command: 'update', state, options });\n const immediate = this.deliverToDataLayer(value);\n\n this.logger.info('Updated consent state.', {\n immediate,\n state,\n options\n });\n }\n\n teardown(): void {\n this.logger.info('Tearing down GTM client instance.', { dataLayerName: this.resolvedDataLayerName });\n this.scriptManager.teardown();\n if (this.dataLayerState) {\n this.dataLayerState.restore();\n }\n this.queue.length = 0;\n this.queuedConsentSignatures.clear();\n this.deliveredConsentSignatures.clear();\n this.snapshotSignatures = null;\n this.initialized = false;\n this.dataLayerState = null;\n }\n\n isInitialized(): boolean {\n return this.initialized;\n }\n\n whenReady(): Promise<ScriptLoadState[]> {\n return this.scriptManager.whenReady();\n }\n\n onReady(callback: (state: ScriptLoadState[]) => void): () => void {\n return this.scriptManager.onReady(callback);\n }\n\n get dataLayerName(): string {\n return this.resolvedDataLayerName;\n }\n\n private flushQueue(): void {\n if (!this.dataLayerState) {\n return;\n }\n\n while (this.queue.length) {\n const entry = this.queue.shift();\n if (!entry) {\n continue;\n }\n\n this.pushValueToDataLayer(entry.value, entry.signature);\n\n if (entry.signature) {\n this.queuedConsentSignatures.delete(entry.signature);\n }\n }\n }\n\n private deliverToDataLayer(value: DataLayerValue): boolean {\n if (this.initialized && this.dataLayerState) {\n this.pushValueToDataLayer(value);\n return true;\n }\n\n this.queueValue(value);\n return false;\n }\n\n private pushStartEvent(): void {\n if (!this.dataLayerState) {\n return;\n }\n\n if (this.hasExistingStartEvent()) {\n this.logger.debug('Detected existing gtm.js event; skipping duplicate start push.');\n return;\n }\n\n const startEvent = { 'gtm.start': this.startTimestamp, event: 'gtm.js' } as const;\n this.pushValueToDataLayer(startEvent);\n }\n\n private captureSnapshotSignatures(): void {\n if (!this.dataLayerState) {\n return;\n }\n\n const snapshot = this.dataLayerState.snapshot ?? [];\n this.snapshotSignatures = new Set<string>();\n\n for (const value of snapshot) {\n const signature = serializeDataLayerValue(value);\n if (signature) {\n this.snapshotSignatures.add(signature);\n if (isConsentCommandValue(value)) {\n this.deliveredConsentSignatures.add(signature);\n }\n }\n }\n }\n\n private queueValue(value: DataLayerValue): void {\n const signature = isConsentCommandValue(value) ? serializeDataLayerValue(value) : null;\n\n if (signature && this.queuedConsentSignatures.has(signature)) {\n this.logger.debug('Skipping duplicate queued dataLayer value.', { value });\n return;\n }\n\n const entry: QueuedEntry = { value, signature };\n\n if (isConsentCommandValue(value)) {\n const firstNonConsentIndex = this.queue.findIndex((queued) => !isConsentCommandValue(queued.value));\n\n if (firstNonConsentIndex === -1) {\n this.queue.push(entry);\n } else {\n this.queue.splice(firstNonConsentIndex, 0, entry);\n }\n } else {\n this.queue.push(entry);\n }\n\n if (signature) {\n this.queuedConsentSignatures.add(signature);\n }\n }\n\n private pushValueToDataLayer(value: DataLayerValue, existingSignature?: string | null): void {\n if (!this.dataLayerState) {\n return;\n }\n\n const signature = existingSignature ?? serializeDataLayerValue(value);\n const isConsentCommand = isConsentCommandValue(value);\n const seenInSnapshot = signature ? this.snapshotSignatures?.has(signature) : false;\n const alreadyDeliveredConsent =\n isConsentCommand && signature ? this.deliveredConsentSignatures.has(signature) : false;\n\n if (signature && seenInSnapshot) {\n this.logger.debug('Skipping duplicate dataLayer value detected during hydration.', { value });\n if (isConsentCommand) {\n this.deliveredConsentSignatures.add(signature);\n }\n return;\n }\n\n if (isConsentCommand && alreadyDeliveredConsent) {\n this.logger.debug('Skipping duplicate consent command.', { value });\n return;\n }\n\n pushToDataLayer(this.dataLayerState, value);\n\n if (isConsentCommand && signature) {\n this.deliveredConsentSignatures.add(signature);\n }\n }\n\n private hasExistingStartEvent(): boolean {\n if (!this.dataLayerState) {\n return false;\n }\n\n const snapshot = this.dataLayerState.snapshot ?? [];\n if (snapshot.some(isStartEvent)) {\n return true;\n }\n\n return this.dataLayerState.dataLayer.some(isStartEvent);\n }\n}\n\nexport const createGtmClient = (options: CreateGtmClientOptions): GtmClient => {\n const instanceId = `gtm-kit-${++instanceCounter}`;\n return new GtmClientImpl(options, instanceId);\n};\n","import type { GtmClient } from '../types';\nimport type {\n EcommerceEvent,\n EcommerceEventName,\n EcommercePayload,\n EventForName,\n EventPayload,\n GtmEvent\n} from './types';\n\nconst isRecord = (value: unknown): value is Record<string, unknown> => typeof value === 'object' && value !== null;\n\nconst clonePayload = <TPayload extends EventPayload | undefined>(payload: TPayload): TPayload => {\n if (!payload) {\n return payload;\n }\n\n return { ...payload } as TPayload;\n};\n\nexport const pushEvent = <TName extends string, TPayload extends EventPayload = EventPayload>(\n client: Pick<GtmClient, 'push'>,\n name: TName,\n payload?: TPayload\n): EventForName<TName> => {\n if (!name) {\n throw new Error('An event name is required when pushing to the dataLayer.');\n }\n\n if (payload !== undefined && !isRecord(payload)) {\n throw new Error('Event payloads must be plain objects when pushing to the dataLayer.');\n }\n\n const event = {\n event: name,\n ...(clonePayload(payload) ?? {})\n } as GtmEvent<TName, TPayload>;\n\n client.push(event);\n return event as EventForName<TName>;\n};\n\nexport interface PushEcommerceOptions<TExtras extends EventPayload = EventPayload> {\n extras?: TExtras;\n}\n\nexport const pushEcommerce = <TName extends EcommerceEventName, TExtras extends EventPayload = EventPayload>(\n client: Pick<GtmClient, 'push'>,\n name: TName,\n ecommerce: EcommercePayload,\n options?: PushEcommerceOptions<TExtras>\n): EcommerceEvent<TName, TExtras> => {\n if (!isRecord(ecommerce)) {\n throw new Error('Ecommerce payload must be an object.');\n }\n\n const extras = options?.extras ?? {};\n\n if (!isRecord(extras)) {\n throw new Error('Ecommerce extras must be an object when provided.');\n }\n\n const payload = { ...extras, ecommerce } as { ecommerce: EcommercePayload } & TExtras;\n return pushEvent(client, name, payload) as EcommerceEvent<TName, TExtras>;\n};\n","import type { ContainerConfigInput, ContainerDescriptor } from './types';\nimport { DEFAULT_GTM_HOST } from './constants';\nimport { normalizeContainer, buildGtmNoscriptUrl, escapeAttributeValue } from './url-utils';\n\nconst DEFAULT_IFRAME_ATTRIBUTES: Record<string, string> = {\n height: '0',\n width: '0',\n style: 'display:none;visibility:hidden',\n title: 'Google Tag Manager'\n};\n\nconst buildAttributeString = (attributes: Record<string, string | number | boolean> | undefined): string => {\n if (!attributes) {\n return '';\n }\n\n const entries = Object.entries(attributes);\n if (!entries.length) {\n return '';\n }\n\n return entries.map(([key, value]) => `${key}=\"${escapeAttributeValue(String(value))}\"`).join(' ');\n};\n\nexport interface NoscriptOptions {\n host?: string;\n defaultQueryParams?: Record<string, string | number | boolean>;\n iframeAttributes?: Record<string, string | number | boolean>;\n}\n\nconst buildNoscriptForContainer = (container: ContainerDescriptor, options: NoscriptOptions): string => {\n if (!container.id) {\n throw new Error('Container id is required to build noscript markup.');\n }\n\n const host = options.host ?? DEFAULT_GTM_HOST;\n const params = {\n ...options.defaultQueryParams,\n ...container.queryParams\n };\n\n const src = buildGtmNoscriptUrl(host, container.id, params);\n const iframeAttributes = {\n ...DEFAULT_IFRAME_ATTRIBUTES,\n ...options.iframeAttributes\n };\n const attributeString = buildAttributeString(iframeAttributes);\n\n const attrs = attributeString ? ` ${attributeString}` : '';\n return `<noscript><iframe src=\"${escapeAttributeValue(src)}\"${attrs}></iframe></noscript>`;\n};\n\nexport const createNoscriptMarkup = (\n containers: ContainerConfigInput[] | ContainerConfigInput,\n options: NoscriptOptions = {}\n): string => {\n const normalizedContainers = Array.isArray(containers)\n ? containers.map(normalizeContainer)\n : [normalizeContainer(containers)];\n\n if (!normalizedContainers.length) {\n throw new Error('At least one container is required to build noscript markup.');\n }\n\n return normalizedContainers.map((container) => buildNoscriptForContainer(container, options)).join('');\n};\n\nexport const DEFAULT_NOSCRIPT_IFRAME_ATTRIBUTES = { ...DEFAULT_IFRAME_ATTRIBUTES };\n","/**\n * Auto-queue: Automatic dataLayer buffering for race condition elimination.\n *\n * This module provides automatic buffering of dataLayer pushes that occur before\n * GTM.js loads. Events are captured, stored in order, and replayed once GTM is ready.\n *\n * @example\n * ```ts\n * // Call as early as possible (ideally inline in <head>)\n * import { installAutoQueue } from '@jwiedeman/gtm-kit';\n *\n * installAutoQueue(); // Start buffering immediately\n *\n * // Later, events pushed before GTM loads are automatically queued\n * window.dataLayer.push({ event: 'early_event' }); // Buffered!\n *\n * // When GTM loads, all buffered events replay in order\n * ```\n *\n * @example\n * ```html\n * <!-- Inline script for earliest possible buffering -->\n * <script>\n * // Minimal inline version for <head>\n * (function(w,d,n){\n * w[n]=w[n]||[];var q=[],o=w[n].push.bind(w[n]);\n * w[n].push=function(){q.push(arguments);return o.apply(this,arguments)};\n * w.__gtmkit_buffer=q;\n * })(window,document,'dataLayer');\n * </script>\n * ```\n */\n\nimport { DEFAULT_DATA_LAYER_NAME } from './constants';\nimport type { DataLayerValue } from './types';\n\n/**\n * Escape a string for safe use in JavaScript string literals.\n * Prevents XSS when interpolating values into inline scripts.\n */\nconst escapeJsString = (value: string): string =>\n value\n .replace(/\\\\/g, '\\\\\\\\')\n .replace(/'/g, \"\\\\'\")\n .replace(/\"/g, '\\\\\"')\n .replace(/\\n/g, '\\\\n')\n .replace(/\\r/g, '\\\\r')\n .replace(/</g, '\\\\x3c')\n .replace(/>/g, '\\\\x3e')\n .replace(/\\u2028/g, '\\\\u2028')\n .replace(/\\u2029/g, '\\\\u2029');\n\n/** Options for configuring the auto-queue behavior */\nexport interface AutoQueueOptions {\n /**\n * Name of the dataLayer array. Defaults to 'dataLayer'.\n */\n dataLayerName?: string;\n\n /**\n * Interval in milliseconds to check if GTM has loaded.\n * Lower values = faster detection, higher CPU usage.\n * @default 50\n */\n pollInterval?: number;\n\n /**\n * Maximum time in milliseconds to wait for GTM before giving up.\n * Set to 0 for unlimited waiting.\n * @default 30000 (30 seconds)\n */\n timeout?: number;\n\n /**\n * Maximum number of events to buffer.\n * Prevents memory issues if GTM never loads.\n * @default 1000\n */\n maxBufferSize?: number;\n\n /**\n * Callback fired when the buffer is replayed.\n */\n onReplay?: (bufferedCount: number) => void;\n\n /**\n * Callback fired if timeout is reached before GTM loads.\n */\n onTimeout?: (bufferedCount: number) => void;\n}\n\n/** State of the auto-queue system */\nexport interface AutoQueueState {\n /** Whether the auto-queue is currently active */\n active: boolean;\n /** Number of events currently buffered */\n bufferedCount: number;\n /** Whether GTM has been detected as ready */\n gtmReady: boolean;\n /** Manually trigger replay (useful for testing) */\n replay: () => void;\n /** Uninstall the auto-queue and restore original push */\n uninstall: () => void;\n}\n\ninterface BufferedEntry {\n value: DataLayerValue;\n timestamp: number;\n}\n\n/**\n * Installs automatic dataLayer buffering that captures events before GTM loads.\n *\n * Call this as early as possible in your application lifecycle, ideally before\n * any other scripts that might push to the dataLayer.\n *\n * The auto-queue:\n * 1. Creates the dataLayer if it doesn't exist\n * 2. Intercepts all pushes to capture them in a buffer\n * 3. Detects when GTM.js loads by watching for the 'gtm.js' event\n * 4. Replays all buffered events in order once GTM is ready\n * 5. Removes itself, allowing normal dataLayer operation\n *\n * @param options - Configuration options\n * @returns State object with control methods\n *\n * @example\n * ```ts\n * const queue = installAutoQueue({\n * onReplay: (count) => console.log(`Replayed ${count} buffered events`),\n * onTimeout: (count) => console.warn(`GTM didn't load, ${count} events buffered`)\n * });\n *\n * // Check state\n * console.log(queue.bufferedCount); // Number of events waiting\n *\n * // Manual control (usually not needed)\n * queue.replay(); // Force replay now\n * queue.uninstall(); // Remove the interceptor\n * ```\n */\nexport function installAutoQueue(options: AutoQueueOptions = {}): AutoQueueState {\n const {\n dataLayerName = DEFAULT_DATA_LAYER_NAME,\n pollInterval = 50,\n timeout = 30000,\n maxBufferSize = 1000,\n onReplay,\n onTimeout\n } = options;\n\n // Skip in non-browser environments\n if (typeof globalThis === 'undefined' || typeof globalThis.document === 'undefined') {\n return createNoopState();\n }\n\n const globalScope = globalThis as Record<string, unknown>;\n\n // Create dataLayer if it doesn't exist\n if (!Array.isArray(globalScope[dataLayerName])) {\n globalScope[dataLayerName] = [];\n }\n\n const dataLayer = globalScope[dataLayerName] as DataLayerValue[];\n const buffer: BufferedEntry[] = [];\n const originalPush = dataLayer.push.bind(dataLayer);\n\n let active = true;\n let gtmReady = false;\n let pollTimer: ReturnType<typeof setInterval> | null = null;\n let timeoutTimer: ReturnType<typeof setTimeout> | null = null;\n\n // Check if GTM.js event is present (indicating GTM has loaded)\n const isGtmLoaded = (): boolean => {\n return dataLayer.some(\n (entry) =>\n entry !== null &&\n typeof entry === 'object' &&\n !Array.isArray(entry) &&\n (entry as Record<string, unknown>).event === 'gtm.js'\n );\n };\n\n // Replay all buffered events to the dataLayer\n const replay = (): void => {\n if (!active) return;\n\n active = false;\n gtmReady = true;\n\n // Clear timers\n if (pollTimer) {\n clearInterval(pollTimer);\n pollTimer = null;\n }\n if (timeoutTimer) {\n clearTimeout(timeoutTimer);\n timeoutTimer = null;\n }\n\n // Restore original push\n dataLayer.push = originalPush;\n\n // Replay buffered events in order\n const count = buffer.length;\n for (const entry of buffer) {\n originalPush(entry.value);\n }\n buffer.length = 0;\n\n onReplay?.(count);\n };\n\n // Uninstall without replaying\n const uninstall = (): void => {\n if (!active) return;\n\n active = false;\n\n if (pollTimer) {\n clearInterval(pollTimer);\n pollTimer = null;\n }\n if (timeoutTimer) {\n clearTimeout(timeoutTimer);\n timeoutTimer = null;\n }\n\n dataLayer.push = originalPush;\n buffer.length = 0;\n };\n\n // Create intercepted push function\n const interceptedPush = function (this: DataLayerValue[], ...args: DataLayerValue[]): number {\n for (const value of args) {\n // Always push to actual dataLayer (GTM may already be listening)\n originalPush(value);\n\n // Buffer the value for potential replay\n if (active && buffer.length < maxBufferSize) {\n buffer.push({\n value,\n timestamp: Date.now()\n });\n }\n\n // Check if this push indicates GTM is ready\n if (\n active &&\n value !== null &&\n typeof value === 'object' &&\n !Array.isArray(value) &&\n (value as Record<string, unknown>).event === 'gtm.js'\n ) {\n // GTM just loaded! Trigger replay on next tick to ensure\n // this event is fully processed first\n setTimeout(replay, 0);\n }\n }\n\n return dataLayer.length;\n };\n\n // Install the interceptor\n dataLayer.push = interceptedPush;\n\n // Check if GTM was already loaded before we installed\n if (isGtmLoaded()) {\n // GTM already present, replay immediately\n setTimeout(replay, 0);\n } else {\n // Poll for GTM readiness as backup detection\n pollTimer = setInterval(() => {\n if (isGtmLoaded()) {\n replay();\n }\n }, pollInterval);\n\n // Set timeout if configured\n if (timeout > 0) {\n timeoutTimer = setTimeout(() => {\n if (active) {\n onTimeout?.(buffer.length);\n // Don't uninstall on timeout - keep buffering in case GTM loads late\n }\n }, timeout);\n }\n }\n\n // Return state object\n return {\n get active() {\n return active;\n },\n get bufferedCount() {\n return buffer.length;\n },\n get gtmReady() {\n return gtmReady;\n },\n replay,\n uninstall\n };\n}\n\n/**\n * Creates a minimal inline script for earliest possible buffering.\n *\n * This returns a script that can be embedded directly in the HTML `<head>`\n * before any other scripts. It's a minimal version of installAutoQueue()\n * that captures events until the full GTM Kit is loaded.\n *\n * @param dataLayerName - Name of the dataLayer array\n * @returns Inline script string to embed in HTML\n *\n * @example\n * ```ts\n * // In your SSR template\n * const inlineScript = createAutoQueueScript();\n * // Output: <script>{inlineScript}</script> in <head>\n * ```\n */\nexport function createAutoQueueScript(dataLayerName: string = DEFAULT_DATA_LAYER_NAME): string {\n // Minified inline script that:\n // 1. Creates dataLayer if missing\n // 2. Overrides push to capture events\n // 3. Stores buffer in __gtmkit_buffer for later retrieval\n // SECURITY: Escape the dataLayerName to prevent XSS via malicious input\n const safeName = escapeJsString(dataLayerName);\n return `(function(w,n){w[n]=w[n]||[];var q=[],o=w[n].push.bind(w[n]);w[n].push=function(){for(var i=0;i<arguments.length;i++){q.push({v:arguments[i],t:Date.now()});o(arguments[i])}return w[n].length};w.__gtmkit_buffer={q:q,o:o,n:n}})(window,'${safeName}');`;\n}\n\n/**\n * Attaches to an existing inline buffer created by createAutoQueueScript().\n *\n * If you used the inline script in your HTML head, call this when the full\n * GTM Kit loads to take over buffer management and enable replay.\n *\n * @param options - Configuration options\n * @returns State object, or null if no inline buffer exists\n *\n * @example\n * ```ts\n * // After GTM Kit bundle loads\n * const queue = attachToInlineBuffer({\n * onReplay: (count) => console.log(`Replayed ${count} events`)\n * });\n *\n * if (queue) {\n * console.log(`Taking over ${queue.bufferedCount} buffered events`);\n * }\n * ```\n */\nexport function attachToInlineBuffer(options: Omit<AutoQueueOptions, 'dataLayerName'> = {}): AutoQueueState | null {\n if (typeof globalThis === 'undefined') {\n return null;\n }\n\n const globalScope = globalThis as Record<string, unknown>;\n const inlineBuffer = globalScope.__gtmkit_buffer as\n | {\n q: { v: DataLayerValue; t: number }[];\n o: (...args: DataLayerValue[]) => number;\n n: string;\n }\n | undefined;\n\n if (!inlineBuffer) {\n return null;\n }\n\n const { n: dataLayerName } = inlineBuffer;\n\n // Clean up the global reference\n delete globalScope.__gtmkit_buffer;\n\n // Install full auto-queue with the same dataLayer name\n // The buffer from the inline script is already in the dataLayer,\n // so we just need to continue monitoring from here\n return installAutoQueue({\n ...options,\n dataLayerName\n });\n}\n\n/** Creates a no-op state for SSR environments */\nfunction createNoopState(): AutoQueueState {\n // No-op functions for SSR - these do nothing intentionally\n const noop = (): void => {\n /* no-op for SSR */\n };\n return {\n active: false,\n bufferedCount: 0,\n gtmReady: false,\n replay: noop,\n uninstall: noop\n };\n}\n"]}
package/dist/index.d.cts CHANGED
@@ -316,7 +316,7 @@ declare const DEFAULT_NOSCRIPT_IFRAME_ATTRIBUTES: {
316
316
  * @example
317
317
  * ```ts
318
318
  * // Call as early as possible (ideally inline in <head>)
319
- * import { installAutoQueue } from '@react-gtm-kit/core';
319
+ * import { installAutoQueue } from '@jwiedeman/gtm-kit';
320
320
  *
321
321
  * installAutoQueue(); // Start buffering immediately
322
322
  *
@@ -458,4 +458,37 @@ declare function createAutoQueueScript(dataLayerName?: string): string;
458
458
  */
459
459
  declare function attachToInlineBuffer(options?: Omit<AutoQueueOptions, 'dataLayerName'>): AutoQueueState | null;
460
460
 
461
- export { AdsConversionEvent, AdsConversionPayload, AutoQueueOptions, AutoQueueState, ConsentCommand, ConsentRegionOptions, ConsentState, ContainerConfigInput, ContainerDescriptor, CreateGtmClientOptions, CustomEvent, DEFAULT_DATA_LAYER_NAME, DEFAULT_GTM_HOST, DEFAULT_NOSCRIPT_IFRAME_ATTRIBUTES, DataLayerValue, EcommerceEvent, EcommerceEventName, EcommerceItem, EcommercePayload, EventForName, EventName, EventPayload, GtmClient, GtmEvent, NoscriptOptions, PageViewEvent, PageViewPayload, PushEcommerceOptions, ScriptAttributes, ScriptLoadState, ScriptLoadStatus, allGranted, analyticsOnly, attachToInlineBuffer, buildConsentCommand, consent, consentPresets, createAutoQueueScript, createConsentDefaultsCommand, createConsentUpdateCommand, createGtmClient, createNoscriptMarkup, eeaDefault, getConsentPreset, installAutoQueue, pushEcommerce, pushEvent };
461
+ /**
462
+ * Type guard to check if a value is a string.
463
+ */
464
+ declare const isString: (value: unknown) => value is string;
465
+ /**
466
+ * Normalize a container input to a ContainerDescriptor.
467
+ */
468
+ declare const normalizeContainer: (input: ContainerConfigInput) => ContainerDescriptor;
469
+ /**
470
+ * Normalize container inputs to an array of ContainerDescriptors.
471
+ */
472
+ declare const normalizeContainers: (containers: ContainerConfigInput | ContainerConfigInput[]) => ContainerDescriptor[];
473
+ /**
474
+ * Convert query params to a Record<string, string>.
475
+ */
476
+ declare const toRecord: (params?: Record<string, string | number | boolean>) => Record<string, string>;
477
+ /**
478
+ * Normalize a host URL by removing trailing slashes.
479
+ */
480
+ declare const normalizeHost: (host: string) => string;
481
+ /**
482
+ * Build a GTM script URL.
483
+ */
484
+ declare const buildGtmScriptUrl: (host: string, containerId: string, queryParams?: Record<string, string | number | boolean>, dataLayerName?: string) => string;
485
+ /**
486
+ * Build a GTM noscript iframe URL.
487
+ */
488
+ declare const buildGtmNoscriptUrl: (host: string, containerId: string, queryParams?: Record<string, string | number | boolean>, dataLayerName?: string) => string;
489
+ /**
490
+ * Escape a string for use in HTML attributes.
491
+ */
492
+ declare const escapeAttributeValue: (value: string) => string;
493
+
494
+ export { AdsConversionEvent, AdsConversionPayload, AutoQueueOptions, AutoQueueState, ConsentCommand, ConsentRegionOptions, ConsentState, ContainerConfigInput, ContainerDescriptor, CreateGtmClientOptions, CustomEvent, DEFAULT_DATA_LAYER_NAME, DEFAULT_GTM_HOST, DEFAULT_NOSCRIPT_IFRAME_ATTRIBUTES, DataLayerValue, EcommerceEvent, EcommerceEventName, EcommerceItem, EcommercePayload, EventForName, EventName, EventPayload, GtmClient, GtmEvent, NoscriptOptions, PageViewEvent, PageViewPayload, PushEcommerceOptions, ScriptAttributes, ScriptLoadState, ScriptLoadStatus, allGranted, analyticsOnly, attachToInlineBuffer, buildConsentCommand, buildGtmNoscriptUrl, buildGtmScriptUrl, consent, consentPresets, createAutoQueueScript, createConsentDefaultsCommand, createConsentUpdateCommand, createGtmClient, createNoscriptMarkup, eeaDefault, escapeAttributeValue, getConsentPreset, installAutoQueue, isString, normalizeContainer, normalizeContainers, normalizeHost, pushEcommerce, pushEvent, toRecord };
package/dist/index.d.ts CHANGED
@@ -316,7 +316,7 @@ declare const DEFAULT_NOSCRIPT_IFRAME_ATTRIBUTES: {
316
316
  * @example
317
317
  * ```ts
318
318
  * // Call as early as possible (ideally inline in <head>)
319
- * import { installAutoQueue } from '@react-gtm-kit/core';
319
+ * import { installAutoQueue } from '@jwiedeman/gtm-kit';
320
320
  *
321
321
  * installAutoQueue(); // Start buffering immediately
322
322
  *
@@ -458,4 +458,37 @@ declare function createAutoQueueScript(dataLayerName?: string): string;
458
458
  */
459
459
  declare function attachToInlineBuffer(options?: Omit<AutoQueueOptions, 'dataLayerName'>): AutoQueueState | null;
460
460
 
461
- export { AdsConversionEvent, AdsConversionPayload, AutoQueueOptions, AutoQueueState, ConsentCommand, ConsentRegionOptions, ConsentState, ContainerConfigInput, ContainerDescriptor, CreateGtmClientOptions, CustomEvent, DEFAULT_DATA_LAYER_NAME, DEFAULT_GTM_HOST, DEFAULT_NOSCRIPT_IFRAME_ATTRIBUTES, DataLayerValue, EcommerceEvent, EcommerceEventName, EcommerceItem, EcommercePayload, EventForName, EventName, EventPayload, GtmClient, GtmEvent, NoscriptOptions, PageViewEvent, PageViewPayload, PushEcommerceOptions, ScriptAttributes, ScriptLoadState, ScriptLoadStatus, allGranted, analyticsOnly, attachToInlineBuffer, buildConsentCommand, consent, consentPresets, createAutoQueueScript, createConsentDefaultsCommand, createConsentUpdateCommand, createGtmClient, createNoscriptMarkup, eeaDefault, getConsentPreset, installAutoQueue, pushEcommerce, pushEvent };
461
+ /**
462
+ * Type guard to check if a value is a string.
463
+ */
464
+ declare const isString: (value: unknown) => value is string;
465
+ /**
466
+ * Normalize a container input to a ContainerDescriptor.
467
+ */
468
+ declare const normalizeContainer: (input: ContainerConfigInput) => ContainerDescriptor;
469
+ /**
470
+ * Normalize container inputs to an array of ContainerDescriptors.
471
+ */
472
+ declare const normalizeContainers: (containers: ContainerConfigInput | ContainerConfigInput[]) => ContainerDescriptor[];
473
+ /**
474
+ * Convert query params to a Record<string, string>.
475
+ */
476
+ declare const toRecord: (params?: Record<string, string | number | boolean>) => Record<string, string>;
477
+ /**
478
+ * Normalize a host URL by removing trailing slashes.
479
+ */
480
+ declare const normalizeHost: (host: string) => string;
481
+ /**
482
+ * Build a GTM script URL.
483
+ */
484
+ declare const buildGtmScriptUrl: (host: string, containerId: string, queryParams?: Record<string, string | number | boolean>, dataLayerName?: string) => string;
485
+ /**
486
+ * Build a GTM noscript iframe URL.
487
+ */
488
+ declare const buildGtmNoscriptUrl: (host: string, containerId: string, queryParams?: Record<string, string | number | boolean>, dataLayerName?: string) => string;
489
+ /**
490
+ * Escape a string for use in HTML attributes.
491
+ */
492
+ declare const escapeAttributeValue: (value: string) => string;
493
+
494
+ export { AdsConversionEvent, AdsConversionPayload, AutoQueueOptions, AutoQueueState, ConsentCommand, ConsentRegionOptions, ConsentState, ContainerConfigInput, ContainerDescriptor, CreateGtmClientOptions, CustomEvent, DEFAULT_DATA_LAYER_NAME, DEFAULT_GTM_HOST, DEFAULT_NOSCRIPT_IFRAME_ATTRIBUTES, DataLayerValue, EcommerceEvent, EcommerceEventName, EcommerceItem, EcommercePayload, EventForName, EventName, EventPayload, GtmClient, GtmEvent, NoscriptOptions, PageViewEvent, PageViewPayload, PushEcommerceOptions, ScriptAttributes, ScriptLoadState, ScriptLoadStatus, allGranted, analyticsOnly, attachToInlineBuffer, buildConsentCommand, buildGtmNoscriptUrl, buildGtmScriptUrl, consent, consentPresets, createAutoQueueScript, createConsentDefaultsCommand, createConsentUpdateCommand, createGtmClient, createNoscriptMarkup, eeaDefault, escapeAttributeValue, getConsentPreset, installAutoQueue, isString, normalizeContainer, normalizeContainers, normalizeHost, pushEcommerce, pushEvent, toRecord };
package/dist/index.js CHANGED
@@ -1,5 +1,5 @@
1
- var C="https://www.googletagmanager.com",g="dataLayer";var k="consent",I="default",V="update",ne=["ad_storage","analytics_storage","ad_user_data","ad_personalization"],re=e=>ne.includes(e),ae=e=>e==="granted"||e==="denied",oe=e=>{if(!Array.isArray(e))throw new Error("Consent region list must be an array of ISO region codes.");for(let t of e)if(typeof t!="string"||t.trim().length===0)throw new Error("Consent region codes must be non-empty strings.")},ie=e=>{if(!Number.isFinite(e)||e<0)throw new Error("waitForUpdate must be a non-negative finite number.")},M=e=>{let t=Object.entries(e!=null?e:{}).map(([r,a])=>{if(!re(r))throw new Error(`Invalid consent key: ${r}`);if(!ae(a))throw new Error(`Invalid consent value for key "${r}". Expected "granted" or "denied".`);return [r,a]});if(!t.length)throw new Error("At least one consent key/value pair is required.");let n={};for(let[r,a]of t)n[r]=a;return Object.freeze(n)},se=e=>{if(!e)return;let t={};return e.region&&(oe(e.region),e.region.length&&(t.region=[...e.region])),typeof e.waitForUpdate=="number"&&(ie(e.waitForUpdate),t.wait_for_update=e.waitForUpdate),Object.keys(t).length?t:void 0},w=({command:e,state:t,options:n})=>{if(e!==I&&e!==V)throw new Error(`Unsupported consent command: ${e}`);let r=M(t),a=se(n);return a?[k,e,r,a]:[k,e,r]},S=e=>[...w(e)],j=(e,t)=>S({command:I,state:e,options:t}),z=(e,t)=>S({command:V,state:e,options:t}),de={buildConsentCommand:w,createConsentDefaultsCommand:j,createConsentUpdateCommand:z,normalizeConsentState:M};var v={eeaDefault:Object.freeze({ad_storage:"denied",analytics_storage:"denied",ad_user_data:"denied",ad_personalization:"denied"}),allGranted:Object.freeze({ad_storage:"granted",analytics_storage:"granted",ad_user_data:"granted",ad_personalization:"granted"}),analyticsOnly:Object.freeze({ad_storage:"denied",analytics_storage:"granted",ad_user_data:"denied",ad_personalization:"denied"})},ue=e=>({...v[e]}),ce=v.eeaDefault,le=v.allGranted,pe=v.analyticsOnly;var b=e=>Array.isArray(e),G=e=>{let t=globalThis,n=t[e],r=b(n)?[...n]:void 0;return b(n)||(t[e]=[]),{name:e,dataLayer:t[e],created:!b(n),restore(){if(!b(n)){delete t[e];return}let a=r?[...r]:[];t[e]=a;},snapshot:r}},U=(e,t)=>{e.dataLayer.push(t);};var me=["debug","info","warn","error"],fe=()=>{},T=e=>{let t={};for(let n of me){let r=e==null?void 0:e[n];if(typeof r=="function"){t[n]=r.bind(e);continue}t[n]=fe;}return t};var $="data-gtm-container-id",ge="data-gtm-kit-instance",q=()=>{let e=!1,t,n=new Promise(r=>{t=r;});return {get settled(){return e},promise:n,resolve:r=>{e||(e=!0,t(r));}}},ye=e=>e?Object.entries(e).reduce((t,[n,r])=>(t[n]=String(r),t),{}):{},he=(e,t,n)=>{let r=e.endsWith("/")?e.slice(0,-1):e,a=new URLSearchParams({id:t}),o=ye(n);for(let[s,d]of Object.entries(o))s!=="id"&&a.set(s,d);return `${r}/gtm.js?${a.toString()}`},Ce=e=>{if(typeof document=="undefined")return null;let t=`script[${$}="${e}"]`,n=document.querySelector(t);return n||Array.from(document.getElementsByTagName("script")).find(a=>a.src.includes(`id=${encodeURIComponent(e)}`))||null},Se=e=>{if(e instanceof ErrorEvent){if(e.error)return String(e.error);if(e.message)return e.message}return "Failed to load GTM script."},F,Q,A=class{constructor(t){this.options=t;this.logger=T(this.options.logger);this.host=(F=this.options.host)!=null?F:C;this.dataLayerName=(Q=this.options.dataLayerName)!=null?Q:g;this.defaultQueryParams=this.options.defaultQueryParams;this.scriptAttributes=this.options.scriptAttributes;this.insertedScripts=new Set;this.readyCallbacks=new Set;this.readiness=q();this.loadStates=new Map;this.pendingContainers=new Set;}whenReady(){return this.readiness.promise}onReady(t){return this.readyCallbacks.add(t),this.readiness.settled&&t(Array.from(this.loadStates.values())),()=>{this.readyCallbacks.delete(t);}}notifyReady(){let t=Array.from(this.loadStates.values());this.readiness.settled||this.readiness.resolve(t);for(let n of this.readyCallbacks)n(t);}maybeNotifyReady(){this.pendingContainers.size===0&&this.notifyReady();}recordState(t){this.loadStates.set(t.containerId,t);}resetReadiness(){this.pendingContainers.clear(),this.loadStates.clear(),this.readiness=q();}ensure(t){var a;if(typeof document=="undefined"){this.logger.warn("No document available \u2013 skipping script injection.");for(let o of t)o.id&&this.recordState({containerId:o.id,status:"skipped",error:"Document unavailable for script injection."});return this.maybeNotifyReady(),{inserted:[]}}let n=[],r=document.head||document.body;if(!r){this.logger.error("Unable to find document.head or document.body for script injection.");for(let o of t)o.id&&this.recordState({containerId:o.id,status:"skipped",error:"Missing document.head and document.body for GTM script injection."});return this.maybeNotifyReady(),{inserted:[]}}for(let o of t){if(!o.id){this.logger.warn("Skipping container with missing id.",{container:o});continue}let s=Ce(o.id);if(s){this.logger.debug("Container script already present, skipping injection.",{containerId:o.id}),this.recordState({containerId:o.id,src:s.src,status:"loaded",fromCache:!0});continue}let d={...this.defaultQueryParams,...o.queryParams};this.dataLayerName!==g&&d.l===void 0&&(d.l=this.dataLayerName);let i=document.createElement("script"),l=he(this.host,o.id,d);i.src=l,i.setAttribute($,o.id),i.setAttribute(ge,this.options.instanceId);let f=(a=this.scriptAttributes)!=null?a:{};f.async!==void 0?i.async=f.async:i.async=!0,f.defer!==void 0&&(i.defer=f.defer);for(let[p,u]of Object.entries(f)){if(p==="async"||p==="defer"||u==null)continue;let c=String(u);p==="nonce"&&(i.nonce=c),i.setAttribute(p,c);}this.pendingContainers.add(o.id);let m=(p,u)=>{if(!this.pendingContainers.has(o.id))return;this.pendingContainers.delete(o.id);let c={containerId:o.id,src:l,status:p,fromCache:!1};p==="failed"&&u&&(c.error=Se(u),this.logger.error("Failed to load GTM container script.",{containerId:o.id,src:l,error:c.error})),this.recordState(c),this.maybeNotifyReady();};i.addEventListener("load",()=>m("loaded")),i.addEventListener("error",p=>m("failed",p)),r.appendChild(i),this.insertedScripts.add(i),n.push(i),this.logger.info("Injected GTM container script.",{containerId:o.id,src:l});}return this.maybeNotifyReady(),{inserted:n}}teardown(){if(typeof document!="undefined"){for(let t of this.insertedScripts)t.parentNode&&t.parentNode.removeChild(t);this.insertedScripts.clear(),this.resetReadiness();}}};var ve=e=>typeof e=="string",B=e=>ve(e)?{id:e}:e,x=e=>{if(typeof e!="object"||e===null)return !1;let t=Object.getPrototypeOf(e);return t===Object.prototype||t===null},D=e=>{if(e===null||typeof e=="boolean"||typeof e=="number"||typeof e=="string")return JSON.stringify(e);if(Array.isArray(e)){let t=[];for(let n of e){let r=D(n);if(r===null)return null;t.push(r);}return `[${t.join(",")}]`}if(x(e)){let t=Object.keys(e).sort(),n=[];for(let r of t){let a=D(e[r]);if(a===null)return null;n.push(`${JSON.stringify(r)}:${a}`);}return `{${n.join(",")}}`}return null},N=e=>Array.isArray(e)||x(e)?D(e):null,E=e=>Array.isArray(e)&&e.length>=3&&e[0]==="consent"&&(e[1]==="default"||e[1]==="update"),H=e=>x(e)?e.event==="gtm.js":!1,Ee=0,K,R=class{constructor(t,n){this.options=t;this.instanceId=n;this.logger=T(this.options.logger);this.resolvedDataLayerName=(K=this.options.dataLayerName)!=null?K:g;this.containers=Array.isArray(this.options.containers)?this.options.containers.map(B):[B(this.options.containers)];this.queue=[];this.queuedConsentSignatures=new Set;this.deliveredConsentSignatures=new Set;this.snapshotSignatures=null;this.scriptManager=new A({instanceId:this.instanceId,host:this.options.host,dataLayerName:this.resolvedDataLayerName,defaultQueryParams:this.options.defaultQueryParams,scriptAttributes:this.options.scriptAttributes,logger:this.options.logger});this.dataLayerState=null;this.initialized=!1;this.startTimestamp=Date.now();if(!this.containers.length)throw new Error("At least one GTM container ID is required to initialize the client.")}init(){if(this.initialized){this.logger.debug("GTM client already initialized; skipping.");return}this.logger.info("Initializing GTM client.",{containers:this.containers.map(t=>t.id),dataLayerName:this.resolvedDataLayerName}),this.dataLayerState=G(this.resolvedDataLayerName),this.captureSnapshotSignatures(),this.pushStartEvent(),this.flushQueue(),this.scriptManager.ensure(this.containers),this.initialized=!0;}push(t){if(t==null){this.logger.warn("Ignoring falsy dataLayer push.",{value:t});return}this.deliverToDataLayer(t)?this.logger.debug("Pushed value to dataLayer.",{immediate:!0}):this.logger.debug("Queued dataLayer value (pre-init).",{queueLength:this.queue.length});}setConsentDefaults(t,n){let r=S({command:"default",state:t,options:n}),a=this.deliverToDataLayer(r);this.logger.info("Applied consent defaults.",{immediate:a,state:t,options:n});}updateConsent(t,n){let r=S({command:"update",state:t,options:n}),a=this.deliverToDataLayer(r);this.logger.info("Updated consent state.",{immediate:a,state:t,options:n});}teardown(){this.logger.info("Tearing down GTM client instance.",{dataLayerName:this.resolvedDataLayerName}),this.scriptManager.teardown(),this.dataLayerState&&this.dataLayerState.restore(),this.queue.length=0,this.queuedConsentSignatures.clear(),this.deliveredConsentSignatures.clear(),this.snapshotSignatures=null,this.initialized=!1,this.dataLayerState=null;}isInitialized(){return this.initialized}whenReady(){return this.scriptManager.whenReady()}onReady(t){return this.scriptManager.onReady(t)}get dataLayerName(){return this.resolvedDataLayerName}flushQueue(){if(this.dataLayerState)for(;this.queue.length;){let t=this.queue.shift();t&&(this.pushValueToDataLayer(t.value,t.signature),t.signature&&this.queuedConsentSignatures.delete(t.signature));}}deliverToDataLayer(t){return this.initialized&&this.dataLayerState?(this.pushValueToDataLayer(t),!0):(this.queueValue(t),!1)}pushStartEvent(){if(!this.dataLayerState)return;if(this.hasExistingStartEvent()){this.logger.debug("Detected existing gtm.js event; skipping duplicate start push.");return}let t={"gtm.start":this.startTimestamp,event:"gtm.js"};this.pushValueToDataLayer(t);}captureSnapshotSignatures(){var n;if(!this.dataLayerState)return;let t=(n=this.dataLayerState.snapshot)!=null?n:[];this.snapshotSignatures=new Set;for(let r of t){let a=N(r);a&&(this.snapshotSignatures.add(a),E(r)&&this.deliveredConsentSignatures.add(a));}}queueValue(t){let n=E(t)?N(t):null;if(n&&this.queuedConsentSignatures.has(n)){this.logger.debug("Skipping duplicate queued dataLayer value.",{value:t});return}let r={value:t,signature:n};if(E(t)){let a=this.queue.findIndex(o=>!E(o.value));a===-1?this.queue.push(r):this.queue.splice(a,0,r);}else this.queue.push(r);n&&this.queuedConsentSignatures.add(n);}pushValueToDataLayer(t,n){var d;if(!this.dataLayerState)return;let r=n!=null?n:N(t),a=E(t),o=r?(d=this.snapshotSignatures)==null?void 0:d.has(r):!1,s=a&&r?this.deliveredConsentSignatures.has(r):!1;if(r&&o){this.logger.debug("Skipping duplicate dataLayer value detected during hydration.",{value:t}),a&&this.deliveredConsentSignatures.add(r);return}if(a&&s){this.logger.debug("Skipping duplicate consent command.",{value:t});return}U(this.dataLayerState,t),a&&r&&this.deliveredConsentSignatures.add(r);}hasExistingStartEvent(){var n;return this.dataLayerState?((n=this.dataLayerState.snapshot)!=null?n:[]).some(H)?!0:this.dataLayerState.dataLayer.some(H):!1}},Le=e=>{let t=`gtm-kit-${++Ee}`;return new R(e,t)};var P=e=>typeof e=="object"&&e!==null,be=e=>e&&{...e},O=(e,t,n)=>{var a;if(!t)throw new Error("An event name is required when pushing to the dataLayer.");if(n!==void 0&&!P(n))throw new Error("Event payloads must be plain objects when pushing to the dataLayer.");let r={event:t,...(a=be(n))!=null?a:{}};return e.push(r),r},Y=(e,t,n,r)=>{var s;if(!P(n))throw new Error("Ecommerce payload must be an object.");let a=(s=r==null?void 0:r.extras)!=null?s:{};if(!P(a))throw new Error("Ecommerce extras must be an object when provided.");let o={...a,ecommerce:n};return O(e,t,o)};var W={height:"0",width:"0",style:"display:none;visibility:hidden",title:"Google Tag Manager"},Te=e=>typeof e=="string",J=e=>Te(e)?{id:e}:e,Ae=e=>e?Object.entries(e).reduce((t,[n,r])=>(t[n]=String(r),t),{}):{},X=e=>e.replace(/&/g,"&amp;").replace(/"/g,"&quot;").replace(/</g,"&lt;").replace(/>/g,"&gt;"),De=(e,t,n)=>{let r=e.endsWith("/")?e.slice(0,-1):e,a=new URLSearchParams({id:t}),o=Ae(n);for(let[s,d]of Object.entries(o))s!=="id"&&a.set(s,d);return `${r}/ns.html?${a.toString()}`},we=e=>{if(!e)return "";let t=Object.entries(e);return t.length?t.map(([n,r])=>`${n}="${X(String(r))}"`).join(" "):""},Ne=(e,t)=>{var i;if(!e.id)throw new Error("Container id is required to build noscript markup.");let n=(i=t.host)!=null?i:C,r={...t.defaultQueryParams,...e.queryParams},a=De(n,e.id,r),o={...W,...t.iframeAttributes},s=we(o),d=s?` ${s}`:"";return `<noscript><iframe src="${X(a)}"${d}></iframe></noscript>`},Re=(e,t={})=>{let n=Array.isArray(e)?e.map(J):[J(e)];if(!n.length)throw new Error("At least one container is required to build noscript markup.");return n.map(r=>Ne(r,t)).join("")},xe={...W};var Pe=e=>e.replace(/\\/g,"\\\\").replace(/'/g,"\\'").replace(/"/g,'\\"').replace(/\n/g,"\\n").replace(/\r/g,"\\r").replace(/</g,"\\x3c").replace(/>/g,"\\x3e").replace(/\u2028/g,"\\u2028").replace(/\u2029/g,"\\u2029");function Z(e={}){let{dataLayerName:t=g,pollInterval:n=50,timeout:r=3e4,maxBufferSize:a=1e3,onReplay:o,onTimeout:s}=e;if(typeof globalThis=="undefined"||typeof globalThis.document=="undefined")return ke();let d=globalThis;Array.isArray(d[t])||(d[t]=[]);let i=d[t],l=[],f=i.push.bind(i),m=!0,p=!1,u=null,c=null,_=()=>i.some(y=>y!==null&&typeof y=="object"&&!Array.isArray(y)&&y.event==="gtm.js"),L=()=>{if(!m)return;m=!1,p=!0,u&&(clearInterval(u),u=null),c&&(clearTimeout(c),c=null),i.push=f;let y=l.length;for(let h of l)f(h.value);l.length=0,o==null||o(y);},ee=()=>{m&&(m=!1,u&&(clearInterval(u),u=null),c&&(clearTimeout(c),c=null),i.push=f,l.length=0);},te=function(...y){for(let h of y)f(h),m&&l.length<a&&l.push({value:h,timestamp:Date.now()}),m&&h!==null&&typeof h=="object"&&!Array.isArray(h)&&h.event==="gtm.js"&&setTimeout(L,0);return i.length};return i.push=te,_()?setTimeout(L,0):(u=setInterval(()=>{_()&&L();},n),r>0&&(c=setTimeout(()=>{m&&(s==null||s(l.length));},r))),{get active(){return m},get bufferedCount(){return l.length},get gtmReady(){return p},replay:L,uninstall:ee}}function Oe(e=g){return `(function(w,n){w[n]=w[n]||[];var q=[],o=w[n].push.bind(w[n]);w[n].push=function(){for(var i=0;i<arguments.length;i++){q.push({v:arguments[i],t:Date.now()});o(arguments[i])}return w[n].length};w.__gtmkit_buffer={q:q,o:o,n:n}})(window,'${Pe(e)}');`}function _e(e={}){if(typeof globalThis=="undefined")return null;let t=globalThis,n=t.__gtmkit_buffer;if(!n)return null;let{n:r}=n;return delete t.__gtmkit_buffer,Z({...e,dataLayerName:r})}function ke(){let e=()=>{};return {active:!1,bufferedCount:0,gtmReady:!1,replay:e,uninstall:e}}
1
+ var C="https://www.googletagmanager.com",p="dataLayer";var z="consent",G="default",U="update",de=["ad_storage","analytics_storage","ad_user_data","ad_personalization"],ue=e=>de.includes(e),ce=e=>e==="granted"||e==="denied",le=e=>{if(!Array.isArray(e))throw new Error("Consent region list must be an array of ISO region codes.");for(let t of e)if(typeof t!="string"||t.trim().length===0)throw new Error("Consent region codes must be non-empty strings.")},pe=e=>{if(!Number.isFinite(e)||e<0)throw new Error("waitForUpdate must be a non-negative finite number.")},j=e=>{let t=Object.entries(e!=null?e:{}).map(([r,a])=>{if(!ue(r))throw new Error(`Invalid consent key: ${r}`);if(!ce(a))throw new Error(`Invalid consent value for key "${r}". Expected "granted" or "denied".`);return [r,a]});if(!t.length)throw new Error("At least one consent key/value pair is required.");let n={};for(let[r,a]of t)n[r]=a;return Object.freeze(n)},me=e=>{if(!e)return;let t={};return e.region&&(le(e.region),e.region.length&&(t.region=[...e.region])),typeof e.waitForUpdate=="number"&&(pe(e.waitForUpdate),t.wait_for_update=e.waitForUpdate),Object.keys(t).length?t:void 0},x=({command:e,state:t,options:n})=>{if(e!==G&&e!==U)throw new Error(`Unsupported consent command: ${e}`);let r=j(t),a=me(n);return a?[z,e,r,a]:[z,e,r]},v=e=>[...x(e)],q=(e,t)=>v({command:G,state:e,options:t}),F=(e,t)=>v({command:U,state:e,options:t}),fe={buildConsentCommand:x,createConsentDefaultsCommand:q,createConsentUpdateCommand:F,normalizeConsentState:j};var E={eeaDefault:Object.freeze({ad_storage:"denied",analytics_storage:"denied",ad_user_data:"denied",ad_personalization:"denied"}),allGranted:Object.freeze({ad_storage:"granted",analytics_storage:"granted",ad_user_data:"granted",ad_personalization:"granted"}),analyticsOnly:Object.freeze({ad_storage:"denied",analytics_storage:"granted",ad_user_data:"denied",ad_personalization:"denied"})},ge=e=>({...E[e]}),ye=E.eeaDefault,he=E.allGranted,Ce=E.analyticsOnly;var T=e=>Array.isArray(e),Q=e=>{let t=globalThis,n=t[e],r=T(n)?[...n]:void 0;return T(n)||(t[e]=[]),{name:e,dataLayer:t[e],created:!T(n),restore(){if(!T(n)){delete t[e];return}let a=r?[...r]:[];t[e]=a;},snapshot:r}},$=(e,t)=>{e.dataLayer.push(t);};var Se=["debug","info","warn","error"],ve=()=>{},A=e=>{let t={};for(let n of Se){let r=e==null?void 0:e[n];if(typeof r=="function"){t[n]=r.bind(e);continue}t[n]=ve;}return t};var H=e=>typeof e=="string",S=e=>H(e)?{id:e}:e,Ee=e=>Array.isArray(e)?e.map(S):[S(e)],B=e=>e?Object.entries(e).reduce((t,[n,r])=>(t[n]=String(r),t),{}):{},K=e=>e.endsWith("/")?e.slice(0,-1):e,Y=(e,t,n,r,a=p)=>{let o=K(t),s=new URLSearchParams({id:n}),u=B(r);a!==p&&u.l===void 0&&(u.l=a);for(let[d,m]of Object.entries(u))d!=="id"&&s.set(d,m);return `${o}/${e==="gtm"?"gtm.js":"ns.html"}?${s.toString()}`},R=(e,t,n,r=p)=>Y("gtm",e,t,n,r),_=(e,t,n,r=p)=>Y("ns",e,t,n,r),D=e=>e.replace(/&/g,"&amp;").replace(/"/g,"&quot;").replace(/</g,"&lt;").replace(/>/g,"&gt;");var Z="data-gtm-container-id",Le="data-gtm-kit-instance",J=()=>{let e=!1,t,n=new Promise(r=>{t=r;});return {get settled(){return e},promise:n,resolve:r=>{e||(e=!0,t(r));}}},be=e=>{if(typeof document=="undefined")return null;let t=`script[${Z}="${e}"]`,n=document.querySelector(t);return n||Array.from(document.getElementsByTagName("script")).find(a=>a.src.includes(`id=${encodeURIComponent(e)}`))||null},Te=e=>{if(e instanceof ErrorEvent){if(e.error)return String(e.error);if(e.message)return e.message}return "Failed to load GTM script."},W,X,w=class{constructor(t){this.options=t;this.logger=A(this.options.logger);this.host=(W=this.options.host)!=null?W:C;this.dataLayerName=(X=this.options.dataLayerName)!=null?X:p;this.defaultQueryParams=this.options.defaultQueryParams;this.scriptAttributes=this.options.scriptAttributes;this.insertedScripts=new Set;this.readyCallbacks=new Set;this.readiness=J();this.loadStates=new Map;this.pendingContainers=new Set;}whenReady(){return this.readiness.promise}onReady(t){return this.readyCallbacks.add(t),this.readiness.settled&&t(Array.from(this.loadStates.values())),()=>{this.readyCallbacks.delete(t);}}notifyReady(){let t=Array.from(this.loadStates.values());this.readiness.settled||this.readiness.resolve(t);for(let n of this.readyCallbacks)n(t);}maybeNotifyReady(){this.pendingContainers.size===0&&this.notifyReady();}recordState(t){this.loadStates.set(t.containerId,t);}resetReadiness(){this.pendingContainers.clear(),this.loadStates.clear(),this.readiness=J();}ensure(t){var a;if(typeof document=="undefined"){this.logger.warn("No document available \u2013 skipping script injection.");for(let o of t)o.id&&this.recordState({containerId:o.id,status:"skipped",error:"Document unavailable for script injection."});return this.maybeNotifyReady(),{inserted:[]}}let n=[],r=document.head||document.body;if(!r){this.logger.error("Unable to find document.head or document.body for script injection.");for(let o of t)o.id&&this.recordState({containerId:o.id,status:"skipped",error:"Missing document.head and document.body for GTM script injection."});return this.maybeNotifyReady(),{inserted:[]}}for(let o of t){if(!o.id){this.logger.warn("Skipping container with missing id.",{container:o});continue}let s=be(o.id);if(s){this.logger.debug("Container script already present, skipping injection.",{containerId:o.id}),this.recordState({containerId:o.id,src:s.src,status:"loaded",fromCache:!0});continue}let u={...this.defaultQueryParams,...o.queryParams},i=document.createElement("script"),d=R(this.host,o.id,u,this.dataLayerName);i.src=d,i.setAttribute(Z,o.id),i.setAttribute(Le,this.options.instanceId);let m=(a=this.scriptAttributes)!=null?a:{};m.async!==void 0?i.async=m.async:i.async=!0,m.defer!==void 0&&(i.defer=m.defer);for(let[f,c]of Object.entries(m)){if(f==="async"||f==="defer"||c==null)continue;let l=String(c);f==="nonce"&&(i.nonce=l),i.setAttribute(f,l);}this.pendingContainers.add(o.id);let g=(f,c)=>{if(!this.pendingContainers.has(o.id))return;this.pendingContainers.delete(o.id);let l={containerId:o.id,src:d,status:f,fromCache:!1};f==="failed"&&c&&(l.error=Te(c),this.logger.error("Failed to load GTM container script.",{containerId:o.id,src:d,error:l.error})),this.recordState(l),this.maybeNotifyReady();};i.addEventListener("load",()=>g("loaded")),i.addEventListener("error",f=>g("failed",f)),r.appendChild(i),this.insertedScripts.add(i),n.push(i),this.logger.info("Injected GTM container script.",{containerId:o.id,src:d});}return this.maybeNotifyReady(),{inserted:n}}teardown(){if(typeof document!="undefined"){for(let t of this.insertedScripts)t.parentNode&&t.parentNode.removeChild(t);this.insertedScripts.clear(),this.resetReadiness();}}};var Ae=e=>typeof e=="string",ee=e=>Ae(e)?{id:e}:e,I=e=>{if(typeof e!="object"||e===null)return !1;let t=Object.getPrototypeOf(e);return t===Object.prototype||t===null},N=e=>{if(e===null||typeof e=="boolean"||typeof e=="number"||typeof e=="string")return JSON.stringify(e);if(Array.isArray(e)){let t=[];for(let n of e){let r=N(n);if(r===null)return null;t.push(r);}return `[${t.join(",")}]`}if(I(e)){let t=Object.keys(e).sort(),n=[];for(let r of t){let a=N(e[r]);if(a===null)return null;n.push(`${JSON.stringify(r)}:${a}`);}return `{${n.join(",")}}`}return null},O=e=>Array.isArray(e)||I(e)?N(e):null,L=e=>Array.isArray(e)&&e.length>=3&&e[0]==="consent"&&(e[1]==="default"||e[1]==="update"),te=e=>I(e)?e.event==="gtm.js":!1,De=0,ne,P=class{constructor(t,n){this.options=t;this.instanceId=n;this.logger=A(this.options.logger);this.resolvedDataLayerName=(ne=this.options.dataLayerName)!=null?ne:p;this.containers=Array.isArray(this.options.containers)?this.options.containers.map(ee):[ee(this.options.containers)];this.queue=[];this.queuedConsentSignatures=new Set;this.deliveredConsentSignatures=new Set;this.snapshotSignatures=null;this.scriptManager=new w({instanceId:this.instanceId,host:this.options.host,dataLayerName:this.resolvedDataLayerName,defaultQueryParams:this.options.defaultQueryParams,scriptAttributes:this.options.scriptAttributes,logger:this.options.logger});this.dataLayerState=null;this.initialized=!1;this.startTimestamp=Date.now();if(!this.containers.length)throw new Error("At least one GTM container ID is required to initialize the client.")}init(){if(this.initialized){this.logger.debug("GTM client already initialized; skipping.");return}this.logger.info("Initializing GTM client.",{containers:this.containers.map(t=>t.id),dataLayerName:this.resolvedDataLayerName}),this.dataLayerState=Q(this.resolvedDataLayerName),this.captureSnapshotSignatures(),this.pushStartEvent(),this.flushQueue(),this.scriptManager.ensure(this.containers),this.initialized=!0;}push(t){if(t==null){this.logger.warn("Ignoring falsy dataLayer push.",{value:t});return}this.deliverToDataLayer(t)?this.logger.debug("Pushed value to dataLayer.",{immediate:!0}):this.logger.debug("Queued dataLayer value (pre-init).",{queueLength:this.queue.length});}setConsentDefaults(t,n){let r=v({command:"default",state:t,options:n}),a=this.deliverToDataLayer(r);this.logger.info("Applied consent defaults.",{immediate:a,state:t,options:n});}updateConsent(t,n){let r=v({command:"update",state:t,options:n}),a=this.deliverToDataLayer(r);this.logger.info("Updated consent state.",{immediate:a,state:t,options:n});}teardown(){this.logger.info("Tearing down GTM client instance.",{dataLayerName:this.resolvedDataLayerName}),this.scriptManager.teardown(),this.dataLayerState&&this.dataLayerState.restore(),this.queue.length=0,this.queuedConsentSignatures.clear(),this.deliveredConsentSignatures.clear(),this.snapshotSignatures=null,this.initialized=!1,this.dataLayerState=null;}isInitialized(){return this.initialized}whenReady(){return this.scriptManager.whenReady()}onReady(t){return this.scriptManager.onReady(t)}get dataLayerName(){return this.resolvedDataLayerName}flushQueue(){if(this.dataLayerState)for(;this.queue.length;){let t=this.queue.shift();t&&(this.pushValueToDataLayer(t.value,t.signature),t.signature&&this.queuedConsentSignatures.delete(t.signature));}}deliverToDataLayer(t){return this.initialized&&this.dataLayerState?(this.pushValueToDataLayer(t),!0):(this.queueValue(t),!1)}pushStartEvent(){if(!this.dataLayerState)return;if(this.hasExistingStartEvent()){this.logger.debug("Detected existing gtm.js event; skipping duplicate start push.");return}let t={"gtm.start":this.startTimestamp,event:"gtm.js"};this.pushValueToDataLayer(t);}captureSnapshotSignatures(){var n;if(!this.dataLayerState)return;let t=(n=this.dataLayerState.snapshot)!=null?n:[];this.snapshotSignatures=new Set;for(let r of t){let a=O(r);a&&(this.snapshotSignatures.add(a),L(r)&&this.deliveredConsentSignatures.add(a));}}queueValue(t){let n=L(t)?O(t):null;if(n&&this.queuedConsentSignatures.has(n)){this.logger.debug("Skipping duplicate queued dataLayer value.",{value:t});return}let r={value:t,signature:n};if(L(t)){let a=this.queue.findIndex(o=>!L(o.value));a===-1?this.queue.push(r):this.queue.splice(a,0,r);}else this.queue.push(r);n&&this.queuedConsentSignatures.add(n);}pushValueToDataLayer(t,n){var u;if(!this.dataLayerState)return;let r=n!=null?n:O(t),a=L(t),o=r?(u=this.snapshotSignatures)==null?void 0:u.has(r):!1,s=a&&r?this.deliveredConsentSignatures.has(r):!1;if(r&&o){this.logger.debug("Skipping duplicate dataLayer value detected during hydration.",{value:t}),a&&this.deliveredConsentSignatures.add(r);return}if(a&&s){this.logger.debug("Skipping duplicate consent command.",{value:t});return}$(this.dataLayerState,t),a&&r&&this.deliveredConsentSignatures.add(r);}hasExistingStartEvent(){var n;return this.dataLayerState?((n=this.dataLayerState.snapshot)!=null?n:[]).some(te)?!0:this.dataLayerState.dataLayer.some(te):!1}},we=e=>{let t=`gtm-kit-${++De}`;return new P(e,t)};var k=e=>typeof e=="object"&&e!==null,Ne=e=>e&&{...e},V=(e,t,n)=>{var a;if(!t)throw new Error("An event name is required when pushing to the dataLayer.");if(n!==void 0&&!k(n))throw new Error("Event payloads must be plain objects when pushing to the dataLayer.");let r={event:t,...(a=Ne(n))!=null?a:{}};return e.push(r),r},re=(e,t,n,r)=>{var s;if(!k(n))throw new Error("Ecommerce payload must be an object.");let a=(s=r==null?void 0:r.extras)!=null?s:{};if(!k(a))throw new Error("Ecommerce extras must be an object when provided.");let o={...a,ecommerce:n};return V(e,t,o)};var ae={height:"0",width:"0",style:"display:none;visibility:hidden",title:"Google Tag Manager"},xe=e=>{if(!e)return "";let t=Object.entries(e);return t.length?t.map(([n,r])=>`${n}="${D(String(r))}"`).join(" "):""},Re=(e,t)=>{var i;if(!e.id)throw new Error("Container id is required to build noscript markup.");let n=(i=t.host)!=null?i:C,r={...t.defaultQueryParams,...e.queryParams},a=_(n,e.id,r),o={...ae,...t.iframeAttributes},s=xe(o),u=s?` ${s}`:"";return `<noscript><iframe src="${D(a)}"${u}></iframe></noscript>`},_e=(e,t={})=>{let n=Array.isArray(e)?e.map(S):[S(e)];if(!n.length)throw new Error("At least one container is required to build noscript markup.");return n.map(r=>Re(r,t)).join("")},Oe={...ae};var Pe=e=>e.replace(/\\/g,"\\\\").replace(/'/g,"\\'").replace(/"/g,'\\"').replace(/\n/g,"\\n").replace(/\r/g,"\\r").replace(/</g,"\\x3c").replace(/>/g,"\\x3e").replace(/\u2028/g,"\\u2028").replace(/\u2029/g,"\\u2029");function oe(e={}){let{dataLayerName:t=p,pollInterval:n=50,timeout:r=3e4,maxBufferSize:a=1e3,onReplay:o,onTimeout:s}=e;if(typeof globalThis=="undefined"||typeof globalThis.document=="undefined")return Ve();let u=globalThis;Array.isArray(u[t])||(u[t]=[]);let i=u[t],d=[],m=i.push.bind(i),g=!0,f=!1,c=null,l=null,M=()=>i.some(y=>y!==null&&typeof y=="object"&&!Array.isArray(y)&&y.event==="gtm.js"),b=()=>{if(!g)return;g=!1,f=!0,c&&(clearInterval(c),c=null),l&&(clearTimeout(l),l=null),i.push=m;let y=d.length;for(let h of d)m(h.value);d.length=0,o==null||o(y);},ie=()=>{g&&(g=!1,c&&(clearInterval(c),c=null),l&&(clearTimeout(l),l=null),i.push=m,d.length=0);},se=function(...y){for(let h of y)m(h),g&&d.length<a&&d.push({value:h,timestamp:Date.now()}),g&&h!==null&&typeof h=="object"&&!Array.isArray(h)&&h.event==="gtm.js"&&setTimeout(b,0);return i.length};return i.push=se,M()?setTimeout(b,0):(c=setInterval(()=>{M()&&b();},n),r>0&&(l=setTimeout(()=>{g&&(s==null||s(d.length));},r))),{get active(){return g},get bufferedCount(){return d.length},get gtmReady(){return f},replay:b,uninstall:ie}}function Ie(e=p){return `(function(w,n){w[n]=w[n]||[];var q=[],o=w[n].push.bind(w[n]);w[n].push=function(){for(var i=0;i<arguments.length;i++){q.push({v:arguments[i],t:Date.now()});o(arguments[i])}return w[n].length};w.__gtmkit_buffer={q:q,o:o,n:n}})(window,'${Pe(e)}');`}function ke(e={}){if(typeof globalThis=="undefined")return null;let t=globalThis,n=t.__gtmkit_buffer;if(!n)return null;let{n:r}=n;return delete t.__gtmkit_buffer,oe({...e,dataLayerName:r})}function Ve(){let e=()=>{};return {active:!1,bufferedCount:0,gtmReady:!1,replay:e,uninstall:e}}
2
2
 
3
- export { g as DEFAULT_DATA_LAYER_NAME, C as DEFAULT_GTM_HOST, xe as DEFAULT_NOSCRIPT_IFRAME_ATTRIBUTES, le as allGranted, pe as analyticsOnly, _e as attachToInlineBuffer, w as buildConsentCommand, de as consent, v as consentPresets, Oe as createAutoQueueScript, j as createConsentDefaultsCommand, z as createConsentUpdateCommand, Le as createGtmClient, Re as createNoscriptMarkup, ce as eeaDefault, ue as getConsentPreset, Z as installAutoQueue, Y as pushEcommerce, O as pushEvent };
3
+ export { p as DEFAULT_DATA_LAYER_NAME, C as DEFAULT_GTM_HOST, Oe as DEFAULT_NOSCRIPT_IFRAME_ATTRIBUTES, he as allGranted, Ce as analyticsOnly, ke as attachToInlineBuffer, x as buildConsentCommand, _ as buildGtmNoscriptUrl, R as buildGtmScriptUrl, fe as consent, E as consentPresets, Ie as createAutoQueueScript, q as createConsentDefaultsCommand, F as createConsentUpdateCommand, we as createGtmClient, _e as createNoscriptMarkup, ye as eeaDefault, D as escapeAttributeValue, ge as getConsentPreset, oe as installAutoQueue, H as isString, S as normalizeContainer, Ee as normalizeContainers, K as normalizeHost, re as pushEcommerce, V as pushEvent, B as toRecord };
4
4
  //# sourceMappingURL=out.js.map
5
5
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/constants.ts","../src/consent.ts","../src/consent/presets.ts","../src/data-layer.ts","../src/logger.ts","../src/script-manager.ts","../src/client.ts","../src/events/push.ts","../src/noscript.ts","../src/auto-queue.ts"],"names":["DEFAULT_GTM_HOST","DEFAULT_DATA_LAYER_NAME","CONSENT_COMMAND","CONSENT_DEFAULT","CONSENT_UPDATE","CONSENT_KEYS","isConsentKey","value","isConsentDecision","assertValidRegions","regions","region","assertValidWaitForUpdate","waitForUpdate","normalizeConsentState","state","normalizedEntries","key","normalizedState","normalizeOptions","options","payload","buildConsentCommand","command","normalizedOptions","createConsentCommandValue","input","createConsentDefaultsCommand","createConsentUpdateCommand","consent","consentPresets","getConsentPreset","name","eeaDefault","allGranted","analyticsOnly","isArray","ensureDataLayer","globalScope","existing","snapshot","clone","pushToDataLayer","levels","noop","createLogger","logger","safeLogger","level","provided","CONTAINER_ATTR","INSTANCE_ATTR","createDeferred","resolved","resolver","promise","resolve","toRecord","params","acc","buildScriptUrl","host","containerId","queryParams","normalizedHost","searchParams","findExistingScript","attrSelector","existingWithAttr","script","formatErrorMessage","event","_a","_b","ScriptManager","callback","containers","container","inserted","targetParent","url","attributes","stringValue","settle","status","isString","normalizeContainer","isPlainObject","prototype","serializeUnknown","parts","entry","serialized","keys","serializeDataLayerValue","isConsentCommandValue","isStartEvent","instanceCounter","GtmClientImpl","instanceId","immediate","startEvent","signature","firstNonConsentIndex","queued","existingSignature","isConsentCommand","seenInSnapshot","alreadyDeliveredConsent","createGtmClient","isRecord","clonePayload","pushEvent","client","pushEcommerce","ecommerce","extras","DEFAULT_IFRAME_ATTRIBUTES","escapeAttributeValue","buildNoscriptUrl","buildAttributeString","entries","buildNoscriptForContainer","src","iframeAttributes","attributeString","attrs","createNoscriptMarkup","normalizedContainers","DEFAULT_NOSCRIPT_IFRAME_ATTRIBUTES","escapeJsString","installAutoQueue","dataLayerName","pollInterval","timeout","maxBufferSize","onReplay","onTimeout","createNoopState","dataLayer","buffer","originalPush","active","gtmReady","pollTimer","timeoutTimer","isGtmLoaded","replay","count","uninstall","interceptedPush","args","createAutoQueueScript","attachToInlineBuffer","inlineBuffer"],"mappings":"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","sourcesContent":["export const DEFAULT_GTM_HOST = 'https://www.googletagmanager.com';\nexport const DEFAULT_DATA_LAYER_NAME = 'dataLayer';\n","import type { DataLayerValue } from './types';\n\nconst CONSENT_COMMAND = 'consent' as const;\nconst CONSENT_DEFAULT = 'default' as const;\nconst CONSENT_UPDATE = 'update' as const;\n\n/**\n * The four consent categories tracked by Google Consent Mode v2.\n */\nconst CONSENT_KEYS = ['ad_storage', 'analytics_storage', 'ad_user_data', 'ad_personalization'] as const;\n\n/**\n * A consent category key: 'ad_storage' | 'analytics_storage' | 'ad_user_data' | 'ad_personalization'\n */\nexport type ConsentKey = (typeof CONSENT_KEYS)[number];\n\n/**\n * A consent decision: 'granted' or 'denied'\n */\nexport type ConsentDecision = 'granted' | 'denied';\n\n/**\n * Consent state object for one or more categories.\n *\n * This is a **partial** record - you only need to specify the categories you want to set.\n * Unspecified categories retain their previous state when using `updateConsent()`.\n *\n * @example\n * ```ts\n * // All four categories\n * const fullState: ConsentState = {\n * ad_storage: 'granted',\n * analytics_storage: 'granted',\n * ad_user_data: 'granted',\n * ad_personalization: 'granted'\n * };\n *\n * // Single category (partial update)\n * const partialState: ConsentState = {\n * analytics_storage: 'granted'\n * };\n *\n * // Multiple specific categories\n * const mixedState: ConsentState = {\n * analytics_storage: 'granted',\n * ad_storage: 'denied'\n * };\n * ```\n */\nexport type ConsentState = Partial<Record<ConsentKey, ConsentDecision>>;\n\nexport interface ConsentRegionOptions {\n /**\n * ISO 3166-2 region codes (e.g., `US-CA`, `EEA`) that the consent command applies to.\n */\n region?: readonly string[];\n /**\n * Milliseconds to wait for an explicit update before firing tags when using the default command.\n */\n waitForUpdate?: number;\n}\n\nexport type ConsentCommand = typeof CONSENT_DEFAULT | typeof CONSENT_UPDATE;\n\nexport interface ConsentCommandInput {\n command: ConsentCommand;\n state: ConsentState;\n options?: ConsentRegionOptions;\n}\n\nexport type ConsentCommandValue =\n | [typeof CONSENT_COMMAND, ConsentCommand, ConsentState]\n | [typeof CONSENT_COMMAND, ConsentCommand, ConsentState, Record<string, unknown>];\n\nconst isConsentKey = (value: string): value is ConsentKey => (CONSENT_KEYS as readonly string[]).includes(value);\n\nconst isConsentDecision = (value: unknown): value is ConsentDecision => value === 'granted' || value === 'denied';\n\nconst assertValidRegions = (regions: readonly string[]) => {\n if (!Array.isArray(regions)) {\n throw new Error('Consent region list must be an array of ISO region codes.');\n }\n\n for (const region of regions) {\n if (typeof region !== 'string' || region.trim().length === 0) {\n throw new Error('Consent region codes must be non-empty strings.');\n }\n }\n};\n\nconst assertValidWaitForUpdate = (waitForUpdate: number) => {\n if (!Number.isFinite(waitForUpdate) || waitForUpdate < 0) {\n throw new Error('waitForUpdate must be a non-negative finite number.');\n }\n};\n\nexport const normalizeConsentState = (state: ConsentState): ConsentState => {\n const normalizedEntries = Object.entries(state ?? {}).map(([key, value]) => {\n if (!isConsentKey(key)) {\n throw new Error(`Invalid consent key: ${key}`);\n }\n\n if (!isConsentDecision(value)) {\n throw new Error(`Invalid consent value for key \"${key}\". Expected \"granted\" or \"denied\".`);\n }\n\n return [key, value] as const;\n });\n\n if (!normalizedEntries.length) {\n throw new Error('At least one consent key/value pair is required.');\n }\n\n const normalizedState = {} as ConsentState;\n for (const [key, value] of normalizedEntries) {\n normalizedState[key as ConsentKey] = value;\n }\n\n return Object.freeze(normalizedState);\n};\n\nconst normalizeOptions = (options?: ConsentRegionOptions): Record<string, unknown> | undefined => {\n if (!options) {\n return undefined;\n }\n\n const payload: Record<string, unknown> = {};\n\n if (options.region) {\n assertValidRegions(options.region);\n if (options.region.length) {\n payload.region = [...options.region];\n }\n }\n\n if (typeof options.waitForUpdate === 'number') {\n assertValidWaitForUpdate(options.waitForUpdate);\n payload.wait_for_update = options.waitForUpdate;\n }\n\n return Object.keys(payload).length ? payload : undefined;\n};\n\nexport const buildConsentCommand = ({ command, state, options }: ConsentCommandInput): ConsentCommandValue => {\n if (command !== CONSENT_DEFAULT && command !== CONSENT_UPDATE) {\n throw new Error(`Unsupported consent command: ${command}`);\n }\n\n const normalizedState = normalizeConsentState(state);\n const normalizedOptions = normalizeOptions(options);\n\n if (normalizedOptions) {\n return [CONSENT_COMMAND, command, normalizedState, normalizedOptions];\n }\n\n return [CONSENT_COMMAND, command, normalizedState];\n};\n\nexport const createConsentCommandValue = (input: ConsentCommandInput): DataLayerValue => {\n // Spread the tuple to convert it to a plain array for DataLayerValue compatibility\n return [...buildConsentCommand(input)];\n};\n\nexport const createConsentDefaultsCommand = (state: ConsentState, options?: ConsentRegionOptions): DataLayerValue =>\n createConsentCommandValue({ command: CONSENT_DEFAULT, state, options });\n\nexport const createConsentUpdateCommand = (state: ConsentState, options?: ConsentRegionOptions): DataLayerValue =>\n createConsentCommandValue({ command: CONSENT_UPDATE, state, options });\n\nexport const consent = {\n buildConsentCommand,\n createConsentDefaultsCommand,\n createConsentUpdateCommand,\n normalizeConsentState\n};\n","import type { ConsentState } from '../consent';\n\n/**\n * Pre-configured consent state presets for common scenarios.\n *\n * These presets cover the most common consent patterns:\n * - `eeaDefault`: All denied (GDPR compliant default)\n * - `allGranted`: All granted (user accepts everything)\n * - `analyticsOnly`: Mixed state (analytics only, no ads)\n *\n * For custom combinations, pass a partial `ConsentState` object to `updateConsent()`.\n * You only need to specify the categories you want to update.\n *\n * @example\n * ```ts\n * // Use a preset\n * client.updateConsent(consentPresets.allGranted);\n *\n * // Or create a custom state\n * client.updateConsent({\n * analytics_storage: 'granted',\n * ad_storage: 'denied'\n * });\n *\n * // Partial updates (only update specific categories)\n * client.updateConsent({ analytics_storage: 'granted' });\n * ```\n */\nexport const consentPresets = {\n /**\n * All categories denied - GDPR/EEA compliant default.\n *\n * Use as the initial state for regions requiring explicit opt-in consent.\n * Tags will be blocked until the user grants specific permissions.\n *\n * | Category | State |\n * |----------|-------|\n * | ad_storage | denied |\n * | analytics_storage | denied |\n * | ad_user_data | denied |\n * | ad_personalization | denied |\n */\n eeaDefault: Object.freeze({\n ad_storage: 'denied',\n analytics_storage: 'denied',\n ad_user_data: 'denied',\n ad_personalization: 'denied'\n } satisfies ConsentState),\n\n /**\n * All categories granted - user accepts all tracking.\n *\n * Use when the user clicks \"Accept All\" or in regions where consent is implied.\n *\n * | Category | State |\n * |----------|-------|\n * | ad_storage | granted |\n * | analytics_storage | granted |\n * | ad_user_data | granted |\n * | ad_personalization | granted |\n */\n allGranted: Object.freeze({\n ad_storage: 'granted',\n analytics_storage: 'granted',\n ad_user_data: 'granted',\n ad_personalization: 'granted'\n } satisfies ConsentState),\n\n /**\n * Analytics allowed, advertising denied - mixed consent state.\n *\n * Use when the user accepts analytics/statistics but rejects advertising cookies.\n * This is a common \"essential + analytics\" consent pattern.\n *\n * | Category | State |\n * |----------|-------|\n * | ad_storage | denied |\n * | analytics_storage | granted |\n * | ad_user_data | denied |\n * | ad_personalization | denied |\n */\n analyticsOnly: Object.freeze({\n ad_storage: 'denied',\n analytics_storage: 'granted',\n ad_user_data: 'denied',\n ad_personalization: 'denied'\n } satisfies ConsentState)\n} as const;\n\nexport type ConsentPresetName = keyof typeof consentPresets;\n\nexport const getConsentPreset = (name: ConsentPresetName): ConsentState => ({\n ...consentPresets[name]\n});\n\n/**\n * Convenience export for the EEA default consent state.\n * All categories denied - GDPR/EEA compliant default.\n */\nexport const eeaDefault = consentPresets.eeaDefault;\n\n/**\n * Convenience export for the all-granted consent state.\n * All categories granted - user accepts all tracking.\n */\nexport const allGranted = consentPresets.allGranted;\n\n/**\n * Convenience export for the analytics-only consent state.\n * Analytics allowed, advertising denied.\n */\nexport const analyticsOnly = consentPresets.analyticsOnly;\n","import type { DataLayerState, DataLayerValue } from './types';\n\ninterface EnsureDataLayerResult extends DataLayerState {\n snapshot: DataLayerValue[] | undefined;\n}\n\nconst isArray = (value: unknown): value is DataLayerValue[] => Array.isArray(value);\n\nexport const ensureDataLayer = (name: string): EnsureDataLayerResult => {\n const globalScope = globalThis as Record<string, unknown>;\n const existing = globalScope[name];\n const snapshot = isArray(existing) ? [...existing] : undefined;\n\n if (!isArray(existing)) {\n globalScope[name] = [] as DataLayerValue[];\n }\n\n return {\n name,\n dataLayer: globalScope[name] as DataLayerValue[],\n created: !isArray(existing),\n restore() {\n if (!isArray(existing)) {\n delete globalScope[name];\n return;\n }\n\n const clone = snapshot ? [...snapshot] : [];\n globalScope[name] = clone;\n },\n snapshot\n };\n};\n\nexport const pushToDataLayer = (state: DataLayerState, value: DataLayerValue) => {\n state.dataLayer.push(value);\n};\n","import type { Logger, PartialLogger } from './types';\n\ntype LogLevel = keyof Logger;\n\nconst levels: LogLevel[] = ['debug', 'info', 'warn', 'error'];\n\nconst noop = () => {\n /* intentionally empty */\n};\n\nexport const createLogger = (logger?: PartialLogger): Logger => {\n const safeLogger: Partial<Record<LogLevel, (message: string, details?: Record<string, unknown>) => void>> = {};\n\n for (const level of levels) {\n const provided = logger?.[level];\n if (typeof provided === 'function') {\n safeLogger[level] = provided.bind(logger);\n continue;\n }\n safeLogger[level] = noop;\n }\n\n return safeLogger as Logger;\n};\n\nexport type LoggerFacade = ReturnType<typeof createLogger>;\n","import { DEFAULT_DATA_LAYER_NAME, DEFAULT_GTM_HOST } from './constants';\nimport { createLogger } from './logger';\nimport type {\n ContainerDescriptor,\n CreateGtmClientOptions,\n ScriptAttributes,\n ScriptLoadState,\n ScriptLoadStatus\n} from './types';\n\nconst CONTAINER_ATTR = 'data-gtm-container-id';\nconst INSTANCE_ATTR = 'data-gtm-kit-instance';\n\ninterface Deferred<T> {\n promise: Promise<T>;\n resolve: (value: T) => void;\n settled: boolean;\n}\n\nconst createDeferred = <T>(): Deferred<T> => {\n let resolved = false;\n let resolver: (value: T) => void;\n\n const promise = new Promise<T>((resolve) => {\n resolver = resolve;\n });\n\n return {\n get settled() {\n return resolved;\n },\n promise,\n resolve: (value: T) => {\n if (resolved) {\n return;\n }\n\n resolved = true;\n resolver(value);\n }\n } as Deferred<T>;\n};\n\nexport interface NormalizedContainer extends ContainerDescriptor {\n queryParams?: Record<string, string | number | boolean>;\n}\n\nexport interface ScriptManagerOptions {\n instanceId: string;\n host?: string;\n dataLayerName?: string;\n scriptAttributes?: ScriptAttributes;\n defaultQueryParams?: Record<string, string | number | boolean>;\n logger?: CreateGtmClientOptions['logger'];\n}\n\nexport interface EnsureResult {\n inserted: HTMLScriptElement[];\n}\n\nconst toRecord = (\n params?: Record<string, string | number | boolean>\n): Record<string, string> => {\n if (!params) {\n return {};\n }\n\n return Object.entries(params).reduce<Record<string, string>>((acc, [key, value]) => {\n acc[key] = String(value);\n return acc;\n }, {});\n};\n\nconst buildScriptUrl = (\n host: string,\n containerId: string,\n queryParams?: Record<string, string | number | boolean>\n): string => {\n const normalizedHost = host.endsWith('/') ? host.slice(0, -1) : host;\n const searchParams = new URLSearchParams({ id: containerId });\n\n const params = toRecord(queryParams);\n for (const [key, value] of Object.entries(params)) {\n if (key === 'id') {\n continue;\n }\n searchParams.set(key, value);\n }\n\n return `${normalizedHost}/gtm.js?${searchParams.toString()}`;\n};\n\nconst findExistingScript = (containerId: string): HTMLScriptElement | null => {\n if (typeof document === 'undefined') {\n return null;\n }\n\n const attrSelector = `script[${CONTAINER_ATTR}=\"${containerId}\"]`;\n const existingWithAttr = document.querySelector(attrSelector);\n if (existingWithAttr) {\n return existingWithAttr as HTMLScriptElement;\n }\n\n const scripts = Array.from(document.getElementsByTagName('script'));\n return (\n scripts.find((script) => script.src.includes(`id=${encodeURIComponent(containerId)}`)) || null\n );\n};\n\nconst formatErrorMessage = (event: Event): string => {\n if (event instanceof ErrorEvent) {\n if (event.error) {\n return String(event.error);\n }\n\n if (event.message) {\n return event.message;\n }\n }\n\n return 'Failed to load GTM script.';\n};\n\nexport class ScriptManager {\n private readonly logger = createLogger(this.options.logger);\n private readonly host = this.options.host ?? DEFAULT_GTM_HOST;\n private readonly dataLayerName = this.options.dataLayerName ?? DEFAULT_DATA_LAYER_NAME;\n private readonly defaultQueryParams = this.options.defaultQueryParams;\n private readonly scriptAttributes = this.options.scriptAttributes;\n private readonly insertedScripts = new Set<HTMLScriptElement>();\n private readonly readyCallbacks = new Set<(state: ScriptLoadState[]) => void>();\n private readiness = createDeferred<ScriptLoadState[]>();\n private readonly loadStates = new Map<string, ScriptLoadState>();\n private readonly pendingContainers = new Set<string>();\n\n constructor(private readonly options: ScriptManagerOptions) {}\n\n whenReady(): Promise<ScriptLoadState[]> {\n return this.readiness.promise;\n }\n\n onReady(callback: (state: ScriptLoadState[]) => void): () => void {\n this.readyCallbacks.add(callback);\n\n if (this.readiness.settled) {\n callback(Array.from(this.loadStates.values()));\n }\n\n return () => {\n this.readyCallbacks.delete(callback);\n };\n }\n\n private notifyReady(): void {\n const snapshot = Array.from(this.loadStates.values());\n\n if (!this.readiness.settled) {\n this.readiness.resolve(snapshot);\n }\n\n for (const callback of this.readyCallbacks) {\n callback(snapshot);\n }\n }\n\n private maybeNotifyReady(): void {\n if (this.pendingContainers.size === 0) {\n this.notifyReady();\n }\n }\n\n private recordState(state: ScriptLoadState): void {\n this.loadStates.set(state.containerId, state);\n }\n\n private resetReadiness(): void {\n this.pendingContainers.clear();\n this.loadStates.clear();\n this.readiness = createDeferred<ScriptLoadState[]>();\n }\n\n ensure(containers: NormalizedContainer[]): EnsureResult {\n if (typeof document === 'undefined') {\n this.logger.warn('No document available – skipping script injection.');\n\n for (const container of containers) {\n if (!container.id) {\n continue;\n }\n\n this.recordState({\n containerId: container.id,\n status: 'skipped',\n error: 'Document unavailable for script injection.'\n });\n }\n\n this.maybeNotifyReady();\n return { inserted: [] };\n }\n\n const inserted: HTMLScriptElement[] = [];\n const targetParent = document.head || document.body;\n if (!targetParent) {\n this.logger.error('Unable to find document.head or document.body for script injection.');\n\n for (const container of containers) {\n if (!container.id) {\n continue;\n }\n\n this.recordState({\n containerId: container.id,\n status: 'skipped',\n error: 'Missing document.head and document.body for GTM script injection.'\n });\n }\n\n this.maybeNotifyReady();\n return { inserted: [] };\n }\n\n for (const container of containers) {\n if (!container.id) {\n this.logger.warn('Skipping container with missing id.', { container });\n continue;\n }\n\n const existing = findExistingScript(container.id);\n if (existing) {\n this.logger.debug('Container script already present, skipping injection.', {\n containerId: container.id\n });\n\n this.recordState({\n containerId: container.id,\n src: existing.src,\n status: 'loaded',\n fromCache: true\n });\n continue;\n }\n\n const params = {\n ...this.defaultQueryParams,\n ...container.queryParams\n };\n\n if (this.dataLayerName !== DEFAULT_DATA_LAYER_NAME && params.l === undefined) {\n params.l = this.dataLayerName;\n }\n\n const script = document.createElement('script');\n const url = buildScriptUrl(this.host, container.id, params);\n script.src = url;\n script.setAttribute(CONTAINER_ATTR, container.id);\n script.setAttribute(INSTANCE_ATTR, this.options.instanceId);\n\n const attributes = this.scriptAttributes ?? {};\n if (attributes.async !== undefined) {\n script.async = attributes.async;\n } else {\n script.async = true;\n }\n if (attributes.defer !== undefined) {\n script.defer = attributes.defer;\n }\n\n for (const [key, value] of Object.entries(attributes)) {\n if (key === 'async' || key === 'defer') {\n continue;\n }\n\n if (value === undefined || value === null) {\n continue;\n }\n\n const stringValue = String(value);\n\n if (key === 'nonce') {\n script.nonce = stringValue;\n }\n\n script.setAttribute(key, stringValue);\n }\n\n this.pendingContainers.add(container.id);\n\n const settle = (status: ScriptLoadStatus, event?: Event): void => {\n if (!this.pendingContainers.has(container.id)) {\n return;\n }\n\n this.pendingContainers.delete(container.id);\n\n const state: ScriptLoadState = {\n containerId: container.id,\n src: url,\n status,\n fromCache: false\n };\n\n if (status === 'failed' && event) {\n state.error = formatErrorMessage(event);\n this.logger.error('Failed to load GTM container script.', {\n containerId: container.id,\n src: url,\n error: state.error\n });\n }\n\n this.recordState(state);\n this.maybeNotifyReady();\n };\n\n script.addEventListener('load', () => settle('loaded'));\n script.addEventListener('error', (event) => settle('failed', event));\n\n targetParent.appendChild(script);\n this.insertedScripts.add(script);\n inserted.push(script);\n this.logger.info('Injected GTM container script.', { containerId: container.id, src: url });\n }\n\n this.maybeNotifyReady();\n return { inserted };\n }\n\n teardown() {\n if (typeof document === 'undefined') {\n return;\n }\n\n for (const script of this.insertedScripts) {\n if (script.parentNode) {\n script.parentNode.removeChild(script);\n }\n }\n\n this.insertedScripts.clear();\n this.resetReadiness();\n }\n}\n","import { DEFAULT_DATA_LAYER_NAME } from './constants';\nimport { ensureDataLayer, pushToDataLayer } from './data-layer';\nimport { createConsentCommandValue } from './consent';\nimport type { ConsentRegionOptions, ConsentState } from './consent';\nimport { createLogger } from './logger';\nimport { ScriptManager } from './script-manager';\nimport type {\n ContainerConfigInput,\n ContainerDescriptor,\n CreateGtmClientOptions,\n DataLayerValue,\n GtmClient,\n ScriptLoadState\n} from './types';\n\nconst isString = (value: unknown): value is string => typeof value === 'string';\n\nconst normalizeContainer = (input: ContainerConfigInput): ContainerDescriptor => {\n if (isString(input)) {\n return { id: input };\n }\n\n return input;\n};\n\nconst isPlainObject = (value: unknown): value is Record<string, unknown> => {\n if (typeof value !== 'object' || value === null) {\n return false;\n }\n\n const prototype = Object.getPrototypeOf(value);\n return prototype === Object.prototype || prototype === null;\n};\n\nconst serializeUnknown = (value: unknown): string | null => {\n if (value === null || typeof value === 'boolean' || typeof value === 'number') {\n return JSON.stringify(value);\n }\n\n if (typeof value === 'string') {\n return JSON.stringify(value);\n }\n\n if (Array.isArray(value)) {\n const parts: string[] = [];\n for (const entry of value) {\n const serialized = serializeUnknown(entry);\n if (serialized === null) {\n return null;\n }\n parts.push(serialized);\n }\n return `[${parts.join(',')}]`;\n }\n\n if (isPlainObject(value)) {\n const keys = Object.keys(value).sort();\n const parts: string[] = [];\n for (const key of keys) {\n const serialized = serializeUnknown((value as Record<string, unknown>)[key]);\n if (serialized === null) {\n return null;\n }\n parts.push(`${JSON.stringify(key)}:${serialized}`);\n }\n return `{${parts.join(',')}}`;\n }\n\n return null;\n};\n\nconst serializeDataLayerValue = (value: DataLayerValue): string | null => {\n if (Array.isArray(value)) {\n return serializeUnknown(value);\n }\n\n if (isPlainObject(value)) {\n return serializeUnknown(value);\n }\n\n return null;\n};\n\nconst isConsentCommandValue = (value: DataLayerValue): value is unknown[] =>\n Array.isArray(value) &&\n value.length >= 3 &&\n value[0] === 'consent' &&\n (value[1] === 'default' || value[1] === 'update');\n\nconst isStartEvent = (value: DataLayerValue): boolean => {\n if (!isPlainObject(value)) {\n return false;\n }\n\n return (value.event as unknown) === 'gtm.js';\n};\n\ninterface QueuedEntry {\n value: DataLayerValue;\n signature: string | null;\n}\n\nlet instanceCounter = 0;\n\nexport class GtmClientImpl implements GtmClient {\n private readonly logger = createLogger(this.options.logger);\n private readonly resolvedDataLayerName = this.options.dataLayerName ?? DEFAULT_DATA_LAYER_NAME;\n private readonly containers = Array.isArray(this.options.containers)\n ? this.options.containers.map(normalizeContainer)\n : [normalizeContainer(this.options.containers)];\n private readonly queue: QueuedEntry[] = [];\n private readonly queuedConsentSignatures = new Set<string>();\n private readonly deliveredConsentSignatures = new Set<string>();\n private snapshotSignatures: Set<string> | null = null;\n private readonly scriptManager = new ScriptManager({\n instanceId: this.instanceId,\n host: this.options.host,\n dataLayerName: this.resolvedDataLayerName,\n defaultQueryParams: this.options.defaultQueryParams,\n scriptAttributes: this.options.scriptAttributes,\n logger: this.options.logger\n });\n private dataLayerState: ReturnType<typeof ensureDataLayer> | null = null;\n private initialized = false;\n private readonly startTimestamp = Date.now();\n\n constructor(\n private readonly options: CreateGtmClientOptions,\n private readonly instanceId: string\n ) {\n if (!this.containers.length) {\n throw new Error('At least one GTM container ID is required to initialize the client.');\n }\n }\n\n init(): void {\n if (this.initialized) {\n this.logger.debug('GTM client already initialized; skipping.');\n return;\n }\n\n this.logger.info('Initializing GTM client.', {\n containers: this.containers.map((container) => container.id),\n dataLayerName: this.resolvedDataLayerName\n });\n\n this.dataLayerState = ensureDataLayer(this.resolvedDataLayerName);\n this.captureSnapshotSignatures();\n this.pushStartEvent();\n this.flushQueue();\n this.scriptManager.ensure(this.containers);\n\n this.initialized = true;\n }\n\n push(value: DataLayerValue): void {\n if (value === undefined || value === null) {\n this.logger.warn('Ignoring falsy dataLayer push.', { value });\n return;\n }\n\n const immediate = this.deliverToDataLayer(value);\n\n if (immediate) {\n this.logger.debug('Pushed value to dataLayer.', { immediate: true });\n } else {\n this.logger.debug('Queued dataLayer value (pre-init).', { queueLength: this.queue.length });\n }\n }\n\n setConsentDefaults(state: ConsentState, options?: ConsentRegionOptions): void {\n const value = createConsentCommandValue({ command: 'default', state, options });\n const immediate = this.deliverToDataLayer(value);\n\n this.logger.info('Applied consent defaults.', {\n immediate,\n state,\n options\n });\n }\n\n updateConsent(state: ConsentState, options?: ConsentRegionOptions): void {\n const value = createConsentCommandValue({ command: 'update', state, options });\n const immediate = this.deliverToDataLayer(value);\n\n this.logger.info('Updated consent state.', {\n immediate,\n state,\n options\n });\n }\n\n teardown(): void {\n this.logger.info('Tearing down GTM client instance.', { dataLayerName: this.resolvedDataLayerName });\n this.scriptManager.teardown();\n if (this.dataLayerState) {\n this.dataLayerState.restore();\n }\n this.queue.length = 0;\n this.queuedConsentSignatures.clear();\n this.deliveredConsentSignatures.clear();\n this.snapshotSignatures = null;\n this.initialized = false;\n this.dataLayerState = null;\n }\n\n isInitialized(): boolean {\n return this.initialized;\n }\n\n whenReady(): Promise<ScriptLoadState[]> {\n return this.scriptManager.whenReady();\n }\n\n onReady(callback: (state: ScriptLoadState[]) => void): () => void {\n return this.scriptManager.onReady(callback);\n }\n\n get dataLayerName(): string {\n return this.resolvedDataLayerName;\n }\n\n private flushQueue(): void {\n if (!this.dataLayerState) {\n return;\n }\n\n while (this.queue.length) {\n const entry = this.queue.shift();\n if (!entry) {\n continue;\n }\n\n this.pushValueToDataLayer(entry.value, entry.signature);\n\n if (entry.signature) {\n this.queuedConsentSignatures.delete(entry.signature);\n }\n }\n }\n\n private deliverToDataLayer(value: DataLayerValue): boolean {\n if (this.initialized && this.dataLayerState) {\n this.pushValueToDataLayer(value);\n return true;\n }\n\n this.queueValue(value);\n return false;\n }\n\n private pushStartEvent(): void {\n if (!this.dataLayerState) {\n return;\n }\n\n if (this.hasExistingStartEvent()) {\n this.logger.debug('Detected existing gtm.js event; skipping duplicate start push.');\n return;\n }\n\n const startEvent = { 'gtm.start': this.startTimestamp, event: 'gtm.js' } as const;\n this.pushValueToDataLayer(startEvent);\n }\n\n private captureSnapshotSignatures(): void {\n if (!this.dataLayerState) {\n return;\n }\n\n const snapshot = this.dataLayerState.snapshot ?? [];\n this.snapshotSignatures = new Set<string>();\n\n for (const value of snapshot) {\n const signature = serializeDataLayerValue(value);\n if (signature) {\n this.snapshotSignatures.add(signature);\n if (isConsentCommandValue(value)) {\n this.deliveredConsentSignatures.add(signature);\n }\n }\n }\n }\n\n private queueValue(value: DataLayerValue): void {\n const signature = isConsentCommandValue(value) ? serializeDataLayerValue(value) : null;\n\n if (signature && this.queuedConsentSignatures.has(signature)) {\n this.logger.debug('Skipping duplicate queued dataLayer value.', { value });\n return;\n }\n\n const entry: QueuedEntry = { value, signature };\n\n if (isConsentCommandValue(value)) {\n const firstNonConsentIndex = this.queue.findIndex((queued) => !isConsentCommandValue(queued.value));\n\n if (firstNonConsentIndex === -1) {\n this.queue.push(entry);\n } else {\n this.queue.splice(firstNonConsentIndex, 0, entry);\n }\n } else {\n this.queue.push(entry);\n }\n\n if (signature) {\n this.queuedConsentSignatures.add(signature);\n }\n }\n\n private pushValueToDataLayer(value: DataLayerValue, existingSignature?: string | null): void {\n if (!this.dataLayerState) {\n return;\n }\n\n const signature = existingSignature ?? serializeDataLayerValue(value);\n const isConsentCommand = isConsentCommandValue(value);\n const seenInSnapshot = signature ? this.snapshotSignatures?.has(signature) : false;\n const alreadyDeliveredConsent =\n isConsentCommand && signature ? this.deliveredConsentSignatures.has(signature) : false;\n\n if (signature && seenInSnapshot) {\n this.logger.debug('Skipping duplicate dataLayer value detected during hydration.', { value });\n if (isConsentCommand) {\n this.deliveredConsentSignatures.add(signature);\n }\n return;\n }\n\n if (isConsentCommand && alreadyDeliveredConsent) {\n this.logger.debug('Skipping duplicate consent command.', { value });\n return;\n }\n\n pushToDataLayer(this.dataLayerState, value);\n\n if (isConsentCommand && signature) {\n this.deliveredConsentSignatures.add(signature);\n }\n }\n\n private hasExistingStartEvent(): boolean {\n if (!this.dataLayerState) {\n return false;\n }\n\n const snapshot = this.dataLayerState.snapshot ?? [];\n if (snapshot.some(isStartEvent)) {\n return true;\n }\n\n return this.dataLayerState.dataLayer.some(isStartEvent);\n }\n}\n\nexport const createGtmClient = (options: CreateGtmClientOptions): GtmClient => {\n const instanceId = `gtm-kit-${++instanceCounter}`;\n return new GtmClientImpl(options, instanceId);\n};\n","import type { GtmClient } from '../types';\nimport type {\n EcommerceEvent,\n EcommerceEventName,\n EcommercePayload,\n EventForName,\n EventPayload,\n GtmEvent\n} from './types';\n\nconst isRecord = (value: unknown): value is Record<string, unknown> => typeof value === 'object' && value !== null;\n\nconst clonePayload = <TPayload extends EventPayload | undefined>(payload: TPayload): TPayload => {\n if (!payload) {\n return payload;\n }\n\n return { ...payload } as TPayload;\n};\n\nexport const pushEvent = <TName extends string, TPayload extends EventPayload = EventPayload>(\n client: Pick<GtmClient, 'push'>,\n name: TName,\n payload?: TPayload\n): EventForName<TName> => {\n if (!name) {\n throw new Error('An event name is required when pushing to the dataLayer.');\n }\n\n if (payload !== undefined && !isRecord(payload)) {\n throw new Error('Event payloads must be plain objects when pushing to the dataLayer.');\n }\n\n const event = {\n event: name,\n ...(clonePayload(payload) ?? {})\n } as GtmEvent<TName, TPayload>;\n\n client.push(event);\n return event as EventForName<TName>;\n};\n\nexport interface PushEcommerceOptions<TExtras extends EventPayload = EventPayload> {\n extras?: TExtras;\n}\n\nexport const pushEcommerce = <TName extends EcommerceEventName, TExtras extends EventPayload = EventPayload>(\n client: Pick<GtmClient, 'push'>,\n name: TName,\n ecommerce: EcommercePayload,\n options?: PushEcommerceOptions<TExtras>\n): EcommerceEvent<TName, TExtras> => {\n if (!isRecord(ecommerce)) {\n throw new Error('Ecommerce payload must be an object.');\n }\n\n const extras = options?.extras ?? {};\n\n if (!isRecord(extras)) {\n throw new Error('Ecommerce extras must be an object when provided.');\n }\n\n const payload = { ...extras, ecommerce } as { ecommerce: EcommercePayload } & TExtras;\n return pushEvent(client, name, payload) as EcommerceEvent<TName, TExtras>;\n};\n","import type { ContainerConfigInput, ContainerDescriptor } from './types';\nimport { DEFAULT_GTM_HOST } from './constants';\nconst DEFAULT_IFRAME_ATTRIBUTES: Record<string, string> = {\n height: '0',\n width: '0',\n style: 'display:none;visibility:hidden',\n title: 'Google Tag Manager'\n};\n\nconst isString = (value: unknown): value is string => typeof value === 'string';\n\nconst normalizeContainer = (input: ContainerConfigInput): ContainerDescriptor => {\n if (isString(input)) {\n return { id: input };\n }\n\n return input;\n};\n\nconst toRecord = (params?: Record<string, string | number | boolean>): Record<string, string> => {\n if (!params) {\n return {};\n }\n\n return Object.entries(params).reduce<Record<string, string>>((acc, [key, value]) => {\n acc[key] = String(value);\n return acc;\n }, {});\n};\n\nconst escapeAttributeValue = (value: string): string =>\n value.replace(/&/g, '&amp;').replace(/\"/g, '&quot;').replace(/</g, '&lt;').replace(/>/g, '&gt;');\n\nconst buildNoscriptUrl = (\n host: string,\n containerId: string,\n queryParams?: Record<string, string | number | boolean>\n): string => {\n const normalizedHost = host.endsWith('/') ? host.slice(0, -1) : host;\n const searchParams = new URLSearchParams({ id: containerId });\n\n const params = toRecord(queryParams);\n for (const [key, value] of Object.entries(params)) {\n if (key === 'id') {\n continue;\n }\n searchParams.set(key, value);\n }\n\n return `${normalizedHost}/ns.html?${searchParams.toString()}`;\n};\n\nconst buildAttributeString = (attributes: Record<string, string | number | boolean> | undefined): string => {\n if (!attributes) {\n return '';\n }\n\n const entries = Object.entries(attributes);\n if (!entries.length) {\n return '';\n }\n\n return entries.map(([key, value]) => `${key}=\"${escapeAttributeValue(String(value))}\"`).join(' ');\n};\n\nexport interface NoscriptOptions {\n host?: string;\n defaultQueryParams?: Record<string, string | number | boolean>;\n iframeAttributes?: Record<string, string | number | boolean>;\n}\n\nconst buildNoscriptForContainer = (container: ContainerDescriptor, options: NoscriptOptions): string => {\n if (!container.id) {\n throw new Error('Container id is required to build noscript markup.');\n }\n\n const host = options.host ?? DEFAULT_GTM_HOST;\n const params = {\n ...options.defaultQueryParams,\n ...container.queryParams\n };\n\n const src = buildNoscriptUrl(host, container.id, params);\n const iframeAttributes = {\n ...DEFAULT_IFRAME_ATTRIBUTES,\n ...options.iframeAttributes\n };\n const attributeString = buildAttributeString(iframeAttributes);\n\n const attrs = attributeString ? ` ${attributeString}` : '';\n return `<noscript><iframe src=\"${escapeAttributeValue(src)}\"${attrs}></iframe></noscript>`;\n};\n\nexport const createNoscriptMarkup = (\n containers: ContainerConfigInput[] | ContainerConfigInput,\n options: NoscriptOptions = {}\n): string => {\n const normalizedContainers = Array.isArray(containers)\n ? containers.map(normalizeContainer)\n : [normalizeContainer(containers)];\n\n if (!normalizedContainers.length) {\n throw new Error('At least one container is required to build noscript markup.');\n }\n\n return normalizedContainers.map((container) => buildNoscriptForContainer(container, options)).join('');\n};\n\nexport const DEFAULT_NOSCRIPT_IFRAME_ATTRIBUTES = { ...DEFAULT_IFRAME_ATTRIBUTES };\n","/**\n * Auto-queue: Automatic dataLayer buffering for race condition elimination.\n *\n * This module provides automatic buffering of dataLayer pushes that occur before\n * GTM.js loads. Events are captured, stored in order, and replayed once GTM is ready.\n *\n * @example\n * ```ts\n * // Call as early as possible (ideally inline in <head>)\n * import { installAutoQueue } from '@react-gtm-kit/core';\n *\n * installAutoQueue(); // Start buffering immediately\n *\n * // Later, events pushed before GTM loads are automatically queued\n * window.dataLayer.push({ event: 'early_event' }); // Buffered!\n *\n * // When GTM loads, all buffered events replay in order\n * ```\n *\n * @example\n * ```html\n * <!-- Inline script for earliest possible buffering -->\n * <script>\n * // Minimal inline version for <head>\n * (function(w,d,n){\n * w[n]=w[n]||[];var q=[],o=w[n].push.bind(w[n]);\n * w[n].push=function(){q.push(arguments);return o.apply(this,arguments)};\n * w.__gtmkit_buffer=q;\n * })(window,document,'dataLayer');\n * </script>\n * ```\n */\n\nimport { DEFAULT_DATA_LAYER_NAME } from './constants';\nimport type { DataLayerValue } from './types';\n\n/**\n * Escape a string for safe use in JavaScript string literals.\n * Prevents XSS when interpolating values into inline scripts.\n */\nconst escapeJsString = (value: string): string =>\n value\n .replace(/\\\\/g, '\\\\\\\\')\n .replace(/'/g, \"\\\\'\")\n .replace(/\"/g, '\\\\\"')\n .replace(/\\n/g, '\\\\n')\n .replace(/\\r/g, '\\\\r')\n .replace(/</g, '\\\\x3c')\n .replace(/>/g, '\\\\x3e')\n .replace(/\\u2028/g, '\\\\u2028')\n .replace(/\\u2029/g, '\\\\u2029');\n\n/** Options for configuring the auto-queue behavior */\nexport interface AutoQueueOptions {\n /**\n * Name of the dataLayer array. Defaults to 'dataLayer'.\n */\n dataLayerName?: string;\n\n /**\n * Interval in milliseconds to check if GTM has loaded.\n * Lower values = faster detection, higher CPU usage.\n * @default 50\n */\n pollInterval?: number;\n\n /**\n * Maximum time in milliseconds to wait for GTM before giving up.\n * Set to 0 for unlimited waiting.\n * @default 30000 (30 seconds)\n */\n timeout?: number;\n\n /**\n * Maximum number of events to buffer.\n * Prevents memory issues if GTM never loads.\n * @default 1000\n */\n maxBufferSize?: number;\n\n /**\n * Callback fired when the buffer is replayed.\n */\n onReplay?: (bufferedCount: number) => void;\n\n /**\n * Callback fired if timeout is reached before GTM loads.\n */\n onTimeout?: (bufferedCount: number) => void;\n}\n\n/** State of the auto-queue system */\nexport interface AutoQueueState {\n /** Whether the auto-queue is currently active */\n active: boolean;\n /** Number of events currently buffered */\n bufferedCount: number;\n /** Whether GTM has been detected as ready */\n gtmReady: boolean;\n /** Manually trigger replay (useful for testing) */\n replay: () => void;\n /** Uninstall the auto-queue and restore original push */\n uninstall: () => void;\n}\n\ninterface BufferedEntry {\n value: DataLayerValue;\n timestamp: number;\n}\n\n/**\n * Installs automatic dataLayer buffering that captures events before GTM loads.\n *\n * Call this as early as possible in your application lifecycle, ideally before\n * any other scripts that might push to the dataLayer.\n *\n * The auto-queue:\n * 1. Creates the dataLayer if it doesn't exist\n * 2. Intercepts all pushes to capture them in a buffer\n * 3. Detects when GTM.js loads by watching for the 'gtm.js' event\n * 4. Replays all buffered events in order once GTM is ready\n * 5. Removes itself, allowing normal dataLayer operation\n *\n * @param options - Configuration options\n * @returns State object with control methods\n *\n * @example\n * ```ts\n * const queue = installAutoQueue({\n * onReplay: (count) => console.log(`Replayed ${count} buffered events`),\n * onTimeout: (count) => console.warn(`GTM didn't load, ${count} events buffered`)\n * });\n *\n * // Check state\n * console.log(queue.bufferedCount); // Number of events waiting\n *\n * // Manual control (usually not needed)\n * queue.replay(); // Force replay now\n * queue.uninstall(); // Remove the interceptor\n * ```\n */\nexport function installAutoQueue(options: AutoQueueOptions = {}): AutoQueueState {\n const {\n dataLayerName = DEFAULT_DATA_LAYER_NAME,\n pollInterval = 50,\n timeout = 30000,\n maxBufferSize = 1000,\n onReplay,\n onTimeout\n } = options;\n\n // Skip in non-browser environments\n if (typeof globalThis === 'undefined' || typeof globalThis.document === 'undefined') {\n return createNoopState();\n }\n\n const globalScope = globalThis as Record<string, unknown>;\n\n // Create dataLayer if it doesn't exist\n if (!Array.isArray(globalScope[dataLayerName])) {\n globalScope[dataLayerName] = [];\n }\n\n const dataLayer = globalScope[dataLayerName] as DataLayerValue[];\n const buffer: BufferedEntry[] = [];\n const originalPush = dataLayer.push.bind(dataLayer);\n\n let active = true;\n let gtmReady = false;\n let pollTimer: ReturnType<typeof setInterval> | null = null;\n let timeoutTimer: ReturnType<typeof setTimeout> | null = null;\n\n // Check if GTM.js event is present (indicating GTM has loaded)\n const isGtmLoaded = (): boolean => {\n return dataLayer.some(\n (entry) =>\n entry !== null &&\n typeof entry === 'object' &&\n !Array.isArray(entry) &&\n (entry as Record<string, unknown>).event === 'gtm.js'\n );\n };\n\n // Replay all buffered events to the dataLayer\n const replay = (): void => {\n if (!active) return;\n\n active = false;\n gtmReady = true;\n\n // Clear timers\n if (pollTimer) {\n clearInterval(pollTimer);\n pollTimer = null;\n }\n if (timeoutTimer) {\n clearTimeout(timeoutTimer);\n timeoutTimer = null;\n }\n\n // Restore original push\n dataLayer.push = originalPush;\n\n // Replay buffered events in order\n const count = buffer.length;\n for (const entry of buffer) {\n originalPush(entry.value);\n }\n buffer.length = 0;\n\n onReplay?.(count);\n };\n\n // Uninstall without replaying\n const uninstall = (): void => {\n if (!active) return;\n\n active = false;\n\n if (pollTimer) {\n clearInterval(pollTimer);\n pollTimer = null;\n }\n if (timeoutTimer) {\n clearTimeout(timeoutTimer);\n timeoutTimer = null;\n }\n\n dataLayer.push = originalPush;\n buffer.length = 0;\n };\n\n // Create intercepted push function\n const interceptedPush = function (this: DataLayerValue[], ...args: DataLayerValue[]): number {\n for (const value of args) {\n // Always push to actual dataLayer (GTM may already be listening)\n originalPush(value);\n\n // Buffer the value for potential replay\n if (active && buffer.length < maxBufferSize) {\n buffer.push({\n value,\n timestamp: Date.now()\n });\n }\n\n // Check if this push indicates GTM is ready\n if (\n active &&\n value !== null &&\n typeof value === 'object' &&\n !Array.isArray(value) &&\n (value as Record<string, unknown>).event === 'gtm.js'\n ) {\n // GTM just loaded! Trigger replay on next tick to ensure\n // this event is fully processed first\n setTimeout(replay, 0);\n }\n }\n\n return dataLayer.length;\n };\n\n // Install the interceptor\n dataLayer.push = interceptedPush;\n\n // Check if GTM was already loaded before we installed\n if (isGtmLoaded()) {\n // GTM already present, replay immediately\n setTimeout(replay, 0);\n } else {\n // Poll for GTM readiness as backup detection\n pollTimer = setInterval(() => {\n if (isGtmLoaded()) {\n replay();\n }\n }, pollInterval);\n\n // Set timeout if configured\n if (timeout > 0) {\n timeoutTimer = setTimeout(() => {\n if (active) {\n onTimeout?.(buffer.length);\n // Don't uninstall on timeout - keep buffering in case GTM loads late\n }\n }, timeout);\n }\n }\n\n // Return state object\n return {\n get active() {\n return active;\n },\n get bufferedCount() {\n return buffer.length;\n },\n get gtmReady() {\n return gtmReady;\n },\n replay,\n uninstall\n };\n}\n\n/**\n * Creates a minimal inline script for earliest possible buffering.\n *\n * This returns a script that can be embedded directly in the HTML `<head>`\n * before any other scripts. It's a minimal version of installAutoQueue()\n * that captures events until the full GTM Kit is loaded.\n *\n * @param dataLayerName - Name of the dataLayer array\n * @returns Inline script string to embed in HTML\n *\n * @example\n * ```ts\n * // In your SSR template\n * const inlineScript = createAutoQueueScript();\n * // Output: <script>{inlineScript}</script> in <head>\n * ```\n */\nexport function createAutoQueueScript(dataLayerName: string = DEFAULT_DATA_LAYER_NAME): string {\n // Minified inline script that:\n // 1. Creates dataLayer if missing\n // 2. Overrides push to capture events\n // 3. Stores buffer in __gtmkit_buffer for later retrieval\n // SECURITY: Escape the dataLayerName to prevent XSS via malicious input\n const safeName = escapeJsString(dataLayerName);\n return `(function(w,n){w[n]=w[n]||[];var q=[],o=w[n].push.bind(w[n]);w[n].push=function(){for(var i=0;i<arguments.length;i++){q.push({v:arguments[i],t:Date.now()});o(arguments[i])}return w[n].length};w.__gtmkit_buffer={q:q,o:o,n:n}})(window,'${safeName}');`;\n}\n\n/**\n * Attaches to an existing inline buffer created by createAutoQueueScript().\n *\n * If you used the inline script in your HTML head, call this when the full\n * GTM Kit loads to take over buffer management and enable replay.\n *\n * @param options - Configuration options\n * @returns State object, or null if no inline buffer exists\n *\n * @example\n * ```ts\n * // After GTM Kit bundle loads\n * const queue = attachToInlineBuffer({\n * onReplay: (count) => console.log(`Replayed ${count} events`)\n * });\n *\n * if (queue) {\n * console.log(`Taking over ${queue.bufferedCount} buffered events`);\n * }\n * ```\n */\nexport function attachToInlineBuffer(options: Omit<AutoQueueOptions, 'dataLayerName'> = {}): AutoQueueState | null {\n if (typeof globalThis === 'undefined') {\n return null;\n }\n\n const globalScope = globalThis as Record<string, unknown>;\n const inlineBuffer = globalScope.__gtmkit_buffer as\n | {\n q: { v: DataLayerValue; t: number }[];\n o: (...args: DataLayerValue[]) => number;\n n: string;\n }\n | undefined;\n\n if (!inlineBuffer) {\n return null;\n }\n\n const { n: dataLayerName } = inlineBuffer;\n\n // Clean up the global reference\n delete globalScope.__gtmkit_buffer;\n\n // Install full auto-queue with the same dataLayer name\n // The buffer from the inline script is already in the dataLayer,\n // so we just need to continue monitoring from here\n return installAutoQueue({\n ...options,\n dataLayerName\n });\n}\n\n/** Creates a no-op state for SSR environments */\nfunction createNoopState(): AutoQueueState {\n // No-op functions for SSR - these do nothing intentionally\n const noop = (): void => {\n /* no-op for SSR */\n };\n return {\n active: false,\n bufferedCount: 0,\n gtmReady: false,\n replay: noop,\n uninstall: noop\n };\n}\n"]}
1
+ {"version":3,"sources":["../src/constants.ts","../src/consent.ts","../src/consent/presets.ts","../src/data-layer.ts","../src/logger.ts","../src/url-utils.ts","../src/script-manager.ts","../src/client.ts","../src/events/push.ts","../src/noscript.ts","../src/auto-queue.ts"],"names":["DEFAULT_GTM_HOST","DEFAULT_DATA_LAYER_NAME","CONSENT_COMMAND","CONSENT_DEFAULT","CONSENT_UPDATE","CONSENT_KEYS","isConsentKey","value","isConsentDecision","assertValidRegions","regions","region","assertValidWaitForUpdate","waitForUpdate","normalizeConsentState","state","normalizedEntries","key","normalizedState","normalizeOptions","options","payload","buildConsentCommand","command","normalizedOptions","createConsentCommandValue","input","createConsentDefaultsCommand","createConsentUpdateCommand","consent","consentPresets","getConsentPreset","name","eeaDefault","allGranted","analyticsOnly","isArray","ensureDataLayer","globalScope","existing","snapshot","clone","pushToDataLayer","levels","noop","createLogger","logger","safeLogger","level","provided","isString","normalizeContainer","normalizeContainers","containers","toRecord","params","acc","normalizeHost","host","buildUrl","kind","containerId","queryParams","dataLayerName","normalizedHost","searchParams","buildGtmScriptUrl","buildGtmNoscriptUrl","escapeAttributeValue","CONTAINER_ATTR","INSTANCE_ATTR","createDeferred","resolved","resolver","promise","resolve","findExistingScript","attrSelector","existingWithAttr","script","formatErrorMessage","event","_a","_b","ScriptManager","callback","container","inserted","targetParent","url","attributes","stringValue","settle","status","isPlainObject","prototype","serializeUnknown","parts","entry","serialized","keys","serializeDataLayerValue","isConsentCommandValue","isStartEvent","instanceCounter","GtmClientImpl","instanceId","immediate","startEvent","signature","firstNonConsentIndex","queued","existingSignature","isConsentCommand","seenInSnapshot","alreadyDeliveredConsent","createGtmClient","isRecord","clonePayload","pushEvent","client","pushEcommerce","ecommerce","extras","DEFAULT_IFRAME_ATTRIBUTES","buildAttributeString","entries","buildNoscriptForContainer","src","iframeAttributes","attributeString","attrs","createNoscriptMarkup","normalizedContainers","DEFAULT_NOSCRIPT_IFRAME_ATTRIBUTES","escapeJsString","installAutoQueue","pollInterval","timeout","maxBufferSize","onReplay","onTimeout","createNoopState","dataLayer","buffer","originalPush","active","gtmReady","pollTimer","timeoutTimer","isGtmLoaded","replay","count","uninstall","interceptedPush","args","createAutoQueueScript","attachToInlineBuffer","inlineBuffer"],"mappings":"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","sourcesContent":["export const DEFAULT_GTM_HOST = 'https://www.googletagmanager.com';\nexport const DEFAULT_DATA_LAYER_NAME = 'dataLayer';\n","import type { DataLayerValue } from './types';\n\nconst CONSENT_COMMAND = 'consent' as const;\nconst CONSENT_DEFAULT = 'default' as const;\nconst CONSENT_UPDATE = 'update' as const;\n\n/**\n * The four consent categories tracked by Google Consent Mode v2.\n */\nconst CONSENT_KEYS = ['ad_storage', 'analytics_storage', 'ad_user_data', 'ad_personalization'] as const;\n\n/**\n * A consent category key: 'ad_storage' | 'analytics_storage' | 'ad_user_data' | 'ad_personalization'\n */\nexport type ConsentKey = (typeof CONSENT_KEYS)[number];\n\n/**\n * A consent decision: 'granted' or 'denied'\n */\nexport type ConsentDecision = 'granted' | 'denied';\n\n/**\n * Consent state object for one or more categories.\n *\n * This is a **partial** record - you only need to specify the categories you want to set.\n * Unspecified categories retain their previous state when using `updateConsent()`.\n *\n * @example\n * ```ts\n * // All four categories\n * const fullState: ConsentState = {\n * ad_storage: 'granted',\n * analytics_storage: 'granted',\n * ad_user_data: 'granted',\n * ad_personalization: 'granted'\n * };\n *\n * // Single category (partial update)\n * const partialState: ConsentState = {\n * analytics_storage: 'granted'\n * };\n *\n * // Multiple specific categories\n * const mixedState: ConsentState = {\n * analytics_storage: 'granted',\n * ad_storage: 'denied'\n * };\n * ```\n */\nexport type ConsentState = Partial<Record<ConsentKey, ConsentDecision>>;\n\nexport interface ConsentRegionOptions {\n /**\n * ISO 3166-2 region codes (e.g., `US-CA`, `EEA`) that the consent command applies to.\n */\n region?: readonly string[];\n /**\n * Milliseconds to wait for an explicit update before firing tags when using the default command.\n */\n waitForUpdate?: number;\n}\n\nexport type ConsentCommand = typeof CONSENT_DEFAULT | typeof CONSENT_UPDATE;\n\nexport interface ConsentCommandInput {\n command: ConsentCommand;\n state: ConsentState;\n options?: ConsentRegionOptions;\n}\n\nexport type ConsentCommandValue =\n | [typeof CONSENT_COMMAND, ConsentCommand, ConsentState]\n | [typeof CONSENT_COMMAND, ConsentCommand, ConsentState, Record<string, unknown>];\n\nconst isConsentKey = (value: string): value is ConsentKey => (CONSENT_KEYS as readonly string[]).includes(value);\n\nconst isConsentDecision = (value: unknown): value is ConsentDecision => value === 'granted' || value === 'denied';\n\nconst assertValidRegions = (regions: readonly string[]) => {\n if (!Array.isArray(regions)) {\n throw new Error('Consent region list must be an array of ISO region codes.');\n }\n\n for (const region of regions) {\n if (typeof region !== 'string' || region.trim().length === 0) {\n throw new Error('Consent region codes must be non-empty strings.');\n }\n }\n};\n\nconst assertValidWaitForUpdate = (waitForUpdate: number) => {\n if (!Number.isFinite(waitForUpdate) || waitForUpdate < 0) {\n throw new Error('waitForUpdate must be a non-negative finite number.');\n }\n};\n\nexport const normalizeConsentState = (state: ConsentState): ConsentState => {\n const normalizedEntries = Object.entries(state ?? {}).map(([key, value]) => {\n if (!isConsentKey(key)) {\n throw new Error(`Invalid consent key: ${key}`);\n }\n\n if (!isConsentDecision(value)) {\n throw new Error(`Invalid consent value for key \"${key}\". Expected \"granted\" or \"denied\".`);\n }\n\n return [key, value] as const;\n });\n\n if (!normalizedEntries.length) {\n throw new Error('At least one consent key/value pair is required.');\n }\n\n const normalizedState = {} as ConsentState;\n for (const [key, value] of normalizedEntries) {\n normalizedState[key as ConsentKey] = value;\n }\n\n return Object.freeze(normalizedState);\n};\n\nconst normalizeOptions = (options?: ConsentRegionOptions): Record<string, unknown> | undefined => {\n if (!options) {\n return undefined;\n }\n\n const payload: Record<string, unknown> = {};\n\n if (options.region) {\n assertValidRegions(options.region);\n if (options.region.length) {\n payload.region = [...options.region];\n }\n }\n\n if (typeof options.waitForUpdate === 'number') {\n assertValidWaitForUpdate(options.waitForUpdate);\n payload.wait_for_update = options.waitForUpdate;\n }\n\n return Object.keys(payload).length ? payload : undefined;\n};\n\nexport const buildConsentCommand = ({ command, state, options }: ConsentCommandInput): ConsentCommandValue => {\n if (command !== CONSENT_DEFAULT && command !== CONSENT_UPDATE) {\n throw new Error(`Unsupported consent command: ${command}`);\n }\n\n const normalizedState = normalizeConsentState(state);\n const normalizedOptions = normalizeOptions(options);\n\n if (normalizedOptions) {\n return [CONSENT_COMMAND, command, normalizedState, normalizedOptions];\n }\n\n return [CONSENT_COMMAND, command, normalizedState];\n};\n\nexport const createConsentCommandValue = (input: ConsentCommandInput): DataLayerValue => {\n // Spread the tuple to convert it to a plain array for DataLayerValue compatibility\n return [...buildConsentCommand(input)];\n};\n\nexport const createConsentDefaultsCommand = (state: ConsentState, options?: ConsentRegionOptions): DataLayerValue =>\n createConsentCommandValue({ command: CONSENT_DEFAULT, state, options });\n\nexport const createConsentUpdateCommand = (state: ConsentState, options?: ConsentRegionOptions): DataLayerValue =>\n createConsentCommandValue({ command: CONSENT_UPDATE, state, options });\n\nexport const consent = {\n buildConsentCommand,\n createConsentDefaultsCommand,\n createConsentUpdateCommand,\n normalizeConsentState\n};\n","import type { ConsentState } from '../consent';\n\n/**\n * Pre-configured consent state presets for common scenarios.\n *\n * These presets cover the most common consent patterns:\n * - `eeaDefault`: All denied (GDPR compliant default)\n * - `allGranted`: All granted (user accepts everything)\n * - `analyticsOnly`: Mixed state (analytics only, no ads)\n *\n * For custom combinations, pass a partial `ConsentState` object to `updateConsent()`.\n * You only need to specify the categories you want to update.\n *\n * @example\n * ```ts\n * // Use a preset\n * client.updateConsent(consentPresets.allGranted);\n *\n * // Or create a custom state\n * client.updateConsent({\n * analytics_storage: 'granted',\n * ad_storage: 'denied'\n * });\n *\n * // Partial updates (only update specific categories)\n * client.updateConsent({ analytics_storage: 'granted' });\n * ```\n */\nexport const consentPresets = {\n /**\n * All categories denied - GDPR/EEA compliant default.\n *\n * Use as the initial state for regions requiring explicit opt-in consent.\n * Tags will be blocked until the user grants specific permissions.\n *\n * | Category | State |\n * |----------|-------|\n * | ad_storage | denied |\n * | analytics_storage | denied |\n * | ad_user_data | denied |\n * | ad_personalization | denied |\n */\n eeaDefault: Object.freeze({\n ad_storage: 'denied',\n analytics_storage: 'denied',\n ad_user_data: 'denied',\n ad_personalization: 'denied'\n } satisfies ConsentState),\n\n /**\n * All categories granted - user accepts all tracking.\n *\n * Use when the user clicks \"Accept All\" or in regions where consent is implied.\n *\n * | Category | State |\n * |----------|-------|\n * | ad_storage | granted |\n * | analytics_storage | granted |\n * | ad_user_data | granted |\n * | ad_personalization | granted |\n */\n allGranted: Object.freeze({\n ad_storage: 'granted',\n analytics_storage: 'granted',\n ad_user_data: 'granted',\n ad_personalization: 'granted'\n } satisfies ConsentState),\n\n /**\n * Analytics allowed, advertising denied - mixed consent state.\n *\n * Use when the user accepts analytics/statistics but rejects advertising cookies.\n * This is a common \"essential + analytics\" consent pattern.\n *\n * | Category | State |\n * |----------|-------|\n * | ad_storage | denied |\n * | analytics_storage | granted |\n * | ad_user_data | denied |\n * | ad_personalization | denied |\n */\n analyticsOnly: Object.freeze({\n ad_storage: 'denied',\n analytics_storage: 'granted',\n ad_user_data: 'denied',\n ad_personalization: 'denied'\n } satisfies ConsentState)\n} as const;\n\nexport type ConsentPresetName = keyof typeof consentPresets;\n\nexport const getConsentPreset = (name: ConsentPresetName): ConsentState => ({\n ...consentPresets[name]\n});\n\n/**\n * Convenience export for the EEA default consent state.\n * All categories denied - GDPR/EEA compliant default.\n */\nexport const eeaDefault = consentPresets.eeaDefault;\n\n/**\n * Convenience export for the all-granted consent state.\n * All categories granted - user accepts all tracking.\n */\nexport const allGranted = consentPresets.allGranted;\n\n/**\n * Convenience export for the analytics-only consent state.\n * Analytics allowed, advertising denied.\n */\nexport const analyticsOnly = consentPresets.analyticsOnly;\n","import type { DataLayerState, DataLayerValue } from './types';\n\ninterface EnsureDataLayerResult extends DataLayerState {\n snapshot: DataLayerValue[] | undefined;\n}\n\nconst isArray = (value: unknown): value is DataLayerValue[] => Array.isArray(value);\n\nexport const ensureDataLayer = (name: string): EnsureDataLayerResult => {\n const globalScope = globalThis as Record<string, unknown>;\n const existing = globalScope[name];\n const snapshot = isArray(existing) ? [...existing] : undefined;\n\n if (!isArray(existing)) {\n globalScope[name] = [] as DataLayerValue[];\n }\n\n return {\n name,\n dataLayer: globalScope[name] as DataLayerValue[],\n created: !isArray(existing),\n restore() {\n if (!isArray(existing)) {\n delete globalScope[name];\n return;\n }\n\n const clone = snapshot ? [...snapshot] : [];\n globalScope[name] = clone;\n },\n snapshot\n };\n};\n\nexport const pushToDataLayer = (state: DataLayerState, value: DataLayerValue) => {\n state.dataLayer.push(value);\n};\n","import type { Logger, PartialLogger } from './types';\n\ntype LogLevel = keyof Logger;\n\nconst levels: LogLevel[] = ['debug', 'info', 'warn', 'error'];\n\nconst noop = () => {\n /* intentionally empty */\n};\n\nexport const createLogger = (logger?: PartialLogger): Logger => {\n const safeLogger: Partial<Record<LogLevel, (message: string, details?: Record<string, unknown>) => void>> = {};\n\n for (const level of levels) {\n const provided = logger?.[level];\n if (typeof provided === 'function') {\n safeLogger[level] = provided.bind(logger);\n continue;\n }\n safeLogger[level] = noop;\n }\n\n return safeLogger as Logger;\n};\n\nexport type LoggerFacade = ReturnType<typeof createLogger>;\n","import { DEFAULT_DATA_LAYER_NAME, DEFAULT_GTM_HOST } from './constants';\nimport type { ContainerConfigInput, ContainerDescriptor } from './types';\n\n/**\n * Type guard to check if a value is a string.\n */\nexport const isString = (value: unknown): value is string => typeof value === 'string';\n\n/**\n * Normalize a container input to a ContainerDescriptor.\n */\nexport const normalizeContainer = (input: ContainerConfigInput): ContainerDescriptor => {\n if (isString(input)) {\n return { id: input };\n }\n return input;\n};\n\n/**\n * Normalize container inputs to an array of ContainerDescriptors.\n */\nexport const normalizeContainers = (\n containers: ContainerConfigInput | ContainerConfigInput[]\n): ContainerDescriptor[] => {\n if (Array.isArray(containers)) {\n return containers.map(normalizeContainer);\n }\n return [normalizeContainer(containers)];\n};\n\n/**\n * Convert query params to a Record<string, string>.\n */\nexport const toRecord = (params?: Record<string, string | number | boolean>): Record<string, string> => {\n if (!params) {\n return {};\n }\n return Object.entries(params).reduce<Record<string, string>>((acc, [key, value]) => {\n acc[key] = String(value);\n return acc;\n }, {});\n};\n\n/**\n * Normalize a host URL by removing trailing slashes.\n */\nexport const normalizeHost = (host: string): string => (host.endsWith('/') ? host.slice(0, -1) : host);\n\ntype UrlKind = 'gtm' | 'ns';\n\nconst buildUrl = (\n kind: UrlKind,\n host: string,\n containerId: string,\n queryParams?: Record<string, string | number | boolean>,\n dataLayerName: string = DEFAULT_DATA_LAYER_NAME\n): string => {\n const normalizedHost = normalizeHost(host);\n const searchParams = new URLSearchParams({ id: containerId });\n\n const params = toRecord(queryParams);\n\n // Add dataLayer name parameter if using custom name\n if (dataLayerName !== DEFAULT_DATA_LAYER_NAME && params.l === undefined) {\n params.l = dataLayerName;\n }\n\n for (const [key, value] of Object.entries(params)) {\n if (key === 'id') {\n continue;\n }\n searchParams.set(key, value);\n }\n\n const suffix = kind === 'gtm' ? 'gtm.js' : 'ns.html';\n return `${normalizedHost}/${suffix}?${searchParams.toString()}`;\n};\n\n/**\n * Build a GTM script URL.\n */\nexport const buildGtmScriptUrl = (\n host: string,\n containerId: string,\n queryParams?: Record<string, string | number | boolean>,\n dataLayerName: string = DEFAULT_DATA_LAYER_NAME\n): string => buildUrl('gtm', host, containerId, queryParams, dataLayerName);\n\n/**\n * Build a GTM noscript iframe URL.\n */\nexport const buildGtmNoscriptUrl = (\n host: string,\n containerId: string,\n queryParams?: Record<string, string | number | boolean>,\n dataLayerName: string = DEFAULT_DATA_LAYER_NAME\n): string => buildUrl('ns', host, containerId, queryParams, dataLayerName);\n\n/**\n * Escape a string for use in HTML attributes.\n */\nexport const escapeAttributeValue = (value: string): string =>\n value.replace(/&/g, '&amp;').replace(/\"/g, '&quot;').replace(/</g, '&lt;').replace(/>/g, '&gt;');\n\n// Re-export default host for convenience\nexport { DEFAULT_GTM_HOST };\n","import { DEFAULT_DATA_LAYER_NAME, DEFAULT_GTM_HOST } from './constants';\nimport { createLogger } from './logger';\nimport type {\n ContainerDescriptor,\n CreateGtmClientOptions,\n ScriptAttributes,\n ScriptLoadState,\n ScriptLoadStatus\n} from './types';\nimport { buildGtmScriptUrl } from './url-utils';\n\nconst CONTAINER_ATTR = 'data-gtm-container-id';\nconst INSTANCE_ATTR = 'data-gtm-kit-instance';\n\ninterface Deferred<T> {\n promise: Promise<T>;\n resolve: (value: T) => void;\n settled: boolean;\n}\n\nconst createDeferred = <T>(): Deferred<T> => {\n let resolved = false;\n let resolver: (value: T) => void;\n\n const promise = new Promise<T>((resolve) => {\n resolver = resolve;\n });\n\n return {\n get settled() {\n return resolved;\n },\n promise,\n resolve: (value: T) => {\n if (resolved) {\n return;\n }\n\n resolved = true;\n resolver(value);\n }\n } as Deferred<T>;\n};\n\nexport interface NormalizedContainer extends ContainerDescriptor {\n queryParams?: Record<string, string | number | boolean>;\n}\n\nexport interface ScriptManagerOptions {\n instanceId: string;\n host?: string;\n dataLayerName?: string;\n scriptAttributes?: ScriptAttributes;\n defaultQueryParams?: Record<string, string | number | boolean>;\n logger?: CreateGtmClientOptions['logger'];\n}\n\nexport interface EnsureResult {\n inserted: HTMLScriptElement[];\n}\n\nconst findExistingScript = (containerId: string): HTMLScriptElement | null => {\n if (typeof document === 'undefined') {\n return null;\n }\n\n const attrSelector = `script[${CONTAINER_ATTR}=\"${containerId}\"]`;\n const existingWithAttr = document.querySelector(attrSelector);\n if (existingWithAttr) {\n return existingWithAttr as HTMLScriptElement;\n }\n\n const scripts = Array.from(document.getElementsByTagName('script'));\n return scripts.find((script) => script.src.includes(`id=${encodeURIComponent(containerId)}`)) || null;\n};\n\nconst formatErrorMessage = (event: Event): string => {\n if (event instanceof ErrorEvent) {\n if (event.error) {\n return String(event.error);\n }\n\n if (event.message) {\n return event.message;\n }\n }\n\n return 'Failed to load GTM script.';\n};\n\nexport class ScriptManager {\n private readonly logger = createLogger(this.options.logger);\n private readonly host = this.options.host ?? DEFAULT_GTM_HOST;\n private readonly dataLayerName = this.options.dataLayerName ?? DEFAULT_DATA_LAYER_NAME;\n private readonly defaultQueryParams = this.options.defaultQueryParams;\n private readonly scriptAttributes = this.options.scriptAttributes;\n private readonly insertedScripts = new Set<HTMLScriptElement>();\n private readonly readyCallbacks = new Set<(state: ScriptLoadState[]) => void>();\n private readiness = createDeferred<ScriptLoadState[]>();\n private readonly loadStates = new Map<string, ScriptLoadState>();\n private readonly pendingContainers = new Set<string>();\n\n constructor(private readonly options: ScriptManagerOptions) {}\n\n whenReady(): Promise<ScriptLoadState[]> {\n return this.readiness.promise;\n }\n\n onReady(callback: (state: ScriptLoadState[]) => void): () => void {\n this.readyCallbacks.add(callback);\n\n if (this.readiness.settled) {\n callback(Array.from(this.loadStates.values()));\n }\n\n return () => {\n this.readyCallbacks.delete(callback);\n };\n }\n\n private notifyReady(): void {\n const snapshot = Array.from(this.loadStates.values());\n\n if (!this.readiness.settled) {\n this.readiness.resolve(snapshot);\n }\n\n for (const callback of this.readyCallbacks) {\n callback(snapshot);\n }\n }\n\n private maybeNotifyReady(): void {\n if (this.pendingContainers.size === 0) {\n this.notifyReady();\n }\n }\n\n private recordState(state: ScriptLoadState): void {\n this.loadStates.set(state.containerId, state);\n }\n\n private resetReadiness(): void {\n this.pendingContainers.clear();\n this.loadStates.clear();\n this.readiness = createDeferred<ScriptLoadState[]>();\n }\n\n ensure(containers: NormalizedContainer[]): EnsureResult {\n if (typeof document === 'undefined') {\n this.logger.warn('No document available – skipping script injection.');\n\n for (const container of containers) {\n if (!container.id) {\n continue;\n }\n\n this.recordState({\n containerId: container.id,\n status: 'skipped',\n error: 'Document unavailable for script injection.'\n });\n }\n\n this.maybeNotifyReady();\n return { inserted: [] };\n }\n\n const inserted: HTMLScriptElement[] = [];\n const targetParent = document.head || document.body;\n if (!targetParent) {\n this.logger.error('Unable to find document.head or document.body for script injection.');\n\n for (const container of containers) {\n if (!container.id) {\n continue;\n }\n\n this.recordState({\n containerId: container.id,\n status: 'skipped',\n error: 'Missing document.head and document.body for GTM script injection.'\n });\n }\n\n this.maybeNotifyReady();\n return { inserted: [] };\n }\n\n for (const container of containers) {\n if (!container.id) {\n this.logger.warn('Skipping container with missing id.', { container });\n continue;\n }\n\n const existing = findExistingScript(container.id);\n if (existing) {\n this.logger.debug('Container script already present, skipping injection.', {\n containerId: container.id\n });\n\n this.recordState({\n containerId: container.id,\n src: existing.src,\n status: 'loaded',\n fromCache: true\n });\n continue;\n }\n\n const params = {\n ...this.defaultQueryParams,\n ...container.queryParams\n };\n\n const script = document.createElement('script');\n const url = buildGtmScriptUrl(this.host, container.id, params, this.dataLayerName);\n script.src = url;\n script.setAttribute(CONTAINER_ATTR, container.id);\n script.setAttribute(INSTANCE_ATTR, this.options.instanceId);\n\n const attributes = this.scriptAttributes ?? {};\n if (attributes.async !== undefined) {\n script.async = attributes.async;\n } else {\n script.async = true;\n }\n if (attributes.defer !== undefined) {\n script.defer = attributes.defer;\n }\n\n for (const [key, value] of Object.entries(attributes)) {\n if (key === 'async' || key === 'defer') {\n continue;\n }\n\n if (value === undefined || value === null) {\n continue;\n }\n\n const stringValue = String(value);\n\n if (key === 'nonce') {\n script.nonce = stringValue;\n }\n\n script.setAttribute(key, stringValue);\n }\n\n this.pendingContainers.add(container.id);\n\n const settle = (status: ScriptLoadStatus, event?: Event): void => {\n if (!this.pendingContainers.has(container.id)) {\n return;\n }\n\n this.pendingContainers.delete(container.id);\n\n const state: ScriptLoadState = {\n containerId: container.id,\n src: url,\n status,\n fromCache: false\n };\n\n if (status === 'failed' && event) {\n state.error = formatErrorMessage(event);\n this.logger.error('Failed to load GTM container script.', {\n containerId: container.id,\n src: url,\n error: state.error\n });\n }\n\n this.recordState(state);\n this.maybeNotifyReady();\n };\n\n script.addEventListener('load', () => settle('loaded'));\n script.addEventListener('error', (event) => settle('failed', event));\n\n targetParent.appendChild(script);\n this.insertedScripts.add(script);\n inserted.push(script);\n this.logger.info('Injected GTM container script.', { containerId: container.id, src: url });\n }\n\n this.maybeNotifyReady();\n return { inserted };\n }\n\n teardown() {\n if (typeof document === 'undefined') {\n return;\n }\n\n for (const script of this.insertedScripts) {\n if (script.parentNode) {\n script.parentNode.removeChild(script);\n }\n }\n\n this.insertedScripts.clear();\n this.resetReadiness();\n }\n}\n","import { DEFAULT_DATA_LAYER_NAME } from './constants';\nimport { ensureDataLayer, pushToDataLayer } from './data-layer';\nimport { createConsentCommandValue } from './consent';\nimport type { ConsentRegionOptions, ConsentState } from './consent';\nimport { createLogger } from './logger';\nimport { ScriptManager } from './script-manager';\nimport type {\n ContainerConfigInput,\n ContainerDescriptor,\n CreateGtmClientOptions,\n DataLayerValue,\n GtmClient,\n ScriptLoadState\n} from './types';\n\nconst isString = (value: unknown): value is string => typeof value === 'string';\n\nconst normalizeContainer = (input: ContainerConfigInput): ContainerDescriptor => {\n if (isString(input)) {\n return { id: input };\n }\n\n return input;\n};\n\nconst isPlainObject = (value: unknown): value is Record<string, unknown> => {\n if (typeof value !== 'object' || value === null) {\n return false;\n }\n\n const prototype = Object.getPrototypeOf(value);\n return prototype === Object.prototype || prototype === null;\n};\n\nconst serializeUnknown = (value: unknown): string | null => {\n if (value === null || typeof value === 'boolean' || typeof value === 'number') {\n return JSON.stringify(value);\n }\n\n if (typeof value === 'string') {\n return JSON.stringify(value);\n }\n\n if (Array.isArray(value)) {\n const parts: string[] = [];\n for (const entry of value) {\n const serialized = serializeUnknown(entry);\n if (serialized === null) {\n return null;\n }\n parts.push(serialized);\n }\n return `[${parts.join(',')}]`;\n }\n\n if (isPlainObject(value)) {\n const keys = Object.keys(value).sort();\n const parts: string[] = [];\n for (const key of keys) {\n const serialized = serializeUnknown((value as Record<string, unknown>)[key]);\n if (serialized === null) {\n return null;\n }\n parts.push(`${JSON.stringify(key)}:${serialized}`);\n }\n return `{${parts.join(',')}}`;\n }\n\n return null;\n};\n\nconst serializeDataLayerValue = (value: DataLayerValue): string | null => {\n if (Array.isArray(value)) {\n return serializeUnknown(value);\n }\n\n if (isPlainObject(value)) {\n return serializeUnknown(value);\n }\n\n return null;\n};\n\nconst isConsentCommandValue = (value: DataLayerValue): value is unknown[] =>\n Array.isArray(value) &&\n value.length >= 3 &&\n value[0] === 'consent' &&\n (value[1] === 'default' || value[1] === 'update');\n\nconst isStartEvent = (value: DataLayerValue): boolean => {\n if (!isPlainObject(value)) {\n return false;\n }\n\n return (value.event as unknown) === 'gtm.js';\n};\n\ninterface QueuedEntry {\n value: DataLayerValue;\n signature: string | null;\n}\n\nlet instanceCounter = 0;\n\nexport class GtmClientImpl implements GtmClient {\n private readonly logger = createLogger(this.options.logger);\n private readonly resolvedDataLayerName = this.options.dataLayerName ?? DEFAULT_DATA_LAYER_NAME;\n private readonly containers = Array.isArray(this.options.containers)\n ? this.options.containers.map(normalizeContainer)\n : [normalizeContainer(this.options.containers)];\n private readonly queue: QueuedEntry[] = [];\n private readonly queuedConsentSignatures = new Set<string>();\n private readonly deliveredConsentSignatures = new Set<string>();\n private snapshotSignatures: Set<string> | null = null;\n private readonly scriptManager = new ScriptManager({\n instanceId: this.instanceId,\n host: this.options.host,\n dataLayerName: this.resolvedDataLayerName,\n defaultQueryParams: this.options.defaultQueryParams,\n scriptAttributes: this.options.scriptAttributes,\n logger: this.options.logger\n });\n private dataLayerState: ReturnType<typeof ensureDataLayer> | null = null;\n private initialized = false;\n private readonly startTimestamp = Date.now();\n\n constructor(\n private readonly options: CreateGtmClientOptions,\n private readonly instanceId: string\n ) {\n if (!this.containers.length) {\n throw new Error('At least one GTM container ID is required to initialize the client.');\n }\n }\n\n init(): void {\n if (this.initialized) {\n this.logger.debug('GTM client already initialized; skipping.');\n return;\n }\n\n this.logger.info('Initializing GTM client.', {\n containers: this.containers.map((container) => container.id),\n dataLayerName: this.resolvedDataLayerName\n });\n\n this.dataLayerState = ensureDataLayer(this.resolvedDataLayerName);\n this.captureSnapshotSignatures();\n this.pushStartEvent();\n this.flushQueue();\n this.scriptManager.ensure(this.containers);\n\n this.initialized = true;\n }\n\n push(value: DataLayerValue): void {\n if (value === undefined || value === null) {\n this.logger.warn('Ignoring falsy dataLayer push.', { value });\n return;\n }\n\n const immediate = this.deliverToDataLayer(value);\n\n if (immediate) {\n this.logger.debug('Pushed value to dataLayer.', { immediate: true });\n } else {\n this.logger.debug('Queued dataLayer value (pre-init).', { queueLength: this.queue.length });\n }\n }\n\n setConsentDefaults(state: ConsentState, options?: ConsentRegionOptions): void {\n const value = createConsentCommandValue({ command: 'default', state, options });\n const immediate = this.deliverToDataLayer(value);\n\n this.logger.info('Applied consent defaults.', {\n immediate,\n state,\n options\n });\n }\n\n updateConsent(state: ConsentState, options?: ConsentRegionOptions): void {\n const value = createConsentCommandValue({ command: 'update', state, options });\n const immediate = this.deliverToDataLayer(value);\n\n this.logger.info('Updated consent state.', {\n immediate,\n state,\n options\n });\n }\n\n teardown(): void {\n this.logger.info('Tearing down GTM client instance.', { dataLayerName: this.resolvedDataLayerName });\n this.scriptManager.teardown();\n if (this.dataLayerState) {\n this.dataLayerState.restore();\n }\n this.queue.length = 0;\n this.queuedConsentSignatures.clear();\n this.deliveredConsentSignatures.clear();\n this.snapshotSignatures = null;\n this.initialized = false;\n this.dataLayerState = null;\n }\n\n isInitialized(): boolean {\n return this.initialized;\n }\n\n whenReady(): Promise<ScriptLoadState[]> {\n return this.scriptManager.whenReady();\n }\n\n onReady(callback: (state: ScriptLoadState[]) => void): () => void {\n return this.scriptManager.onReady(callback);\n }\n\n get dataLayerName(): string {\n return this.resolvedDataLayerName;\n }\n\n private flushQueue(): void {\n if (!this.dataLayerState) {\n return;\n }\n\n while (this.queue.length) {\n const entry = this.queue.shift();\n if (!entry) {\n continue;\n }\n\n this.pushValueToDataLayer(entry.value, entry.signature);\n\n if (entry.signature) {\n this.queuedConsentSignatures.delete(entry.signature);\n }\n }\n }\n\n private deliverToDataLayer(value: DataLayerValue): boolean {\n if (this.initialized && this.dataLayerState) {\n this.pushValueToDataLayer(value);\n return true;\n }\n\n this.queueValue(value);\n return false;\n }\n\n private pushStartEvent(): void {\n if (!this.dataLayerState) {\n return;\n }\n\n if (this.hasExistingStartEvent()) {\n this.logger.debug('Detected existing gtm.js event; skipping duplicate start push.');\n return;\n }\n\n const startEvent = { 'gtm.start': this.startTimestamp, event: 'gtm.js' } as const;\n this.pushValueToDataLayer(startEvent);\n }\n\n private captureSnapshotSignatures(): void {\n if (!this.dataLayerState) {\n return;\n }\n\n const snapshot = this.dataLayerState.snapshot ?? [];\n this.snapshotSignatures = new Set<string>();\n\n for (const value of snapshot) {\n const signature = serializeDataLayerValue(value);\n if (signature) {\n this.snapshotSignatures.add(signature);\n if (isConsentCommandValue(value)) {\n this.deliveredConsentSignatures.add(signature);\n }\n }\n }\n }\n\n private queueValue(value: DataLayerValue): void {\n const signature = isConsentCommandValue(value) ? serializeDataLayerValue(value) : null;\n\n if (signature && this.queuedConsentSignatures.has(signature)) {\n this.logger.debug('Skipping duplicate queued dataLayer value.', { value });\n return;\n }\n\n const entry: QueuedEntry = { value, signature };\n\n if (isConsentCommandValue(value)) {\n const firstNonConsentIndex = this.queue.findIndex((queued) => !isConsentCommandValue(queued.value));\n\n if (firstNonConsentIndex === -1) {\n this.queue.push(entry);\n } else {\n this.queue.splice(firstNonConsentIndex, 0, entry);\n }\n } else {\n this.queue.push(entry);\n }\n\n if (signature) {\n this.queuedConsentSignatures.add(signature);\n }\n }\n\n private pushValueToDataLayer(value: DataLayerValue, existingSignature?: string | null): void {\n if (!this.dataLayerState) {\n return;\n }\n\n const signature = existingSignature ?? serializeDataLayerValue(value);\n const isConsentCommand = isConsentCommandValue(value);\n const seenInSnapshot = signature ? this.snapshotSignatures?.has(signature) : false;\n const alreadyDeliveredConsent =\n isConsentCommand && signature ? this.deliveredConsentSignatures.has(signature) : false;\n\n if (signature && seenInSnapshot) {\n this.logger.debug('Skipping duplicate dataLayer value detected during hydration.', { value });\n if (isConsentCommand) {\n this.deliveredConsentSignatures.add(signature);\n }\n return;\n }\n\n if (isConsentCommand && alreadyDeliveredConsent) {\n this.logger.debug('Skipping duplicate consent command.', { value });\n return;\n }\n\n pushToDataLayer(this.dataLayerState, value);\n\n if (isConsentCommand && signature) {\n this.deliveredConsentSignatures.add(signature);\n }\n }\n\n private hasExistingStartEvent(): boolean {\n if (!this.dataLayerState) {\n return false;\n }\n\n const snapshot = this.dataLayerState.snapshot ?? [];\n if (snapshot.some(isStartEvent)) {\n return true;\n }\n\n return this.dataLayerState.dataLayer.some(isStartEvent);\n }\n}\n\nexport const createGtmClient = (options: CreateGtmClientOptions): GtmClient => {\n const instanceId = `gtm-kit-${++instanceCounter}`;\n return new GtmClientImpl(options, instanceId);\n};\n","import type { GtmClient } from '../types';\nimport type {\n EcommerceEvent,\n EcommerceEventName,\n EcommercePayload,\n EventForName,\n EventPayload,\n GtmEvent\n} from './types';\n\nconst isRecord = (value: unknown): value is Record<string, unknown> => typeof value === 'object' && value !== null;\n\nconst clonePayload = <TPayload extends EventPayload | undefined>(payload: TPayload): TPayload => {\n if (!payload) {\n return payload;\n }\n\n return { ...payload } as TPayload;\n};\n\nexport const pushEvent = <TName extends string, TPayload extends EventPayload = EventPayload>(\n client: Pick<GtmClient, 'push'>,\n name: TName,\n payload?: TPayload\n): EventForName<TName> => {\n if (!name) {\n throw new Error('An event name is required when pushing to the dataLayer.');\n }\n\n if (payload !== undefined && !isRecord(payload)) {\n throw new Error('Event payloads must be plain objects when pushing to the dataLayer.');\n }\n\n const event = {\n event: name,\n ...(clonePayload(payload) ?? {})\n } as GtmEvent<TName, TPayload>;\n\n client.push(event);\n return event as EventForName<TName>;\n};\n\nexport interface PushEcommerceOptions<TExtras extends EventPayload = EventPayload> {\n extras?: TExtras;\n}\n\nexport const pushEcommerce = <TName extends EcommerceEventName, TExtras extends EventPayload = EventPayload>(\n client: Pick<GtmClient, 'push'>,\n name: TName,\n ecommerce: EcommercePayload,\n options?: PushEcommerceOptions<TExtras>\n): EcommerceEvent<TName, TExtras> => {\n if (!isRecord(ecommerce)) {\n throw new Error('Ecommerce payload must be an object.');\n }\n\n const extras = options?.extras ?? {};\n\n if (!isRecord(extras)) {\n throw new Error('Ecommerce extras must be an object when provided.');\n }\n\n const payload = { ...extras, ecommerce } as { ecommerce: EcommercePayload } & TExtras;\n return pushEvent(client, name, payload) as EcommerceEvent<TName, TExtras>;\n};\n","import type { ContainerConfigInput, ContainerDescriptor } from './types';\nimport { DEFAULT_GTM_HOST } from './constants';\nimport { normalizeContainer, buildGtmNoscriptUrl, escapeAttributeValue } from './url-utils';\n\nconst DEFAULT_IFRAME_ATTRIBUTES: Record<string, string> = {\n height: '0',\n width: '0',\n style: 'display:none;visibility:hidden',\n title: 'Google Tag Manager'\n};\n\nconst buildAttributeString = (attributes: Record<string, string | number | boolean> | undefined): string => {\n if (!attributes) {\n return '';\n }\n\n const entries = Object.entries(attributes);\n if (!entries.length) {\n return '';\n }\n\n return entries.map(([key, value]) => `${key}=\"${escapeAttributeValue(String(value))}\"`).join(' ');\n};\n\nexport interface NoscriptOptions {\n host?: string;\n defaultQueryParams?: Record<string, string | number | boolean>;\n iframeAttributes?: Record<string, string | number | boolean>;\n}\n\nconst buildNoscriptForContainer = (container: ContainerDescriptor, options: NoscriptOptions): string => {\n if (!container.id) {\n throw new Error('Container id is required to build noscript markup.');\n }\n\n const host = options.host ?? DEFAULT_GTM_HOST;\n const params = {\n ...options.defaultQueryParams,\n ...container.queryParams\n };\n\n const src = buildGtmNoscriptUrl(host, container.id, params);\n const iframeAttributes = {\n ...DEFAULT_IFRAME_ATTRIBUTES,\n ...options.iframeAttributes\n };\n const attributeString = buildAttributeString(iframeAttributes);\n\n const attrs = attributeString ? ` ${attributeString}` : '';\n return `<noscript><iframe src=\"${escapeAttributeValue(src)}\"${attrs}></iframe></noscript>`;\n};\n\nexport const createNoscriptMarkup = (\n containers: ContainerConfigInput[] | ContainerConfigInput,\n options: NoscriptOptions = {}\n): string => {\n const normalizedContainers = Array.isArray(containers)\n ? containers.map(normalizeContainer)\n : [normalizeContainer(containers)];\n\n if (!normalizedContainers.length) {\n throw new Error('At least one container is required to build noscript markup.');\n }\n\n return normalizedContainers.map((container) => buildNoscriptForContainer(container, options)).join('');\n};\n\nexport const DEFAULT_NOSCRIPT_IFRAME_ATTRIBUTES = { ...DEFAULT_IFRAME_ATTRIBUTES };\n","/**\n * Auto-queue: Automatic dataLayer buffering for race condition elimination.\n *\n * This module provides automatic buffering of dataLayer pushes that occur before\n * GTM.js loads. Events are captured, stored in order, and replayed once GTM is ready.\n *\n * @example\n * ```ts\n * // Call as early as possible (ideally inline in <head>)\n * import { installAutoQueue } from '@jwiedeman/gtm-kit';\n *\n * installAutoQueue(); // Start buffering immediately\n *\n * // Later, events pushed before GTM loads are automatically queued\n * window.dataLayer.push({ event: 'early_event' }); // Buffered!\n *\n * // When GTM loads, all buffered events replay in order\n * ```\n *\n * @example\n * ```html\n * <!-- Inline script for earliest possible buffering -->\n * <script>\n * // Minimal inline version for <head>\n * (function(w,d,n){\n * w[n]=w[n]||[];var q=[],o=w[n].push.bind(w[n]);\n * w[n].push=function(){q.push(arguments);return o.apply(this,arguments)};\n * w.__gtmkit_buffer=q;\n * })(window,document,'dataLayer');\n * </script>\n * ```\n */\n\nimport { DEFAULT_DATA_LAYER_NAME } from './constants';\nimport type { DataLayerValue } from './types';\n\n/**\n * Escape a string for safe use in JavaScript string literals.\n * Prevents XSS when interpolating values into inline scripts.\n */\nconst escapeJsString = (value: string): string =>\n value\n .replace(/\\\\/g, '\\\\\\\\')\n .replace(/'/g, \"\\\\'\")\n .replace(/\"/g, '\\\\\"')\n .replace(/\\n/g, '\\\\n')\n .replace(/\\r/g, '\\\\r')\n .replace(/</g, '\\\\x3c')\n .replace(/>/g, '\\\\x3e')\n .replace(/\\u2028/g, '\\\\u2028')\n .replace(/\\u2029/g, '\\\\u2029');\n\n/** Options for configuring the auto-queue behavior */\nexport interface AutoQueueOptions {\n /**\n * Name of the dataLayer array. Defaults to 'dataLayer'.\n */\n dataLayerName?: string;\n\n /**\n * Interval in milliseconds to check if GTM has loaded.\n * Lower values = faster detection, higher CPU usage.\n * @default 50\n */\n pollInterval?: number;\n\n /**\n * Maximum time in milliseconds to wait for GTM before giving up.\n * Set to 0 for unlimited waiting.\n * @default 30000 (30 seconds)\n */\n timeout?: number;\n\n /**\n * Maximum number of events to buffer.\n * Prevents memory issues if GTM never loads.\n * @default 1000\n */\n maxBufferSize?: number;\n\n /**\n * Callback fired when the buffer is replayed.\n */\n onReplay?: (bufferedCount: number) => void;\n\n /**\n * Callback fired if timeout is reached before GTM loads.\n */\n onTimeout?: (bufferedCount: number) => void;\n}\n\n/** State of the auto-queue system */\nexport interface AutoQueueState {\n /** Whether the auto-queue is currently active */\n active: boolean;\n /** Number of events currently buffered */\n bufferedCount: number;\n /** Whether GTM has been detected as ready */\n gtmReady: boolean;\n /** Manually trigger replay (useful for testing) */\n replay: () => void;\n /** Uninstall the auto-queue and restore original push */\n uninstall: () => void;\n}\n\ninterface BufferedEntry {\n value: DataLayerValue;\n timestamp: number;\n}\n\n/**\n * Installs automatic dataLayer buffering that captures events before GTM loads.\n *\n * Call this as early as possible in your application lifecycle, ideally before\n * any other scripts that might push to the dataLayer.\n *\n * The auto-queue:\n * 1. Creates the dataLayer if it doesn't exist\n * 2. Intercepts all pushes to capture them in a buffer\n * 3. Detects when GTM.js loads by watching for the 'gtm.js' event\n * 4. Replays all buffered events in order once GTM is ready\n * 5. Removes itself, allowing normal dataLayer operation\n *\n * @param options - Configuration options\n * @returns State object with control methods\n *\n * @example\n * ```ts\n * const queue = installAutoQueue({\n * onReplay: (count) => console.log(`Replayed ${count} buffered events`),\n * onTimeout: (count) => console.warn(`GTM didn't load, ${count} events buffered`)\n * });\n *\n * // Check state\n * console.log(queue.bufferedCount); // Number of events waiting\n *\n * // Manual control (usually not needed)\n * queue.replay(); // Force replay now\n * queue.uninstall(); // Remove the interceptor\n * ```\n */\nexport function installAutoQueue(options: AutoQueueOptions = {}): AutoQueueState {\n const {\n dataLayerName = DEFAULT_DATA_LAYER_NAME,\n pollInterval = 50,\n timeout = 30000,\n maxBufferSize = 1000,\n onReplay,\n onTimeout\n } = options;\n\n // Skip in non-browser environments\n if (typeof globalThis === 'undefined' || typeof globalThis.document === 'undefined') {\n return createNoopState();\n }\n\n const globalScope = globalThis as Record<string, unknown>;\n\n // Create dataLayer if it doesn't exist\n if (!Array.isArray(globalScope[dataLayerName])) {\n globalScope[dataLayerName] = [];\n }\n\n const dataLayer = globalScope[dataLayerName] as DataLayerValue[];\n const buffer: BufferedEntry[] = [];\n const originalPush = dataLayer.push.bind(dataLayer);\n\n let active = true;\n let gtmReady = false;\n let pollTimer: ReturnType<typeof setInterval> | null = null;\n let timeoutTimer: ReturnType<typeof setTimeout> | null = null;\n\n // Check if GTM.js event is present (indicating GTM has loaded)\n const isGtmLoaded = (): boolean => {\n return dataLayer.some(\n (entry) =>\n entry !== null &&\n typeof entry === 'object' &&\n !Array.isArray(entry) &&\n (entry as Record<string, unknown>).event === 'gtm.js'\n );\n };\n\n // Replay all buffered events to the dataLayer\n const replay = (): void => {\n if (!active) return;\n\n active = false;\n gtmReady = true;\n\n // Clear timers\n if (pollTimer) {\n clearInterval(pollTimer);\n pollTimer = null;\n }\n if (timeoutTimer) {\n clearTimeout(timeoutTimer);\n timeoutTimer = null;\n }\n\n // Restore original push\n dataLayer.push = originalPush;\n\n // Replay buffered events in order\n const count = buffer.length;\n for (const entry of buffer) {\n originalPush(entry.value);\n }\n buffer.length = 0;\n\n onReplay?.(count);\n };\n\n // Uninstall without replaying\n const uninstall = (): void => {\n if (!active) return;\n\n active = false;\n\n if (pollTimer) {\n clearInterval(pollTimer);\n pollTimer = null;\n }\n if (timeoutTimer) {\n clearTimeout(timeoutTimer);\n timeoutTimer = null;\n }\n\n dataLayer.push = originalPush;\n buffer.length = 0;\n };\n\n // Create intercepted push function\n const interceptedPush = function (this: DataLayerValue[], ...args: DataLayerValue[]): number {\n for (const value of args) {\n // Always push to actual dataLayer (GTM may already be listening)\n originalPush(value);\n\n // Buffer the value for potential replay\n if (active && buffer.length < maxBufferSize) {\n buffer.push({\n value,\n timestamp: Date.now()\n });\n }\n\n // Check if this push indicates GTM is ready\n if (\n active &&\n value !== null &&\n typeof value === 'object' &&\n !Array.isArray(value) &&\n (value as Record<string, unknown>).event === 'gtm.js'\n ) {\n // GTM just loaded! Trigger replay on next tick to ensure\n // this event is fully processed first\n setTimeout(replay, 0);\n }\n }\n\n return dataLayer.length;\n };\n\n // Install the interceptor\n dataLayer.push = interceptedPush;\n\n // Check if GTM was already loaded before we installed\n if (isGtmLoaded()) {\n // GTM already present, replay immediately\n setTimeout(replay, 0);\n } else {\n // Poll for GTM readiness as backup detection\n pollTimer = setInterval(() => {\n if (isGtmLoaded()) {\n replay();\n }\n }, pollInterval);\n\n // Set timeout if configured\n if (timeout > 0) {\n timeoutTimer = setTimeout(() => {\n if (active) {\n onTimeout?.(buffer.length);\n // Don't uninstall on timeout - keep buffering in case GTM loads late\n }\n }, timeout);\n }\n }\n\n // Return state object\n return {\n get active() {\n return active;\n },\n get bufferedCount() {\n return buffer.length;\n },\n get gtmReady() {\n return gtmReady;\n },\n replay,\n uninstall\n };\n}\n\n/**\n * Creates a minimal inline script for earliest possible buffering.\n *\n * This returns a script that can be embedded directly in the HTML `<head>`\n * before any other scripts. It's a minimal version of installAutoQueue()\n * that captures events until the full GTM Kit is loaded.\n *\n * @param dataLayerName - Name of the dataLayer array\n * @returns Inline script string to embed in HTML\n *\n * @example\n * ```ts\n * // In your SSR template\n * const inlineScript = createAutoQueueScript();\n * // Output: <script>{inlineScript}</script> in <head>\n * ```\n */\nexport function createAutoQueueScript(dataLayerName: string = DEFAULT_DATA_LAYER_NAME): string {\n // Minified inline script that:\n // 1. Creates dataLayer if missing\n // 2. Overrides push to capture events\n // 3. Stores buffer in __gtmkit_buffer for later retrieval\n // SECURITY: Escape the dataLayerName to prevent XSS via malicious input\n const safeName = escapeJsString(dataLayerName);\n return `(function(w,n){w[n]=w[n]||[];var q=[],o=w[n].push.bind(w[n]);w[n].push=function(){for(var i=0;i<arguments.length;i++){q.push({v:arguments[i],t:Date.now()});o(arguments[i])}return w[n].length};w.__gtmkit_buffer={q:q,o:o,n:n}})(window,'${safeName}');`;\n}\n\n/**\n * Attaches to an existing inline buffer created by createAutoQueueScript().\n *\n * If you used the inline script in your HTML head, call this when the full\n * GTM Kit loads to take over buffer management and enable replay.\n *\n * @param options - Configuration options\n * @returns State object, or null if no inline buffer exists\n *\n * @example\n * ```ts\n * // After GTM Kit bundle loads\n * const queue = attachToInlineBuffer({\n * onReplay: (count) => console.log(`Replayed ${count} events`)\n * });\n *\n * if (queue) {\n * console.log(`Taking over ${queue.bufferedCount} buffered events`);\n * }\n * ```\n */\nexport function attachToInlineBuffer(options: Omit<AutoQueueOptions, 'dataLayerName'> = {}): AutoQueueState | null {\n if (typeof globalThis === 'undefined') {\n return null;\n }\n\n const globalScope = globalThis as Record<string, unknown>;\n const inlineBuffer = globalScope.__gtmkit_buffer as\n | {\n q: { v: DataLayerValue; t: number }[];\n o: (...args: DataLayerValue[]) => number;\n n: string;\n }\n | undefined;\n\n if (!inlineBuffer) {\n return null;\n }\n\n const { n: dataLayerName } = inlineBuffer;\n\n // Clean up the global reference\n delete globalScope.__gtmkit_buffer;\n\n // Install full auto-queue with the same dataLayer name\n // The buffer from the inline script is already in the dataLayer,\n // so we just need to continue monitoring from here\n return installAutoQueue({\n ...options,\n dataLayerName\n });\n}\n\n/** Creates a no-op state for SSR environments */\nfunction createNoopState(): AutoQueueState {\n // No-op functions for SSR - these do nothing intentionally\n const noop = (): void => {\n /* no-op for SSR */\n };\n return {\n active: false,\n bufferedCount: 0,\n gtmReady: false,\n replay: noop,\n uninstall: noop\n };\n}\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jwiedeman/gtm-kit",
3
- "version": "1.1.2",
3
+ "version": "1.1.3",
4
4
  "description": "Framework-agnostic Google Tag Manager client - the core of GTM Kit.",
5
5
  "repository": {
6
6
  "type": "git",