@jwiedeman/gtm-kit 1.1.0 → 1.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.cjs +3 -3
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +2 -2
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
var A="https://www.googletagmanager.com",g="dataLayer";var _="consent",k="default",I="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.")},V=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!==k&&e!==I)throw new Error(`Unsupported consent command: ${e}`);let r=V(t),a=se(n);return a?[_,e,r,a]:[_,e,r]},C=e=>[...w(e)],M=(e,t)=>C({command:k,state:e,options:t}),j=(e,t)=>C({command:I,state:e,options:t}),de={buildConsentCommand:w,createConsentDefaultsCommand:M,createConsentUpdateCommand:j,normalizeConsentState:V};var z={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=>({...z[e]});var E=e=>Array.isArray(e),U=e=>{let t=globalThis,n=t[e],r=E(n)?[...n]:void 0;return E(n)||(t[e]=[]),{name:e,dataLayer:t[e],created:!E(n),restore(){if(!E(n)){delete t[e];return}let a=r?[...r]:[];t[e]=a;},snapshot:r}},q=(e,t)=>{e.dataLayer.push(t);};var ce=["debug","info","warn","error"],le=()=>{},L=e=>{let t={};for(let n of ce){let r=e==null?void 0:e[n];if(typeof r=="function"){t[n]=r.bind(e);continue}t[n]=le;}return t};var $="data-gtm-container-id",pe="data-gtm-kit-instance",F=()=>{let e=!1,t,n=new Promise(r=>{t=r;});return {get settled(){return e},promise:n,resolve:r=>{e||(e=!0,t(r));}}},me=e=>e?Object.entries(e).reduce((t,[n,r])=>(t[n]=String(r),t),{}):{},fe=(e,t,n)=>{let r=e.endsWith("/")?e.slice(0,-1):e,a=new URLSearchParams({id:t}),o=me(n);for(let[s,d]of Object.entries(o))s!=="id"&&a.set(s,d);return `${r}/gtm.js?${a.toString()}`},ge=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},ye=e=>{if(e instanceof ErrorEvent){if(e.error)return String(e.error);if(e.message)return e.message}return "Failed to load GTM script."},G,Q,b=class{constructor(t){this.options=t;this.logger=L(this.options.logger);this.host=(G=this.options.host)!=null?G:A;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=F();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=F();}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=ge(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=fe(this.host,o.id,d);i.src=l,i.setAttribute($,o.id),i.setAttribute(pe,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=ye(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 he=e=>typeof e=="string",B=e=>he(e)?{id:e}:e,R=e=>{if(typeof e!="object"||e===null)return !1;let t=Object.getPrototypeOf(e);return t===Object.prototype||t===null},T=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=T(n);if(r===null)return null;t.push(r);}return `[${t.join(",")}]`}if(R(e)){let t=Object.keys(e).sort(),n=[];for(let r of t){let a=T(e[r]);if(a===null)return null;n.push(`${JSON.stringify(r)}:${a}`);}return `{${n.join(",")}}`}return null},D=e=>Array.isArray(e)||R(e)?T(e):null,S=e=>Array.isArray(e)&&e.length>=3&&e[0]==="consent"&&(e[1]==="default"||e[1]==="update"),H=e=>R(e)?e.event==="gtm.js":!1,Ce=0,K,N=class{constructor(t,n){this.options=t;this.instanceId=n;this.logger=L(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 b({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=U(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=C({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=C({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=D(r);a&&(this.snapshotSignatures.add(a),S(r)&&this.deliveredConsentSignatures.add(a));}}queueValue(t){let n=S(t)?D(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(S(t)){let a=this.queue.findIndex(o=>!S(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:D(t),a=S(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}q(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}},Se=e=>{let t=`gtm-kit-${++Ce}`;return new N(e,t)};var x=e=>typeof e=="object"&&e!==null,ve=e=>e&&{...e},P=(e,t,n)=>{var a;if(!t)throw new Error("An event name is required when pushing to the dataLayer.");if(n!==void 0&&!x(n))throw new Error("Event payloads must be plain objects when pushing to the dataLayer.");let r={event:t,...(a=ve(n))!=null?a:{}};return e.push(r),r},Y=(e,t,n,r)=>{var s;if(!x(n))throw new Error("Ecommerce payload must be an object.");let a=(s=r==null?void 0:r.extras)!=null?s:{};if(!x(a))throw new Error("Ecommerce extras must be an object when provided.");let o={...a,ecommerce:n};return P(e,t,o)};var Ee="https://www.googletagmanager.com",J={height:"0",width:"0",style:"display:none;visibility:hidden",title:"Google Tag Manager"},Le=e=>typeof e=="string",W=e=>Le(e)?{id:e}:e,be=e=>e?Object.entries(e).reduce((t,[n,r])=>(t[n]=String(r),t),{}):{},X=e=>e.replace(/&/g,"&").replace(/"/g,""").replace(/</g,"<").replace(/>/g,">"),Te=(e,t,n)=>{let r=e.endsWith("/")?e.slice(0,-1):e,a=new URLSearchParams({id:t}),o=be(n);for(let[s,d]of Object.entries(o))s!=="id"&&a.set(s,d);return `${r}/ns.html?${a.toString()}`},Ae=e=>{if(!e)return "";let t=Object.entries(e);return t.length?t.map(([n,r])=>`${n}="${X(String(r))}"`).join(" "):""},we=(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:Ee,r={...t.defaultQueryParams,...e.queryParams},a=Te(n,e.id,r),o={...J,...t.iframeAttributes},s=Ae(o),d=s?` ${s}`:"";return `<noscript><iframe src="${X(a)}"${d}></iframe></noscript>`},De=(e,t={})=>{let n=Array.isArray(e)?e.map(W):[W(e)];if(!n.length)throw new Error("At least one container is required to build noscript markup.");return n.map(r=>we(r,t)).join("")},Ne={...J};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 Pe();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,O=()=>i.some(y=>y!==null&&typeof y=="object"&&!Array.isArray(y)&&y.event==="gtm.js"),v=()=>{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(v,0);return i.length};return i.push=te,O()?setTimeout(v,0):(u=setInterval(()=>{O()&&v();},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:v,uninstall:ee}}function Re(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,'${e}');`}function xe(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 Pe(){let e=()=>{};return {active:!1,bufferedCount:0,gtmReady:!1,replay:e,uninstall:e}}
|
|
3
|
+
var A="https://www.googletagmanager.com",g="dataLayer";var _="consent",k="default",I="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.")},V=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!==k&&e!==I)throw new Error(`Unsupported consent command: ${e}`);let r=V(t),a=se(n);return a?[_,e,r,a]:[_,e,r]},C=e=>[...w(e)],M=(e,t)=>C({command:k,state:e,options:t}),j=(e,t)=>C({command:I,state:e,options:t}),de={buildConsentCommand:w,createConsentDefaultsCommand:M,createConsentUpdateCommand:j,normalizeConsentState:V};var z={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=>({...z[e]});var E=e=>Array.isArray(e),U=e=>{let t=globalThis,n=t[e],r=E(n)?[...n]:void 0;return E(n)||(t[e]=[]),{name:e,dataLayer:t[e],created:!E(n),restore(){if(!E(n)){delete t[e];return}let a=r?[...r]:[];t[e]=a;},snapshot:r}},q=(e,t)=>{e.dataLayer.push(t);};var ce=["debug","info","warn","error"],le=()=>{},L=e=>{let t={};for(let n of ce){let r=e==null?void 0:e[n];if(typeof r=="function"){t[n]=r.bind(e);continue}t[n]=le;}return t};var $="data-gtm-container-id",pe="data-gtm-kit-instance",F=()=>{let e=!1,t,n=new Promise(r=>{t=r;});return {get settled(){return e},promise:n,resolve:r=>{e||(e=!0,t(r));}}},me=e=>e?Object.entries(e).reduce((t,[n,r])=>(t[n]=String(r),t),{}):{},fe=(e,t,n)=>{let r=e.endsWith("/")?e.slice(0,-1):e,a=new URLSearchParams({id:t}),o=me(n);for(let[s,d]of Object.entries(o))s!=="id"&&a.set(s,d);return `${r}/gtm.js?${a.toString()}`},ge=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},ye=e=>{if(e instanceof ErrorEvent){if(e.error)return String(e.error);if(e.message)return e.message}return "Failed to load GTM script."},G,Q,b=class{constructor(t){this.options=t;this.logger=L(this.options.logger);this.host=(G=this.options.host)!=null?G:A;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=F();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=F();}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=ge(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=fe(this.host,o.id,d);i.src=l,i.setAttribute($,o.id),i.setAttribute(pe,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=ye(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 he=e=>typeof e=="string",B=e=>he(e)?{id:e}:e,R=e=>{if(typeof e!="object"||e===null)return !1;let t=Object.getPrototypeOf(e);return t===Object.prototype||t===null},T=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=T(n);if(r===null)return null;t.push(r);}return `[${t.join(",")}]`}if(R(e)){let t=Object.keys(e).sort(),n=[];for(let r of t){let a=T(e[r]);if(a===null)return null;n.push(`${JSON.stringify(r)}:${a}`);}return `{${n.join(",")}}`}return null},D=e=>Array.isArray(e)||R(e)?T(e):null,S=e=>Array.isArray(e)&&e.length>=3&&e[0]==="consent"&&(e[1]==="default"||e[1]==="update"),H=e=>R(e)?e.event==="gtm.js":!1,Ce=0,K,N=class{constructor(t,n){this.options=t;this.instanceId=n;this.logger=L(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 b({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=U(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=C({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=C({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=D(r);a&&(this.snapshotSignatures.add(a),S(r)&&this.deliveredConsentSignatures.add(a));}}queueValue(t){let n=S(t)?D(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(S(t)){let a=this.queue.findIndex(o=>!S(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:D(t),a=S(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}q(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}},Se=e=>{let t=`gtm-kit-${++Ce}`;return new N(e,t)};var x=e=>typeof e=="object"&&e!==null,ve=e=>e&&{...e},P=(e,t,n)=>{var a;if(!t)throw new Error("An event name is required when pushing to the dataLayer.");if(n!==void 0&&!x(n))throw new Error("Event payloads must be plain objects when pushing to the dataLayer.");let r={event:t,...(a=ve(n))!=null?a:{}};return e.push(r),r},Y=(e,t,n,r)=>{var s;if(!x(n))throw new Error("Ecommerce payload must be an object.");let a=(s=r==null?void 0:r.extras)!=null?s:{};if(!x(a))throw new Error("Ecommerce extras must be an object when provided.");let o={...a,ecommerce:n};return P(e,t,o)};var Ee="https://www.googletagmanager.com",W={height:"0",width:"0",style:"display:none;visibility:hidden",title:"Google Tag Manager"},Le=e=>typeof e=="string",J=e=>Le(e)?{id:e}:e,be=e=>e?Object.entries(e).reduce((t,[n,r])=>(t[n]=String(r),t),{}):{},X=e=>e.replace(/&/g,"&").replace(/"/g,""").replace(/</g,"<").replace(/>/g,">"),Te=(e,t,n)=>{let r=e.endsWith("/")?e.slice(0,-1):e,a=new URLSearchParams({id:t}),o=be(n);for(let[s,d]of Object.entries(o))s!=="id"&&a.set(s,d);return `${r}/ns.html?${a.toString()}`},Ae=e=>{if(!e)return "";let t=Object.entries(e);return t.length?t.map(([n,r])=>`${n}="${X(String(r))}"`).join(" "):""},we=(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:Ee,r={...t.defaultQueryParams,...e.queryParams},a=Te(n,e.id,r),o={...W,...t.iframeAttributes},s=Ae(o),d=s?` ${s}`:"";return `<noscript><iframe src="${X(a)}"${d}></iframe></noscript>`},De=(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=>we(r,t)).join("")},Ne={...W};var Re=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 Oe();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,O=()=>i.some(y=>y!==null&&typeof y=="object"&&!Array.isArray(y)&&y.event==="gtm.js"),v=()=>{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(v,0);return i.length};return i.push=te,O()?setTimeout(v,0):(u=setInterval(()=>{O()&&v();},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:v,uninstall:ee}}function xe(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,'${Re(e)}');`}function Pe(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 Oe(){let e=()=>{};return {active:!1,bufferedCount:0,gtmReady:!1,replay:e,uninstall:e}}
|
|
4
4
|
|
|
5
5
|
exports.DEFAULT_DATA_LAYER_NAME = g;
|
|
6
6
|
exports.DEFAULT_GTM_HOST = A;
|
|
7
7
|
exports.DEFAULT_NOSCRIPT_IFRAME_ATTRIBUTES = Ne;
|
|
8
|
-
exports.attachToInlineBuffer =
|
|
8
|
+
exports.attachToInlineBuffer = Pe;
|
|
9
9
|
exports.buildConsentCommand = w;
|
|
10
10
|
exports.consent = de;
|
|
11
11
|
exports.consentPresets = z;
|
|
12
|
-
exports.createAutoQueueScript =
|
|
12
|
+
exports.createAutoQueueScript = xe;
|
|
13
13
|
exports.createConsentDefaultsCommand = M;
|
|
14
14
|
exports.createConsentUpdateCommand = j;
|
|
15
15
|
exports.createGtmClient = Se;
|
package/dist/index.cjs.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","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_HOST","DEFAULT_IFRAME_ATTRIBUTES","escapeAttributeValue","buildNoscriptUrl","buildAttributeString","entries","buildNoscriptForContainer","src","iframeAttributes","attributeString","attrs","createNoscriptMarkup","normalizedContainers","DEFAULT_NOSCRIPT_IFRAME_ATTRIBUTES","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","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';\n\nconst DEFAULT_HOST = 'https://www.googletagmanager.com';\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 = (\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 escapeAttributeValue = (value: string): string =>\n value\n .replace(/&/g, '&')\n .replace(/\"/g, '"')\n .replace(/</g, '<')\n .replace(/>/g, '>');\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 = (\n attributes: Record<string, string | number | boolean> | undefined\n): 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\n .map(([key, value]) => `${key}=\"${escapeAttributeValue(String(value))}\"`)\n .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 = (\n container: ContainerDescriptor,\n options: NoscriptOptions\n): 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_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\n .map((container) => buildNoscriptForContainer(container, options))\n .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/** 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 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,'${dataLayerName}');`;\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/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","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_HOST","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","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';\n\nconst DEFAULT_HOST = 'https://www.googletagmanager.com';\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 = (\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 escapeAttributeValue = (value: string): string =>\n value\n .replace(/&/g, '&')\n .replace(/\"/g, '"')\n .replace(/</g, '<')\n .replace(/>/g, '>');\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 = (\n attributes: Record<string, string | number | boolean> | undefined\n): 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\n .map(([key, value]) => `${key}=\"${escapeAttributeValue(String(value))}\"`)\n .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 = (\n container: ContainerDescriptor,\n options: NoscriptOptions\n): 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_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\n .map((container) => buildNoscriptForContainer(container, options))\n .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"]}
|
package/dist/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
var A="https://www.googletagmanager.com",g="dataLayer";var _="consent",k="default",I="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.")},V=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!==k&&e!==I)throw new Error(`Unsupported consent command: ${e}`);let r=V(t),a=se(n);return a?[_,e,r,a]:[_,e,r]},C=e=>[...w(e)],M=(e,t)=>C({command:k,state:e,options:t}),j=(e,t)=>C({command:I,state:e,options:t}),de={buildConsentCommand:w,createConsentDefaultsCommand:M,createConsentUpdateCommand:j,normalizeConsentState:V};var z={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=>({...z[e]});var E=e=>Array.isArray(e),U=e=>{let t=globalThis,n=t[e],r=E(n)?[...n]:void 0;return E(n)||(t[e]=[]),{name:e,dataLayer:t[e],created:!E(n),restore(){if(!E(n)){delete t[e];return}let a=r?[...r]:[];t[e]=a;},snapshot:r}},q=(e,t)=>{e.dataLayer.push(t);};var ce=["debug","info","warn","error"],le=()=>{},L=e=>{let t={};for(let n of ce){let r=e==null?void 0:e[n];if(typeof r=="function"){t[n]=r.bind(e);continue}t[n]=le;}return t};var $="data-gtm-container-id",pe="data-gtm-kit-instance",F=()=>{let e=!1,t,n=new Promise(r=>{t=r;});return {get settled(){return e},promise:n,resolve:r=>{e||(e=!0,t(r));}}},me=e=>e?Object.entries(e).reduce((t,[n,r])=>(t[n]=String(r),t),{}):{},fe=(e,t,n)=>{let r=e.endsWith("/")?e.slice(0,-1):e,a=new URLSearchParams({id:t}),o=me(n);for(let[s,d]of Object.entries(o))s!=="id"&&a.set(s,d);return `${r}/gtm.js?${a.toString()}`},ge=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},ye=e=>{if(e instanceof ErrorEvent){if(e.error)return String(e.error);if(e.message)return e.message}return "Failed to load GTM script."},G,Q,b=class{constructor(t){this.options=t;this.logger=L(this.options.logger);this.host=(G=this.options.host)!=null?G:A;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=F();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=F();}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=ge(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=fe(this.host,o.id,d);i.src=l,i.setAttribute($,o.id),i.setAttribute(pe,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=ye(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 he=e=>typeof e=="string",B=e=>he(e)?{id:e}:e,R=e=>{if(typeof e!="object"||e===null)return !1;let t=Object.getPrototypeOf(e);return t===Object.prototype||t===null},T=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=T(n);if(r===null)return null;t.push(r);}return `[${t.join(",")}]`}if(R(e)){let t=Object.keys(e).sort(),n=[];for(let r of t){let a=T(e[r]);if(a===null)return null;n.push(`${JSON.stringify(r)}:${a}`);}return `{${n.join(",")}}`}return null},D=e=>Array.isArray(e)||R(e)?T(e):null,S=e=>Array.isArray(e)&&e.length>=3&&e[0]==="consent"&&(e[1]==="default"||e[1]==="update"),H=e=>R(e)?e.event==="gtm.js":!1,Ce=0,K,N=class{constructor(t,n){this.options=t;this.instanceId=n;this.logger=L(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 b({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=U(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=C({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=C({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=D(r);a&&(this.snapshotSignatures.add(a),S(r)&&this.deliveredConsentSignatures.add(a));}}queueValue(t){let n=S(t)?D(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(S(t)){let a=this.queue.findIndex(o=>!S(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:D(t),a=S(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}q(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}},Se=e=>{let t=`gtm-kit-${++Ce}`;return new N(e,t)};var x=e=>typeof e=="object"&&e!==null,ve=e=>e&&{...e},P=(e,t,n)=>{var a;if(!t)throw new Error("An event name is required when pushing to the dataLayer.");if(n!==void 0&&!x(n))throw new Error("Event payloads must be plain objects when pushing to the dataLayer.");let r={event:t,...(a=ve(n))!=null?a:{}};return e.push(r),r},Y=(e,t,n,r)=>{var s;if(!x(n))throw new Error("Ecommerce payload must be an object.");let a=(s=r==null?void 0:r.extras)!=null?s:{};if(!x(a))throw new Error("Ecommerce extras must be an object when provided.");let o={...a,ecommerce:n};return P(e,t,o)};var Ee="https://www.googletagmanager.com",J={height:"0",width:"0",style:"display:none;visibility:hidden",title:"Google Tag Manager"},Le=e=>typeof e=="string",W=e=>Le(e)?{id:e}:e,be=e=>e?Object.entries(e).reduce((t,[n,r])=>(t[n]=String(r),t),{}):{},X=e=>e.replace(/&/g,"&").replace(/"/g,""").replace(/</g,"<").replace(/>/g,">"),Te=(e,t,n)=>{let r=e.endsWith("/")?e.slice(0,-1):e,a=new URLSearchParams({id:t}),o=be(n);for(let[s,d]of Object.entries(o))s!=="id"&&a.set(s,d);return `${r}/ns.html?${a.toString()}`},Ae=e=>{if(!e)return "";let t=Object.entries(e);return t.length?t.map(([n,r])=>`${n}="${X(String(r))}"`).join(" "):""},we=(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:Ee,r={...t.defaultQueryParams,...e.queryParams},a=Te(n,e.id,r),o={...J,...t.iframeAttributes},s=Ae(o),d=s?` ${s}`:"";return `<noscript><iframe src="${X(a)}"${d}></iframe></noscript>`},De=(e,t={})=>{let n=Array.isArray(e)?e.map(W):[W(e)];if(!n.length)throw new Error("At least one container is required to build noscript markup.");return n.map(r=>we(r,t)).join("")},Ne={...J};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 Pe();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,O=()=>i.some(y=>y!==null&&typeof y=="object"&&!Array.isArray(y)&&y.event==="gtm.js"),v=()=>{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(v,0);return i.length};return i.push=te,O()?setTimeout(v,0):(u=setInterval(()=>{O()&&v();},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:v,uninstall:ee}}function Re(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,'${e}');`}function xe(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 Pe(){let e=()=>{};return {active:!1,bufferedCount:0,gtmReady:!1,replay:e,uninstall:e}}
|
|
1
|
+
var A="https://www.googletagmanager.com",g="dataLayer";var _="consent",k="default",I="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.")},V=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!==k&&e!==I)throw new Error(`Unsupported consent command: ${e}`);let r=V(t),a=se(n);return a?[_,e,r,a]:[_,e,r]},C=e=>[...w(e)],M=(e,t)=>C({command:k,state:e,options:t}),j=(e,t)=>C({command:I,state:e,options:t}),de={buildConsentCommand:w,createConsentDefaultsCommand:M,createConsentUpdateCommand:j,normalizeConsentState:V};var z={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=>({...z[e]});var E=e=>Array.isArray(e),U=e=>{let t=globalThis,n=t[e],r=E(n)?[...n]:void 0;return E(n)||(t[e]=[]),{name:e,dataLayer:t[e],created:!E(n),restore(){if(!E(n)){delete t[e];return}let a=r?[...r]:[];t[e]=a;},snapshot:r}},q=(e,t)=>{e.dataLayer.push(t);};var ce=["debug","info","warn","error"],le=()=>{},L=e=>{let t={};for(let n of ce){let r=e==null?void 0:e[n];if(typeof r=="function"){t[n]=r.bind(e);continue}t[n]=le;}return t};var $="data-gtm-container-id",pe="data-gtm-kit-instance",F=()=>{let e=!1,t,n=new Promise(r=>{t=r;});return {get settled(){return e},promise:n,resolve:r=>{e||(e=!0,t(r));}}},me=e=>e?Object.entries(e).reduce((t,[n,r])=>(t[n]=String(r),t),{}):{},fe=(e,t,n)=>{let r=e.endsWith("/")?e.slice(0,-1):e,a=new URLSearchParams({id:t}),o=me(n);for(let[s,d]of Object.entries(o))s!=="id"&&a.set(s,d);return `${r}/gtm.js?${a.toString()}`},ge=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},ye=e=>{if(e instanceof ErrorEvent){if(e.error)return String(e.error);if(e.message)return e.message}return "Failed to load GTM script."},G,Q,b=class{constructor(t){this.options=t;this.logger=L(this.options.logger);this.host=(G=this.options.host)!=null?G:A;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=F();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=F();}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=ge(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=fe(this.host,o.id,d);i.src=l,i.setAttribute($,o.id),i.setAttribute(pe,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=ye(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 he=e=>typeof e=="string",B=e=>he(e)?{id:e}:e,R=e=>{if(typeof e!="object"||e===null)return !1;let t=Object.getPrototypeOf(e);return t===Object.prototype||t===null},T=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=T(n);if(r===null)return null;t.push(r);}return `[${t.join(",")}]`}if(R(e)){let t=Object.keys(e).sort(),n=[];for(let r of t){let a=T(e[r]);if(a===null)return null;n.push(`${JSON.stringify(r)}:${a}`);}return `{${n.join(",")}}`}return null},D=e=>Array.isArray(e)||R(e)?T(e):null,S=e=>Array.isArray(e)&&e.length>=3&&e[0]==="consent"&&(e[1]==="default"||e[1]==="update"),H=e=>R(e)?e.event==="gtm.js":!1,Ce=0,K,N=class{constructor(t,n){this.options=t;this.instanceId=n;this.logger=L(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 b({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=U(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=C({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=C({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=D(r);a&&(this.snapshotSignatures.add(a),S(r)&&this.deliveredConsentSignatures.add(a));}}queueValue(t){let n=S(t)?D(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(S(t)){let a=this.queue.findIndex(o=>!S(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:D(t),a=S(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}q(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}},Se=e=>{let t=`gtm-kit-${++Ce}`;return new N(e,t)};var x=e=>typeof e=="object"&&e!==null,ve=e=>e&&{...e},P=(e,t,n)=>{var a;if(!t)throw new Error("An event name is required when pushing to the dataLayer.");if(n!==void 0&&!x(n))throw new Error("Event payloads must be plain objects when pushing to the dataLayer.");let r={event:t,...(a=ve(n))!=null?a:{}};return e.push(r),r},Y=(e,t,n,r)=>{var s;if(!x(n))throw new Error("Ecommerce payload must be an object.");let a=(s=r==null?void 0:r.extras)!=null?s:{};if(!x(a))throw new Error("Ecommerce extras must be an object when provided.");let o={...a,ecommerce:n};return P(e,t,o)};var Ee="https://www.googletagmanager.com",W={height:"0",width:"0",style:"display:none;visibility:hidden",title:"Google Tag Manager"},Le=e=>typeof e=="string",J=e=>Le(e)?{id:e}:e,be=e=>e?Object.entries(e).reduce((t,[n,r])=>(t[n]=String(r),t),{}):{},X=e=>e.replace(/&/g,"&").replace(/"/g,""").replace(/</g,"<").replace(/>/g,">"),Te=(e,t,n)=>{let r=e.endsWith("/")?e.slice(0,-1):e,a=new URLSearchParams({id:t}),o=be(n);for(let[s,d]of Object.entries(o))s!=="id"&&a.set(s,d);return `${r}/ns.html?${a.toString()}`},Ae=e=>{if(!e)return "";let t=Object.entries(e);return t.length?t.map(([n,r])=>`${n}="${X(String(r))}"`).join(" "):""},we=(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:Ee,r={...t.defaultQueryParams,...e.queryParams},a=Te(n,e.id,r),o={...W,...t.iframeAttributes},s=Ae(o),d=s?` ${s}`:"";return `<noscript><iframe src="${X(a)}"${d}></iframe></noscript>`},De=(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=>we(r,t)).join("")},Ne={...W};var Re=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 Oe();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,O=()=>i.some(y=>y!==null&&typeof y=="object"&&!Array.isArray(y)&&y.event==="gtm.js"),v=()=>{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(v,0);return i.length};return i.push=te,O()?setTimeout(v,0):(u=setInterval(()=>{O()&&v();},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:v,uninstall:ee}}function xe(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,'${Re(e)}');`}function Pe(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 Oe(){let e=()=>{};return {active:!1,bufferedCount:0,gtmReady:!1,replay:e,uninstall:e}}
|
|
2
2
|
|
|
3
|
-
export { g as DEFAULT_DATA_LAYER_NAME, A as DEFAULT_GTM_HOST, Ne as DEFAULT_NOSCRIPT_IFRAME_ATTRIBUTES,
|
|
3
|
+
export { g as DEFAULT_DATA_LAYER_NAME, A as DEFAULT_GTM_HOST, Ne as DEFAULT_NOSCRIPT_IFRAME_ATTRIBUTES, Pe as attachToInlineBuffer, w as buildConsentCommand, de as consent, z as consentPresets, xe as createAutoQueueScript, M as createConsentDefaultsCommand, j as createConsentUpdateCommand, Se as createGtmClient, De as createNoscriptMarkup, ue as getConsentPreset, Z as installAutoQueue, Y as pushEcommerce, P as pushEvent };
|
|
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","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_HOST","DEFAULT_IFRAME_ATTRIBUTES","escapeAttributeValue","buildNoscriptUrl","buildAttributeString","entries","buildNoscriptForContainer","src","iframeAttributes","attributeString","attrs","createNoscriptMarkup","normalizedContainers","DEFAULT_NOSCRIPT_IFRAME_ATTRIBUTES","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","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';\n\nconst DEFAULT_HOST = 'https://www.googletagmanager.com';\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 = (\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 escapeAttributeValue = (value: string): string =>\n value\n .replace(/&/g, '&')\n .replace(/\"/g, '"')\n .replace(/</g, '<')\n .replace(/>/g, '>');\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 = (\n attributes: Record<string, string | number | boolean> | undefined\n): 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\n .map(([key, value]) => `${key}=\"${escapeAttributeValue(String(value))}\"`)\n .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 = (\n container: ContainerDescriptor,\n options: NoscriptOptions\n): 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_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\n .map((container) => buildNoscriptForContainer(container, options))\n .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/** 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 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,'${dataLayerName}');`;\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/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","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_HOST","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","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';\n\nconst DEFAULT_HOST = 'https://www.googletagmanager.com';\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 = (\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 escapeAttributeValue = (value: string): string =>\n value\n .replace(/&/g, '&')\n .replace(/\"/g, '"')\n .replace(/</g, '<')\n .replace(/>/g, '>');\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 = (\n attributes: Record<string, string | number | boolean> | undefined\n): 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\n .map(([key, value]) => `${key}=\"${escapeAttributeValue(String(value))}\"`)\n .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 = (\n container: ContainerDescriptor,\n options: NoscriptOptions\n): 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_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\n .map((container) => buildNoscriptForContainer(container, options))\n .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"]}
|