@mrrocket/sdk 0.2.0 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +16 -0
- package/README.md +142 -2
- package/dist/batch.d.ts +33 -0
- package/dist/batch.js +50 -0
- package/dist/batch.js.map +1 -0
- package/dist/browser/mrrocket-replay.js +78 -0
- package/dist/browser/mrrocket.js +1 -0
- package/dist/browser.d.ts +1 -1
- package/dist/browser.js +3 -1
- package/dist/browser.js.map +1 -1
- package/dist/client.d.ts +23 -3
- package/dist/client.js +44 -83
- package/dist/client.js.map +1 -1
- package/dist/context.d.ts +2 -0
- package/dist/context.js.map +1 -1
- package/dist/core.d.ts +94 -1
- package/dist/core.js +88 -4
- package/dist/core.js.map +1 -1
- package/dist/errors.d.ts +35 -0
- package/dist/errors.js +83 -0
- package/dist/errors.js.map +1 -0
- package/dist/index.d.ts +2 -2
- package/dist/index.js +8 -0
- package/dist/index.js.map +1 -1
- package/dist/react-native/index.d.cts +259 -0
- package/dist/react-native/index.d.ts +259 -0
- package/dist/react-native/index.js +922 -0
- package/dist/react-native/index.js.map +1 -0
- package/dist/react-native/package.json +3 -0
- package/dist/referral.d.ts +6 -0
- package/dist/referral.js +7 -3
- package/dist/referral.js.map +1 -1
- package/dist/server.d.ts +13 -1
- package/dist/server.js +8 -1
- package/dist/server.js.map +1 -1
- package/dist/styles.d.ts +1 -1
- package/dist/styles.js +2 -2
- package/dist/styles.js.map +1 -1
- package/dist/web-state.d.ts +36 -0
- package/dist/web-state.js +64 -0
- package/dist/web-state.js.map +1 -0
- package/package.json +56 -6
- package/react-native/package.json +7 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
(function(){'use strict';var _t=Object.defineProperty;var It=(e,t,n)=>t in e?_t(e,t,{enumerable:true,configurable:true,writable:true,value:n}):e[t]=n;var D=(e,t,n)=>It(e,typeof t!="symbol"?t+"":t,n);var Qe=e=>{let t=0;for(let n=0;n<e.length;n++){let o=e.charCodeAt(n);o<128?t+=1:o<2048?t+=2:o>=55296&&o<=56319&&n+1<e.length&&(e.charCodeAt(n+1)&64512)===56320?(t+=4,n++):t+=3;}return t},St=(e,t,n)=>{let o=`{"key":${JSON.stringify(e)},"sdk":${JSON.stringify(n)},"events":[`,i=Qe(o)+2,a=[],l=[],f=[],g=i,m=()=>{l.length&&a.push({body:`${o}${l.join(",")}]}`,events:f}),l=[],f=[],g=i;};for(let E of t){let I=JSON.stringify(E),p=Qe(I)+1;i+p>6e4||((l.length===100||g+p>6e4)&&m(),l.push(I),f.push(E),g+=p);}return m(),a},Ze=(e,t,n)=>St(e,t,n).map(o=>o.body);var Mt='a[href],button,input,select,textarea,label,summary,[role="button"],[role="link"],[role="tab"],[role="menuitem"],[role="checkbox"],[role="switch"],[onclick],[data-ck-goal]',At="[data-ck-mask]",$t="[data-ck-block]",et=/\d{4,}|^[a-z]{1,3}-[a-z0-9]{5,}$|^_|__|css-|^sc-|^jsx-/i,Ot=e=>{let t=e.tagName.toLowerCase();if(e.id&&!et.test(e.id)&&e.id.length<=40)return `${t}#${e.id}`;let n=typeof e.className=="string"?e.className.split(/\s+/).filter(a=>a&&!et.test(a)&&a.length<=30&&!a.includes(":")):[],o=n.length?`${t}.${n.slice(0,2).join(".")}`:t,i=e.parentElement?[...e.parentElement.children].filter(a=>a.tagName===e.tagName):[];return i.length>1?`${o}:nth-of-type(${i.indexOf(e)+1})`:o},Lt=e=>{let t=[],n=e;for(;n&&t.length<3&&n.tagName&&n.tagName!=="BODY"&&n.tagName!=="HTML";){let o=Ot(n);if(t.unshift(o),o.includes("#"))break;n=n.parentElement;}return t.join(" > ").slice(0,160)},ee=e=>(e!=null?e:"").replace(/\s+/g," ").trim().slice(0,40),Tt=e=>{var n,o,i,a,l;if(e.closest(`${At},${$t}`))return "";let t=e.tagName.toLowerCase();if(t==="input"){let f=((n=e.getAttribute("type"))!=null?n:"text").toLowerCase();return ee(f==="submit"||f==="button"?(o=e.getAttribute("aria-label"))!=null?o:e.value:e.getAttribute("aria-label"))}return ee(t==="select"||t==="textarea"?e.getAttribute("aria-label"):(l=(a=(i=e.getAttribute("aria-label"))!=null?i:e.getAttribute("title"))!=null?a:e.innerText)!=null?l:e.textContent)},tt=e=>{var a,l;let t=e.target instanceof Element?e.target:null;if(!t)return null;let n=t.closest(Mt),o=n!=null?n:t,i=document.documentElement;return {x:Math.round(e.clientX),y:Math.round(e.clientY+window.scrollY),vw:window.innerWidth,vh:window.innerHeight,docH:Math.max(i.scrollHeight,(l=(a=document.body)==null?void 0:a.scrollHeight)!=null?l:0),selector:Lt(o),label:n?Tt(n):"",interactive:!!n}},te=class{constructor(t=1e3,n=30,o=3){D(this,"windowMs",t);D(this,"radius",n);D(this,"count",o);D(this,"clicks",[]);D(this,"fired",false);}push(t,n,o){let i=this.clicks.filter(a=>o-a.t<=this.windowMs&&Math.hypot(a.x-t,a.y-n)<=this.radius);return i.length!==this.clicks.length&&(this.fired=false),this.clicks=[...i,{x:t,y:n,t:o}],this.clicks.length>=this.count&&!this.fired?(this.fired=true,true):false}},nt=()=>{var n,o;let e=document.documentElement,t=Math.max(e.scrollHeight,(o=(n=document.body)==null?void 0:n.scrollHeight)!=null?o:0);return t?Math.min(100,Math.round((window.scrollY+window.innerHeight)/t*100)):0};var ne="0.4.0",Pt=(e,t)=>{let n=t.trim().toLowerCase();for(let[o,i]of Object.entries(e))if(o.toLowerCase()===n)return i},Nt=(e,t)=>{let n=e.trim().toLowerCase(),o=t.trim().toLowerCase();return o.startsWith("@")?n.endsWith(o):n===o},Dt=(e,t)=>{if(e.op==="in cohort")return false;let n=Pt(t,e.property),o=n==null?"":String(n);if(e.op==="\u2265"){let a=Number(o),l=Number(e.values[0]);return o!==""&&Number.isFinite(a)&&Number.isFinite(l)&&a>=l}let i=o!==""&&e.values.some(a=>Nt(o,a));return e.op==="is"||e.op==="in"?i:!i},Ut=(e,t,n={})=>{var f;if(!e.on)return false;let o={id:t,...n},i=e.sets.length?e.sets.find(g=>g.conditions.every(m=>Dt(m,o))):{rollout:100};if(!i||rt(`${e.key}:rollout:${t}`)*100>=i.rollout)return false;if(!((f=e.variants)!=null&&f.length))return true;let a=rt(`${e.key}:variant:${t}`)*100,l=0;for(let g of e.variants)if(l+=g.split,a<l)return g.key;return e.variants[e.variants.length-1].key},Re=(e,t,n,o={})=>{var a;let i=(a=t==null?void 0:t.flags)==null?void 0:a.find(l=>l.key===e);return i?Ut(i,n,o):false},Ht=["utm_source","utm_medium","utm_campaign","utm_term","utm_content"],ot="ck_preview",st="ck_embed",L="ck_id",U="ck_sid";var Ft=e=>{var n;let t={};for(let o of Ht){let i=(n=e(o))==null?void 0:n.trim();i&&(t[o]=i.slice(0,150));}return t},it=e=>{let t=new URLSearchParams(e);return Ft(n=>t.get(n))};var Kt=e=>{let t=2166136261;for(let n=0;n<e.length;n++)t^=e.charCodeAt(n),t=Math.imul(t,16777619);return (t>>>0)/4294967296},rt=e=>{let t=2166136261;for(let n=0;n<e.length;n++)t^=e.charCodeAt(n),t=Math.imul(t,16777619);return t^=t>>>16,t=Math.imul(t,2246822507),t^=t>>>13,t=Math.imul(t,3266489909),t^=t>>>16,(t>>>0)/4294967296},jt=(e,t)=>{var a,l,f,g;let n=(l=(a=e.variants[0])==null?void 0:a.id)!=null?l:"control";if(e.winner)return e.winner;if(e.status!=="running")return n;let o=Kt(`${e.key}:${t}`),i=0;for(let m of e.variants)if(i+=m.weight,o<i)return m.id;return (g=(f=e.variants[e.variants.length-1])==null?void 0:f.id)!=null?g:n},at=e=>{let t=Array.isArray(e)?e[0]:e;if(!t)return {};let n={};for(let o of t.split(",")){let[i,a]=o.split(":");i&&a&&(n[i.trim()]=a.trim());}return n};var re=(e,t,n,o)=>{if(o[e])return o[e];let i=t==null?void 0:t.experiments.find(a=>a.key===e);return i?jt(i,n):"control"},ct=(e,t,n)=>{var i,a,l,f,g;let o={};for(let m of (i=e==null?void 0:e.experiments)!=null?i:[]){let E=re(m.key,e,t,n);E!==((l=(a=m.variants[0])==null?void 0:a.id)!=null?l:"control")&&Object.assign(o,(g=(f=m.variants.find(I=>I.id===E))==null?void 0:f.changes)!=null?g:{});}return o};var Ee=()=>typeof crypto!="undefined"&&"randomUUID"in crypto?crypto.randomUUID():`ck_${Math.random().toString(36).slice(2)}${Date.now().toString(36)}`;var V=(e,t)=>e.length>t?`${e.slice(0,t-1)}\u2026`:e,Vt=e=>{var t;if(e instanceof Error)return {type:e.name||"Error",message:V(e.message||String(e),1e3),stack:e.stack?V(e.stack,8192):void 0};if(typeof e=="string")return {type:"Error",message:V(e,1e3)};try{return {type:"Error",message:V((t=JSON.stringify(e))!=null?t:String(e),1e3)}}catch(n){return {type:"Error",message:String(e)}}},lt=e=>{let t=[],n=[],o=new Set,i=0,a=`${e.host}/api/sdk/errors`,l=(p=false)=>{for(;t.length;){let h=JSON.stringify({key:e.apiKey,errors:t.splice(0,4)});p&&typeof navigator!="undefined"&&"sendBeacon"in navigator&&navigator.sendBeacon(a,h)||fetch(a,{method:"POST",headers:{"content-type":"application/json"},body:h,keepalive:true}).then(C=>{!C.ok&&C.status!==429&&e.warn(`Error reports were rejected with ${C.status}. Check the public key and NEXT_PUBLIC_MRROCKET_HOST.`);}).catch(()=>{});}},f=(p,h="manual")=>{let S=e.context();if(!S.send||i>=20)return;let C=Vt(p);if(/\/api\/sdk\/errors/.test(C.message))return;let H=`${C.type}:${C.message}`;o.has(H)||(o.add(H),i++,t.push({...C,mechanism:h,handled:h==="manual",url:window.location.href,ts:Date.now(),crumbs:n.slice(),...e.release?{release:e.release}:{},...S.distinctId?{distinctId:S.distinctId}:{},...S.sessionId?{sessionId:S.sessionId}:{}}));},g=p=>{var h;!p.error&&(!p.message||p.message==="Script error.")||f((h=p.error)!=null?h:new Error(p.message),"onerror");},m=p=>{var h;return f((h=p.reason)!=null?h:new Error("Unhandled promise rejection"),"onunhandledrejection")},E=()=>l(true);window.addEventListener("error",g),window.addEventListener("unhandledrejection",m),window.addEventListener("pagehide",E);let I=window.setInterval(()=>l(),2e3);return {capture:f,crumb:(p,h)=>{n.push({ts:Date.now(),type:p,message:V(h,120)}),n.length>20&&n.shift();},stop:()=>{window.removeEventListener("error",g),window.removeEventListener("unhandledrejection",m),window.removeEventListener("pagehide",E),window.clearInterval(I),l(true);}}};var ie="mrr_ref",ae="mrr_ref",Bt="mrr_ref",Yt="mrr_click";var B=/^[A-Za-z0-9_-]{1,64}$/;var qt=(e,t="ref")=>{let n=new URLSearchParams(e);return Wt(o=>n.get(o),t)},Wt=(e,t="ref")=>{let n=e(Bt);if(n&&B.test(n)){let i=e(Yt);return {code:n,clickId:i&&B.test(i)?i:null,direct:false}}let o=e(t);return o&&B.test(o)?{code:o,clickId:null,direct:true}:null},dt=e=>{var t;return `${e.code}.${(t=e.clickId)!=null?t:""}.${e.at}`},oe=(e,t=60,n=Date.now())=>{if(!e)return null;let[o,i,a]=e.split("."),l=Number(a);return !o||!B.test(o)||!Number.isFinite(l)||i&&!B.test(i)||n-l>t*864e5||l>n+864e5?null:{code:o,clickId:i||null,at:l}};var R=null,ce=()=>typeof document!="undefined",ut=e=>{var n;if(!ce())return;let t=(n=document.cookie.split("; ").find(o=>o.startsWith(`${e}=`)))==null?void 0:n.slice(e.length+1);try{return t?decodeURIComponent(t):void 0}catch(o){return t}},le=()=>{try{return typeof localStorage=="undefined"?null:localStorage}catch(e){return null}},Ce=()=>{var e,t;return R||(ce()?(t=oe(ut(ie),3650))!=null?t:oe((e=le())==null?void 0:e.getItem(ae),3650):null)};var se=(e,t)=>{var i;if(!ce())return;let n=Math.max(1,Math.round(t))*86400,o=typeof location!="undefined"&&location.protocol==="https:"?"; secure":"";document.cookie=`${ie}=${encodeURIComponent(dt(e))}; path=/; max-age=${n}; samesite=lax${o}`;try{(i=le())==null||i.setItem(ae,dt(e));}catch(a){}},de=()=>{var e;if(ce()){document.cookie=`${ie}=; path=/; max-age=0; samesite=lax`;try{(e=le())==null||e.removeItem(ae);}catch(t){}}},ft=()=>{R=null;},pt=async e=>{var o,i,a;let t=qt(e.search,e.param),n=(o=e.now)!=null?o:Date.now();if(!t)return !R&&e.store&&(R=(a=oe(ut(ie),e.days,n))!=null?a:oe((i=le())==null?void 0:i.getItem(ae),e.days,n),R||de()),R;if(R={code:t.code,clickId:t.clickId,at:n},e.store&&se(R,e.days),e.beacon)try{let l=await e.beacon({code:t.code,clickId:t.clickId});l&&R&&R.code===t.code&&!R.clickId&&(R={...R,clickId:l},e.store&&se(R,e.days));}catch(l){}return R},gt=e=>e==="$identify"||!e.startsWith("$");var be=3600*24*365,kt=e=>{var n;if(typeof document=="undefined")return;let t=(n=document.cookie.split("; ").find(o=>o.startsWith(`${e}=`)))==null?void 0:n.slice(e.length+1);if(t)try{return decodeURIComponent(t)}catch(o){return t}},ue=(e,t,n)=>{let o=window.location.protocol==="https:"?"; secure":"";document.cookie=`${e}=${encodeURIComponent(t)}; path=/; max-age=${n}; samesite=lax${o}`;},Y=e=>{document.cookie=`${e}=; path=/; max-age=0; samesite=lax`;},w={utm:{}},ht=()=>{var e;return (e=w.id)!=null?e:w.id=Ee()},ve=(e,t)=>{if(e!=="cookies")return ht();let n=kt(L);if(n)return w.id=n;let o=ht();return ue(L,o,be),o},_e=e=>{var i;let t=Date.now(),n=w.session,o=(i=e?kt(U):void 0)!=null?i:n&&t-n.last<18e5?n.id:Ee();return w.session={id:o,last:t},e&&ue(U,o,18e5/1e3),o},Ie=()=>{var n;let e=window.location.href;if(w.url===e)return;w.referrer=(n=w.url)!=null?n:document.referrer||void 0,w.url=e;let t=it(window.location.search);Object.keys(t).length&&(w.utm=t);},yt=()=>{let e=navigator,t=window;return e.doNotTrack==="1"||t.doNotTrack==="1"||e.globalPrivacyControl===true};var T=window,Jt=()=>{var e,t;return (t=(e=document.currentScript)!=null?e:document.querySelector('script[data-key][src*="sdk"]'))!=null?t:document.querySelector("script[data-key][src*='mrrocket']")},wt=new Set,_=e=>{wt.has(e)||(wt.add(e),console.warn(`[MRRocket] ${e}`));},Gt=()=>{var qe,We,Xe,Je,Ge;let e=Jt(),t=r=>{var s;return (s=e==null?void 0:e.getAttribute(`data-${r}`))!=null?s:void 0},n=(r,s)=>{let c=t(r);return c===void 0?s:!["false","0","off","no"].includes(c.trim().toLowerCase())},o=(qe=e==null?void 0:e.src)!=null?qe:"",i="";try{i=o?new URL(o,window.location.href).origin:"";}catch(r){i="";}let a=((We=t("key"))!=null?We:"").trim(),l=((Xe=t("api-host"))!=null?Xe:i).trim().replace(/\/+$/,""),f=t("consent"),g=n("respect-dnt",false),m=n("heatmaps",true),E=n("errors",true),I=t("release"),p=t("attribution"),h=p==="always"||p==="off"?p:"consent",S=t("referral-param")||"ref",C=(Je=t("replay-src"))!=null?Je:o?o.replace(/(\.min)?\.js(\?[^#]*)?(#.*)?$/,`-replay$1.js?v=${ne}`):"",H=new URLSearchParams(window.location.search),q=at(H.get(ot)),W=H.get(st)==="1",Rt=yt(),Se=f==="cookies"||f==="off"?f:"cookieless",Me=n("replay",false),fe="",$,X,k=null,pe=false,Ae=false,$e=null,Oe=null,Le=window.location.pathname,P=[],ge=new Set,Te=[],b=()=>g&&Rt?"off":Se,M=()=>!W&&b()!=="off",O=()=>$!=null?$:fe,J=()=>b()==="cookies"?O():$;a||_('No public key. Add data-key="mrr_pub_\u2026" to the script tag; nothing is tracked until then.');let Pe=(r=false)=>{if(!P.length)return;if(!a){P.length=0;return}let s=`${l}/api/sdk/events`;for(let c of Ze(a,P.splice(0),{v:ne,errors:E,platform:"web"}))r&&"sendBeacon"in navigator&&navigator.sendBeacon(s,c)||fetch(s,{method:"POST",headers:{"content-type":"application/json"},body:c,keepalive:true}).then(u=>{u.ok||_(`Events were rejected with ${u.status}. Check data-key (MRRocket \u203A Settings \u203A Products).`);}).catch(()=>_(`Could not reach ${l||"this origin"} to send events. Check data-api-host.`));},x=null,v=null,F=(r,s,c=Date.now(),d)=>{if(!M())return;let u=gt(r)?Ce():null;P.push({event:r,props:u?{...s,$referral:{code:u.code,clickId:u.clickId}}:s,distinctId:J(),sessionId:_e(b()==="cookies"),ts:c,url:d!=null?d:window.location.href,referrer:w.referrer,...w.utm,screen:{w:window.screen.width,h:window.screen.height}});},G=(r,s)=>{typeof r!="string"||!r||(F(r,s),M()&&(x==null||x.event(r)));},Ne=(r,s)=>{if(!M())return;let c=`${window.location.pathname}|${r}`;ge.has(c)||(ge.add(c),G("$exposure",{experiment:r,variant:s}));},K=()=>{if(!k)return;let r=document.querySelectorAll("[data-ck-text]");if(!r.length)return;let s=ct(k,O(),q),c=new Set;r.forEach(d=>{var Z,ze;let u=d.dataset.ckText;if(!u)return;c.add(u),d.dataset.ckDefault===void 0&&(d.dataset.ckDefault=(Z=d.textContent)!=null?Z:"");let y=(ze=s[u])!=null?ze:d.dataset.ckDefault;d.textContent!==y&&(d.textContent=y);});for(let d of k.experiments)d.variants.some(u=>{var y;return Object.keys((y=u.changes)!=null?y:{}).some(Z=>c.has(Z))})&&Ne(d.key,re(d.key,k,O(),q));},De=()=>{var r,s;return (s=(r=k==null?void 0:k.referral)==null?void 0:r.cookieDays)!=null?s:60},Ue=()=>h==="always"?b()!=="off":h==="consent"&&b()==="cookies",He=null,me=false,he=()=>{if(!pe||W)return;if(h==="off"||b()==="off"){ft(),(h==="off"||me)&&de();return}let r=window.location.href;He!==r&&(He=r,pt({search:window.location.search,param:S,store:Ue(),days:De(),beacon:a?({code:s,clickId:c})=>fetch(`${l}/api/sdk/affiliate/click`,{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify({key:a,code:s,clickId:c,url:r,referrer:document.referrer||void 0}),keepalive:true}).then(d=>d.ok?d.json():null).then(d=>{var u;return (u=d==null?void 0:d.clickId)!=null?u:null}):void 0}));},Et=()=>{let r=Ue(),s=Ce();r&&s?se(s,De()):!r&&me&&de(),me=r;},z=null,Ct=()=>z!=null?z:z=new Promise((r,s)=>{if(T.__mrrocketReplay)return r(T.__mrrocketReplay);if(!C)return s(new Error("no recorder address"));let c=document.createElement("script");c.async=true,c.src=C,c.onload=()=>T.__mrrocketReplay?r(T.__mrrocketReplay):s(new Error("recorder missing")),c.onerror=()=>s(new Error("recorder did not load")),document.head.appendChild(c);}).catch(r=>{throw z=null,r}),Fe=0,Ke=false,je=()=>{var d,u;let r=M()&&Me&&!!(k!=null&&k.replay)&&!!a;if(r===Ke)return;Ke=r;let s=++Fe;if(!r){x==null||x.stop(true),x=null;return}let c=(u=(d=k==null?void 0:k.replay)==null?void 0:d.sampleRate)!=null?u:.1;Ct().then(y=>y.startReplay({host:l,apiKey:a,sessionId:_e(b()==="cookies"),distinctId:J(),identified:!!$,sampleRate:c,warn:_})).then(y=>{s!==Fe?y==null||y.stop(true):x=y;}).catch(()=>_("Session recording could not start (the recorder did not load)."));},ke=()=>{!Ae||!M()||Oe===window.location.pathname||(Oe=window.location.pathname,G("$pageview",{path:window.location.pathname}));},A=null,Ve=()=>{let r=A;m&&r&&r.max>0&&F("$scroll",{depth:r.max,docH:r.docH,vw:window.innerWidth,vh:window.innerHeight},Date.now(),r.url),A=null;},j=()=>{var u,y;if(!m||!M()||W)return;let r=document.documentElement,s=Math.max(r.scrollHeight,(y=(u=document.body)==null?void 0:u.scrollHeight)!=null?y:0),c=nt(),d=window.location.href;(A==null?void 0:A.url)!==d?A={url:d,max:c,docH:s}:c>A.max&&(A={url:d,max:c,docH:s});},Be=()=>{let r=window.location.pathname;r!==Le&&(Le=r,Ve(),Ie(),x==null||x.page(r),v==null||v.crumb("navigation",r),ke(),he(),window.setTimeout(()=>{j(),K();},0));},Ye=()=>{let r=b(),s=$e;$e=r,s==="cookies"&&r!=="cookies"&&(Y(L),Y(U)),r==="off"&&(P.length=0,w.session=void 0),fe=ve(r),Et(),he(),je(),ke(),j();},Q={track:G,identify:(r,s)=>{if(typeof r!="string"||!r)return;let c=J();$=r,F("$identify",{...s,id:r,...c&&c!==r?{previousId:c}:{}}),x==null||x.identify(r),b()==="cookies"&&ue(L,r,be),X={...s,id:r},K();},reset:()=>{$=void 0,X=void 0,ge.clear(),b()==="cookies"&&(Y(L),Y(U)),w.id=void 0,w.session=void 0,fe=ve(b()),K();},captureError:r=>v==null?void 0:v.capture(r,"manual"),setConsent:r=>{let{analytics:s,replay:c}=typeof r=="string"?{analytics:r,replay:void 0}:r!=null?r:{};if(s!==void 0&&s!=="cookieless"&&s!=="cookies"&&s!=="off"){_(`setConsent: "${String(s)}" is not a consent mode. Use "cookieless", "cookies" or "off".`);return}s==="off"&&(P.length=0),s&&(Se=s),typeof c=="boolean"&&(Me=c),Ye();},getFlag:r=>Re(r,k,O(),{...X})!==false,getFlagVariant:r=>{let s=Re(r,k,O(),{...X});return typeof s=="string"?s:null},getVariant:r=>{let s=re(r,k,O(),q);return (k||q[r])&&Ne(r,s),s},ready:r=>{typeof r=="function"&&(pe?window.setTimeout(()=>r(Q),0):Te.push(r));},getDistinctId:O,version:ne};Ie(),Ye();let N=T.mrrocket,bt=(Ge=N==null?void 0:N.q)!=null?Ge:[],ye=N&&typeof N=="object"?N:{};Object.assign(ye,Q,{__loaded:true}),delete ye.q,T.mrrocket=ye;for(let[r,s]of bt){let c=Q[r];if(typeof c=="function")try{c(...Array.from(s!=null?s:[]));}catch(d){console.error(d);}}Ae=true,ke(),E&&a&&!W&&(v=lt({host:l,apiKey:a,release:I,context:()=>{var r;return {send:M(),distinctId:J(),sessionId:(r=w.session)==null?void 0:r.id}},warn:_}),v.crumb("navigation",window.location.pathname));for(let r of ["pushState","replaceState"]){let s=history[r];history[r]=function(...c){let d=s.apply(this,c);return Be(),d};}window.addEventListener("popstate",Be);let vt=new te;document.addEventListener("click",r=>{var y;let s=r.target instanceof Element?r.target.closest("[data-ck-goal]"):null;if(s!=null&&s.dataset.ckGoal&&G(s.dataset.ckGoal,{text:(y=s.textContent)==null?void 0:y.trim().slice(0,80)}),!M())return;let c=tt(r);if(!c)return;let d=vt.push(c.x,c.y,Date.now());if((c.label||c.selector)&&(v==null||v.crumb("click",c.label||c.selector)),x==null||x.click(c,d),!m)return;let u={x:c.x,y:c.y,vw:c.vw,vh:c.vh,docH:c.docH,selector:c.selector,label:c.label,dead:!c.interactive};F("$click",u),d&&F("$rageclick",u);});let we=0;window.addEventListener("scroll",()=>{we||(we=window.requestAnimationFrame(()=>{we=0,j();}));},{passive:true}),document.readyState==="loading"?document.addEventListener("DOMContentLoaded",()=>j()):j(),window.setInterval(()=>Pe(),2e3),window.addEventListener("pagehide",()=>{Ve(),Pe(true);});let xe=()=>{pe=true,he(),je(),document.readyState==="loading"?document.addEventListener("DOMContentLoaded",K):K();for(let r of Te.splice(0))try{r(Q);}catch(s){console.error(s);}};a?fetch(`${l}/api/sdk/config?key=${encodeURIComponent(a)}`).then(r=>(r.ok||_(`Config request to ${l||"this origin"} answered ${r.status}. Check data-key and data-api-host.`),r.ok?r.json():null)).then(r=>{r&&Array.isArray(r.experiments)&&(k=r),xe();}).catch(()=>{_(`Could not reach ${l||"this origin"}. Check data-api-host. The page keeps its own texts and flags stay off.`),xe();}):xe();},xt;typeof window!="undefined"&&typeof document!="undefined"&&!((xt=T.mrrocket)!=null&&xt.__loaded)&&Gt();})();
|
package/dist/browser.d.ts
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
* that the build keeps one client chunk: server components import these as client references, while the plain
|
|
4
4
|
* helpers (`core`, `server`, `proxy`) stay free of the directive.
|
|
5
5
|
*/
|
|
6
|
-
export { CockpitProvider, Experiment, Text, useCockpit, useVariant, type CockpitProviderProps } from "./client.js";
|
|
6
|
+
export { CockpitProvider, Experiment, Text, useCockpit, useFlag, useFlagVariant, useVariant, type CockpitProviderProps } from "./client.js";
|
|
7
7
|
export { FeedbackBoard, FeedbackWidget, useFeedback, type FeedbackBoardProps, type FeedbackWidgetProps } from "./feedback.js";
|
|
8
8
|
export { Survey } from "./survey.js";
|
|
9
9
|
export type { CockpitUser } from "./context.js";
|
package/dist/browser.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
|
|
3
3
|
// src/browser.ts
|
|
4
|
-
import { CockpitProvider, Experiment, Text, useCockpit, useVariant } from "./client.js";
|
|
4
|
+
import { CockpitProvider, Experiment, Text, useCockpit, useFlag, useFlagVariant, useVariant } from "./client.js";
|
|
5
5
|
import { FeedbackBoard, FeedbackWidget, useFeedback } from "./feedback.js";
|
|
6
6
|
import { Survey } from "./survey.js";
|
|
7
7
|
import {
|
|
@@ -19,6 +19,8 @@ export {
|
|
|
19
19
|
createApi,
|
|
20
20
|
useCockpit,
|
|
21
21
|
useFeedback,
|
|
22
|
+
useFlag,
|
|
23
|
+
useFlagVariant,
|
|
22
24
|
useStyles,
|
|
23
25
|
useVariant
|
|
24
26
|
};
|
package/dist/browser.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/browser.ts"],"sourcesContent":["\"use client\";\n\n/**\n * Everything of the SDK that needs the browser or React state. It is its own file with the \"use client\" directive so\n * that the build keeps one client chunk: server components import these as client references, while the plain\n * helpers (`core`, `server`, `proxy`) stay free of the directive.\n */\nexport { CockpitProvider, Experiment, Text, useCockpit, useVariant, type CockpitProviderProps } from \"./client\";\nexport { FeedbackBoard, FeedbackWidget, useFeedback, type FeedbackBoardProps, type FeedbackWidgetProps } from \"./feedback\";\nexport { Survey } from \"./survey\";\nexport type { CockpitUser } from \"./context\";\nexport {\n type ChangelogEntry,\n type FeedbackBoardInfo,\n type FeedbackComment,\n type FeedbackList,\n type FeedbackPost,\n type FeedbackRoadmap,\n type PostStatus,\n type SurveyAnswer,\n type SurveyDefinition,\n type SurveyQuestion,\n type SurveyQuestionKind,\n createApi,\n} from \"./api\";\nexport { CSS as WIDGET_CSS, useStyles } from \"./styles\";\n"],"mappings":";;;AAOA,SAAS,iBAAiB,YAAY,MAAM,YAAY,kBAA6C;
|
|
1
|
+
{"version":3,"sources":["../src/browser.ts"],"sourcesContent":["\"use client\";\n\n/**\n * Everything of the SDK that needs the browser or React state. It is its own file with the \"use client\" directive so\n * that the build keeps one client chunk: server components import these as client references, while the plain\n * helpers (`core`, `server`, `proxy`) stay free of the directive.\n */\nexport { CockpitProvider, Experiment, Text, useCockpit, useFlag, useFlagVariant, useVariant, type CockpitProviderProps } from \"./client\";\nexport { FeedbackBoard, FeedbackWidget, useFeedback, type FeedbackBoardProps, type FeedbackWidgetProps } from \"./feedback\";\nexport { Survey } from \"./survey\";\nexport type { CockpitUser } from \"./context\";\nexport {\n type ChangelogEntry,\n type FeedbackBoardInfo,\n type FeedbackComment,\n type FeedbackList,\n type FeedbackPost,\n type FeedbackRoadmap,\n type PostStatus,\n type SurveyAnswer,\n type SurveyDefinition,\n type SurveyQuestion,\n type SurveyQuestionKind,\n createApi,\n} from \"./api\";\nexport { CSS as WIDGET_CSS, useStyles } from \"./styles\";\n"],"mappings":";;;AAOA,SAAS,iBAAiB,YAAY,MAAM,YAAY,SAAS,gBAAgB,kBAA6C;AAC9H,SAAS,eAAe,gBAAgB,mBAAsE;AAC9G,SAAS,cAAc;AAEvB;AAAA,EAYI;AAAA,OACG;AACP,SAAgB,KAAY,iBAAiB;","names":[]}
|
package/dist/client.d.ts
CHANGED
|
@@ -32,6 +32,14 @@ export interface CockpitProviderProps {
|
|
|
32
32
|
heatmaps?: boolean;
|
|
33
33
|
/** Ask visitors the surveys you run in Cockpit. Set false to switch them off. */
|
|
34
34
|
surveys?: boolean;
|
|
35
|
+
/**
|
|
36
|
+
* Error tracking for Build › Errors (default true): uncaught errors and unhandled promise rejections, with the page
|
|
37
|
+
* and the last pages and clicks, under the normal analytics consent. Report errors you catch with
|
|
38
|
+
* `useCockpit().captureError(error)`.
|
|
39
|
+
*/
|
|
40
|
+
errors?: boolean;
|
|
41
|
+
/** Your release (a version or a commit), so MRRocket can tell which release an error came with. */
|
|
42
|
+
release?: string;
|
|
35
43
|
/**
|
|
36
44
|
* Affiliate referrals (`?ref=` links and MRRocket's tracked links). "consent" (default): stored in the first-party
|
|
37
45
|
* `mrr_ref` cookie only while consent is "cookies", in memory otherwise. "always": stored whatever the consent (only
|
|
@@ -49,14 +57,16 @@ export interface CockpitProviderProps {
|
|
|
49
57
|
* `preview` forces variants for this render, for example from the `ck_preview` search param on the server,
|
|
50
58
|
* so a Cockpit preview never flickers. Without it the provider reads the param itself after the first render.
|
|
51
59
|
*/
|
|
52
|
-
export declare const CockpitProvider: ({ apiKey: apiKeyProp, host: hostProp, preview: forced, embedded: embeddedProp, config: bootstrapConfig, distinctId: bootstrapId, consent: consentProp, respectDnt, replay: replayProp, heatmaps, surveys, attribution, referralParam, children, }: CockpitProviderProps) => import("react").JSX.Element;
|
|
60
|
+
export declare const CockpitProvider: ({ apiKey: apiKeyProp, host: hostProp, preview: forced, embedded: embeddedProp, config: bootstrapConfig, distinctId: bootstrapId, consent: consentProp, respectDnt, replay: replayProp, heatmaps, surveys, errors, release, attribution, referralParam, children, }: CockpitProviderProps) => import("react").JSX.Element;
|
|
53
61
|
/**
|
|
54
|
-
* `track`, `identify`, the consent mode, the replay consent and their setter, the visitor id and whether
|
|
55
|
-
* experiments have loaded. `setConsent("cookies")` sets the mode, `setConsent({ replay: true })` the
|
|
62
|
+
* `track`, `identify`, `captureError`, the consent mode, the replay consent and their setter, the visitor id and whether
|
|
63
|
+
* the experiments and flags have loaded. `setConsent("cookies")` sets the mode, `setConsent({ replay: true })` the
|
|
64
|
+
* replay consent.
|
|
56
65
|
*/
|
|
57
66
|
export declare const useCockpit: () => {
|
|
58
67
|
track: (event: string, props?: Record<string, unknown>) => void;
|
|
59
68
|
identify: (id: string, props?: Record<string, unknown>) => void;
|
|
69
|
+
captureError: (error: unknown) => void;
|
|
60
70
|
distinctId: string;
|
|
61
71
|
ready: boolean;
|
|
62
72
|
embedded: boolean;
|
|
@@ -64,6 +74,16 @@ export declare const useCockpit: () => {
|
|
|
64
74
|
replay: boolean;
|
|
65
75
|
setConsent: (choice: ConsentChoice) => void;
|
|
66
76
|
};
|
|
77
|
+
/**
|
|
78
|
+
* A feature flag from MRRocket › Build › Feature flags: true while it is on for this visitor (its audience and rollout
|
|
79
|
+
* in Production), false while it is off, unknown, or the flags have not loaded yet (`useCockpit().ready`). The person's
|
|
80
|
+
* properties from `identify(id, props)` feed the audience conditions ("Email", "Plan" …).
|
|
81
|
+
*
|
|
82
|
+
* const newCheckout = useFlag("new-checkout");
|
|
83
|
+
*/
|
|
84
|
+
export declare const useFlag: (key: string) => boolean;
|
|
85
|
+
/** The variant of a multivariate flag for this visitor ("control", "test" …), or null while the flag is off. */
|
|
86
|
+
export declare const useFlagVariant: (key: string) => string | null;
|
|
67
87
|
/** The variant this visitor sees, for example `"control"` or `"a"`. Records the exposure. */
|
|
68
88
|
export declare const useVariant: (key: string) => VariantId;
|
|
69
89
|
/** Renders the element for the visitor's variant; control when the variant has no element. */
|
package/dist/client.js
CHANGED
|
@@ -6,6 +6,7 @@ import { usePathname } from "next/navigation";
|
|
|
6
6
|
import { Ctx, useCtx } from "./context.js";
|
|
7
7
|
import { Survey, forgetStoredDismissals } from "./survey.js";
|
|
8
8
|
import { RageDetector, clickInfo, seenDepth } from "./capture.js";
|
|
9
|
+
import { startErrorCapture } from "./errors.js";
|
|
9
10
|
import {
|
|
10
11
|
DEFAULT_REFERRAL_DAYS,
|
|
11
12
|
captureReferral,
|
|
@@ -19,97 +20,24 @@ import {
|
|
|
19
20
|
EMBED_PARAM,
|
|
20
21
|
ID_COOKIE,
|
|
21
22
|
PREVIEW_PARAM,
|
|
23
|
+
SDK_VERSION,
|
|
22
24
|
SESSION_COOKIE,
|
|
23
|
-
SESSION_TIMEOUT_MS,
|
|
24
25
|
envKey,
|
|
25
|
-
|
|
26
|
+
flagFor,
|
|
26
27
|
parsePreview,
|
|
27
|
-
readUtm,
|
|
28
28
|
resolveHost,
|
|
29
29
|
resolveVariant,
|
|
30
30
|
textChanges
|
|
31
31
|
} from "./core.js";
|
|
32
|
+
import { bodies } from "./batch.js";
|
|
33
|
+
import { YEAR_S, deleteCookie, enterPage, page, privacySignal, touchSession, visitorId, writeCookie } from "./web-state.js";
|
|
32
34
|
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
33
|
-
var YEAR_S = 60 * 60 * 24 * 365;
|
|
34
|
-
var MAX_BATCH = 100;
|
|
35
|
-
var MAX_BYTES = 6e4;
|
|
36
|
-
var readCookie = (name) => {
|
|
37
|
-
if (typeof document === "undefined") return void 0;
|
|
38
|
-
const raw = document.cookie.split("; ").find((c) => c.startsWith(`${name}=`))?.slice(name.length + 1);
|
|
39
|
-
if (!raw) return void 0;
|
|
40
|
-
try {
|
|
41
|
-
return decodeURIComponent(raw);
|
|
42
|
-
} catch {
|
|
43
|
-
return raw;
|
|
44
|
-
}
|
|
45
|
-
};
|
|
46
|
-
var writeCookie = (name, value, maxAge) => {
|
|
47
|
-
const secure = window.location.protocol === "https:" ? "; secure" : "";
|
|
48
|
-
document.cookie = `${name}=${encodeURIComponent(value)}; path=/; max-age=${maxAge}; samesite=lax${secure}`;
|
|
49
|
-
};
|
|
50
|
-
var deleteCookie = (name) => {
|
|
51
|
-
document.cookie = `${name}=; path=/; max-age=0; samesite=lax`;
|
|
52
|
-
};
|
|
53
|
-
var page = { utm: {} };
|
|
54
|
-
var memoryId = () => page.id ??= newDistinctId();
|
|
55
|
-
var visitorId = (mode, write) => {
|
|
56
|
-
if (mode !== "cookies") return memoryId();
|
|
57
|
-
const stored = readCookie(ID_COOKIE);
|
|
58
|
-
if (stored) return page.id = stored;
|
|
59
|
-
const id = memoryId();
|
|
60
|
-
if (write) writeCookie(ID_COOKIE, id, YEAR_S);
|
|
61
|
-
return id;
|
|
62
|
-
};
|
|
63
|
-
var touchSession = (persist) => {
|
|
64
|
-
const now = Date.now();
|
|
65
|
-
const s = page.session;
|
|
66
|
-
const id = (persist ? readCookie(SESSION_COOKIE) : void 0) ?? (s && now - s.last < SESSION_TIMEOUT_MS ? s.id : newDistinctId());
|
|
67
|
-
page.session = { id, last: now };
|
|
68
|
-
if (persist) writeCookie(SESSION_COOKIE, id, SESSION_TIMEOUT_MS / 1e3);
|
|
69
|
-
return id;
|
|
70
|
-
};
|
|
71
|
-
var enterPage = () => {
|
|
72
|
-
const href = window.location.href;
|
|
73
|
-
if (page.url === href) return;
|
|
74
|
-
page.referrer = page.url ?? (document.referrer || void 0);
|
|
75
|
-
page.url = href;
|
|
76
|
-
const utm = readUtm(window.location.search);
|
|
77
|
-
if (Object.keys(utm).length) page.utm = utm;
|
|
78
|
-
};
|
|
79
|
-
var privacySignal = () => {
|
|
80
|
-
const nav = navigator;
|
|
81
|
-
const win = window;
|
|
82
|
-
return nav.doNotTrack === "1" || win.doNotTrack === "1" || nav.globalPrivacyControl === true;
|
|
83
|
-
};
|
|
84
35
|
var warned = /* @__PURE__ */ new Set();
|
|
85
36
|
var warnOnce = (message) => {
|
|
86
37
|
if (warned.has(message)) return;
|
|
87
38
|
warned.add(message);
|
|
88
39
|
console.warn(`[MRRocket] ${message}`);
|
|
89
40
|
};
|
|
90
|
-
var byteLength = (s) => new TextEncoder().encode(s).length;
|
|
91
|
-
var bodies = (key, events) => {
|
|
92
|
-
const head = `{"key":${JSON.stringify(key)},"events":[`;
|
|
93
|
-
const empty = byteLength(head) + 2;
|
|
94
|
-
const out = [];
|
|
95
|
-
let chunk = [];
|
|
96
|
-
let size = empty;
|
|
97
|
-
const close = () => {
|
|
98
|
-
if (chunk.length) out.push(`${head}${chunk.join(",")}]}`);
|
|
99
|
-
chunk = [];
|
|
100
|
-
size = empty;
|
|
101
|
-
};
|
|
102
|
-
for (const event of events) {
|
|
103
|
-
const json = JSON.stringify(event);
|
|
104
|
-
const n = byteLength(json) + 1;
|
|
105
|
-
if (empty + n > MAX_BYTES) continue;
|
|
106
|
-
if (chunk.length === MAX_BATCH || size + n > MAX_BYTES) close();
|
|
107
|
-
chunk.push(json);
|
|
108
|
-
size += n;
|
|
109
|
-
}
|
|
110
|
-
close();
|
|
111
|
-
return out;
|
|
112
|
-
};
|
|
113
41
|
var CockpitProvider = ({
|
|
114
42
|
apiKey: apiKeyProp,
|
|
115
43
|
host: hostProp,
|
|
@@ -122,6 +50,8 @@ var CockpitProvider = ({
|
|
|
122
50
|
replay: replayProp = false,
|
|
123
51
|
heatmaps = true,
|
|
124
52
|
surveys = true,
|
|
53
|
+
errors = true,
|
|
54
|
+
release,
|
|
125
55
|
attribution = "consent",
|
|
126
56
|
referralParam = "ref",
|
|
127
57
|
children
|
|
@@ -222,7 +152,7 @@ var CockpitProvider = ({
|
|
|
222
152
|
return;
|
|
223
153
|
}
|
|
224
154
|
const url = `${host}/api/sdk/events`;
|
|
225
|
-
for (const body of bodies(apiKey, queue.current.splice(0))) {
|
|
155
|
+
for (const body of bodies(apiKey, queue.current.splice(0), { v: SDK_VERSION, errors })) {
|
|
226
156
|
const sent = beacon && typeof navigator !== "undefined" && "sendBeacon" in navigator && navigator.sendBeacon(url, body);
|
|
227
157
|
if (!sent)
|
|
228
158
|
fetch(url, { method: "POST", headers: { "content-type": "application/json" }, body, keepalive: true }).then((r) => {
|
|
@@ -230,7 +160,7 @@ var CockpitProvider = ({
|
|
|
230
160
|
}).catch(() => warnOnce(`Could not reach ${host || "this origin"} to send events. Check NEXT_PUBLIC_MRROCKET_HOST.`));
|
|
231
161
|
}
|
|
232
162
|
},
|
|
233
|
-
[apiKey, host]
|
|
163
|
+
[apiKey, host, errors]
|
|
234
164
|
);
|
|
235
165
|
useEffect(() => {
|
|
236
166
|
const timer = window.setInterval(() => flush(), 2e3);
|
|
@@ -265,6 +195,7 @@ var CockpitProvider = ({
|
|
|
265
195
|
});
|
|
266
196
|
}, []);
|
|
267
197
|
const replayer = useRef(null);
|
|
198
|
+
const errorer = useRef(null);
|
|
268
199
|
const track = useCallback(
|
|
269
200
|
(event, props) => {
|
|
270
201
|
record(event, props);
|
|
@@ -340,7 +271,25 @@ var CockpitProvider = ({
|
|
|
340
271
|
useEffect(() => {
|
|
341
272
|
enterPage();
|
|
342
273
|
replayer.current?.page(pathname);
|
|
274
|
+
errorer.current?.crumb("navigation", pathname);
|
|
343
275
|
}, [pathname]);
|
|
276
|
+
useEffect(() => {
|
|
277
|
+
if (!errors || !apiKey || embedded) return;
|
|
278
|
+
const capture = startErrorCapture({
|
|
279
|
+
host,
|
|
280
|
+
apiKey,
|
|
281
|
+
release,
|
|
282
|
+
context: () => ({ send: live.current.tracking, distinctId: live.current.sendId, sessionId: page.session?.id }),
|
|
283
|
+
warn: warnOnce
|
|
284
|
+
});
|
|
285
|
+
errorer.current = capture;
|
|
286
|
+
capture.crumb("navigation", window.location.pathname);
|
|
287
|
+
return () => {
|
|
288
|
+
capture.stop();
|
|
289
|
+
if (errorer.current === capture) errorer.current = null;
|
|
290
|
+
};
|
|
291
|
+
}, [errors, apiKey, host, release, embedded]);
|
|
292
|
+
const captureError = useCallback((error) => errorer.current?.capture(error, "manual"), []);
|
|
344
293
|
useEffect(() => {
|
|
345
294
|
if (!tracking || lastPage.current === pathname) return;
|
|
346
295
|
lastPage.current = pathname;
|
|
@@ -355,6 +304,7 @@ var CockpitProvider = ({
|
|
|
355
304
|
const c = clickInfo(e);
|
|
356
305
|
if (!c) return;
|
|
357
306
|
const raged = rage.current.push(c.x, c.y, Date.now());
|
|
307
|
+
if (c.label || c.selector) errorer.current?.crumb("click", c.label || c.selector);
|
|
358
308
|
replayer.current?.click(c, raged);
|
|
359
309
|
if (!heatmaps) return;
|
|
360
310
|
const props = { x: c.x, y: c.y, vw: c.vw, vh: c.vh, docH: c.docH, selector: c.selector, label: c.label, dead: !c.interactive };
|
|
@@ -430,8 +380,8 @@ var CockpitProvider = ({
|
|
|
430
380
|
}, [recording, replayConfig, host, apiKey]);
|
|
431
381
|
const texts = useMemo(() => textChanges(config, distinctId, preview), [config, distinctId, preview]);
|
|
432
382
|
const value = useMemo(
|
|
433
|
-
() => ({ apiKey, host, user, ready, distinctId, config, preview, embedded, consent, effectiveConsent, replayConsent, setConsent, texts, track, identify, expose }),
|
|
434
|
-
[apiKey, host, user, ready, distinctId, config, preview, embedded, consent, effectiveConsent, replayConsent, setConsent, texts, track, identify, expose]
|
|
383
|
+
() => ({ apiKey, host, user, ready, distinctId, config, preview, embedded, consent, effectiveConsent, replayConsent, setConsent, texts, track, identify, expose, captureError }),
|
|
384
|
+
[apiKey, host, user, ready, distinctId, config, preview, embedded, consent, effectiveConsent, replayConsent, setConsent, texts, track, identify, expose, captureError]
|
|
435
385
|
);
|
|
436
386
|
return /* @__PURE__ */ jsxs(Ctx.Provider, { value, children: [
|
|
437
387
|
children,
|
|
@@ -439,8 +389,17 @@ var CockpitProvider = ({
|
|
|
439
389
|
] });
|
|
440
390
|
};
|
|
441
391
|
var useCockpit = () => {
|
|
442
|
-
const { track, identify, distinctId, ready, embedded, consent, replayConsent, setConsent } = useCtx();
|
|
443
|
-
return { track, identify, distinctId, ready, embedded, consent, replay: replayConsent, setConsent };
|
|
392
|
+
const { track, identify, captureError, distinctId, ready, embedded, consent, replayConsent, setConsent } = useCtx();
|
|
393
|
+
return { track, identify, captureError, distinctId, ready, embedded, consent, replay: replayConsent, setConsent };
|
|
394
|
+
};
|
|
395
|
+
var useFlag = (key) => useFlagValue(key) !== false;
|
|
396
|
+
var useFlagVariant = (key) => {
|
|
397
|
+
const v = useFlagValue(key);
|
|
398
|
+
return typeof v === "string" ? v : null;
|
|
399
|
+
};
|
|
400
|
+
var useFlagValue = (key) => {
|
|
401
|
+
const { config, distinctId, user } = useCtx();
|
|
402
|
+
return useMemo(() => flagFor(key, config, distinctId, { ...user }), [key, config, distinctId, user]);
|
|
444
403
|
};
|
|
445
404
|
var useVariant = (key) => {
|
|
446
405
|
const { config, distinctId, preview, expose } = useCtx();
|
|
@@ -463,6 +422,8 @@ export {
|
|
|
463
422
|
Experiment,
|
|
464
423
|
Text,
|
|
465
424
|
useCockpit,
|
|
425
|
+
useFlag,
|
|
426
|
+
useFlagVariant,
|
|
466
427
|
useVariant
|
|
467
428
|
};
|
|
468
429
|
//# sourceMappingURL=client.js.map
|
package/dist/client.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/client.tsx"],"sourcesContent":["\"use client\";\n\nimport { type ReactNode, useCallback, useEffect, useMemo, useRef, useState } from \"react\";\nimport { usePathname } from \"next/navigation\";\nimport { Ctx, type CockpitContext, type CockpitUser, useCtx } from \"./context\";\nimport { Survey, forgetStoredDismissals } from \"./survey\";\nimport { RageDetector, clickInfo, seenDepth } from \"./capture\";\nimport type { ReplayHandle } from \"./replay\";\nimport {\n type AttributionMode,\n DEFAULT_REFERRAL_DAYS,\n captureReferral,\n carriesReferral,\n forgetStoredReferral,\n getReferral,\n resetReferral,\n storeReferral,\n} from \"./referral\";\nimport {\n type CockpitConfig,\n type CockpitEvent,\n type ConsentChoice,\n type ConsentMode,\n EMBED_PARAM,\n ID_COOKIE,\n PREVIEW_PARAM,\n SESSION_COOKIE,\n SESSION_TIMEOUT_MS,\n type UtmKey,\n type VariantId,\n envKey,\n newDistinctId,\n parsePreview,\n readUtm,\n resolveHost,\n resolveVariant,\n textChanges,\n} from \"./core\";\n\nconst YEAR_S = 60 * 60 * 24 * 365;\n/** The server takes at most 100 events and 64 KB per request; stay a little under the size. */\nconst MAX_BATCH = 100;\nconst MAX_BYTES = 60_000;\n\nconst readCookie = (name: string) => {\n if (typeof document === \"undefined\") return undefined;\n const raw = document.cookie\n .split(\"; \")\n .find((c) => c.startsWith(`${name}=`))\n ?.slice(name.length + 1);\n if (!raw) return undefined;\n try {\n return decodeURIComponent(raw);\n } catch {\n return raw;\n }\n};\n\nconst writeCookie = (name: string, value: string, maxAge: number) => {\n const secure = window.location.protocol === \"https:\" ? \"; secure\" : \"\";\n document.cookie = `${name}=${encodeURIComponent(value)}; path=/; max-age=${maxAge}; samesite=lax${secure}`;\n};\n\nconst deleteCookie = (name: string) => {\n document.cookie = `${name}=; path=/; max-age=0; samesite=lax`;\n};\n\n/**\n * What lives for one page load, outside React so it survives a provider that remounts on navigation: the in-memory\n * visitor id, the session, the current page's URL and referrer, and the campaign (UTM) parameters.\n * Only touched in the browser (effects and event handlers), never during a server render.\n */\nconst page: {\n id?: string;\n session?: { id: string; last: number };\n url?: string;\n referrer?: string;\n utm: Partial<Record<UtmKey, string>>;\n} = { utm: {} };\n\nconst memoryId = () => (page.id ??= newDistinctId());\n\n/**\n * The visitor id for experiments, surveys and feedback. Only \"cookies\" reads or writes `ck_id`; the other modes keep a\n * random id in memory for this page load. The page id follows the cookie, so leaving \"cookies\" keeps the same variants.\n */\nconst visitorId = (mode: ConsentMode, write: boolean) => {\n if (mode !== \"cookies\") return memoryId();\n const stored = readCookie(ID_COOKIE);\n if (stored) return (page.id = stored);\n const id = memoryId();\n if (write) writeCookie(ID_COOKIE, id, YEAR_S);\n return id;\n};\n\n/** The session id for an event. Every event renews it; after 30 minutes without one a new session starts. */\nconst touchSession = (persist: boolean) => {\n const now = Date.now();\n const s = page.session;\n // In \"cookies\" mode the cookie wins, so tabs share a session.\n const id = (persist ? readCookie(SESSION_COOKIE) : undefined) ?? (s && now - s.last < SESSION_TIMEOUT_MS ? s.id : newDistinctId());\n page.session = { id, last: now };\n if (persist) writeCookie(SESSION_COOKIE, id, SESSION_TIMEOUT_MS / 1000);\n return id;\n};\n\n/** On every route change: the previous in-app URL becomes the referrer, and a URL with UTM parameters replaces the old ones. */\nconst enterPage = () => {\n const href = window.location.href;\n if (page.url === href) return;\n page.referrer = page.url ?? (document.referrer || undefined);\n page.url = href;\n const utm = readUtm(window.location.search);\n if (Object.keys(utm).length) page.utm = utm;\n};\n\n/** Do Not Track or Global Privacy Control. */\nconst privacySignal = () => {\n const nav = navigator as Navigator & { doNotTrack?: string | null; globalPrivacyControl?: boolean };\n const win = window as unknown as { doNotTrack?: string | null };\n return nav.doNotTrack === \"1\" || win.doNotTrack === \"1\" || nav.globalPrivacyControl === true;\n};\n\n/** Tells the developer once, in the browser console, why the SDK is not reaching MRRocket (a wrong host or key is the usual cause). */\nconst warned = new Set<string>();\nconst warnOnce = (message: string) => {\n if (warned.has(message)) return;\n warned.add(message);\n console.warn(`[MRRocket] ${message}`);\n};\n\nconst byteLength = (s: string) => new TextEncoder().encode(s).length;\n\n/** Request bodies of at most 100 events each, under the size limit. An event too large on its own is dropped. */\nconst bodies = (key: string, events: CockpitEvent[]) => {\n const head = `{\"key\":${JSON.stringify(key)},\"events\":[`;\n const empty = byteLength(head) + 2;\n const out: string[] = [];\n let chunk: string[] = [];\n let size = empty;\n const close = () => {\n if (chunk.length) out.push(`${head}${chunk.join(\",\")}]}`);\n chunk = [];\n size = empty;\n };\n for (const event of events) {\n const json = JSON.stringify(event);\n const n = byteLength(json) + 1;\n if (empty + n > MAX_BYTES) continue;\n if (chunk.length === MAX_BATCH || size + n > MAX_BYTES) close();\n chunk.push(json);\n size += n;\n }\n close();\n return out;\n};\n\nexport interface CockpitProviderProps {\n /** The product's public key from Cockpit › Settings › SDK. Safe in the browser. Default: `NEXT_PUBLIC_MRROCKET_KEY`. */\n apiKey?: string;\n /** Where MRRocket runs, like `https://app.example.com`. Default: `NEXT_PUBLIC_MRROCKET_HOST`, else the page's own origin. */\n host?: string;\n preview?: Record<string, VariantId>;\n embedded?: boolean;\n /** Config fetched on the server, to render the right variant on the first paint. */\n config?: CockpitConfig;\n /** Your own id for this visitor, for example a signed-in user id. Keeps variants stable without cookies. */\n distinctId?: string;\n /**\n * \"cookieless\" (default) stores nothing in the browser, \"cookies\" keeps a visitor cookie for a year, \"off\" sends\n * nothing. The SDK does not remember the choice: set it on every page load, from the prop or setConsent().\n */\n consent?: ConsentMode;\n /** Send nothing when the browser asks not to be tracked (Do Not Track or Global Privacy Control). */\n respectDnt?: boolean;\n /**\n * The visitor's consent to session recordings, separate from analytics (default false). Recording also needs the\n * product to record (Cockpit › Sessions) and analytics not \"off\". Without it nothing is recorded or buffered.\n * Change it later with `setConsent({ replay })`.\n */\n replay?: boolean;\n /**\n * Clicks and scroll depth for Heatmaps (default true): where a click landed, a short selector and a link's or\n * button's label, never typed text. Sent under the normal analytics consent as `$click`, `$rageclick` and `$scroll`.\n */\n heatmaps?: boolean;\n /** Ask visitors the surveys you run in Cockpit. Set false to switch them off. */\n surveys?: boolean;\n /**\n * Affiliate referrals (`?ref=` links and MRRocket's tracked links). \"consent\" (default): stored in the first-party\n * `mrr_ref` cookie only while consent is \"cookies\", in memory otherwise. \"always\": stored whatever the consent (only\n * where your legal basis allows it). \"off\": referrals are ignored.\n */\n attribution?: AttributionMode;\n /** The query parameter of direct partner links, default `ref` (`https://your.site/?ref=maya`). */\n referralParam?: string;\n children: ReactNode;\n}\n\n/**\n * Put it once around your app, in `app/layout.tsx`. It loads your experiments, keeps each visitor in one variant,\n * records page views and sends your events to Cockpit in small batches.\n *\n * `preview` forces variants for this render, for example from the `ck_preview` search param on the server,\n * so a Cockpit preview never flickers. Without it the provider reads the param itself after the first render.\n */\nexport const CockpitProvider = ({\n apiKey: apiKeyProp,\n host: hostProp,\n preview: forced,\n embedded: embeddedProp,\n config: bootstrapConfig,\n distinctId: bootstrapId,\n consent: consentProp = \"cookieless\",\n respectDnt = false,\n replay: replayProp = false,\n heatmaps = true,\n surveys = true,\n attribution = \"consent\",\n referralParam = \"ref\",\n children,\n}: CockpitProviderProps) => {\n const pathname = usePathname();\n const apiKey = apiKeyProp ?? envKey() ?? \"\";\n const host = resolveHost(hostProp);\n const [config, setConfig] = useState<CockpitConfig | null>(bootstrapConfig ?? null);\n const [consent, setConsentState] = useState<ConsentMode>(consentProp);\n const [replayConsent, setReplayConsent] = useState(replayProp);\n const [mounted, setMounted] = useState(false);\n const [dnt, setDnt] = useState(false);\n // The visitor id and the consent mode it was made for; null until the browser has been read.\n const [visitor, setVisitor] = useState<{ mode: ConsentMode; id: string } | null>(null);\n const [identified, setIdentified] = useState<string | undefined>();\n const [urlPreview, setUrlPreview] = useState<Record<string, VariantId>>({});\n const [urlEmbedded, setUrlEmbedded] = useState(false);\n const [user, setUser] = useState<CockpitUser | undefined>();\n const [ready, setReady] = useState(!!bootstrapConfig);\n const queue = useRef<CockpitEvent[]>([]);\n // Calls from before the visitor was known (a child's effect runs before ours); replayed once it is.\n const early = useRef<(() => void)[]>([]);\n const exposed = useRef(new Set<string>());\n const lastPage = useRef<string | null>(null);\n\n const bootstrap = bootstrapId && bootstrapId !== \"anonymous\" ? bootstrapId : undefined;\n const preview = useMemo(() => ({ ...urlPreview, ...forced }), [urlPreview, forced]);\n const embedded = embeddedProp ?? urlEmbedded;\n const effectiveConsent: ConsentMode = respectDnt && dnt ? \"off\" : consent;\n const distinctId = identified ?? bootstrap ?? visitor?.id ?? \"anonymous\";\n const tracking = !embedded && effectiveConsent !== \"off\" && visitor?.mode === effectiveConsent;\n // Without cookies, events carry an id only after identify(); the server counts everyone else by its daily hash.\n const sendId = effectiveConsent === \"cookies\" ? distinctId : identified;\n\n // Preview flags and the browser's privacy signal, once, in the browser.\n useEffect(() => {\n const params = new URLSearchParams(window.location.search);\n setUrlPreview(parsePreview(params.get(PREVIEW_PARAM)));\n setUrlEmbedded(params.get(EMBED_PARAM) === \"1\");\n setDnt(privacySignal());\n setMounted(true);\n }, []);\n\n const setConsent = useCallback((choice: ConsentChoice) => {\n const { analytics: mode, replay } = typeof choice === \"string\" ? { analytics: choice, replay: undefined } : choice;\n if (mode === \"off\") queue.current.length = 0;\n if (mode) setConsentState(mode);\n if (typeof replay === \"boolean\") setReplayConsent(replay);\n }, []);\n\n // Follow the `consent` and `replay` props when they change; setConsent() calls in between stay until they do.\n const lastProp = useRef(consentProp);\n useEffect(() => {\n if (lastProp.current === consentProp) return;\n lastProp.current = consentProp;\n setConsent(consentProp);\n }, [consentProp, setConsent]);\n const lastReplayProp = useRef(replayProp);\n useEffect(() => {\n if (lastReplayProp.current === replayProp) return;\n lastReplayProp.current = replayProp;\n setConsent({ replay: replayProp });\n }, [replayProp, setConsent]);\n\n // Storage follows consent. Leaving \"cookies\" deletes what the SDK stored; this page keeps its id in memory.\n const storedFor = useRef<ConsentMode | null>(null);\n useEffect(() => {\n if (!mounted) return;\n const before = storedFor.current;\n storedFor.current = effectiveConsent;\n if (before === \"cookies\" && effectiveConsent !== \"cookies\") {\n deleteCookie(ID_COOKIE);\n deleteCookie(SESSION_COOKIE);\n forgetStoredDismissals();\n }\n if (effectiveConsent === \"off\") {\n queue.current.length = 0;\n page.session = undefined;\n }\n setVisitor({ mode: effectiveConsent, id: visitorId(effectiveConsent, !bootstrap) });\n }, [mounted, effectiveConsent, bootstrap]);\n\n // Experiments for this key. Without a key or when MRRocket is unreachable the page keeps control and carries on.\n useEffect(() => {\n if (!apiKey) warnOnce(\"No public key. Pass apiKey or set NEXT_PUBLIC_MRROCKET_KEY; nothing is tracked until then.\");\n if (bootstrapConfig || !apiKey) {\n setReady(true);\n return;\n }\n let live = true;\n fetch(`${host}/api/sdk/config?key=${encodeURIComponent(apiKey)}`)\n .then((r) => {\n if (!r.ok) warnOnce(`Config request to ${host || \"this origin\"} answered ${r.status}. Check the public key and NEXT_PUBLIC_MRROCKET_HOST.`);\n return r.ok ? r.json() : null;\n })\n .then((c: CockpitConfig | null) => {\n if (!live) return;\n if (c && Array.isArray(c.experiments)) setConfig(c);\n setReady(true);\n })\n .catch(() => {\n warnOnce(`Could not reach ${host || \"this origin\"}. Check NEXT_PUBLIC_MRROCKET_HOST. The page keeps working with control variants.`);\n if (live) setReady(true);\n });\n return () => {\n live = false;\n };\n }, [apiKey, host, bootstrapConfig]);\n\n const flush = useCallback(\n (beacon = false) => {\n if (!queue.current.length) return;\n if (!apiKey) {\n queue.current.length = 0;\n return;\n }\n const url = `${host}/api/sdk/events`;\n for (const body of bodies(apiKey, queue.current.splice(0))) {\n const sent = beacon && typeof navigator !== \"undefined\" && \"sendBeacon\" in navigator && navigator.sendBeacon(url, body);\n if (!sent)\n fetch(url, { method: \"POST\", headers: { \"content-type\": \"application/json\" }, body, keepalive: true })\n .then((r) => {\n if (!r.ok) warnOnce(`Events were rejected with ${r.status}. Check the public key (Settings › SDK) and NEXT_PUBLIC_MRROCKET_HOST.`);\n })\n .catch(() => warnOnce(`Could not reach ${host || \"this origin\"} to send events. Check NEXT_PUBLIC_MRROCKET_HOST.`));\n }\n },\n [apiKey, host],\n );\n\n useEffect(() => {\n const timer = window.setInterval(() => flush(), 2000);\n const leave = () => flush(true);\n window.addEventListener(\"pagehide\", leave);\n return () => {\n window.clearInterval(timer);\n window.removeEventListener(\"pagehide\", leave);\n flush(true);\n };\n }, [flush]);\n\n // The latest values for callbacks that must not change identity, so effects that use them run once.\n const live = useRef({ resolved: false, tracking, distinctId, sendId, persist: false, identified: false });\n live.current = { resolved: visitor !== null, tracking, distinctId, sendId, persist: visitor?.mode === \"cookies\", identified: !!identified };\n\n const record = useCallback((event: string, props: Record<string, unknown> | undefined, ts = Date.now(), url?: string) => {\n const now = live.current;\n if (!now.resolved) {\n early.current.push(() => record(event, props, ts, url));\n return;\n }\n // Previews in Cockpit never count, and nothing is sent without consent.\n if (!now.tracking) return;\n const referral = carriesReferral(event) ? getReferral() : null;\n queue.current.push({\n event,\n props: referral ? { ...props, $referral: { code: referral.code, clickId: referral.clickId } } : props,\n distinctId: now.sendId,\n sessionId: touchSession(now.persist),\n ts,\n url: url ?? window.location.href,\n referrer: page.referrer,\n ...page.utm,\n screen: { w: window.screen.width, h: window.screen.height },\n });\n }, []);\n\n // The running recorder, if this visit records (see the recording effect below).\n const replayer = useRef<ReplayHandle | null>(null);\n\n const track = useCallback(\n (event: string, props?: Record<string, unknown>) => {\n record(event, props);\n if (live.current.tracking) replayer.current?.event(event);\n },\n [record],\n );\n\n const identify = useCallback(\n (id: string, props?: Record<string, unknown>) => {\n const now = live.current;\n if (!now.resolved) {\n early.current.push(() => identify(id, props));\n return;\n }\n const previous = now.sendId;\n // From here on events carry this id in every mode; only \"cookies\" keeps it for the next visit.\n live.current = { ...now, distinctId: id, sendId: id, identified: true };\n record(\"$identify\", { ...props, id, ...(previous && previous !== id ? { previousId: previous } : {}) });\n replayer.current?.identify(id);\n if (now.persist) writeCookie(ID_COOKIE, id, YEAR_S);\n setIdentified(id);\n setUser({ id, ...(props as Omit<CockpitUser, \"id\">) });\n },\n [record],\n );\n\n // Replay what came in before the visitor was known: sent when tracking is on, dropped when it is not.\n useEffect(() => {\n if (!visitor) return;\n for (const run of early.current.splice(0)) run();\n }, [visitor]);\n\n // Affiliate referrals: read once the consent is known and the program's cookie days have loaded.\n const referralDays = config?.referral?.cookieDays ?? DEFAULT_REFERRAL_DAYS;\n const storeReferrals = attribution === \"always\" ? effectiveConsent !== \"off\" : attribution === \"consent\" && effectiveConsent === \"cookies\";\n const captured = useRef<string | null>(null);\n const storedReferral = useRef(false);\n useEffect(() => {\n if (!visitor || !ready || embedded) return;\n if (attribution === \"off\" || effectiveConsent === \"off\") {\n resetReferral();\n if (attribution === \"off\" || storedReferral.current) forgetStoredReferral();\n return;\n }\n const href = window.location.href;\n if (captured.current === href) return;\n captured.current = href;\n void captureReferral({\n search: window.location.search,\n param: referralParam,\n store: storeReferrals,\n days: referralDays,\n beacon: apiKey\n ? ({ code, clickId }) =>\n fetch(`${host}/api/sdk/affiliate/click`, {\n method: \"POST\",\n headers: { \"content-type\": \"application/json\" },\n body: JSON.stringify({ key: apiKey, code, clickId, url: href, referrer: document.referrer || undefined }),\n keepalive: true,\n })\n .then((r) => (r.ok ? (r.json() as Promise<{ clickId?: string | null }>) : null))\n .then((b) => b?.clickId ?? null)\n : undefined,\n });\n }, [visitor, ready, embedded, attribution, effectiveConsent, pathname, referralParam, storeReferrals, referralDays, apiKey, host]);\n\n // Consent given later keeps this page load's referral; consent withdrawn deletes what was stored.\n useEffect(() => {\n if (!mounted) return;\n const r = getReferral();\n if (storeReferrals && r) storeReferral(r, referralDays);\n else if (!storeReferrals && storedReferral.current) forgetStoredReferral();\n storedReferral.current = storeReferrals;\n }, [mounted, storeReferrals, referralDays]);\n\n // `tracking` is a dependency so that variants seen before consent are recorded once tracking starts.\n const expose = useCallback(\n (key: string, variant: VariantId) => {\n if (!tracking) return;\n const mark = `${pathname}|${key}`;\n if (exposed.current.has(mark)) return;\n exposed.current.add(mark);\n track(\"$exposure\", { experiment: key, variant });\n },\n [pathname, track, tracking],\n );\n\n // The referrer and campaign of each page; before the page view below, so it already carries them.\n useEffect(() => {\n enterPage();\n replayer.current?.page(pathname);\n }, [pathname]);\n\n // One page view per route change while tracking, so a later setConsent() records the current page right away\n // (a ref keeps React's dev double-run from counting twice).\n useEffect(() => {\n if (!tracking || lastPage.current === pathname) return;\n lastPage.current = pathname;\n // Exposures are remembered per path (\"/pricing|test\"), so nothing needs clearing here.\n track(\"$pageview\", { path: pathname });\n }, [pathname, tracking, track]);\n\n // Clicks on anything marked `data-ck-goal=\"signup_started\"` count as that goal. Every click also feeds Heatmaps\n // (`$click`, `$rageclick` on the third quick click on one spot) and, when this visit records, the recording.\n const rage = useRef(new RageDetector());\n useEffect(() => {\n const onClick = (e: MouseEvent) => {\n const el = (e.target as HTMLElement | null)?.closest<HTMLElement>(\"[data-ck-goal]\");\n if (el?.dataset.ckGoal) track(el.dataset.ckGoal, { text: el.textContent?.trim().slice(0, 80) });\n if (!live.current.tracking) return;\n const c = clickInfo(e);\n if (!c) return;\n const raged = rage.current.push(c.x, c.y, Date.now());\n replayer.current?.click(c, raged);\n if (!heatmaps) return;\n const props = { x: c.x, y: c.y, vw: c.vw, vh: c.vh, docH: c.docH, selector: c.selector, label: c.label, dead: !c.interactive };\n record(\"$click\", props);\n if (raged) record(\"$rageclick\", props);\n };\n document.addEventListener(\"click\", onClick);\n return () => document.removeEventListener(\"click\", onClick);\n }, [track, record, heatmaps]);\n\n // Scroll depth for Heatmaps: the deepest point seen on each page, sent when the page changes or hides.\n const depth = useRef<{ url: string; max: number; docH: number } | null>(null);\n useEffect(() => {\n if (!heatmaps || !tracking || embedded) return;\n const send = () => {\n const d = depth.current;\n if (d && d.max > 0) record(\"$scroll\", { depth: d.max, docH: d.docH, vw: window.innerWidth, vh: window.innerHeight }, Date.now(), d.url);\n depth.current = null;\n };\n const measure = () => {\n const doc = document.documentElement;\n const docH = Math.max(doc.scrollHeight, document.body?.scrollHeight ?? 0);\n const max = seenDepth();\n const href = window.location.href;\n if (depth.current?.url !== href) depth.current = { url: href, max, docH };\n else if (max > depth.current.max) depth.current = { url: href, max, docH };\n };\n measure();\n let frame = 0;\n const onScroll = () => {\n if (frame) return;\n frame = window.requestAnimationFrame(() => {\n frame = 0;\n measure();\n });\n };\n const onHide = () => {\n send();\n flush(true);\n };\n window.addEventListener(\"scroll\", onScroll, { passive: true });\n window.addEventListener(\"pagehide\", onHide);\n return () => {\n window.removeEventListener(\"scroll\", onScroll);\n window.removeEventListener(\"pagehide\", onHide);\n window.cancelAnimationFrame(frame);\n send();\n };\n }, [heatmaps, tracking, embedded, pathname, record, flush]);\n\n // Session recording: only with the visitor's replay consent, while tracking, and when the product records. The\n // recorder and rrweb are loaded here, lazily; withdrawing consent stops it and drops what was not sent.\n const replayConfig = config?.replay;\n const recording = tracking && replayConsent && !!replayConfig && !!apiKey;\n useEffect(() => {\n if (!recording || !replayConfig) return;\n let active = true;\n let handle: ReplayHandle | null = null;\n // `typeof window` is a build-time constant in Next.js: on the server this folds to null and the import (with\n // rrweb) never enters the server bundle.\n void (typeof window === \"undefined\" ? Promise.resolve(null) : import(\"./replay\"))\n .then((m) =>\n m?.startReplay({\n host,\n apiKey,\n sessionId: touchSession(live.current.persist),\n distinctId: live.current.sendId,\n identified: live.current.identified,\n sampleRate: replayConfig.sampleRate,\n warn: warnOnce,\n }),\n )\n .then((h) => {\n if (!active) h?.stop(true);\n else replayer.current = handle = h ?? null;\n })\n .catch(() => warnOnce(\"Session recording could not start (the recorder did not load).\"));\n return () => {\n active = false;\n handle?.stop(true);\n if (replayer.current === handle) replayer.current = null;\n };\n }, [recording, replayConfig, host, apiKey]);\n\n const texts = useMemo(() => textChanges(config, distinctId, preview), [config, distinctId, preview]);\n\n const value = useMemo<CockpitContext>(\n () => ({ apiKey, host, user, ready, distinctId, config, preview, embedded, consent, effectiveConsent, replayConsent, setConsent, texts, track, identify, expose }),\n [apiKey, host, user, ready, distinctId, config, preview, embedded, consent, effectiveConsent, replayConsent, setConsent, texts, track, identify, expose],\n );\n return (\n <Ctx.Provider value={value}>\n {children}\n {surveys ? <Survey /> : null}\n </Ctx.Provider>\n );\n};\n\n/**\n * `track`, `identify`, the consent mode, the replay consent and their setter, the visitor id and whether the\n * experiments have loaded. `setConsent(\"cookies\")` sets the mode, `setConsent({ replay: true })` the replay consent.\n */\nexport const useCockpit = () => {\n const { track, identify, distinctId, ready, embedded, consent, replayConsent, setConsent } = useCtx();\n return { track, identify, distinctId, ready, embedded, consent, replay: replayConsent, setConsent };\n};\n\n/** The variant this visitor sees, for example `\"control\"` or `\"a\"`. Records the exposure. */\nexport const useVariant = (key: string): VariantId => {\n const { config, distinctId, preview, expose } = useCtx();\n const variant = resolveVariant(key, config, distinctId, preview);\n useEffect(() => {\n if (config || preview[key]) expose(key, variant);\n }, [config, preview, key, variant, expose]);\n return variant;\n};\n\n/** Renders the element for the visitor's variant; control when the variant has no element. */\nexport const Experiment = ({ name, variants }: { name: string; variants: Record<VariantId, ReactNode> & { control: ReactNode } }) => {\n const variant = useVariant(name);\n return <>{variants[variant] ?? variants.control}</>;\n};\n\n/**\n * A text that experiments may change without a deploy: Cockpit sends the new copy for `id` and this renders it.\n * Write your default as the children.\n */\nexport const Text = ({ id, children }: { id: string; children: string }) => {\n const { texts } = useCtx();\n return <>{texts[id] ?? children}</>;\n};\n"],"mappings":"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","names":["live"]}
|
|
1
|
+
{"version":3,"sources":["../src/client.tsx"],"sourcesContent":["\"use client\";\n\nimport { type ReactNode, useCallback, useEffect, useMemo, useRef, useState } from \"react\";\nimport { usePathname } from \"next/navigation\";\nimport { Ctx, type CockpitContext, type CockpitUser, useCtx } from \"./context\";\nimport { Survey, forgetStoredDismissals } from \"./survey\";\nimport { RageDetector, clickInfo, seenDepth } from \"./capture\";\nimport type { ReplayHandle } from \"./replay\";\nimport { type ErrorCapture, startErrorCapture } from \"./errors\";\nimport {\n type AttributionMode,\n DEFAULT_REFERRAL_DAYS,\n captureReferral,\n carriesReferral,\n forgetStoredReferral,\n getReferral,\n resetReferral,\n storeReferral,\n} from \"./referral\";\nimport {\n type CockpitConfig,\n type CockpitEvent,\n type ConsentChoice,\n type ConsentMode,\n EMBED_PARAM,\n ID_COOKIE,\n PREVIEW_PARAM,\n SDK_VERSION,\n SESSION_COOKIE,\n type VariantId,\n envKey,\n flagFor,\n parsePreview,\n resolveHost,\n resolveVariant,\n textChanges,\n} from \"./core\";\nimport { bodies } from \"./batch\";\nimport { YEAR_S, deleteCookie, enterPage, page, privacySignal, touchSession, visitorId, writeCookie } from \"./web-state\";\n\n/** Tells the developer once, in the browser console, why the SDK is not reaching MRRocket (a wrong host or key is the usual cause). */\nconst warned = new Set<string>();\nconst warnOnce = (message: string) => {\n if (warned.has(message)) return;\n warned.add(message);\n console.warn(`[MRRocket] ${message}`);\n};\n\nexport interface CockpitProviderProps {\n /** The product's public key from Cockpit › Settings › SDK. Safe in the browser. Default: `NEXT_PUBLIC_MRROCKET_KEY`. */\n apiKey?: string;\n /** Where MRRocket runs, like `https://app.example.com`. Default: `NEXT_PUBLIC_MRROCKET_HOST`, else the page's own origin. */\n host?: string;\n preview?: Record<string, VariantId>;\n embedded?: boolean;\n /** Config fetched on the server, to render the right variant on the first paint. */\n config?: CockpitConfig;\n /** Your own id for this visitor, for example a signed-in user id. Keeps variants stable without cookies. */\n distinctId?: string;\n /**\n * \"cookieless\" (default) stores nothing in the browser, \"cookies\" keeps a visitor cookie for a year, \"off\" sends\n * nothing. The SDK does not remember the choice: set it on every page load, from the prop or setConsent().\n */\n consent?: ConsentMode;\n /** Send nothing when the browser asks not to be tracked (Do Not Track or Global Privacy Control). */\n respectDnt?: boolean;\n /**\n * The visitor's consent to session recordings, separate from analytics (default false). Recording also needs the\n * product to record (Cockpit › Sessions) and analytics not \"off\". Without it nothing is recorded or buffered.\n * Change it later with `setConsent({ replay })`.\n */\n replay?: boolean;\n /**\n * Clicks and scroll depth for Heatmaps (default true): where a click landed, a short selector and a link's or\n * button's label, never typed text. Sent under the normal analytics consent as `$click`, `$rageclick` and `$scroll`.\n */\n heatmaps?: boolean;\n /** Ask visitors the surveys you run in Cockpit. Set false to switch them off. */\n surveys?: boolean;\n /**\n * Error tracking for Build › Errors (default true): uncaught errors and unhandled promise rejections, with the page\n * and the last pages and clicks, under the normal analytics consent. Report errors you catch with\n * `useCockpit().captureError(error)`.\n */\n errors?: boolean;\n /** Your release (a version or a commit), so MRRocket can tell which release an error came with. */\n release?: string;\n /**\n * Affiliate referrals (`?ref=` links and MRRocket's tracked links). \"consent\" (default): stored in the first-party\n * `mrr_ref` cookie only while consent is \"cookies\", in memory otherwise. \"always\": stored whatever the consent (only\n * where your legal basis allows it). \"off\": referrals are ignored.\n */\n attribution?: AttributionMode;\n /** The query parameter of direct partner links, default `ref` (`https://your.site/?ref=maya`). */\n referralParam?: string;\n children: ReactNode;\n}\n\n/**\n * Put it once around your app, in `app/layout.tsx`. It loads your experiments, keeps each visitor in one variant,\n * records page views and sends your events to Cockpit in small batches.\n *\n * `preview` forces variants for this render, for example from the `ck_preview` search param on the server,\n * so a Cockpit preview never flickers. Without it the provider reads the param itself after the first render.\n */\nexport const CockpitProvider = ({\n apiKey: apiKeyProp,\n host: hostProp,\n preview: forced,\n embedded: embeddedProp,\n config: bootstrapConfig,\n distinctId: bootstrapId,\n consent: consentProp = \"cookieless\",\n respectDnt = false,\n replay: replayProp = false,\n heatmaps = true,\n surveys = true,\n errors = true,\n release,\n attribution = \"consent\",\n referralParam = \"ref\",\n children,\n}: CockpitProviderProps) => {\n const pathname = usePathname();\n const apiKey = apiKeyProp ?? envKey() ?? \"\";\n const host = resolveHost(hostProp);\n const [config, setConfig] = useState<CockpitConfig | null>(bootstrapConfig ?? null);\n const [consent, setConsentState] = useState<ConsentMode>(consentProp);\n const [replayConsent, setReplayConsent] = useState(replayProp);\n const [mounted, setMounted] = useState(false);\n const [dnt, setDnt] = useState(false);\n // The visitor id and the consent mode it was made for; null until the browser has been read.\n const [visitor, setVisitor] = useState<{ mode: ConsentMode; id: string } | null>(null);\n const [identified, setIdentified] = useState<string | undefined>();\n const [urlPreview, setUrlPreview] = useState<Record<string, VariantId>>({});\n const [urlEmbedded, setUrlEmbedded] = useState(false);\n const [user, setUser] = useState<CockpitUser | undefined>();\n const [ready, setReady] = useState(!!bootstrapConfig);\n const queue = useRef<CockpitEvent[]>([]);\n // Calls from before the visitor was known (a child's effect runs before ours); replayed once it is.\n const early = useRef<(() => void)[]>([]);\n const exposed = useRef(new Set<string>());\n const lastPage = useRef<string | null>(null);\n\n const bootstrap = bootstrapId && bootstrapId !== \"anonymous\" ? bootstrapId : undefined;\n const preview = useMemo(() => ({ ...urlPreview, ...forced }), [urlPreview, forced]);\n const embedded = embeddedProp ?? urlEmbedded;\n const effectiveConsent: ConsentMode = respectDnt && dnt ? \"off\" : consent;\n const distinctId = identified ?? bootstrap ?? visitor?.id ?? \"anonymous\";\n const tracking = !embedded && effectiveConsent !== \"off\" && visitor?.mode === effectiveConsent;\n // Without cookies, events carry an id only after identify(); the server counts everyone else by its daily hash.\n const sendId = effectiveConsent === \"cookies\" ? distinctId : identified;\n\n // Preview flags and the browser's privacy signal, once, in the browser.\n useEffect(() => {\n const params = new URLSearchParams(window.location.search);\n setUrlPreview(parsePreview(params.get(PREVIEW_PARAM)));\n setUrlEmbedded(params.get(EMBED_PARAM) === \"1\");\n setDnt(privacySignal());\n setMounted(true);\n }, []);\n\n const setConsent = useCallback((choice: ConsentChoice) => {\n const { analytics: mode, replay } = typeof choice === \"string\" ? { analytics: choice, replay: undefined } : choice;\n if (mode === \"off\") queue.current.length = 0;\n if (mode) setConsentState(mode);\n if (typeof replay === \"boolean\") setReplayConsent(replay);\n }, []);\n\n // Follow the `consent` and `replay` props when they change; setConsent() calls in between stay until they do.\n const lastProp = useRef(consentProp);\n useEffect(() => {\n if (lastProp.current === consentProp) return;\n lastProp.current = consentProp;\n setConsent(consentProp);\n }, [consentProp, setConsent]);\n const lastReplayProp = useRef(replayProp);\n useEffect(() => {\n if (lastReplayProp.current === replayProp) return;\n lastReplayProp.current = replayProp;\n setConsent({ replay: replayProp });\n }, [replayProp, setConsent]);\n\n // Storage follows consent. Leaving \"cookies\" deletes what the SDK stored; this page keeps its id in memory.\n const storedFor = useRef<ConsentMode | null>(null);\n useEffect(() => {\n if (!mounted) return;\n const before = storedFor.current;\n storedFor.current = effectiveConsent;\n if (before === \"cookies\" && effectiveConsent !== \"cookies\") {\n deleteCookie(ID_COOKIE);\n deleteCookie(SESSION_COOKIE);\n forgetStoredDismissals();\n }\n if (effectiveConsent === \"off\") {\n queue.current.length = 0;\n page.session = undefined;\n }\n setVisitor({ mode: effectiveConsent, id: visitorId(effectiveConsent, !bootstrap) });\n }, [mounted, effectiveConsent, bootstrap]);\n\n // Experiments for this key. Without a key or when MRRocket is unreachable the page keeps control and carries on.\n useEffect(() => {\n if (!apiKey) warnOnce(\"No public key. Pass apiKey or set NEXT_PUBLIC_MRROCKET_KEY; nothing is tracked until then.\");\n if (bootstrapConfig || !apiKey) {\n setReady(true);\n return;\n }\n let live = true;\n fetch(`${host}/api/sdk/config?key=${encodeURIComponent(apiKey)}`)\n .then((r) => {\n if (!r.ok) warnOnce(`Config request to ${host || \"this origin\"} answered ${r.status}. Check the public key and NEXT_PUBLIC_MRROCKET_HOST.`);\n return r.ok ? r.json() : null;\n })\n .then((c: CockpitConfig | null) => {\n if (!live) return;\n if (c && Array.isArray(c.experiments)) setConfig(c);\n setReady(true);\n })\n .catch(() => {\n warnOnce(`Could not reach ${host || \"this origin\"}. Check NEXT_PUBLIC_MRROCKET_HOST. The page keeps working with control variants.`);\n if (live) setReady(true);\n });\n return () => {\n live = false;\n };\n }, [apiKey, host, bootstrapConfig]);\n\n const flush = useCallback(\n (beacon = false) => {\n if (!queue.current.length) return;\n if (!apiKey) {\n queue.current.length = 0;\n return;\n }\n const url = `${host}/api/sdk/events`;\n for (const body of bodies(apiKey, queue.current.splice(0), { v: SDK_VERSION, errors })) {\n const sent = beacon && typeof navigator !== \"undefined\" && \"sendBeacon\" in navigator && navigator.sendBeacon(url, body);\n if (!sent)\n fetch(url, { method: \"POST\", headers: { \"content-type\": \"application/json\" }, body, keepalive: true })\n .then((r) => {\n if (!r.ok) warnOnce(`Events were rejected with ${r.status}. Check the public key (Settings › SDK) and NEXT_PUBLIC_MRROCKET_HOST.`);\n })\n .catch(() => warnOnce(`Could not reach ${host || \"this origin\"} to send events. Check NEXT_PUBLIC_MRROCKET_HOST.`));\n }\n },\n [apiKey, host, errors],\n );\n\n useEffect(() => {\n const timer = window.setInterval(() => flush(), 2000);\n const leave = () => flush(true);\n window.addEventListener(\"pagehide\", leave);\n return () => {\n window.clearInterval(timer);\n window.removeEventListener(\"pagehide\", leave);\n flush(true);\n };\n }, [flush]);\n\n // The latest values for callbacks that must not change identity, so effects that use them run once.\n const live = useRef({ resolved: false, tracking, distinctId, sendId, persist: false, identified: false });\n live.current = { resolved: visitor !== null, tracking, distinctId, sendId, persist: visitor?.mode === \"cookies\", identified: !!identified };\n\n const record = useCallback((event: string, props: Record<string, unknown> | undefined, ts = Date.now(), url?: string) => {\n const now = live.current;\n if (!now.resolved) {\n early.current.push(() => record(event, props, ts, url));\n return;\n }\n // Previews in Cockpit never count, and nothing is sent without consent.\n if (!now.tracking) return;\n const referral = carriesReferral(event) ? getReferral() : null;\n queue.current.push({\n event,\n props: referral ? { ...props, $referral: { code: referral.code, clickId: referral.clickId } } : props,\n distinctId: now.sendId,\n sessionId: touchSession(now.persist),\n ts,\n url: url ?? window.location.href,\n referrer: page.referrer,\n ...page.utm,\n screen: { w: window.screen.width, h: window.screen.height },\n });\n }, []);\n\n // The running recorder, if this visit records (see the recording effect below).\n const replayer = useRef<ReplayHandle | null>(null);\n // Error tracking, while `errors` is on (see the effect below).\n const errorer = useRef<ErrorCapture | null>(null);\n\n const track = useCallback(\n (event: string, props?: Record<string, unknown>) => {\n record(event, props);\n if (live.current.tracking) replayer.current?.event(event);\n },\n [record],\n );\n\n const identify = useCallback(\n (id: string, props?: Record<string, unknown>) => {\n const now = live.current;\n if (!now.resolved) {\n early.current.push(() => identify(id, props));\n return;\n }\n const previous = now.sendId;\n // From here on events carry this id in every mode; only \"cookies\" keeps it for the next visit.\n live.current = { ...now, distinctId: id, sendId: id, identified: true };\n record(\"$identify\", { ...props, id, ...(previous && previous !== id ? { previousId: previous } : {}) });\n replayer.current?.identify(id);\n if (now.persist) writeCookie(ID_COOKIE, id, YEAR_S);\n setIdentified(id);\n setUser({ id, ...(props as Omit<CockpitUser, \"id\">) });\n },\n [record],\n );\n\n // Replay what came in before the visitor was known: sent when tracking is on, dropped when it is not.\n useEffect(() => {\n if (!visitor) return;\n for (const run of early.current.splice(0)) run();\n }, [visitor]);\n\n // Affiliate referrals: read once the consent is known and the program's cookie days have loaded.\n const referralDays = config?.referral?.cookieDays ?? DEFAULT_REFERRAL_DAYS;\n const storeReferrals = attribution === \"always\" ? effectiveConsent !== \"off\" : attribution === \"consent\" && effectiveConsent === \"cookies\";\n const captured = useRef<string | null>(null);\n const storedReferral = useRef(false);\n useEffect(() => {\n if (!visitor || !ready || embedded) return;\n if (attribution === \"off\" || effectiveConsent === \"off\") {\n resetReferral();\n if (attribution === \"off\" || storedReferral.current) forgetStoredReferral();\n return;\n }\n const href = window.location.href;\n if (captured.current === href) return;\n captured.current = href;\n void captureReferral({\n search: window.location.search,\n param: referralParam,\n store: storeReferrals,\n days: referralDays,\n beacon: apiKey\n ? ({ code, clickId }) =>\n fetch(`${host}/api/sdk/affiliate/click`, {\n method: \"POST\",\n headers: { \"content-type\": \"application/json\" },\n body: JSON.stringify({ key: apiKey, code, clickId, url: href, referrer: document.referrer || undefined }),\n keepalive: true,\n })\n .then((r) => (r.ok ? (r.json() as Promise<{ clickId?: string | null }>) : null))\n .then((b) => b?.clickId ?? null)\n : undefined,\n });\n }, [visitor, ready, embedded, attribution, effectiveConsent, pathname, referralParam, storeReferrals, referralDays, apiKey, host]);\n\n // Consent given later keeps this page load's referral; consent withdrawn deletes what was stored.\n useEffect(() => {\n if (!mounted) return;\n const r = getReferral();\n if (storeReferrals && r) storeReferral(r, referralDays);\n else if (!storeReferrals && storedReferral.current) forgetStoredReferral();\n storedReferral.current = storeReferrals;\n }, [mounted, storeReferrals, referralDays]);\n\n // `tracking` is a dependency so that variants seen before consent are recorded once tracking starts.\n const expose = useCallback(\n (key: string, variant: VariantId) => {\n if (!tracking) return;\n const mark = `${pathname}|${key}`;\n if (exposed.current.has(mark)) return;\n exposed.current.add(mark);\n track(\"$exposure\", { experiment: key, variant });\n },\n [pathname, track, tracking],\n );\n\n // The referrer and campaign of each page; before the page view below, so it already carries them.\n useEffect(() => {\n enterPage();\n replayer.current?.page(pathname);\n errorer.current?.crumb(\"navigation\", pathname);\n }, [pathname]);\n\n // Errors of the page for Build › Errors; sent only while tracking (consent, not a preview), like events.\n useEffect(() => {\n if (!errors || !apiKey || embedded) return;\n const capture = startErrorCapture({\n host,\n apiKey,\n release,\n context: () => ({ send: live.current.tracking, distinctId: live.current.sendId, sessionId: page.session?.id }),\n warn: warnOnce,\n });\n errorer.current = capture;\n capture.crumb(\"navigation\", window.location.pathname);\n return () => {\n capture.stop();\n if (errorer.current === capture) errorer.current = null;\n };\n }, [errors, apiKey, host, release, embedded]);\n\n const captureError = useCallback((error: unknown) => errorer.current?.capture(error, \"manual\"), []);\n\n // One page view per route change while tracking, so a later setConsent() records the current page right away\n // (a ref keeps React's dev double-run from counting twice).\n useEffect(() => {\n if (!tracking || lastPage.current === pathname) return;\n lastPage.current = pathname;\n // Exposures are remembered per path (\"/pricing|test\"), so nothing needs clearing here.\n track(\"$pageview\", { path: pathname });\n }, [pathname, tracking, track]);\n\n // Clicks on anything marked `data-ck-goal=\"signup_started\"` count as that goal. Every click also feeds Heatmaps\n // (`$click`, `$rageclick` on the third quick click on one spot) and, when this visit records, the recording.\n const rage = useRef(new RageDetector());\n useEffect(() => {\n const onClick = (e: MouseEvent) => {\n const el = (e.target as HTMLElement | null)?.closest<HTMLElement>(\"[data-ck-goal]\");\n if (el?.dataset.ckGoal) track(el.dataset.ckGoal, { text: el.textContent?.trim().slice(0, 80) });\n if (!live.current.tracking) return;\n const c = clickInfo(e);\n if (!c) return;\n const raged = rage.current.push(c.x, c.y, Date.now());\n if (c.label || c.selector) errorer.current?.crumb(\"click\", c.label || c.selector);\n replayer.current?.click(c, raged);\n if (!heatmaps) return;\n const props = { x: c.x, y: c.y, vw: c.vw, vh: c.vh, docH: c.docH, selector: c.selector, label: c.label, dead: !c.interactive };\n record(\"$click\", props);\n if (raged) record(\"$rageclick\", props);\n };\n document.addEventListener(\"click\", onClick);\n return () => document.removeEventListener(\"click\", onClick);\n }, [track, record, heatmaps]);\n\n // Scroll depth for Heatmaps: the deepest point seen on each page, sent when the page changes or hides.\n const depth = useRef<{ url: string; max: number; docH: number } | null>(null);\n useEffect(() => {\n if (!heatmaps || !tracking || embedded) return;\n const send = () => {\n const d = depth.current;\n if (d && d.max > 0) record(\"$scroll\", { depth: d.max, docH: d.docH, vw: window.innerWidth, vh: window.innerHeight }, Date.now(), d.url);\n depth.current = null;\n };\n const measure = () => {\n const doc = document.documentElement;\n const docH = Math.max(doc.scrollHeight, document.body?.scrollHeight ?? 0);\n const max = seenDepth();\n const href = window.location.href;\n if (depth.current?.url !== href) depth.current = { url: href, max, docH };\n else if (max > depth.current.max) depth.current = { url: href, max, docH };\n };\n measure();\n let frame = 0;\n const onScroll = () => {\n if (frame) return;\n frame = window.requestAnimationFrame(() => {\n frame = 0;\n measure();\n });\n };\n const onHide = () => {\n send();\n flush(true);\n };\n window.addEventListener(\"scroll\", onScroll, { passive: true });\n window.addEventListener(\"pagehide\", onHide);\n return () => {\n window.removeEventListener(\"scroll\", onScroll);\n window.removeEventListener(\"pagehide\", onHide);\n window.cancelAnimationFrame(frame);\n send();\n };\n }, [heatmaps, tracking, embedded, pathname, record, flush]);\n\n // Session recording: only with the visitor's replay consent, while tracking, and when the product records. The\n // recorder and rrweb are loaded here, lazily; withdrawing consent stops it and drops what was not sent.\n const replayConfig = config?.replay;\n const recording = tracking && replayConsent && !!replayConfig && !!apiKey;\n useEffect(() => {\n if (!recording || !replayConfig) return;\n let active = true;\n let handle: ReplayHandle | null = null;\n // `typeof window` is a build-time constant in Next.js: on the server this folds to null and the import (with\n // rrweb) never enters the server bundle.\n void (typeof window === \"undefined\" ? Promise.resolve(null) : import(\"./replay\"))\n .then((m) =>\n m?.startReplay({\n host,\n apiKey,\n sessionId: touchSession(live.current.persist),\n distinctId: live.current.sendId,\n identified: live.current.identified,\n sampleRate: replayConfig.sampleRate,\n warn: warnOnce,\n }),\n )\n .then((h) => {\n if (!active) h?.stop(true);\n else replayer.current = handle = h ?? null;\n })\n .catch(() => warnOnce(\"Session recording could not start (the recorder did not load).\"));\n return () => {\n active = false;\n handle?.stop(true);\n if (replayer.current === handle) replayer.current = null;\n };\n }, [recording, replayConfig, host, apiKey]);\n\n const texts = useMemo(() => textChanges(config, distinctId, preview), [config, distinctId, preview]);\n\n const value = useMemo<CockpitContext>(\n () => ({ apiKey, host, user, ready, distinctId, config, preview, embedded, consent, effectiveConsent, replayConsent, setConsent, texts, track, identify, expose, captureError }),\n [apiKey, host, user, ready, distinctId, config, preview, embedded, consent, effectiveConsent, replayConsent, setConsent, texts, track, identify, expose, captureError],\n );\n return (\n <Ctx.Provider value={value}>\n {children}\n {surveys ? <Survey /> : null}\n </Ctx.Provider>\n );\n};\n\n/**\n * `track`, `identify`, `captureError`, the consent mode, the replay consent and their setter, the visitor id and whether\n * the experiments and flags have loaded. `setConsent(\"cookies\")` sets the mode, `setConsent({ replay: true })` the\n * replay consent.\n */\nexport const useCockpit = () => {\n const { track, identify, captureError, distinctId, ready, embedded, consent, replayConsent, setConsent } = useCtx();\n return { track, identify, captureError, distinctId, ready, embedded, consent, replay: replayConsent, setConsent };\n};\n\n/**\n * A feature flag from MRRocket › Build › Feature flags: true while it is on for this visitor (its audience and rollout\n * in Production), false while it is off, unknown, or the flags have not loaded yet (`useCockpit().ready`). The person's\n * properties from `identify(id, props)` feed the audience conditions (\"Email\", \"Plan\" …).\n *\n * const newCheckout = useFlag(\"new-checkout\");\n */\nexport const useFlag = (key: string): boolean => useFlagValue(key) !== false;\n\n/** The variant of a multivariate flag for this visitor (\"control\", \"test\" …), or null while the flag is off. */\nexport const useFlagVariant = (key: string): string | null => {\n const v = useFlagValue(key);\n return typeof v === \"string\" ? v : null;\n};\n\nconst useFlagValue = (key: string) => {\n const { config, distinctId, user } = useCtx();\n return useMemo(() => flagFor(key, config, distinctId, { ...user }), [key, config, distinctId, user]);\n};\n\n/** The variant this visitor sees, for example `\"control\"` or `\"a\"`. Records the exposure. */\nexport const useVariant = (key: string): VariantId => {\n const { config, distinctId, preview, expose } = useCtx();\n const variant = resolveVariant(key, config, distinctId, preview);\n useEffect(() => {\n if (config || preview[key]) expose(key, variant);\n }, [config, preview, key, variant, expose]);\n return variant;\n};\n\n/** Renders the element for the visitor's variant; control when the variant has no element. */\nexport const Experiment = ({ name, variants }: { name: string; variants: Record<VariantId, ReactNode> & { control: ReactNode } }) => {\n const variant = useVariant(name);\n return <>{variants[variant] ?? variants.control}</>;\n};\n\n/**\n * A text that experiments may change without a deploy: Cockpit sends the new copy for `id` and this renders it.\n * Write your default as the children.\n */\nexport const Text = ({ id, children }: { id: string; children: string }) => {\n const { texts } = useCtx();\n return <>{texts[id] ?? children}</>;\n};\n"],"mappings":"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","names":["live"]}
|
package/dist/context.d.ts
CHANGED
|
@@ -28,6 +28,8 @@ export interface CockpitContext {
|
|
|
28
28
|
identify: (id: string, props?: Record<string, unknown>) => void;
|
|
29
29
|
/** Records that the visitor saw a variant, once per experiment and page view. */
|
|
30
30
|
expose: (key: string, variant: VariantId) => void;
|
|
31
|
+
/** Reports an error you caught to Build › Errors (while error tracking is on). */
|
|
32
|
+
captureError: (error: unknown) => void;
|
|
31
33
|
}
|
|
32
34
|
export declare const Ctx: import("react").Context<CockpitContext | null>;
|
|
33
35
|
export declare const useCtx: () => CockpitContext;
|
package/dist/context.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/context.ts"],"sourcesContent":["\"use client\";\n\nimport { createContext, useContext } from \"react\";\nimport type { CockpitConfig, ConsentChoice, ConsentMode, VariantId } from \"./core\";\n\n/** Who the visitor is, from identify(). Sent along with feedback and survey answers. */\nexport interface CockpitUser {\n id: string;\n name?: string;\n email?: string;\n [key: string]: unknown;\n}\n\nexport interface CockpitContext {\n apiKey: string;\n host: string;\n user?: CockpitUser;\n ready: boolean;\n distinctId: string;\n config: CockpitConfig | null;\n preview: Record<string, VariantId>;\n /** Embedded in Cockpit as a preview: nothing is tracked. */\n embedded: boolean;\n /** The consent mode the site set, with the `consent` prop or setConsent(). */\n consent: ConsentMode;\n /** What applies right now: \"off\" when the browser sends Do Not Track and the provider respects it. */\n effectiveConsent: ConsentMode;\n /** The visitor's consent to session recordings (`replay` prop or `setConsent({ replay })`). */\n replayConsent: boolean;\n setConsent: (choice: ConsentChoice) => void;\n texts: Record<string, string>;\n track: (event: string, props?: Record<string, unknown>) => void;\n identify: (id: string, props?: Record<string, unknown>) => void;\n /** Records that the visitor saw a variant, once per experiment and page view. */\n expose: (key: string, variant: VariantId) => void;\n}\n\nexport const Ctx = createContext<CockpitContext | null>(null);\n\nexport const useCtx = () => {\n const c = useContext(Ctx);\n if (!c) throw new Error(\"Cockpit: wrap your app in <CockpitProvider apiKey=…>.\");\n return c;\n};\n"],"mappings":";;;AAEA,SAAS,eAAe,kBAAkB;
|
|
1
|
+
{"version":3,"sources":["../src/context.ts"],"sourcesContent":["\"use client\";\n\nimport { createContext, useContext } from \"react\";\nimport type { CockpitConfig, ConsentChoice, ConsentMode, VariantId } from \"./core\";\n\n/** Who the visitor is, from identify(). Sent along with feedback and survey answers. */\nexport interface CockpitUser {\n id: string;\n name?: string;\n email?: string;\n [key: string]: unknown;\n}\n\nexport interface CockpitContext {\n apiKey: string;\n host: string;\n user?: CockpitUser;\n ready: boolean;\n distinctId: string;\n config: CockpitConfig | null;\n preview: Record<string, VariantId>;\n /** Embedded in Cockpit as a preview: nothing is tracked. */\n embedded: boolean;\n /** The consent mode the site set, with the `consent` prop or setConsent(). */\n consent: ConsentMode;\n /** What applies right now: \"off\" when the browser sends Do Not Track and the provider respects it. */\n effectiveConsent: ConsentMode;\n /** The visitor's consent to session recordings (`replay` prop or `setConsent({ replay })`). */\n replayConsent: boolean;\n setConsent: (choice: ConsentChoice) => void;\n texts: Record<string, string>;\n track: (event: string, props?: Record<string, unknown>) => void;\n identify: (id: string, props?: Record<string, unknown>) => void;\n /** Records that the visitor saw a variant, once per experiment and page view. */\n expose: (key: string, variant: VariantId) => void;\n /** Reports an error you caught to Build › Errors (while error tracking is on). */\n captureError: (error: unknown) => void;\n}\n\nexport const Ctx = createContext<CockpitContext | null>(null);\n\nexport const useCtx = () => {\n const c = useContext(Ctx);\n if (!c) throw new Error(\"Cockpit: wrap your app in <CockpitProvider apiKey=…>.\");\n return c;\n};\n"],"mappings":";;;AAEA,SAAS,eAAe,kBAAkB;AAqCnC,IAAM,MAAM,cAAqC,IAAI;AAErD,IAAM,SAAS,MAAM;AACxB,QAAM,IAAI,WAAW,GAAG;AACxB,MAAI,CAAC,EAAG,OAAM,IAAI,MAAM,4DAAuD;AAC/E,SAAO;AACX;","names":[]}
|