@mrrocket/sdk 0.12.0 → 0.13.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 +6 -0
- package/README.md +26 -0
- package/dist/browser/mrrocket.js +1 -1
- package/dist/core.d.ts +1 -1
- package/dist/core.js +1 -1
- package/dist/core.js.map +1 -1
- package/dist/proxy.d.ts +1 -0
- package/dist/proxy.js +3 -0
- package/dist/proxy.js.map +1 -1
- package/dist/react-native/index.d.cts +1 -1
- package/dist/react-native/index.d.ts +1 -1
- package/dist/react-native/index.js +1 -1
- package/dist/react-native/index.js.map +1 -1
- package/dist/security.d.ts +71 -0
- package/dist/security.js +73 -0
- package/dist/security.js.map +1 -0
- package/dist/server.d.ts +1 -0
- package/dist/server.js +8 -1
- package/dist/server.js.map +1 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,12 @@
|
|
|
2
2
|
|
|
3
3
|
All notable changes to this package. It follows semantic versioning; before 1.0 a minor version may change an API.
|
|
4
4
|
|
|
5
|
+
## 0.13.0 (not published yet)
|
|
6
|
+
|
|
7
|
+
- **Attack reports, from your server only** (`@mrrocket/sdk/next/server`, `watchRequest` and `reportSuspiciousRequest` also in `@mrrocket/sdk/next/proxy`): `reportFailedLogin({ userId, account, ip, reason })`, `reportAuthDenied({ status, path, ip, userId })`, `reportSuspiciousRequest({ path, query, ip, reason })` and `watchRequest(request)` for `proxy.ts`, which reads the path and query and reports only what looks like SQL injection, XSS, path traversal or a scanner's path (`/.env`, `/.git`, `/wp-admin`, `/phpmyadmin` …); an ordinary request sends nothing. They send `$login_failed`, `$auth_denied` and `$suspicious_request` with the product's server key (`MRROCKET_SERVER_KEY`); MRRocket hashes the IP and the account name on arrival (a daily hash) and its Build agent puts a possible attack into Needs you as Important. Every helper resolves to `true`/`false` and never throws. `attackIn(path, query)` and `ipOf(headers)` are exported too.
|
|
8
|
+
- Nothing changes in the browser: the script tag and the client bundle stay as they were.
|
|
9
|
+
- Version reported in event batches: `0.13.0`.
|
|
10
|
+
|
|
5
11
|
## 0.12.0 (not published yet)
|
|
6
12
|
|
|
7
13
|
- **Form leads:** a submitted form marked `data-mrrocket-form="<name>"` becomes a contact in MRRocket (`POST /api/sdk/forms`, sent with `sendBeacon`). It sends the email, the name and the other fields a person typed, plus the form's name, page, referrer, UTM and the state of a checkbox marked `data-mrrocket-consent`; never passwords, card, hidden or file fields, fields whose name looks sensitive (`SENSITIVE`) or anything inside `data-mrrocket-ignore`. With analytics consent the lead carries the visitor id and the SDK calls `identify(email)` when your code has not identified the person yet, so their history carries over. Opt-in auto-detection captures any form with an email field and no password field.
|
package/README.md
CHANGED
|
@@ -519,6 +519,32 @@ A form on your site becomes a contact in MRRocket when someone submits it. Mark
|
|
|
519
519
|
- **Script tag:** marked forms work as they are; the capture code (`sdk-forms.js`, about 2 KB gzip) loads only on pages with one. `data-forms="auto"` on the script tag captures any email form.
|
|
520
520
|
- **Not your form?** Typeform, Tally, Cal.com, Calendly, Webflow and Framer post to the product's lead webhook instead (MRRocket › Settings › Integrations › Leads and API).
|
|
521
521
|
|
|
522
|
+
## 20. Attack reports (0.13.0)
|
|
523
|
+
|
|
524
|
+
From your app's **server** only, with the product's server key in `MRROCKET_SERVER_KEY` (MRRocket › Settings › Products › Sign-in). MRRocket's Build agent watches the reports and puts a possible attack into Needs you as Important: many failed sign-ins on one account or from one address, a wave over many accounts, SQL injection / XSS / path traversal in requests, a scanner probing `/.env` or `/wp-admin`, a spike of 401/403 answers.
|
|
525
|
+
|
|
526
|
+
```ts
|
|
527
|
+
// proxy.ts: reports only requests that look like an attack, ordinary ones send nothing
|
|
528
|
+
import { watchRequest } from "@mrrocket/sdk/next/proxy";
|
|
529
|
+
|
|
530
|
+
export function proxy(request: NextRequest, event: NextFetchEvent) {
|
|
531
|
+
const report = watchRequest(request);
|
|
532
|
+
if (report) event.waitUntil(report);
|
|
533
|
+
return NextResponse.next();
|
|
534
|
+
}
|
|
535
|
+
```
|
|
536
|
+
|
|
537
|
+
```ts
|
|
538
|
+
import { ipOf, reportAuthDenied, reportFailedLogin } from "@mrrocket/sdk/next/server";
|
|
539
|
+
|
|
540
|
+
// where a sign-in fails (userId when the account exists, account = what was typed in)
|
|
541
|
+
await reportFailedLogin({ userId: user?.id, account: email, ip: ipOf(request.headers), reason: "wrong_password" });
|
|
542
|
+
// where your API refuses a request
|
|
543
|
+
await reportAuthDenied({ status: 403, path: url.pathname, ip: ipOf(request.headers), userId: session?.userId });
|
|
544
|
+
```
|
|
545
|
+
|
|
546
|
+
They send `$login_failed`, `$auth_denied` and `$suspicious_request` to `/api/sdk/events` with `server: true`. MRRocket hashes the IP and the account name on arrival (a hash that changes every day) and never stores them as sent. Every helper resolves to `true` or `false` and never throws. Using WorkOS or Auth0 webhooks for sign-in? Their failed sign-ins (WorkOS `authentication.*_failed`, Auth0 log types `f`, `fp`, `fu`, `limit_wc`, `limit_mu`) are read without any code; subscribe the webhook to those events.
|
|
547
|
+
|
|
522
548
|
## Privacy and consent
|
|
523
549
|
|
|
524
550
|
```tsx
|
package/dist/browser/mrrocket.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
(function(){'use strict';var on=Object.defineProperty;var sn=(e,t,r)=>t in e?on(e,t,{enumerable:true,configurable:true,writable:true,value:r}):e[t]=r;var we=(e,t,r)=>sn(e,typeof t!="symbol"?t+"":t,r);var Ct=e=>{let t=0;for(let r=0;r<e.length;r++){let s=e.charCodeAt(r);s<128?t+=1:s<2048?t+=2:s>=55296&&s<=56319&&r+1<e.length&&(e.charCodeAt(r+1)&64512)===56320?(t+=4,r++):t+=3;}return t},an=(e,t,r)=>{let s=`{"key":${JSON.stringify(e)},"sdk":${JSON.stringify(r)},"events":[`,c=Ct(s)+2,a=[],f=[],C=[],v=c,k=()=>{f.length&&a.push({body:`${s}${f.join(",")}]}`,events:C}),f=[],C=[],v=c;};for(let E of t){let g=JSON.stringify(E),w=Ct(g)+1;c+w>6e4||((f.length===100||v+w>6e4)&&k(),f.push(g),C.push(E),v+=w);}return k(),a},Rt=(e,t,r)=>an(e,t,r).map(s=>s.body);var xe=class{constructor(t=1e3,r=30,s=3){we(this,"windowMs",t);we(this,"radius",r);we(this,"count",s);we(this,"clicks",[]);we(this,"fired",false);}push(t,r,s){let c=this.clicks.filter(a=>s-a.t<=this.windowMs&&Math.hypot(a.x-t,a.y-r)<=this.radius);return c.length!==this.clicks.length&&(this.fired=false),this.clicks=[...c,{x:t,y:r,t:s}],this.clicks.length>=this.count&&!this.fired?(this.fired=true,true):false}};var cn='a[href],button,input,select,textarea,label,summary,[role="button"],[role="link"],[role="tab"],[role="menuitem"],[role="checkbox"],[role="switch"],[onclick],[data-ck-goal]',ln="[data-ck-mask]",dn="[data-ck-block]",vt=/\d{4,}|^[a-z]{1,3}-[a-z0-9]{5,}$|^_|__|css-|^sc-|^jsx-/i,un=e=>{let t=e.tagName.toLowerCase();if(e.id&&!vt.test(e.id)&&e.id.length<=40)return `${t}#${e.id}`;let r=typeof e.className=="string"?e.className.split(/\s+/).filter(a=>a&&!vt.test(a)&&a.length<=30&&!a.includes(":")):[],s=r.length?`${t}.${r.slice(0,2).join(".")}`:t,c=e.parentElement?[...e.parentElement.children].filter(a=>a.tagName===e.tagName):[];return c.length>1?`${s}:nth-of-type(${c.indexOf(e)+1})`:s},fn=e=>{let t=[],r=e;for(;r&&t.length<3&&r.tagName&&r.tagName!=="BODY"&&r.tagName!=="HTML";){let s=un(r);if(t.unshift(s),s.includes("#"))break;r=r.parentElement;}return t.join(" > ").slice(0,160)},$e=e=>(e!=null?e:"").replace(/\s+/g," ").trim().slice(0,40),pn=e=>{var r,s,c,a,f;if(e.closest(`${ln},${dn}`))return "";let t=e.tagName.toLowerCase();if(t==="input"){let C=((r=e.getAttribute("type"))!=null?r:"text").toLowerCase();return $e(C==="submit"||C==="button"?(s=e.getAttribute("aria-label"))!=null?s:e.value:e.getAttribute("aria-label"))}return $e(t==="select"||t==="textarea"?e.getAttribute("aria-label"):(f=(a=(c=e.getAttribute("aria-label"))!=null?c:e.getAttribute("title"))!=null?a:e.innerText)!=null?f:e.textContent)},Et=e=>{var a,f;let t=e.target instanceof Element?e.target:null;if(!t)return null;let r=t.closest(cn),s=r!=null?r:t,c=document.documentElement;return {x:Math.round(e.clientX),y:Math.round(e.clientY+window.scrollY),vw:window.innerWidth,vh:window.innerHeight,docH:Math.max(c.scrollHeight,(f=(a=document.body)==null?void 0:a.scrollHeight)!=null?f:0),selector:fn(s),label:r?pn(r):"",interactive:!!r}},_t=()=>{var r,s;let e=document.documentElement,t=Math.max(e.scrollHeight,(s=(r=document.body)==null?void 0:r.scrollHeight)!=null?s:0);return t?Math.min(100,Math.round((window.scrollY+window.innerHeight)/t*100)):0};var Pe="0.12.0",gn=(e,t)=>{let r=t.trim().toLowerCase();for(let[s,c]of Object.entries(e))if(s.toLowerCase()===r)return c},mn=(e,t)=>{let r=e.trim().toLowerCase(),s=t.trim().toLowerCase();return s.startsWith("@")?r.endsWith(s):r===s},hn=(e,t)=>{if(e.op==="in cohort")return false;let r=gn(t,e.property),s=r==null?"":String(r);if(e.op==="\u2265"){let a=Number(s),f=Number(e.values[0]);return s!==""&&Number.isFinite(a)&&Number.isFinite(f)&&a>=f}let c=s!==""&&e.values.some(a=>mn(s,a));return e.op==="is"||e.op==="in"?c:!c},wn=(e,t,r={})=>{var C;if(!e.on)return false;let s={id:t,...r},c=e.sets.length?e.sets.find(v=>v.conditions.every(k=>hn(k,s))):{rollout:100};if(!c||St(`${e.key}:rollout:${t}`)*100>=c.rollout)return false;if(!((C=e.variants)!=null&&C.length))return true;let a=St(`${e.key}:variant:${t}`)*100,f=0;for(let v of e.variants)if(f+=v.split,a<f)return v.key;return e.variants[e.variants.length-1].key},Qe=(e,t,r,s={})=>{var a;let c=(a=t==null?void 0:t.flags)==null?void 0:a.find(f=>f.key===e);return c?wn(c,r,s):false},kn=["utm_source","utm_medium","utm_campaign","utm_term","utm_content"],It="ck_preview",Ot="ck_embed",ce="ck_id",ke="ck_sid";var yn=e=>{var r;let t={};for(let s of kn){let c=(r=e(s))==null?void 0:r.trim();c&&(t[s]=c.slice(0,150));}return t},Ft=e=>{let t=new URLSearchParams(e);return yn(r=>t.get(r))};var bn=e=>{let t=2166136261;for(let r=0;r<e.length;r++)t^=e.charCodeAt(r),t=Math.imul(t,16777619);return (t>>>0)/4294967296},St=e=>{let t=2166136261;for(let r=0;r<e.length;r++)t^=e.charCodeAt(r),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},xn=(e,t)=>{var a,f,C,v;let r=(f=(a=e.variants[0])==null?void 0:a.id)!=null?f:"control";if(e.winner)return e.winner;if(e.status!=="running")return r;let s=bn(`${e.key}:${t}`),c=0;for(let k of e.variants)if(c+=k.weight,s<c)return k.id;return (v=(C=e.variants[e.variants.length-1])==null?void 0:C.id)!=null?v:r},$t=e=>{let t=Array.isArray(e)?e[0]:e;if(!t)return {};let r={};for(let s of t.split(",")){let[c,a]=s.split(":");c&&a&&(r[c.trim()]=a.trim());}return r};var Re=(e,t,r,s)=>{if(s[e])return s[e];let c=t==null?void 0:t.experiments.find(a=>a.key===e);return c?xn(c,r):"control"},Mt=(e,t,r)=>{var c,a,f,C,v,k;let s={...(c=e==null?void 0:e.texts)!=null?c:{}};for(let E of (a=e==null?void 0:e.experiments)!=null?a:[]){let g=Re(E.key,e,t,r);g!==((C=(f=E.variants[0])==null?void 0:f.id)!=null?C:"control")&&Object.assign(s,(k=(v=E.variants.find(w=>w.id===g))==null?void 0:v.changes)!=null?k:{});}return s},Ce="ck_x",Me=e=>e.replace(/\/+$/,"")||"/",Pt=(e,t,r,s={})=>{var C,v;if(!e)return null;let c;try{c=new URL(t);}catch(k){return null}if(c.searchParams.has(Ce))return null;let a=Me(c.pathname),f=(k,E)=>{let g;try{g=new URL(k,c);}catch(w){return}if(!(!/^https?:$/.test(g.protocol)||g.origin===c.origin&&Me(g.pathname)===a))return c.searchParams.forEach((w,S)=>{g.searchParams.has(S)||g.searchParams.set(S,w);}),E&&g.searchParams.set(Ce,E),g.hash||(g.hash=c.hash),g.href};for(let k of (C=e.redirects)!=null?C:[]){let E=k.from&&Me(k.from)===a?f(k.to):void 0;if(E)return {to:E}}for(let k of e.experiments){if(!k.path||Me(k.path)!==a)continue;let E=k.status==="running"&&!k.winner;if(E&&r===null&&!s[k.key])return null;let g=Re(k.key,e,r!=null?r:"",s),w=(v=k.variants.find(M=>M.id===g))==null?void 0:v.url,S=w?f(w,E?`${k.key}:${g}`:void 0):void 0;return E?{to:S,experiment:k.key,variant:g}:S?{to:S}:null}return null},At=(e,t)=>{let[r,s]=(t!=null?t:"").split(":"),c=e==null?void 0:e.experiments.find(a=>a.key===r);return c&&c.variants.some(a=>a.id===s)?{experiment:r,variant:s}:null};var Ze=()=>typeof crypto!="undefined"&&"randomUUID"in crypto?crypto.randomUUID():`ck_${Math.random().toString(36).slice(2)}${Date.now().toString(36)}`;var ve=(e,t)=>e.length>t?`${e.slice(0,t-1)}\u2026`:e,Cn=e=>{var t;if(e instanceof Error)return {type:e.name||"Error",message:ve(e.message||String(e),1e3),stack:e.stack?ve(e.stack,8192):void 0};if(typeof e=="string")return {type:"Error",message:ve(e,1e3)};try{return {type:"Error",message:ve((t=JSON.stringify(e))!=null?t:String(e),1e3)}}catch(r){return {type:"Error",message:String(e)}}},Lt=e=>{let t=[],r=[],s=new Set,c=0,a=`${e.host}/api/sdk/errors`,f=(w=false)=>{for(;t.length;){let S=JSON.stringify({key:e.apiKey,errors:t.splice(0,4)});w&&typeof navigator!="undefined"&&"sendBeacon"in navigator&&navigator.sendBeacon(a,S)||fetch(a,{method:"POST",headers:{"content-type":"application/json"},body:S,keepalive:true}).then(L=>{!L.ok&&L.status!==429&&e.warn(`Error reports were rejected with ${L.status}. Check the public key and NEXT_PUBLIC_MRROCKET_HOST.`);}).catch(()=>{});}},C=(w,S="manual")=>{let M=e.context();if(!M.send||c>=20)return;let L=Cn(w);if(/\/api\/sdk\/errors/.test(L.message))return;let N=`${L.type}:${L.message}`;s.has(N)||(s.add(N),c++,t.push({...L,mechanism:S,handled:S==="manual",url:window.location.href,ts:Date.now(),crumbs:r.slice(),...e.release?{release:e.release}:{},...M.distinctId?{distinctId:M.distinctId}:{},...M.sessionId?{sessionId:M.sessionId}:{}}));},v=w=>{var S;!w.error&&(!w.message||w.message==="Script error.")||C((S=w.error)!=null?S:new Error(w.message),"onerror");},k=w=>{var S;return C((S=w.reason)!=null?S:new Error("Unhandled promise rejection"),"onunhandledrejection")},E=()=>f(true);window.addEventListener("error",v),window.addEventListener("unhandledrejection",k),window.addEventListener("pagehide",E);let g=window.setInterval(()=>f(),2e3);return {capture:C,crumb:(w,S)=>{r.push({ts:Date.now(),type:w,message:ve(S,120)}),r.length>20&&r.shift();},stop:()=>{window.removeEventListener("error",v),window.removeEventListener("unhandledrejection",k),window.removeEventListener("pagehide",E),window.clearInterval(g),f(true);}}};var Ee="$cancel_flow",re="$onboarding",et="$inapp",Rn={version:0,question:"",reasons:[],offers:{},confirm:{title:"Cancel your subscription?",body:"You keep access until the end of the billing period.",button:"Cancel subscription"},offersFor:"business"},vn="Something went wrong. Please try again.",Nt="mrrocket:onboarding",Ae=e=>e.trim().toLowerCase().replace(/[^a-z0-9]+/g,"_").replace(/^_+|_+$/g,""),Tt=new Set(["signed_up","signup","sign_up","registered","account_created","user_signed_up"]),tt=(e,t)=>{let r=Ae(e),s=Ae(t);return !r||!s?false:r===s||Tt.has(r)&&Tt.has(s)},En=e=>/^(first (launch|visit|open|session|login|sign ?in|app open)|app (opened|launched)|on load|every visit|always)?$/i.test(e.trim()),_n=()=>`cf_${Math.random().toString(36).slice(2,10)}${Date.now().toString(36)}`;var Sn=e=>{let t=(e!=null?e:"").trim();return /^https?:\/\//i.test(t)||/^\/(?!\/)/.test(t)?t:void 0},le=e=>{var t;return (t=e.nodes)!=null?t:e.steps},In=e=>Math.max(0,e.amount)*(e.unit==="hours"?36e5:864e5),On=(e,t)=>{let r=e.trim(),s=(t.split(/[?#]/)[0]||"/").replace(/\/+$/,"")||"/";if(!r)return false;if(r.endsWith("*")){let c=r.slice(0,-1).replace(/\/+$/,"");return !c||s===c||s.startsWith(`${c}/`)}return s===(r.replace(/\/+$/,"")||"/")},nt=e=>e.flatMap(t=>t.kind==="condition"?[...nt(t.yes),...nt(t.no)]:t.kind==="wait"?[]:[t]),Dt=e=>{let t=null,r=null,s=null,c=[],a=false,f=null,C,v=null,k=new Map,E=new Set,g=new Set,w=new Set,S=e.auto!==false,M=new Set,L=()=>{var n,i;return (i=(n=e.now)==null?void 0:n.call(e))!=null?i:Date.now()},N=null,z=()=>{var n;return (n=t==null?void 0:t.onboarding)!=null?n:[]},Z=n=>n?z().find(i=>i.id===n):void 0,H=n=>{let i=k.get(n);return i||k.set(n,i={done:new Set,dismissed:false,answers:new Map,at:new Map,reached:new Map}),i},G=false,K=n=>{e.track(re,n),G=true;},oe=(n,i)=>{n.done.add(i),n.at.set(i,L());},Be=n=>{var i,l,m;if((i=n.page)!=null&&i.trim()){let p=(l=e.path)==null?void 0:l.call(e);return p===void 0?false:On(n.page,p)}if((m=n.event)!=null&&m.trim()){if(!a)return;for(let p of M)if(tt(n.event,p))return true;return false}return false},de=(n,i,l,m,p=0,_)=>{var I,b;for(let y=0;y<i.length;y++){let x=i[y];if(x.kind==="wait"){if(l.done.has(x.id)){_=(I=l.at.get(x.id))!=null?I:_;continue}let O=l.reached.get(x.id);if(O===void 0){if(!m)return {at:"pending"};O=_!=null?_:L(),l.reached.set(x.id,O),K({flow:n.id,step:x.id,kind:"wait",action:"shown"});}let B=O+In(x);if(L()<B)return {at:"wait",until:B};if(!m)return {at:"pending"};oe(l,x.id),K({flow:n.id,step:x.id,kind:"wait",action:"completed"}),_=B;continue}if(x.kind==="condition"){let O=l.answers.get(x.id);if(O===void 0){if(!m)return {at:"pending"};if(O=Be(x),O===void 0)return {at:"pending"};l.answers.set(x.id,O),K({flow:n.id,step:x.id,kind:"condition",action:"answered",answer:O?"yes":"no"});}return de(n,O?x.yes:x.no,l,m,p,_)}if(!l.done.has(x.id))return {at:"step",step:x,line:i,index:y,before:p};_=(b=l.at.get(x.id))!=null?b:_,p++;}return {at:"end"}},se=n=>{let i=k.get(n.id);return !!i&&(i.dismissed||de(n,le(n),i,false).at==="end")},ue=n=>`${Nt}:${n!=null?n:"visitor"}`,j=n=>`${Nt}:events:${n!=null?n:"visitor"}`,W=()=>{var l;G=false;let n=(l=e.storage)==null?void 0:l.call(e);if(!n)return;let i={};for(let[m,p]of k)i[m]={done:[...p.done],...p.dismissed?{dismissed:true}:{},...p.answers.size?{answers:Object.fromEntries([...p.answers].map(([_,I])=>[_,I?"yes":"no"]))}:{},...p.at.size?{at:Object.fromEntries(p.at)}:{},...p.reached.size?{reached:Object.fromEntries(p.reached)}:{}};try{n.set(ue(e.personId()),JSON.stringify(i)),M.size&&n.set(j(e.personId()),JSON.stringify([...M].slice(-200)));}catch(m){}},$=n=>{var i,l,m,p;for(let[_,I]of Object.entries(n!=null?n:{})){let b=H(_);for(let y of (i=I.done)!=null?i:[])b.done.add(y);I.dismissed&&(b.dismissed=true);for(let[y,x]of Object.entries((l=I.answers)!=null?l:{}))b.answers.has(y)||b.answers.set(y,x==="yes");for(let[y,x]of Object.entries((m=I.at)!=null?m:{}))typeof x=="number"&&!b.at.has(y)&&b.at.set(y,x);for(let[y,x]of Object.entries((p=I.reached)!=null?p:{}))typeof x=="number"&&(!b.reached.has(y)||x<b.reached.get(y))&&b.reached.set(y,x);}},Q=n=>{var l;N&&clearTimeout(N),N=null;let i=n-L();i>0&&i<=864e5&&(N=setTimeout(()=>(N=null,P()),i+50),(l=N.unref)==null||l.call(N));},fe=false,ie=()=>{fe=false;let n=Z(s);if(!n)return null;let i=H(n.id);if(i.dismissed)return null;let l=de(n,le(n),i,true);if(G&&W(),l.at==="wait"&&Q(l.until),fe=l.at==="wait"||l.at==="pending",l.at!=="step")return null;let{step:m,line:p,index:_}=l,I=O=>!!O&&O.kind!=="wait"&&O.kind!=="condition";if(m.kind==="checklist"){let O=_;for(;O>0&&I(p[O-1])&&p[O-1].kind==="checklist";)O--;let B=_;for(;B+1<p.length&&I(p[B+1])&&p[B+1].kind==="checklist";)B++;let ge=p.slice(O,B+1).filter(I);for(let Y of ge)!i.done.has(Y.id)&&!g.has(`s:${Y.id}`)&&(g.add(`s:${Y.id}`),e.track(re,{flow:n.id,step:Y.id,kind:"checklist",action:"shown"}));return {type:"checklist",flow:n,items:ge.map(Y=>({step:Y,done:i.done.has(Y.id)}))}}g.has(`s:${m.id}`)||(g.add(`s:${m.id}`),e.track(re,{flow:n.id,step:m.id,kind:m.kind,action:"shown"}));let b=p.slice(_+1),y=b.findIndex(O=>O.kind==="condition"),x=(y<0?b:b.slice(0,y)).filter(I).length;return {type:"step",flow:n,step:m,index:l.before,total:l.before+1+x,last:!x&&y<0}},ae=()=>{if(r){let l=r.reason?r.flow.offers[r.reason]:void 0;return {type:"cancel",screen:r.screen,flow:r.flow,reason:r.reason,offer:r.screen==="offer"?l:void 0,price:r.options.price,busy:r.busy,error:r.error,step:r.plain||r.screen==="survey"?1:r.screen==="offer"?2:3,steps:r.plain?1:3}}let n=ie();if(n)return n;s&&!fe&&(s=null);let i=c.find(l=>!E.has(l.id));return i?{type:"message",message:i}:null},P=()=>{let n=ae();(n==null?void 0:n.type)==="message"&&!g.has(`m:${n.message.id}`)&&(g.add(`m:${n.message.id}`),e.track(et,{message:n.message.id,journey:n.message.journey,action:"shown"}));for(let i of [...w])try{i();}catch(l){}},J=n=>s===n.id?true:s&&Z(s)&&!se(Z(s))?false:(s=n.id,e.track(re,{flow:n.id,action:"started"}),P(),true),ye=()=>{var i;if(!S||s||!a||!e.personId()&&!((i=e.storage)!=null&&i.call(e)))return;let n=z().find(l=>En(l.trigger)&&le(l).length&&!se(l));n&&J(n);},Ie=()=>{let n=e.personId();v!==null&&v!==n&&(k.clear(),M.clear(),E.clear(),s=null,C=void 0),v=n;let i=(async()=>{var p,_,I;let l=(p=e.storage)==null?void 0:p.call(e);if(l)try{let b=await l.get(ue(n));b&&$(JSON.parse(b));let y=await l.get(j(n));if(y)for(let x of JSON.parse(y))typeof x=="string"&&M.add(x);}catch(b){}let m=z().length>0||!!(t!=null&&t.messages);if(n&&m&&C!==n){C=n;let b=await e.fetchInApp(n).catch(()=>null);if(b&&e.personId()===n){$(b.onboarding);for(let y of (_=b.events)!=null?_:[])typeof y=="string"&&M.add(Ae(y));c=((I=b.messages)!=null?I:[]).filter(y=>y&&y.id&&y.title);}}n||(c=[]),a=true,ye(),P();})();return f=i.finally(()=>{f===i&&(f=null);}),f},D=(n,i,l)=>{var p;let m=n.reason?n.flow.offers[n.reason]:void 0;return {outcome:i,run:n.run,...n.reason?{reason:n.reason,reasonLabel:(p=n.flow.reasons.find(_=>_.id===n.reason))==null?void 0:p.label}:{},...i==="saved"&&m?{offer:m}:{},...l!=null&&l.trim()?{comment:l.trim().slice(0,500)}:{}}},U=(n,i)=>{var l;e.track(Ee,{action:"finished",run:n.run,version:n.flow.version,outcome:i.outcome,customer:(l=n.options.customer)!=null?l:"consumer",...i.reason?{reason:i.reason}:{},...i.offer?{offer:i.offer.kind}:{},...i.comment?{comment:i.comment}:{}}),r===n&&(r=null),n.resolve(i),P();},V=(n,i,l)=>{if(n.busy)return;let m=D(n,i,l),p=i==="saved"?n.options.onSave:n.options.onCancel;n.busy=true,n.error=void 0,P(),Promise.resolve().then(()=>p==null?void 0:p(m)).then(()=>U(n,m),()=>{n.busy=false,n.error=vn,P();});};return {setConfig:n=>{t=n,s&&!Z(s)&&(s=null),P();},refresh:Ie,onEvent:n=>{var I;if(!n||n.startsWith("$"))return;let i=Ae(n),l=!!i&&!M.has(i);l&&M.add(i);let m=Z(s),p=false;if(m){let b=H(m.id);for(let y of nt(le(m)))y.kind==="checklist"&&y.event&&!b.done.has(y.id)&&tt(y.event,n)&&(oe(b,y.id),p=true,e.track(re,{flow:m.id,step:y.id,kind:y.kind,action:"completed"}));}if(p||l&&m?(W(),P()):l&&W(),!S||!e.personId()&&!((I=e.storage)!=null&&I.call(e)))return;let _=z().find(b=>le(b).length&&tt(b.trigger,n)&&!se(b));_&&J(_);},openCancelFlow:(n={})=>{var I,b;if(r)return r.promise;let i=(I=t==null?void 0:t.cancelFlow)!=null?I:Rn,l=n.customer!=="business"||i.reasons.length<2,m=()=>{},p=new Promise(y=>m=y),_={flow:i,options:n,plain:l,screen:l?"confirm":"survey",run:_n(),busy:false,resolve:m,promise:p};return r=_,e.track(Ee,{action:"shown",run:_.run,version:i.version,customer:(b=n.customer)!=null?b:"consumer",plain:l}),P(),p},startOnboarding:n=>{let i=n?Z(n):z().find(m=>le(m).length&&!se(m));if(!i||!le(i).length)return false;let l=H(i.id);return l.dismissed=false,J(i)},view:ae,subscribe:n=>(w.add(n),()=>w.delete(n)),cancel:{pick:n=>{!r||r.busy||!r.flow.reasons.some(i=>i.id===n)||(r.reason=n,r.error=void 0,P());},next:()=>{let n=r;!n||n.screen!=="survey"||!n.reason||(e.track(Ee,{action:"answered",run:n.run,version:n.flow.version,reason:n.reason}),n.screen=n.flow.offers[n.reason]?"offer":"confirm",P());},accept:n=>{let i=r;if(!i||i.screen!=="offer"||!i.reason)return;let l=i.flow.offers[i.reason];i.busy||e.track(Ee,{action:"offer",run:i.run,version:i.flow.version,reason:i.reason,offer:l==null?void 0:l.kind,outcome:"accepted"}),V(i,"saved",n);},decline:()=>{var i;let n=r;!n||n.screen!=="offer"||n.busy||(e.track(Ee,{action:"offer",run:n.run,version:n.flow.version,reason:n.reason,offer:n.reason?(i=n.flow.offers[n.reason])==null?void 0:i.kind:void 0,outcome:"declined"}),n.screen="confirm",n.error=void 0,P());},confirm:n=>{let i=r;!i||i.screen!=="confirm"||V(i,"cancelled",n);},close:()=>{var l,m;let n=r;if(!n||n.busy)return;let i=D(n,"kept");(m=(l=n.options).onClose)==null||m.call(l,i),U(n,i);}},onboarding:{next:()=>{let n=ie();(n==null?void 0:n.type)==="step"&&(oe(H(n.flow.id),n.step.id),e.track(re,{flow:n.flow.id,step:n.step.id,kind:n.step.kind,action:"completed"}),W(),P());},check:n=>{let i=ie();if((i==null?void 0:i.type)!=="checklist")return;let l=i.items.find(m=>m.step.id===n);!l||l.done||(oe(H(i.flow.id),n),e.track(re,{flow:i.flow.id,step:n,kind:"checklist",action:"completed"}),W(),P());},dismiss:()=>{let n=ie();!n||n.type!=="step"&&n.type!=="checklist"||(H(n.flow.id).dismissed=true,e.track(re,{flow:n.flow.id,...n.type==="step"?{step:n.step.id}:{},action:"dismissed"}),s=null,W(),P());}},message:{click:()=>{var l;let n=ae();if((n==null?void 0:n.type)!=="message")return;E.add(n.message.id),e.track(et,{message:n.message.id,journey:n.message.journey,action:"clicked"});let i=Sn(n.message.url);P(),i&&((l=e.openUrl)==null||l.call(e,i));},dismiss:()=>{let n=ae();(n==null?void 0:n.type)==="message"&&(E.add(n.message.id),e.track(et,{message:n.message.id,journey:n.message.journey,action:"dismissed"}),P());}}}};var Fn="ck_pick",$n="ck_origin",Le=e=>typeof CSS!="undefined"&&CSS.escape?CSS.escape(e):e.replace(/[^a-zA-Z0-9_-]/g,t=>`\\${t}`),Mn=e=>[...e.classList].filter(t=>t.length<30&&!/\d{2,}|[_-][a-z0-9]{5,}$|^(is|has)-|hover|active|focus|open|selected|:|\[/.test(t)).slice(0,2),Ut=e=>{let t=e.ownerDocument,r=f=>{try{return t.querySelectorAll(f).length===1}catch(C){return false}},s=e.getAttribute("data-ck-target");if(s)return `[data-ck-target="${s.replace(/"/g,'\\"')}"]`;if(e.id&&!/\d{3,}/.test(e.id)&&r(`#${Le(e.id)}`))return `#${Le(e.id)}`;let c=[],a=e;for(;a&&a!==t.documentElement&&c.length<6;){if(a!==e&&a.id&&!/\d{3,}/.test(a.id)){c.unshift(`#${Le(a.id)}`);break}let f=a.tagName.toLowerCase(),C=Mn(a).map(w=>`.${Le(w)}`).join(""),v=a.parentElement,k=v?[...v.children].filter(w=>w.tagName===a.tagName):[],E=k.length>1?`:nth-of-type(${k.indexOf(a)+1})`:"";c.unshift(`${f}${C}${E}`);let g=c.join(" > ");if(r(g))return g;a=v;}return c.join(" > ")},jt=()=>{var E;if(typeof window=="undefined"||window.top===window)return null;let e=new URLSearchParams(window.location.search);if(e.get(Fn)!=="1")return null;let t;try{t=new URL((E=e.get($n))!=null?E:"").origin;}catch(g){return null}if(!/^https?:\/\//.test(t))return null;let r=g=>window.parent.postMessage(g,t),s=document.createElement("div");s.setAttribute("aria-hidden","true"),Object.assign(s.style,{position:"fixed",zIndex:"2147483647",pointerEvents:"none",border:"2px solid #0ea5e9",background:"rgba(14,165,233,.12)",borderRadius:"6px",transition:"all 60ms linear",display:"none"});let c=document.createElement("div");Object.assign(c.style,{position:"absolute",left:"-2px",bottom:"calc(100% + 4px)",font:"600 11px/1.4 system-ui, sans-serif",color:"#fff",background:"#0c4a6e",padding:"2px 6px",borderRadius:"4px",whiteSpace:"nowrap",maxWidth:"360px",overflow:"hidden",textOverflow:"ellipsis"}),s.appendChild(c),document.body.appendChild(s);let a=null,f=g=>{if(a=g&&g!==s&&!s.contains(g)?g:null,!a){s.style.display="none";return}let w=a.getBoundingClientRect();Object.assign(s.style,{display:"block",left:`${w.left-2}px`,top:`${w.top-2}px`,width:`${w.width+4}px`,height:`${w.height+4}px`}),c.textContent=Ut(a);},C=g=>f(document.elementFromPoint(g.clientX,g.clientY)),v=g=>{var S,M;g.preventDefault(),g.stopPropagation();let w=(S=document.elementFromPoint(g.clientX,g.clientY))!=null?S:a;!w||w===s||r({type:"mrrocket:picked",selector:Ut(w),text:((M=w.textContent)!=null?M:"").trim().replace(/\s+/g," ").slice(0,80)});},k=g=>{g.key==="Escape"&&r({type:"mrrocket:pick-cancel"});};return window.addEventListener("mousemove",C,true),window.addEventListener("click",v,true),window.addEventListener("keydown",k,true),r({type:"mrrocket:pick-ready"}),()=>{window.removeEventListener("mousemove",C,true),window.removeEventListener("click",v,true),window.removeEventListener("keydown",k,true),s.remove();}};var Pn={get:e=>{try{return window.localStorage.getItem(e)}catch(t){return null}},set:(e,t)=>{try{window.localStorage.setItem(e,t);}catch(r){}}},Ht=()=>{try{for(let e=window.localStorage.length-1;e>=0;e--){let t=window.localStorage.key(e);t!=null&&t.startsWith("mrrocket:onboarding")&&window.localStorage.removeItem(t);}}catch(e){}},Kt=e=>({track:e.track,personId:e.personId,auto:e.auto,storage:()=>e.canStore()?Pn:null,openUrl:t=>window.location.assign(t),path:()=>window.location.pathname,fetchInApp:async t=>{if(!e.apiKey)return null;let r=await fetch(`${e.host}/api/sdk/inapp?key=${encodeURIComponent(e.apiKey)}&distinctId=${encodeURIComponent(t)}`);return r.ok?await r.json():null}});var De="mrr_ref",Ue="mrr_ref",An="mrr_ref",Ln="mrr_click";var _e=/^[A-Za-z0-9_-]{1,64}$/;var Nn=(e,t="ref")=>{let r=new URLSearchParams(e);return Tn(s=>r.get(s),t)},Tn=(e,t="ref")=>{let r=e(An);if(r&&_e.test(r)){let c=e(Ln);return {code:r,clickId:c&&_e.test(c)?c:null,direct:false}}let s=e(t);return s&&_e.test(s)?{code:s,clickId:null,direct:true}:null},Wt=e=>{var t;return `${e.code}.${(t=e.clickId)!=null?t:""}.${e.at}`},Ne=(e,t=60,r=Date.now())=>{if(!e)return null;let[s,c,a]=e.split("."),f=Number(a);return !s||!_e.test(s)||!Number.isFinite(f)||c&&!_e.test(c)||r-f>t*864e5||f>r+864e5?null:{code:s,clickId:c||null,at:f}};var T=null,je=()=>typeof document!="undefined",Vt=e=>{var r;if(!je())return;let t=(r=document.cookie.split("; ").find(s=>s.startsWith(`${e}=`)))==null?void 0:r.slice(e.length+1);try{return t?decodeURIComponent(t):void 0}catch(s){return t}},He=()=>{try{return typeof localStorage=="undefined"?null:localStorage}catch(e){return null}},rt=()=>{var e,t;return T||(je()?(t=Ne(Vt(De),3650))!=null?t:Ne((e=He())==null?void 0:e.getItem(Ue),3650):null)};var Te=(e,t)=>{var c;if(!je())return;let r=Math.max(1,Math.round(t))*86400,s=typeof location!="undefined"&&location.protocol==="https:"?"; secure":"";document.cookie=`${De}=${encodeURIComponent(Wt(e))}; path=/; max-age=${r}; samesite=lax${s}`;try{(c=He())==null||c.setItem(Ue,Wt(e));}catch(a){}},Ke=()=>{var e;if(je()){document.cookie=`${De}=; path=/; max-age=0; samesite=lax`;try{(e=He())==null||e.removeItem(Ue);}catch(t){}}},Bt=()=>{T=null;},qt=async e=>{var s,c,a;let t=Nn(e.search,e.param),r=(s=e.now)!=null?s:Date.now();if(!t)return !T&&e.store&&(T=(a=Ne(Vt(De),e.days,r))!=null?a:Ne((c=He())==null?void 0:c.getItem(Ue),e.days,r),T||Ke()),T;if(T={code:t.code,clickId:t.clickId,at:r},e.store&&Te(T,e.days),e.beacon)try{let f=await e.beacon({code:t.code,clickId:t.clickId});f&&T&&T.code===t.code&&!T.clickId&&(T={...T,clickId:f},e.store&&Te(T,e.days));}catch(f){}return T},Yt=e=>e==="$identify"||!e.startsWith("$");var ot=3600*24*365,Jt=e=>{var r;if(typeof document=="undefined")return;let t=(r=document.cookie.split("; ").find(s=>s.startsWith(`${e}=`)))==null?void 0:r.slice(e.length+1);if(t)try{return decodeURIComponent(t)}catch(s){return t}},We=(e,t,r)=>{let s=window.location.protocol==="https:"?"; secure":"";document.cookie=`${e}=${encodeURIComponent(t)}; path=/; max-age=${r}; samesite=lax${s}`;},Se=e=>{document.cookie=`${e}=; path=/; max-age=0; samesite=lax`;},A={utm:{}},zt=()=>{var e;return (e=A.id)!=null?e:A.id=Ze()},st=(e,t)=>{if(e!=="cookies")return zt();let r=Jt(ce);if(r)return A.id=r;let s=zt();return We(ce,s,ot),s},it=e=>{var c;let t=Date.now(),r=A.session,s=(c=e?Jt(ke):void 0)!=null?c:r&&t-r.last<18e5?r.id:Ze();return A.session={id:s,last:t},e&&We(ke,s,18e5/1e3),s},at=()=>{var r;let e=window.location.href;if(A.url===e)return;A.referrer=(r=A.url)!=null?r:document.referrer||void 0,A.url=e;let t=Ft(window.location.search);Object.keys(t).length&&(A.utm=t);},Gt=()=>{let e=navigator,t=window;return e.doNotTrack==="1"||t.doNotTrack==="1"||e.globalPrivacyControl===true};var Ve=window,Un=()=>{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']")},Qt=new Set,X=e=>{Qt.has(e)||(Qt.add(e),console.warn(`[MRRocket] ${e}`));},jn=()=>{var wt,kt,yt,bt;let e=Un(),t=o=>{var d;return (d=e==null?void 0:e.getAttribute(`data-${o}`))!=null?d:void 0},r=(o,d)=>{let u=t(o);return u===void 0?d:!["false","0","off","no"].includes(u.trim().toLowerCase())},s=(wt=e==null?void 0:e.src)!=null?wt:"",c="";try{c=s?new URL(s,window.location.href).origin:"";}catch(o){c="";}let a=((kt=t("key"))!=null?kt:"").trim(),f=((yt=t("api-host"))!=null?yt:c).trim().replace(/\/+$/,""),C=t("consent"),v=r("respect-dnt",false),k=r("heatmaps",true),E=r("errors",true),g=t("release"),w=t("attribution"),S=w==="always"||w==="off"?w:"consent",M=t("referral-param")||"ref",L={},N=(o,d)=>{var u;return (u=L[o])!=null?u:L[o]=new Promise((h,R)=>{var xt;let F=()=>Ve[d];if(F())return h(F());let q=(xt=t(`${o}-src`))!=null?xt:s?s.replace(/(\.min)?\.js(\?[^#]*)?(#.*)?$/,`-${o}$1.js?v=${Pe}`):"";if(!q)return R(new Error(o));let ne=document.createElement("script");ne.async=true,ne.src=q,ne.onload=()=>F()?h(F()):R(new Error(o)),ne.onerror=()=>R(new Error(o)),document.head.appendChild(ne);}).catch(h=>{throw L[o]=void 0,h})},z=t("theme"),Z={theme:z==="light"||z==="dark"?z:"auto",accent:t("accent")},H=new URLSearchParams(window.location.search),G=$t(H.get(It)),K=H.get(Ot)==="1";K&&jt();let oe=H.get(Ce);if(oe!==null){H.delete(Ce);let o=H.toString();history.replaceState(history.state,"",`${window.location.pathname}${o?`?${o}`:""}${window.location.hash}`);}let Be=Gt(),de=C==="cookies"||C==="off"?C:"cookieless",se=r("replay",false),ue="",j,W,$=null,Q=false,fe=false,ie=null,ae=null,P=window.location.pathname,J=[],ye=new Set,Ie=[],D=()=>v&&Be?"off":de,U=()=>!K&&D()!=="off",V=()=>j!=null?j:ue,pe=()=>D()==="cookies"?V():j;a||X('No public key. Add data-key="mrr_pub_\u2026" to the script tag; nothing is tracked until then.');let Oe=(o=false)=>{if(!J.length)return;if(!a){J.length=0;return}let d=`${f}/api/sdk/events`;for(let u of Rt(a,J.splice(0),{v:Pe,errors:E,platform:"web"}))o&&"sendBeacon"in navigator&&navigator.sendBeacon(d,u)||fetch(d,{method:"POST",headers:{"content-type":"application/json"},body:u,keepalive:true}).then(R=>{R.ok||X(`Events were rejected with ${R.status}. Check data-key (MRRocket \u203A Settings \u203A Products).`);}).catch(()=>X(`Could not reach ${f||"this origin"} to send events. Check data-api-host.`));},n=null,i=null,l=(o,d,u=Date.now(),h)=>{if(!U())return;let R=Yt(o)?rt():null;J.push({event:o,props:R?{...d,$referral:{code:R.code,clickId:R.clickId}}:d,distinctId:pe(),sessionId:it(D()==="cookies"),ts:u,url:h!=null?h:window.location.href,referrer:A.referrer,...A.utm,screen:{w:window.screen.width,h:window.screen.height}});},m=(o,d)=>{typeof o!="string"||!o||(l(o,d),U()&&(n==null||n.event(o)),p.onEvent(o));},p=Dt(Kt({host:f,apiKey:a,track:l,personId:()=>U()?j:void 0,canStore:()=>D()==="cookies",auto:r("onboarding",true)})),_=false;p.subscribe(()=>{_||!p.view()||K||(_=true,N("flows","__mrrocketFlows").then(o=>o.mountFlows(p,Z)).catch(()=>{_=false,X("The cancel flow and onboarding dialogs did not load. Check that sdk-flows.js is served next to sdk.js.");}));});let I=(o,d)=>{if(!U())return;let u=`${window.location.pathname}|${o}`;ye.has(u)||(ye.add(u),m("$exposure",{experiment:o,variant:d}));},b=()=>{let o=Pt($,window.location.href,V(),G);return o!=null&&o.to?(Oe(true),window.location.replace(o.to),true):(o!=null&&o.experiment&&I(o.experiment,o.variant),false)},y=()=>{if(!$)return;let o=document.querySelectorAll("[data-ck-text]");if(!o.length)return;let d=Mt($,V(),G),u=new Set;o.forEach(h=>{var q,ne;let R=h.dataset.ckText;if(!R)return;u.add(R),h.dataset.ckDefault===void 0&&(h.dataset.ckDefault=(q=h.textContent)!=null?q:"");let F=(ne=d[R])!=null?ne:h.dataset.ckDefault;h.textContent!==F&&(h.textContent=F);});for(let h of $.experiments)h.variants.some(R=>{var F;return Object.keys((F=R.changes)!=null?F:{}).some(q=>u.has(q))})&&I(h.key,Re(h.key,$,V(),G));},x=()=>{var o,d;return (d=(o=$==null?void 0:$.referral)==null?void 0:o.cookieDays)!=null?d:60},O=()=>S==="always"?D()!=="off":S==="consent"&&D()==="cookies",B=null,ge=false,Y=()=>{if(!Q||K)return;if(S==="off"||D()==="off"){Bt(),(S==="off"||ge)&&Ke();return}let o=window.location.href;B!==o&&(B=o,qt({search:window.location.search,param:M,store:O(),days:x(),beacon:a?({code:d,clickId:u})=>fetch(`${f}/api/sdk/affiliate/click`,{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify({key:a,code:d,clickId:u,url:o,referrer:document.referrer||void 0}),keepalive:true}).then(h=>h.ok?h.json():null).then(h=>{var R;return (R=h==null?void 0:h.clickId)!=null?R:null}):void 0}));},en=()=>{let o=O(),d=rt();o&&d?Te(d,x()):!o&&ge&&Ke(),ge=o;},ct=0,lt=false,dt=()=>{var h,R;let o=U()&&se&&!!($!=null&&$.replay)&&!!a;if(o===lt)return;lt=o;let d=++ct;if(!o){n==null||n.stop(true),n=null;return}let u=(R=(h=$==null?void 0:$.replay)==null?void 0:h.sampleRate)!=null?R:.1;N("replay","__mrrocketReplay").then(F=>F.startReplay({host:f,apiKey:a,sessionId:it(D()==="cookies"),distinctId:pe(),identified:!!j,sampleRate:u,warn:X})).then(F=>{d!==ct?F==null||F.stop(true):n=F;}).catch(()=>X("Session recording could not start (the recorder did not load)."));},ut=r("messenger",false),ee=null,me=o=>{K||a&&(ee!=null||(ee=N("messenger","__mrrocketMessenger").then(d=>{var F;let u=q=>t(`messenger-${q}`)===void 0?void 0:r(`messenger-${q}`,true),h=t("messenger-position"),R=(F=t("messenger-theme"))!=null?F:z;return d.mountMessenger({host:f,apiKey:a,distinctId:V,user:()=>W,launcher:ut,position:h==="bottom-left"||h==="bottom-right"?h:void 0,title:t("messenger-title"),greeting:t("messenger-greeting"),label:t("messenger-label"),askEmail:u("ask-email"),help:u("help"),sound:u("sound"),preview:u("preview"),theme:R==="light"||R==="dark"||R==="auto"?R:void 0,accent:t("accent")})})),ee.then(o).catch(()=>{ee=null,X("sdk-messenger.js did not load.");}));},ft=t("forms")==="auto",qe=false,Ye=()=>{qe||!a||K||!document.querySelector(ft?"form":"form[data-mrrocket-form]")||(qe=true,N("forms","__mrrocketForms").then(o=>o.watchForms({host:f,apiKey:a,auto:ft,distinctId:()=>U()?pe():void 0,identified:()=>!!j,identify:ht,tracking:U,referrer:()=>A.referrer,utm:()=>A.utm})).catch(()=>qe=false));},Xe=()=>{!fe||!U()||ae===window.location.pathname||(ae=window.location.pathname,m("$pageview",{path:window.location.pathname}));},te=null,pt=()=>{let o=te;k&&o&&o.max>0&&l("$scroll",{depth:o.max,docH:o.docH,vw:window.innerWidth,vh:window.innerHeight},Date.now(),o.url),te=null;},be=()=>{var R,F;if(!k||!U()||K)return;let o=document.documentElement,d=Math.max(o.scrollHeight,(F=(R=document.body)==null?void 0:R.scrollHeight)!=null?F:0),u=_t(),h=window.location.href;(te==null?void 0:te.url)!==h?te={url:h,max:u,docH:d}:u>te.max&&(te={url:h,max:u,docH:d});},gt=()=>{let o=window.location.pathname;o!==P&&(P=o,!(Q&&b())&&(pt(),at(),n==null||n.page(o),i==null||i.crumb("navigation",o),Xe(),Y(),window.setTimeout(()=>{be(),y(),Ye();},0)));},mt=()=>{let o=D(),d=ie;ie=o,d==="cookies"&&o!=="cookies"&&(Se(ce),Se(ke)),o==="off"&&(J.length=0,A.session=void 0),d==="cookies"&&o!=="cookies"&&Ht(),ue=st(o),en(),Y(),dt(),Xe(),be();},tn=o=>{let{analytics:d,replay:u}=typeof o=="string"?{analytics:o,replay:void 0}:o!=null?o:{};if(d!==void 0&&d!=="cookieless"&&d!=="cookies"&&d!=="off"){X(`setConsent: "${String(d)}" is not a consent mode. Use "cookieless", "cookies" or "off".`);return}d==="off"&&(J.length=0),d&&(de=d),typeof u=="boolean"&&(se=u),mt(),Q&&p.refresh();},ht=(o,d)=>{if(typeof o!="string"||!o)return;let u=pe();j=o,l("$identify",{...d,id:o,...u&&u!==o?{previousId:u}:{}}),n==null||n.identify(o),D()==="cookies"&&We(ce,o,ot),W={...d,id:o},y(),Q&&p.refresh();},Fe={track:m,identify:ht,reset:()=>{j=void 0,W=void 0,ye.clear(),D()==="cookies"&&(Se(ce),Se(ke)),A.id=void 0,A.session=void 0,ue=st(D()),y(),Q&&p.refresh();},captureError:o=>i==null?void 0:i.capture(o,"manual"),setConsent:tn,getFlag:o=>Qe(o,$,V(),{...W})!==false,getFlagVariant:o=>{let d=Qe(o,$,V(),{...W});return typeof d=="string"?d:null},getVariant:o=>{let d=Re(o,$,V(),G);return ($||G[o])&&I(o,d),d},ready:o=>{typeof o=="function"&&(Q?window.setTimeout(()=>o(Fe),0):Ie.push(o));},getDistinctId:V,cancelFlow:{open:o=>p.openCancelFlow(o)},onboarding:{start:o=>p.startOnboarding(o)},openCancelFlow:o=>p.openCancelFlow(o),startOnboarding:o=>p.startOnboarding(o),push:{subscribe:o=>{let d=U()?j:void 0;if(!d)return Promise.resolve({status:"not-signed-in"});let u=window.Notification,h=Promise.resolve(u&&u.permission==="default"&&(o==null?void 0:o.ask)!==false?u.requestPermission():u==null?void 0:u.permission);return N("push","__mrrocketPush").then(R=>R.subscribeWebPush({host:f,apiKey:a,distinctId:d,permission:h},o))},unsubscribe:()=>N("push","__mrrocketPush").then(o=>o.unsubscribeWebPush({host:f,apiKey:a,distinctId:j}))},messenger:{open:o=>me(d=>d.open(o)),close:()=>{ee==null?void 0:ee.then(o=>o.close()).catch(()=>{});},toggle:()=>me(o=>o.toggle())},openMessenger:()=>me(o=>o.open()),version:Pe};at(),mt();let he=Ve.mrrocket,nn=(bt=he==null?void 0:he.q)!=null?bt:[],ze=he&&typeof he=="object"?he:{};Object.assign(ze,Fe,{__loaded:true}),delete ze.q,Ve.mrrocket=ze;for(let[o,d]of nn){let u=Fe[o];if(typeof u=="function")try{u(...Array.from(d!=null?d:[]));}catch(h){console.error(h);}}fe=true,Xe(),E&&a&&!K&&(i=Lt({host:f,apiKey:a,release:g,context:()=>{var o;return {send:U(),distinctId:pe(),sessionId:(o=A.session)==null?void 0:o.id}},warn:X}),i.crumb("navigation",window.location.pathname));for(let o of ["pushState","replaceState"]){let d=history[o];history[o]=function(...u){let h=d.apply(this,u);return gt(),h};}window.addEventListener("popstate",gt);let rn=new xe;document.addEventListener("click",o=>{var F;o.target instanceof Element&&o.target.closest("[data-ck-messenger]")&&(o.preventDefault(),me(q=>q.open()));let d=o.target instanceof Element?o.target.closest("[data-ck-goal]"):null;if(d!=null&&d.dataset.ckGoal&&m(d.dataset.ckGoal,{text:(F=d.textContent)==null?void 0:F.trim().slice(0,80)}),!U())return;let u=Et(o);if(!u)return;let h=rn.push(u.x,u.y,Date.now());if((u.label||u.selector)&&(i==null||i.crumb("click",u.label||u.selector)),n==null||n.click(u,h),!k)return;let R={x:u.x,y:u.y,vw:u.vw,vh:u.vh,docH:u.docH,selector:u.selector,label:u.label,dead:!u.interactive};l("$click",R),h&&l("$rageclick",R);});let Je=0;window.addEventListener("scroll",()=>{Je||(Je=window.requestAnimationFrame(()=>{Je=0,be();}));},{passive:true}),document.readyState==="loading"?document.addEventListener("DOMContentLoaded",()=>be()):be(),window.setInterval(()=>Oe(),2e3),window.addEventListener("pagehide",()=>{pt(),Oe(true);});let Ge=()=>{if(Q=true,b())return;let o=At($,oe);o&&I(o.experiment,o.variant),document.documentElement.classList.remove("ck-hide"),Y(),dt(),p.setConfig($),p.refresh(),ut&&(document.body?me():document.addEventListener("DOMContentLoaded",()=>me())),document.readyState==="loading"?document.addEventListener("DOMContentLoaded",()=>{y(),Ye();}):(y(),Ye());for(let d of Ie.splice(0))try{d(Fe);}catch(u){console.error(u);}};a?fetch(`${f}/api/sdk/config?key=${encodeURIComponent(a)}`).then(o=>(o.ok||X(`Config request to ${f||"this origin"} answered ${o.status}. Check data-key and data-api-host.`),o.ok?o.json():null)).then(o=>{o&&Array.isArray(o.experiments)&&($=o),Ge();}).catch(()=>{X(`Could not reach ${f||"this origin"}. Check data-api-host. The page keeps its own texts and flags stay off.`),Ge();}):Ge();},Zt;typeof window!="undefined"&&typeof document!="undefined"&&!((Zt=Ve.mrrocket)!=null&&Zt.__loaded)&&jn();
|
|
1
|
+
(function(){'use strict';var on=Object.defineProperty;var sn=(e,t,r)=>t in e?on(e,t,{enumerable:true,configurable:true,writable:true,value:r}):e[t]=r;var we=(e,t,r)=>sn(e,typeof t!="symbol"?t+"":t,r);var Ct=e=>{let t=0;for(let r=0;r<e.length;r++){let s=e.charCodeAt(r);s<128?t+=1:s<2048?t+=2:s>=55296&&s<=56319&&r+1<e.length&&(e.charCodeAt(r+1)&64512)===56320?(t+=4,r++):t+=3;}return t},an=(e,t,r)=>{let s=`{"key":${JSON.stringify(e)},"sdk":${JSON.stringify(r)},"events":[`,c=Ct(s)+2,a=[],f=[],C=[],v=c,k=()=>{f.length&&a.push({body:`${s}${f.join(",")}]}`,events:C}),f=[],C=[],v=c;};for(let E of t){let g=JSON.stringify(E),w=Ct(g)+1;c+w>6e4||((f.length===100||v+w>6e4)&&k(),f.push(g),C.push(E),v+=w);}return k(),a},Rt=(e,t,r)=>an(e,t,r).map(s=>s.body);var xe=class{constructor(t=1e3,r=30,s=3){we(this,"windowMs",t);we(this,"radius",r);we(this,"count",s);we(this,"clicks",[]);we(this,"fired",false);}push(t,r,s){let c=this.clicks.filter(a=>s-a.t<=this.windowMs&&Math.hypot(a.x-t,a.y-r)<=this.radius);return c.length!==this.clicks.length&&(this.fired=false),this.clicks=[...c,{x:t,y:r,t:s}],this.clicks.length>=this.count&&!this.fired?(this.fired=true,true):false}};var cn='a[href],button,input,select,textarea,label,summary,[role="button"],[role="link"],[role="tab"],[role="menuitem"],[role="checkbox"],[role="switch"],[onclick],[data-ck-goal]',ln="[data-ck-mask]",dn="[data-ck-block]",vt=/\d{4,}|^[a-z]{1,3}-[a-z0-9]{5,}$|^_|__|css-|^sc-|^jsx-/i,un=e=>{let t=e.tagName.toLowerCase();if(e.id&&!vt.test(e.id)&&e.id.length<=40)return `${t}#${e.id}`;let r=typeof e.className=="string"?e.className.split(/\s+/).filter(a=>a&&!vt.test(a)&&a.length<=30&&!a.includes(":")):[],s=r.length?`${t}.${r.slice(0,2).join(".")}`:t,c=e.parentElement?[...e.parentElement.children].filter(a=>a.tagName===e.tagName):[];return c.length>1?`${s}:nth-of-type(${c.indexOf(e)+1})`:s},fn=e=>{let t=[],r=e;for(;r&&t.length<3&&r.tagName&&r.tagName!=="BODY"&&r.tagName!=="HTML";){let s=un(r);if(t.unshift(s),s.includes("#"))break;r=r.parentElement;}return t.join(" > ").slice(0,160)},$e=e=>(e!=null?e:"").replace(/\s+/g," ").trim().slice(0,40),pn=e=>{var r,s,c,a,f;if(e.closest(`${ln},${dn}`))return "";let t=e.tagName.toLowerCase();if(t==="input"){let C=((r=e.getAttribute("type"))!=null?r:"text").toLowerCase();return $e(C==="submit"||C==="button"?(s=e.getAttribute("aria-label"))!=null?s:e.value:e.getAttribute("aria-label"))}return $e(t==="select"||t==="textarea"?e.getAttribute("aria-label"):(f=(a=(c=e.getAttribute("aria-label"))!=null?c:e.getAttribute("title"))!=null?a:e.innerText)!=null?f:e.textContent)},Et=e=>{var a,f;let t=e.target instanceof Element?e.target:null;if(!t)return null;let r=t.closest(cn),s=r!=null?r:t,c=document.documentElement;return {x:Math.round(e.clientX),y:Math.round(e.clientY+window.scrollY),vw:window.innerWidth,vh:window.innerHeight,docH:Math.max(c.scrollHeight,(f=(a=document.body)==null?void 0:a.scrollHeight)!=null?f:0),selector:fn(s),label:r?pn(r):"",interactive:!!r}},_t=()=>{var r,s;let e=document.documentElement,t=Math.max(e.scrollHeight,(s=(r=document.body)==null?void 0:r.scrollHeight)!=null?s:0);return t?Math.min(100,Math.round((window.scrollY+window.innerHeight)/t*100)):0};var Pe="0.13.0",gn=(e,t)=>{let r=t.trim().toLowerCase();for(let[s,c]of Object.entries(e))if(s.toLowerCase()===r)return c},mn=(e,t)=>{let r=e.trim().toLowerCase(),s=t.trim().toLowerCase();return s.startsWith("@")?r.endsWith(s):r===s},hn=(e,t)=>{if(e.op==="in cohort")return false;let r=gn(t,e.property),s=r==null?"":String(r);if(e.op==="\u2265"){let a=Number(s),f=Number(e.values[0]);return s!==""&&Number.isFinite(a)&&Number.isFinite(f)&&a>=f}let c=s!==""&&e.values.some(a=>mn(s,a));return e.op==="is"||e.op==="in"?c:!c},wn=(e,t,r={})=>{var C;if(!e.on)return false;let s={id:t,...r},c=e.sets.length?e.sets.find(v=>v.conditions.every(k=>hn(k,s))):{rollout:100};if(!c||St(`${e.key}:rollout:${t}`)*100>=c.rollout)return false;if(!((C=e.variants)!=null&&C.length))return true;let a=St(`${e.key}:variant:${t}`)*100,f=0;for(let v of e.variants)if(f+=v.split,a<f)return v.key;return e.variants[e.variants.length-1].key},Qe=(e,t,r,s={})=>{var a;let c=(a=t==null?void 0:t.flags)==null?void 0:a.find(f=>f.key===e);return c?wn(c,r,s):false},kn=["utm_source","utm_medium","utm_campaign","utm_term","utm_content"],It="ck_preview",Ot="ck_embed",ce="ck_id",ke="ck_sid";var yn=e=>{var r;let t={};for(let s of kn){let c=(r=e(s))==null?void 0:r.trim();c&&(t[s]=c.slice(0,150));}return t},Ft=e=>{let t=new URLSearchParams(e);return yn(r=>t.get(r))};var bn=e=>{let t=2166136261;for(let r=0;r<e.length;r++)t^=e.charCodeAt(r),t=Math.imul(t,16777619);return (t>>>0)/4294967296},St=e=>{let t=2166136261;for(let r=0;r<e.length;r++)t^=e.charCodeAt(r),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},xn=(e,t)=>{var a,f,C,v;let r=(f=(a=e.variants[0])==null?void 0:a.id)!=null?f:"control";if(e.winner)return e.winner;if(e.status!=="running")return r;let s=bn(`${e.key}:${t}`),c=0;for(let k of e.variants)if(c+=k.weight,s<c)return k.id;return (v=(C=e.variants[e.variants.length-1])==null?void 0:C.id)!=null?v:r},$t=e=>{let t=Array.isArray(e)?e[0]:e;if(!t)return {};let r={};for(let s of t.split(",")){let[c,a]=s.split(":");c&&a&&(r[c.trim()]=a.trim());}return r};var Re=(e,t,r,s)=>{if(s[e])return s[e];let c=t==null?void 0:t.experiments.find(a=>a.key===e);return c?xn(c,r):"control"},Mt=(e,t,r)=>{var c,a,f,C,v,k;let s={...(c=e==null?void 0:e.texts)!=null?c:{}};for(let E of (a=e==null?void 0:e.experiments)!=null?a:[]){let g=Re(E.key,e,t,r);g!==((C=(f=E.variants[0])==null?void 0:f.id)!=null?C:"control")&&Object.assign(s,(k=(v=E.variants.find(w=>w.id===g))==null?void 0:v.changes)!=null?k:{});}return s},Ce="ck_x",Me=e=>e.replace(/\/+$/,"")||"/",Pt=(e,t,r,s={})=>{var C,v;if(!e)return null;let c;try{c=new URL(t);}catch(k){return null}if(c.searchParams.has(Ce))return null;let a=Me(c.pathname),f=(k,E)=>{let g;try{g=new URL(k,c);}catch(w){return}if(!(!/^https?:$/.test(g.protocol)||g.origin===c.origin&&Me(g.pathname)===a))return c.searchParams.forEach((w,S)=>{g.searchParams.has(S)||g.searchParams.set(S,w);}),E&&g.searchParams.set(Ce,E),g.hash||(g.hash=c.hash),g.href};for(let k of (C=e.redirects)!=null?C:[]){let E=k.from&&Me(k.from)===a?f(k.to):void 0;if(E)return {to:E}}for(let k of e.experiments){if(!k.path||Me(k.path)!==a)continue;let E=k.status==="running"&&!k.winner;if(E&&r===null&&!s[k.key])return null;let g=Re(k.key,e,r!=null?r:"",s),w=(v=k.variants.find(M=>M.id===g))==null?void 0:v.url,S=w?f(w,E?`${k.key}:${g}`:void 0):void 0;return E?{to:S,experiment:k.key,variant:g}:S?{to:S}:null}return null},At=(e,t)=>{let[r,s]=(t!=null?t:"").split(":"),c=e==null?void 0:e.experiments.find(a=>a.key===r);return c&&c.variants.some(a=>a.id===s)?{experiment:r,variant:s}:null};var Ze=()=>typeof crypto!="undefined"&&"randomUUID"in crypto?crypto.randomUUID():`ck_${Math.random().toString(36).slice(2)}${Date.now().toString(36)}`;var ve=(e,t)=>e.length>t?`${e.slice(0,t-1)}\u2026`:e,Cn=e=>{var t;if(e instanceof Error)return {type:e.name||"Error",message:ve(e.message||String(e),1e3),stack:e.stack?ve(e.stack,8192):void 0};if(typeof e=="string")return {type:"Error",message:ve(e,1e3)};try{return {type:"Error",message:ve((t=JSON.stringify(e))!=null?t:String(e),1e3)}}catch(r){return {type:"Error",message:String(e)}}},Lt=e=>{let t=[],r=[],s=new Set,c=0,a=`${e.host}/api/sdk/errors`,f=(w=false)=>{for(;t.length;){let S=JSON.stringify({key:e.apiKey,errors:t.splice(0,4)});w&&typeof navigator!="undefined"&&"sendBeacon"in navigator&&navigator.sendBeacon(a,S)||fetch(a,{method:"POST",headers:{"content-type":"application/json"},body:S,keepalive:true}).then(L=>{!L.ok&&L.status!==429&&e.warn(`Error reports were rejected with ${L.status}. Check the public key and NEXT_PUBLIC_MRROCKET_HOST.`);}).catch(()=>{});}},C=(w,S="manual")=>{let M=e.context();if(!M.send||c>=20)return;let L=Cn(w);if(/\/api\/sdk\/errors/.test(L.message))return;let N=`${L.type}:${L.message}`;s.has(N)||(s.add(N),c++,t.push({...L,mechanism:S,handled:S==="manual",url:window.location.href,ts:Date.now(),crumbs:r.slice(),...e.release?{release:e.release}:{},...M.distinctId?{distinctId:M.distinctId}:{},...M.sessionId?{sessionId:M.sessionId}:{}}));},v=w=>{var S;!w.error&&(!w.message||w.message==="Script error.")||C((S=w.error)!=null?S:new Error(w.message),"onerror");},k=w=>{var S;return C((S=w.reason)!=null?S:new Error("Unhandled promise rejection"),"onunhandledrejection")},E=()=>f(true);window.addEventListener("error",v),window.addEventListener("unhandledrejection",k),window.addEventListener("pagehide",E);let g=window.setInterval(()=>f(),2e3);return {capture:C,crumb:(w,S)=>{r.push({ts:Date.now(),type:w,message:ve(S,120)}),r.length>20&&r.shift();},stop:()=>{window.removeEventListener("error",v),window.removeEventListener("unhandledrejection",k),window.removeEventListener("pagehide",E),window.clearInterval(g),f(true);}}};var Ee="$cancel_flow",re="$onboarding",et="$inapp",Rn={version:0,question:"",reasons:[],offers:{},confirm:{title:"Cancel your subscription?",body:"You keep access until the end of the billing period.",button:"Cancel subscription"},offersFor:"business"},vn="Something went wrong. Please try again.",Nt="mrrocket:onboarding",Ae=e=>e.trim().toLowerCase().replace(/[^a-z0-9]+/g,"_").replace(/^_+|_+$/g,""),Tt=new Set(["signed_up","signup","sign_up","registered","account_created","user_signed_up"]),tt=(e,t)=>{let r=Ae(e),s=Ae(t);return !r||!s?false:r===s||Tt.has(r)&&Tt.has(s)},En=e=>/^(first (launch|visit|open|session|login|sign ?in|app open)|app (opened|launched)|on load|every visit|always)?$/i.test(e.trim()),_n=()=>`cf_${Math.random().toString(36).slice(2,10)}${Date.now().toString(36)}`;var Sn=e=>{let t=(e!=null?e:"").trim();return /^https?:\/\//i.test(t)||/^\/(?!\/)/.test(t)?t:void 0},le=e=>{var t;return (t=e.nodes)!=null?t:e.steps},In=e=>Math.max(0,e.amount)*(e.unit==="hours"?36e5:864e5),On=(e,t)=>{let r=e.trim(),s=(t.split(/[?#]/)[0]||"/").replace(/\/+$/,"")||"/";if(!r)return false;if(r.endsWith("*")){let c=r.slice(0,-1).replace(/\/+$/,"");return !c||s===c||s.startsWith(`${c}/`)}return s===(r.replace(/\/+$/,"")||"/")},nt=e=>e.flatMap(t=>t.kind==="condition"?[...nt(t.yes),...nt(t.no)]:t.kind==="wait"?[]:[t]),Dt=e=>{let t=null,r=null,s=null,c=[],a=false,f=null,C,v=null,k=new Map,E=new Set,g=new Set,w=new Set,S=e.auto!==false,M=new Set,L=()=>{var n,i;return (i=(n=e.now)==null?void 0:n.call(e))!=null?i:Date.now()},N=null,z=()=>{var n;return (n=t==null?void 0:t.onboarding)!=null?n:[]},Z=n=>n?z().find(i=>i.id===n):void 0,H=n=>{let i=k.get(n);return i||k.set(n,i={done:new Set,dismissed:false,answers:new Map,at:new Map,reached:new Map}),i},G=false,K=n=>{e.track(re,n),G=true;},oe=(n,i)=>{n.done.add(i),n.at.set(i,L());},Be=n=>{var i,l,m;if((i=n.page)!=null&&i.trim()){let p=(l=e.path)==null?void 0:l.call(e);return p===void 0?false:On(n.page,p)}if((m=n.event)!=null&&m.trim()){if(!a)return;for(let p of M)if(tt(n.event,p))return true;return false}return false},de=(n,i,l,m,p=0,_)=>{var I,b;for(let y=0;y<i.length;y++){let x=i[y];if(x.kind==="wait"){if(l.done.has(x.id)){_=(I=l.at.get(x.id))!=null?I:_;continue}let O=l.reached.get(x.id);if(O===void 0){if(!m)return {at:"pending"};O=_!=null?_:L(),l.reached.set(x.id,O),K({flow:n.id,step:x.id,kind:"wait",action:"shown"});}let B=O+In(x);if(L()<B)return {at:"wait",until:B};if(!m)return {at:"pending"};oe(l,x.id),K({flow:n.id,step:x.id,kind:"wait",action:"completed"}),_=B;continue}if(x.kind==="condition"){let O=l.answers.get(x.id);if(O===void 0){if(!m)return {at:"pending"};if(O=Be(x),O===void 0)return {at:"pending"};l.answers.set(x.id,O),K({flow:n.id,step:x.id,kind:"condition",action:"answered",answer:O?"yes":"no"});}return de(n,O?x.yes:x.no,l,m,p,_)}if(!l.done.has(x.id))return {at:"step",step:x,line:i,index:y,before:p};_=(b=l.at.get(x.id))!=null?b:_,p++;}return {at:"end"}},se=n=>{let i=k.get(n.id);return !!i&&(i.dismissed||de(n,le(n),i,false).at==="end")},ue=n=>`${Nt}:${n!=null?n:"visitor"}`,j=n=>`${Nt}:events:${n!=null?n:"visitor"}`,W=()=>{var l;G=false;let n=(l=e.storage)==null?void 0:l.call(e);if(!n)return;let i={};for(let[m,p]of k)i[m]={done:[...p.done],...p.dismissed?{dismissed:true}:{},...p.answers.size?{answers:Object.fromEntries([...p.answers].map(([_,I])=>[_,I?"yes":"no"]))}:{},...p.at.size?{at:Object.fromEntries(p.at)}:{},...p.reached.size?{reached:Object.fromEntries(p.reached)}:{}};try{n.set(ue(e.personId()),JSON.stringify(i)),M.size&&n.set(j(e.personId()),JSON.stringify([...M].slice(-200)));}catch(m){}},$=n=>{var i,l,m,p;for(let[_,I]of Object.entries(n!=null?n:{})){let b=H(_);for(let y of (i=I.done)!=null?i:[])b.done.add(y);I.dismissed&&(b.dismissed=true);for(let[y,x]of Object.entries((l=I.answers)!=null?l:{}))b.answers.has(y)||b.answers.set(y,x==="yes");for(let[y,x]of Object.entries((m=I.at)!=null?m:{}))typeof x=="number"&&!b.at.has(y)&&b.at.set(y,x);for(let[y,x]of Object.entries((p=I.reached)!=null?p:{}))typeof x=="number"&&(!b.reached.has(y)||x<b.reached.get(y))&&b.reached.set(y,x);}},Q=n=>{var l;N&&clearTimeout(N),N=null;let i=n-L();i>0&&i<=864e5&&(N=setTimeout(()=>(N=null,P()),i+50),(l=N.unref)==null||l.call(N));},fe=false,ie=()=>{fe=false;let n=Z(s);if(!n)return null;let i=H(n.id);if(i.dismissed)return null;let l=de(n,le(n),i,true);if(G&&W(),l.at==="wait"&&Q(l.until),fe=l.at==="wait"||l.at==="pending",l.at!=="step")return null;let{step:m,line:p,index:_}=l,I=O=>!!O&&O.kind!=="wait"&&O.kind!=="condition";if(m.kind==="checklist"){let O=_;for(;O>0&&I(p[O-1])&&p[O-1].kind==="checklist";)O--;let B=_;for(;B+1<p.length&&I(p[B+1])&&p[B+1].kind==="checklist";)B++;let ge=p.slice(O,B+1).filter(I);for(let Y of ge)!i.done.has(Y.id)&&!g.has(`s:${Y.id}`)&&(g.add(`s:${Y.id}`),e.track(re,{flow:n.id,step:Y.id,kind:"checklist",action:"shown"}));return {type:"checklist",flow:n,items:ge.map(Y=>({step:Y,done:i.done.has(Y.id)}))}}g.has(`s:${m.id}`)||(g.add(`s:${m.id}`),e.track(re,{flow:n.id,step:m.id,kind:m.kind,action:"shown"}));let b=p.slice(_+1),y=b.findIndex(O=>O.kind==="condition"),x=(y<0?b:b.slice(0,y)).filter(I).length;return {type:"step",flow:n,step:m,index:l.before,total:l.before+1+x,last:!x&&y<0}},ae=()=>{if(r){let l=r.reason?r.flow.offers[r.reason]:void 0;return {type:"cancel",screen:r.screen,flow:r.flow,reason:r.reason,offer:r.screen==="offer"?l:void 0,price:r.options.price,busy:r.busy,error:r.error,step:r.plain||r.screen==="survey"?1:r.screen==="offer"?2:3,steps:r.plain?1:3}}let n=ie();if(n)return n;s&&!fe&&(s=null);let i=c.find(l=>!E.has(l.id));return i?{type:"message",message:i}:null},P=()=>{let n=ae();(n==null?void 0:n.type)==="message"&&!g.has(`m:${n.message.id}`)&&(g.add(`m:${n.message.id}`),e.track(et,{message:n.message.id,journey:n.message.journey,action:"shown"}));for(let i of [...w])try{i();}catch(l){}},J=n=>s===n.id?true:s&&Z(s)&&!se(Z(s))?false:(s=n.id,e.track(re,{flow:n.id,action:"started"}),P(),true),ye=()=>{var i;if(!S||s||!a||!e.personId()&&!((i=e.storage)!=null&&i.call(e)))return;let n=z().find(l=>En(l.trigger)&&le(l).length&&!se(l));n&&J(n);},Ie=()=>{let n=e.personId();v!==null&&v!==n&&(k.clear(),M.clear(),E.clear(),s=null,C=void 0),v=n;let i=(async()=>{var p,_,I;let l=(p=e.storage)==null?void 0:p.call(e);if(l)try{let b=await l.get(ue(n));b&&$(JSON.parse(b));let y=await l.get(j(n));if(y)for(let x of JSON.parse(y))typeof x=="string"&&M.add(x);}catch(b){}let m=z().length>0||!!(t!=null&&t.messages);if(n&&m&&C!==n){C=n;let b=await e.fetchInApp(n).catch(()=>null);if(b&&e.personId()===n){$(b.onboarding);for(let y of (_=b.events)!=null?_:[])typeof y=="string"&&M.add(Ae(y));c=((I=b.messages)!=null?I:[]).filter(y=>y&&y.id&&y.title);}}n||(c=[]),a=true,ye(),P();})();return f=i.finally(()=>{f===i&&(f=null);}),f},D=(n,i,l)=>{var p;let m=n.reason?n.flow.offers[n.reason]:void 0;return {outcome:i,run:n.run,...n.reason?{reason:n.reason,reasonLabel:(p=n.flow.reasons.find(_=>_.id===n.reason))==null?void 0:p.label}:{},...i==="saved"&&m?{offer:m}:{},...l!=null&&l.trim()?{comment:l.trim().slice(0,500)}:{}}},U=(n,i)=>{var l;e.track(Ee,{action:"finished",run:n.run,version:n.flow.version,outcome:i.outcome,customer:(l=n.options.customer)!=null?l:"consumer",...i.reason?{reason:i.reason}:{},...i.offer?{offer:i.offer.kind}:{},...i.comment?{comment:i.comment}:{}}),r===n&&(r=null),n.resolve(i),P();},V=(n,i,l)=>{if(n.busy)return;let m=D(n,i,l),p=i==="saved"?n.options.onSave:n.options.onCancel;n.busy=true,n.error=void 0,P(),Promise.resolve().then(()=>p==null?void 0:p(m)).then(()=>U(n,m),()=>{n.busy=false,n.error=vn,P();});};return {setConfig:n=>{t=n,s&&!Z(s)&&(s=null),P();},refresh:Ie,onEvent:n=>{var I;if(!n||n.startsWith("$"))return;let i=Ae(n),l=!!i&&!M.has(i);l&&M.add(i);let m=Z(s),p=false;if(m){let b=H(m.id);for(let y of nt(le(m)))y.kind==="checklist"&&y.event&&!b.done.has(y.id)&&tt(y.event,n)&&(oe(b,y.id),p=true,e.track(re,{flow:m.id,step:y.id,kind:y.kind,action:"completed"}));}if(p||l&&m?(W(),P()):l&&W(),!S||!e.personId()&&!((I=e.storage)!=null&&I.call(e)))return;let _=z().find(b=>le(b).length&&tt(b.trigger,n)&&!se(b));_&&J(_);},openCancelFlow:(n={})=>{var I,b;if(r)return r.promise;let i=(I=t==null?void 0:t.cancelFlow)!=null?I:Rn,l=n.customer!=="business"||i.reasons.length<2,m=()=>{},p=new Promise(y=>m=y),_={flow:i,options:n,plain:l,screen:l?"confirm":"survey",run:_n(),busy:false,resolve:m,promise:p};return r=_,e.track(Ee,{action:"shown",run:_.run,version:i.version,customer:(b=n.customer)!=null?b:"consumer",plain:l}),P(),p},startOnboarding:n=>{let i=n?Z(n):z().find(m=>le(m).length&&!se(m));if(!i||!le(i).length)return false;let l=H(i.id);return l.dismissed=false,J(i)},view:ae,subscribe:n=>(w.add(n),()=>w.delete(n)),cancel:{pick:n=>{!r||r.busy||!r.flow.reasons.some(i=>i.id===n)||(r.reason=n,r.error=void 0,P());},next:()=>{let n=r;!n||n.screen!=="survey"||!n.reason||(e.track(Ee,{action:"answered",run:n.run,version:n.flow.version,reason:n.reason}),n.screen=n.flow.offers[n.reason]?"offer":"confirm",P());},accept:n=>{let i=r;if(!i||i.screen!=="offer"||!i.reason)return;let l=i.flow.offers[i.reason];i.busy||e.track(Ee,{action:"offer",run:i.run,version:i.flow.version,reason:i.reason,offer:l==null?void 0:l.kind,outcome:"accepted"}),V(i,"saved",n);},decline:()=>{var i;let n=r;!n||n.screen!=="offer"||n.busy||(e.track(Ee,{action:"offer",run:n.run,version:n.flow.version,reason:n.reason,offer:n.reason?(i=n.flow.offers[n.reason])==null?void 0:i.kind:void 0,outcome:"declined"}),n.screen="confirm",n.error=void 0,P());},confirm:n=>{let i=r;!i||i.screen!=="confirm"||V(i,"cancelled",n);},close:()=>{var l,m;let n=r;if(!n||n.busy)return;let i=D(n,"kept");(m=(l=n.options).onClose)==null||m.call(l,i),U(n,i);}},onboarding:{next:()=>{let n=ie();(n==null?void 0:n.type)==="step"&&(oe(H(n.flow.id),n.step.id),e.track(re,{flow:n.flow.id,step:n.step.id,kind:n.step.kind,action:"completed"}),W(),P());},check:n=>{let i=ie();if((i==null?void 0:i.type)!=="checklist")return;let l=i.items.find(m=>m.step.id===n);!l||l.done||(oe(H(i.flow.id),n),e.track(re,{flow:i.flow.id,step:n,kind:"checklist",action:"completed"}),W(),P());},dismiss:()=>{let n=ie();!n||n.type!=="step"&&n.type!=="checklist"||(H(n.flow.id).dismissed=true,e.track(re,{flow:n.flow.id,...n.type==="step"?{step:n.step.id}:{},action:"dismissed"}),s=null,W(),P());}},message:{click:()=>{var l;let n=ae();if((n==null?void 0:n.type)!=="message")return;E.add(n.message.id),e.track(et,{message:n.message.id,journey:n.message.journey,action:"clicked"});let i=Sn(n.message.url);P(),i&&((l=e.openUrl)==null||l.call(e,i));},dismiss:()=>{let n=ae();(n==null?void 0:n.type)==="message"&&(E.add(n.message.id),e.track(et,{message:n.message.id,journey:n.message.journey,action:"dismissed"}),P());}}}};var Fn="ck_pick",$n="ck_origin",Le=e=>typeof CSS!="undefined"&&CSS.escape?CSS.escape(e):e.replace(/[^a-zA-Z0-9_-]/g,t=>`\\${t}`),Mn=e=>[...e.classList].filter(t=>t.length<30&&!/\d{2,}|[_-][a-z0-9]{5,}$|^(is|has)-|hover|active|focus|open|selected|:|\[/.test(t)).slice(0,2),Ut=e=>{let t=e.ownerDocument,r=f=>{try{return t.querySelectorAll(f).length===1}catch(C){return false}},s=e.getAttribute("data-ck-target");if(s)return `[data-ck-target="${s.replace(/"/g,'\\"')}"]`;if(e.id&&!/\d{3,}/.test(e.id)&&r(`#${Le(e.id)}`))return `#${Le(e.id)}`;let c=[],a=e;for(;a&&a!==t.documentElement&&c.length<6;){if(a!==e&&a.id&&!/\d{3,}/.test(a.id)){c.unshift(`#${Le(a.id)}`);break}let f=a.tagName.toLowerCase(),C=Mn(a).map(w=>`.${Le(w)}`).join(""),v=a.parentElement,k=v?[...v.children].filter(w=>w.tagName===a.tagName):[],E=k.length>1?`:nth-of-type(${k.indexOf(a)+1})`:"";c.unshift(`${f}${C}${E}`);let g=c.join(" > ");if(r(g))return g;a=v;}return c.join(" > ")},jt=()=>{var E;if(typeof window=="undefined"||window.top===window)return null;let e=new URLSearchParams(window.location.search);if(e.get(Fn)!=="1")return null;let t;try{t=new URL((E=e.get($n))!=null?E:"").origin;}catch(g){return null}if(!/^https?:\/\//.test(t))return null;let r=g=>window.parent.postMessage(g,t),s=document.createElement("div");s.setAttribute("aria-hidden","true"),Object.assign(s.style,{position:"fixed",zIndex:"2147483647",pointerEvents:"none",border:"2px solid #0ea5e9",background:"rgba(14,165,233,.12)",borderRadius:"6px",transition:"all 60ms linear",display:"none"});let c=document.createElement("div");Object.assign(c.style,{position:"absolute",left:"-2px",bottom:"calc(100% + 4px)",font:"600 11px/1.4 system-ui, sans-serif",color:"#fff",background:"#0c4a6e",padding:"2px 6px",borderRadius:"4px",whiteSpace:"nowrap",maxWidth:"360px",overflow:"hidden",textOverflow:"ellipsis"}),s.appendChild(c),document.body.appendChild(s);let a=null,f=g=>{if(a=g&&g!==s&&!s.contains(g)?g:null,!a){s.style.display="none";return}let w=a.getBoundingClientRect();Object.assign(s.style,{display:"block",left:`${w.left-2}px`,top:`${w.top-2}px`,width:`${w.width+4}px`,height:`${w.height+4}px`}),c.textContent=Ut(a);},C=g=>f(document.elementFromPoint(g.clientX,g.clientY)),v=g=>{var S,M;g.preventDefault(),g.stopPropagation();let w=(S=document.elementFromPoint(g.clientX,g.clientY))!=null?S:a;!w||w===s||r({type:"mrrocket:picked",selector:Ut(w),text:((M=w.textContent)!=null?M:"").trim().replace(/\s+/g," ").slice(0,80)});},k=g=>{g.key==="Escape"&&r({type:"mrrocket:pick-cancel"});};return window.addEventListener("mousemove",C,true),window.addEventListener("click",v,true),window.addEventListener("keydown",k,true),r({type:"mrrocket:pick-ready"}),()=>{window.removeEventListener("mousemove",C,true),window.removeEventListener("click",v,true),window.removeEventListener("keydown",k,true),s.remove();}};var Pn={get:e=>{try{return window.localStorage.getItem(e)}catch(t){return null}},set:(e,t)=>{try{window.localStorage.setItem(e,t);}catch(r){}}},Ht=()=>{try{for(let e=window.localStorage.length-1;e>=0;e--){let t=window.localStorage.key(e);t!=null&&t.startsWith("mrrocket:onboarding")&&window.localStorage.removeItem(t);}}catch(e){}},Kt=e=>({track:e.track,personId:e.personId,auto:e.auto,storage:()=>e.canStore()?Pn:null,openUrl:t=>window.location.assign(t),path:()=>window.location.pathname,fetchInApp:async t=>{if(!e.apiKey)return null;let r=await fetch(`${e.host}/api/sdk/inapp?key=${encodeURIComponent(e.apiKey)}&distinctId=${encodeURIComponent(t)}`);return r.ok?await r.json():null}});var De="mrr_ref",Ue="mrr_ref",An="mrr_ref",Ln="mrr_click";var _e=/^[A-Za-z0-9_-]{1,64}$/;var Nn=(e,t="ref")=>{let r=new URLSearchParams(e);return Tn(s=>r.get(s),t)},Tn=(e,t="ref")=>{let r=e(An);if(r&&_e.test(r)){let c=e(Ln);return {code:r,clickId:c&&_e.test(c)?c:null,direct:false}}let s=e(t);return s&&_e.test(s)?{code:s,clickId:null,direct:true}:null},Wt=e=>{var t;return `${e.code}.${(t=e.clickId)!=null?t:""}.${e.at}`},Ne=(e,t=60,r=Date.now())=>{if(!e)return null;let[s,c,a]=e.split("."),f=Number(a);return !s||!_e.test(s)||!Number.isFinite(f)||c&&!_e.test(c)||r-f>t*864e5||f>r+864e5?null:{code:s,clickId:c||null,at:f}};var T=null,je=()=>typeof document!="undefined",Vt=e=>{var r;if(!je())return;let t=(r=document.cookie.split("; ").find(s=>s.startsWith(`${e}=`)))==null?void 0:r.slice(e.length+1);try{return t?decodeURIComponent(t):void 0}catch(s){return t}},He=()=>{try{return typeof localStorage=="undefined"?null:localStorage}catch(e){return null}},rt=()=>{var e,t;return T||(je()?(t=Ne(Vt(De),3650))!=null?t:Ne((e=He())==null?void 0:e.getItem(Ue),3650):null)};var Te=(e,t)=>{var c;if(!je())return;let r=Math.max(1,Math.round(t))*86400,s=typeof location!="undefined"&&location.protocol==="https:"?"; secure":"";document.cookie=`${De}=${encodeURIComponent(Wt(e))}; path=/; max-age=${r}; samesite=lax${s}`;try{(c=He())==null||c.setItem(Ue,Wt(e));}catch(a){}},Ke=()=>{var e;if(je()){document.cookie=`${De}=; path=/; max-age=0; samesite=lax`;try{(e=He())==null||e.removeItem(Ue);}catch(t){}}},Bt=()=>{T=null;},qt=async e=>{var s,c,a;let t=Nn(e.search,e.param),r=(s=e.now)!=null?s:Date.now();if(!t)return !T&&e.store&&(T=(a=Ne(Vt(De),e.days,r))!=null?a:Ne((c=He())==null?void 0:c.getItem(Ue),e.days,r),T||Ke()),T;if(T={code:t.code,clickId:t.clickId,at:r},e.store&&Te(T,e.days),e.beacon)try{let f=await e.beacon({code:t.code,clickId:t.clickId});f&&T&&T.code===t.code&&!T.clickId&&(T={...T,clickId:f},e.store&&Te(T,e.days));}catch(f){}return T},Yt=e=>e==="$identify"||!e.startsWith("$");var ot=3600*24*365,Jt=e=>{var r;if(typeof document=="undefined")return;let t=(r=document.cookie.split("; ").find(s=>s.startsWith(`${e}=`)))==null?void 0:r.slice(e.length+1);if(t)try{return decodeURIComponent(t)}catch(s){return t}},We=(e,t,r)=>{let s=window.location.protocol==="https:"?"; secure":"";document.cookie=`${e}=${encodeURIComponent(t)}; path=/; max-age=${r}; samesite=lax${s}`;},Se=e=>{document.cookie=`${e}=; path=/; max-age=0; samesite=lax`;},A={utm:{}},zt=()=>{var e;return (e=A.id)!=null?e:A.id=Ze()},st=(e,t)=>{if(e!=="cookies")return zt();let r=Jt(ce);if(r)return A.id=r;let s=zt();return We(ce,s,ot),s},it=e=>{var c;let t=Date.now(),r=A.session,s=(c=e?Jt(ke):void 0)!=null?c:r&&t-r.last<18e5?r.id:Ze();return A.session={id:s,last:t},e&&We(ke,s,18e5/1e3),s},at=()=>{var r;let e=window.location.href;if(A.url===e)return;A.referrer=(r=A.url)!=null?r:document.referrer||void 0,A.url=e;let t=Ft(window.location.search);Object.keys(t).length&&(A.utm=t);},Gt=()=>{let e=navigator,t=window;return e.doNotTrack==="1"||t.doNotTrack==="1"||e.globalPrivacyControl===true};var Ve=window,Un=()=>{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']")},Qt=new Set,X=e=>{Qt.has(e)||(Qt.add(e),console.warn(`[MRRocket] ${e}`));},jn=()=>{var wt,kt,yt,bt;let e=Un(),t=o=>{var d;return (d=e==null?void 0:e.getAttribute(`data-${o}`))!=null?d:void 0},r=(o,d)=>{let u=t(o);return u===void 0?d:!["false","0","off","no"].includes(u.trim().toLowerCase())},s=(wt=e==null?void 0:e.src)!=null?wt:"",c="";try{c=s?new URL(s,window.location.href).origin:"";}catch(o){c="";}let a=((kt=t("key"))!=null?kt:"").trim(),f=((yt=t("api-host"))!=null?yt:c).trim().replace(/\/+$/,""),C=t("consent"),v=r("respect-dnt",false),k=r("heatmaps",true),E=r("errors",true),g=t("release"),w=t("attribution"),S=w==="always"||w==="off"?w:"consent",M=t("referral-param")||"ref",L={},N=(o,d)=>{var u;return (u=L[o])!=null?u:L[o]=new Promise((h,R)=>{var xt;let F=()=>Ve[d];if(F())return h(F());let q=(xt=t(`${o}-src`))!=null?xt:s?s.replace(/(\.min)?\.js(\?[^#]*)?(#.*)?$/,`-${o}$1.js?v=${Pe}`):"";if(!q)return R(new Error(o));let ne=document.createElement("script");ne.async=true,ne.src=q,ne.onload=()=>F()?h(F()):R(new Error(o)),ne.onerror=()=>R(new Error(o)),document.head.appendChild(ne);}).catch(h=>{throw L[o]=void 0,h})},z=t("theme"),Z={theme:z==="light"||z==="dark"?z:"auto",accent:t("accent")},H=new URLSearchParams(window.location.search),G=$t(H.get(It)),K=H.get(Ot)==="1";K&&jt();let oe=H.get(Ce);if(oe!==null){H.delete(Ce);let o=H.toString();history.replaceState(history.state,"",`${window.location.pathname}${o?`?${o}`:""}${window.location.hash}`);}let Be=Gt(),de=C==="cookies"||C==="off"?C:"cookieless",se=r("replay",false),ue="",j,W,$=null,Q=false,fe=false,ie=null,ae=null,P=window.location.pathname,J=[],ye=new Set,Ie=[],D=()=>v&&Be?"off":de,U=()=>!K&&D()!=="off",V=()=>j!=null?j:ue,pe=()=>D()==="cookies"?V():j;a||X('No public key. Add data-key="mrr_pub_\u2026" to the script tag; nothing is tracked until then.');let Oe=(o=false)=>{if(!J.length)return;if(!a){J.length=0;return}let d=`${f}/api/sdk/events`;for(let u of Rt(a,J.splice(0),{v:Pe,errors:E,platform:"web"}))o&&"sendBeacon"in navigator&&navigator.sendBeacon(d,u)||fetch(d,{method:"POST",headers:{"content-type":"application/json"},body:u,keepalive:true}).then(R=>{R.ok||X(`Events were rejected with ${R.status}. Check data-key (MRRocket \u203A Settings \u203A Products).`);}).catch(()=>X(`Could not reach ${f||"this origin"} to send events. Check data-api-host.`));},n=null,i=null,l=(o,d,u=Date.now(),h)=>{if(!U())return;let R=Yt(o)?rt():null;J.push({event:o,props:R?{...d,$referral:{code:R.code,clickId:R.clickId}}:d,distinctId:pe(),sessionId:it(D()==="cookies"),ts:u,url:h!=null?h:window.location.href,referrer:A.referrer,...A.utm,screen:{w:window.screen.width,h:window.screen.height}});},m=(o,d)=>{typeof o!="string"||!o||(l(o,d),U()&&(n==null||n.event(o)),p.onEvent(o));},p=Dt(Kt({host:f,apiKey:a,track:l,personId:()=>U()?j:void 0,canStore:()=>D()==="cookies",auto:r("onboarding",true)})),_=false;p.subscribe(()=>{_||!p.view()||K||(_=true,N("flows","__mrrocketFlows").then(o=>o.mountFlows(p,Z)).catch(()=>{_=false,X("The cancel flow and onboarding dialogs did not load. Check that sdk-flows.js is served next to sdk.js.");}));});let I=(o,d)=>{if(!U())return;let u=`${window.location.pathname}|${o}`;ye.has(u)||(ye.add(u),m("$exposure",{experiment:o,variant:d}));},b=()=>{let o=Pt($,window.location.href,V(),G);return o!=null&&o.to?(Oe(true),window.location.replace(o.to),true):(o!=null&&o.experiment&&I(o.experiment,o.variant),false)},y=()=>{if(!$)return;let o=document.querySelectorAll("[data-ck-text]");if(!o.length)return;let d=Mt($,V(),G),u=new Set;o.forEach(h=>{var q,ne;let R=h.dataset.ckText;if(!R)return;u.add(R),h.dataset.ckDefault===void 0&&(h.dataset.ckDefault=(q=h.textContent)!=null?q:"");let F=(ne=d[R])!=null?ne:h.dataset.ckDefault;h.textContent!==F&&(h.textContent=F);});for(let h of $.experiments)h.variants.some(R=>{var F;return Object.keys((F=R.changes)!=null?F:{}).some(q=>u.has(q))})&&I(h.key,Re(h.key,$,V(),G));},x=()=>{var o,d;return (d=(o=$==null?void 0:$.referral)==null?void 0:o.cookieDays)!=null?d:60},O=()=>S==="always"?D()!=="off":S==="consent"&&D()==="cookies",B=null,ge=false,Y=()=>{if(!Q||K)return;if(S==="off"||D()==="off"){Bt(),(S==="off"||ge)&&Ke();return}let o=window.location.href;B!==o&&(B=o,qt({search:window.location.search,param:M,store:O(),days:x(),beacon:a?({code:d,clickId:u})=>fetch(`${f}/api/sdk/affiliate/click`,{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify({key:a,code:d,clickId:u,url:o,referrer:document.referrer||void 0}),keepalive:true}).then(h=>h.ok?h.json():null).then(h=>{var R;return (R=h==null?void 0:h.clickId)!=null?R:null}):void 0}));},en=()=>{let o=O(),d=rt();o&&d?Te(d,x()):!o&&ge&&Ke(),ge=o;},ct=0,lt=false,dt=()=>{var h,R;let o=U()&&se&&!!($!=null&&$.replay)&&!!a;if(o===lt)return;lt=o;let d=++ct;if(!o){n==null||n.stop(true),n=null;return}let u=(R=(h=$==null?void 0:$.replay)==null?void 0:h.sampleRate)!=null?R:.1;N("replay","__mrrocketReplay").then(F=>F.startReplay({host:f,apiKey:a,sessionId:it(D()==="cookies"),distinctId:pe(),identified:!!j,sampleRate:u,warn:X})).then(F=>{d!==ct?F==null||F.stop(true):n=F;}).catch(()=>X("Session recording could not start (the recorder did not load)."));},ut=r("messenger",false),ee=null,me=o=>{K||a&&(ee!=null||(ee=N("messenger","__mrrocketMessenger").then(d=>{var F;let u=q=>t(`messenger-${q}`)===void 0?void 0:r(`messenger-${q}`,true),h=t("messenger-position"),R=(F=t("messenger-theme"))!=null?F:z;return d.mountMessenger({host:f,apiKey:a,distinctId:V,user:()=>W,launcher:ut,position:h==="bottom-left"||h==="bottom-right"?h:void 0,title:t("messenger-title"),greeting:t("messenger-greeting"),label:t("messenger-label"),askEmail:u("ask-email"),help:u("help"),sound:u("sound"),preview:u("preview"),theme:R==="light"||R==="dark"||R==="auto"?R:void 0,accent:t("accent")})})),ee.then(o).catch(()=>{ee=null,X("sdk-messenger.js did not load.");}));},ft=t("forms")==="auto",qe=false,Ye=()=>{qe||!a||K||!document.querySelector(ft?"form":"form[data-mrrocket-form]")||(qe=true,N("forms","__mrrocketForms").then(o=>o.watchForms({host:f,apiKey:a,auto:ft,distinctId:()=>U()?pe():void 0,identified:()=>!!j,identify:ht,tracking:U,referrer:()=>A.referrer,utm:()=>A.utm})).catch(()=>qe=false));},Xe=()=>{!fe||!U()||ae===window.location.pathname||(ae=window.location.pathname,m("$pageview",{path:window.location.pathname}));},te=null,pt=()=>{let o=te;k&&o&&o.max>0&&l("$scroll",{depth:o.max,docH:o.docH,vw:window.innerWidth,vh:window.innerHeight},Date.now(),o.url),te=null;},be=()=>{var R,F;if(!k||!U()||K)return;let o=document.documentElement,d=Math.max(o.scrollHeight,(F=(R=document.body)==null?void 0:R.scrollHeight)!=null?F:0),u=_t(),h=window.location.href;(te==null?void 0:te.url)!==h?te={url:h,max:u,docH:d}:u>te.max&&(te={url:h,max:u,docH:d});},gt=()=>{let o=window.location.pathname;o!==P&&(P=o,!(Q&&b())&&(pt(),at(),n==null||n.page(o),i==null||i.crumb("navigation",o),Xe(),Y(),window.setTimeout(()=>{be(),y(),Ye();},0)));},mt=()=>{let o=D(),d=ie;ie=o,d==="cookies"&&o!=="cookies"&&(Se(ce),Se(ke)),o==="off"&&(J.length=0,A.session=void 0),d==="cookies"&&o!=="cookies"&&Ht(),ue=st(o),en(),Y(),dt(),Xe(),be();},tn=o=>{let{analytics:d,replay:u}=typeof o=="string"?{analytics:o,replay:void 0}:o!=null?o:{};if(d!==void 0&&d!=="cookieless"&&d!=="cookies"&&d!=="off"){X(`setConsent: "${String(d)}" is not a consent mode. Use "cookieless", "cookies" or "off".`);return}d==="off"&&(J.length=0),d&&(de=d),typeof u=="boolean"&&(se=u),mt(),Q&&p.refresh();},ht=(o,d)=>{if(typeof o!="string"||!o)return;let u=pe();j=o,l("$identify",{...d,id:o,...u&&u!==o?{previousId:u}:{}}),n==null||n.identify(o),D()==="cookies"&&We(ce,o,ot),W={...d,id:o},y(),Q&&p.refresh();},Fe={track:m,identify:ht,reset:()=>{j=void 0,W=void 0,ye.clear(),D()==="cookies"&&(Se(ce),Se(ke)),A.id=void 0,A.session=void 0,ue=st(D()),y(),Q&&p.refresh();},captureError:o=>i==null?void 0:i.capture(o,"manual"),setConsent:tn,getFlag:o=>Qe(o,$,V(),{...W})!==false,getFlagVariant:o=>{let d=Qe(o,$,V(),{...W});return typeof d=="string"?d:null},getVariant:o=>{let d=Re(o,$,V(),G);return ($||G[o])&&I(o,d),d},ready:o=>{typeof o=="function"&&(Q?window.setTimeout(()=>o(Fe),0):Ie.push(o));},getDistinctId:V,cancelFlow:{open:o=>p.openCancelFlow(o)},onboarding:{start:o=>p.startOnboarding(o)},openCancelFlow:o=>p.openCancelFlow(o),startOnboarding:o=>p.startOnboarding(o),push:{subscribe:o=>{let d=U()?j:void 0;if(!d)return Promise.resolve({status:"not-signed-in"});let u=window.Notification,h=Promise.resolve(u&&u.permission==="default"&&(o==null?void 0:o.ask)!==false?u.requestPermission():u==null?void 0:u.permission);return N("push","__mrrocketPush").then(R=>R.subscribeWebPush({host:f,apiKey:a,distinctId:d,permission:h},o))},unsubscribe:()=>N("push","__mrrocketPush").then(o=>o.unsubscribeWebPush({host:f,apiKey:a,distinctId:j}))},messenger:{open:o=>me(d=>d.open(o)),close:()=>{ee==null?void 0:ee.then(o=>o.close()).catch(()=>{});},toggle:()=>me(o=>o.toggle())},openMessenger:()=>me(o=>o.open()),version:Pe};at(),mt();let he=Ve.mrrocket,nn=(bt=he==null?void 0:he.q)!=null?bt:[],ze=he&&typeof he=="object"?he:{};Object.assign(ze,Fe,{__loaded:true}),delete ze.q,Ve.mrrocket=ze;for(let[o,d]of nn){let u=Fe[o];if(typeof u=="function")try{u(...Array.from(d!=null?d:[]));}catch(h){console.error(h);}}fe=true,Xe(),E&&a&&!K&&(i=Lt({host:f,apiKey:a,release:g,context:()=>{var o;return {send:U(),distinctId:pe(),sessionId:(o=A.session)==null?void 0:o.id}},warn:X}),i.crumb("navigation",window.location.pathname));for(let o of ["pushState","replaceState"]){let d=history[o];history[o]=function(...u){let h=d.apply(this,u);return gt(),h};}window.addEventListener("popstate",gt);let rn=new xe;document.addEventListener("click",o=>{var F;o.target instanceof Element&&o.target.closest("[data-ck-messenger]")&&(o.preventDefault(),me(q=>q.open()));let d=o.target instanceof Element?o.target.closest("[data-ck-goal]"):null;if(d!=null&&d.dataset.ckGoal&&m(d.dataset.ckGoal,{text:(F=d.textContent)==null?void 0:F.trim().slice(0,80)}),!U())return;let u=Et(o);if(!u)return;let h=rn.push(u.x,u.y,Date.now());if((u.label||u.selector)&&(i==null||i.crumb("click",u.label||u.selector)),n==null||n.click(u,h),!k)return;let R={x:u.x,y:u.y,vw:u.vw,vh:u.vh,docH:u.docH,selector:u.selector,label:u.label,dead:!u.interactive};l("$click",R),h&&l("$rageclick",R);});let Je=0;window.addEventListener("scroll",()=>{Je||(Je=window.requestAnimationFrame(()=>{Je=0,be();}));},{passive:true}),document.readyState==="loading"?document.addEventListener("DOMContentLoaded",()=>be()):be(),window.setInterval(()=>Oe(),2e3),window.addEventListener("pagehide",()=>{pt(),Oe(true);});let Ge=()=>{if(Q=true,b())return;let o=At($,oe);o&&I(o.experiment,o.variant),document.documentElement.classList.remove("ck-hide"),Y(),dt(),p.setConfig($),p.refresh(),ut&&(document.body?me():document.addEventListener("DOMContentLoaded",()=>me())),document.readyState==="loading"?document.addEventListener("DOMContentLoaded",()=>{y(),Ye();}):(y(),Ye());for(let d of Ie.splice(0))try{d(Fe);}catch(u){console.error(u);}};a?fetch(`${f}/api/sdk/config?key=${encodeURIComponent(a)}`).then(o=>(o.ok||X(`Config request to ${f||"this origin"} answered ${o.status}. Check data-key and data-api-host.`),o.ok?o.json():null)).then(o=>{o&&Array.isArray(o.experiments)&&($=o),Ge();}).catch(()=>{X(`Could not reach ${f||"this origin"}. Check data-api-host. The page keeps its own texts and flags stay off.`),Ge();}):Ge();},Zt;typeof window!="undefined"&&typeof document!="undefined"&&!((Zt=Ve.mrrocket)!=null&&Zt.__loaded)&&jn();
|
|
2
2
|
})();
|
package/dist/core.d.ts
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
import type { CancelFlowConfig, OnboardingFlow } from "./flows.js";
|
|
6
6
|
export type VariantId = string;
|
|
7
7
|
/** This build of the SDK. Sent with the config request so MRRocket knows what the site runs (error tracking, flags). */
|
|
8
|
-
export declare const SDK_VERSION = "0.
|
|
8
|
+
export declare const SDK_VERSION = "0.13.0";
|
|
9
9
|
/** One variant of an experiment. `changes` replaces texts that the page marks with `<Text id>`. */
|
|
10
10
|
export interface VariantConfig {
|
|
11
11
|
id: VariantId;
|
package/dist/core.js
CHANGED
package/dist/core.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/core.ts"],"sourcesContent":["/**\n * The part of the SDK that runs anywhere: experiment config, variant assignment, preview links and event shapes.\n * No React, no browser APIs, so the server helper, the proxy helper and the client share it.\n */\nimport type { CancelFlowConfig, OnboardingFlow } from \"./flows\";\n\nexport type VariantId = string;\n\n/** This build of the SDK. Sent with the config request so MRRocket knows what the site runs (error tracking, flags). */\nexport const SDK_VERSION = \"0.12.0\";\n\n/** One variant of an experiment. `changes` replaces texts that the page marks with `<Text id>`. */\nexport interface VariantConfig {\n id: VariantId;\n /** Share of traffic, 0 to 1. The weights of an experiment add up to 1. */\n weight: number;\n changes?: Record<string, string>;\n /**\n * A redirect test: where this variant sends the visitor instead of the test's page (a path like \"/pricing-b\" or a\n * full address). Control has none and stays. SDK 0.7.0+.\n */\n url?: string;\n}\n\nexport interface ExperimentConfig {\n /** The experiment's key, the same as in Cockpit. */\n key: string;\n status: \"running\" | \"draft\" | \"stopped\";\n /** Control first. */\n variants: VariantConfig[];\n /** A winner that shipped: everyone gets it, and nobody is assigned anymore. */\n winner?: VariantId;\n /** A redirect test: the path of the page it runs on (\"/pricing\"). Visitors there are sent to their variant's `url`. */\n path?: string;\n}\n\n/** A page redirect set in MRRocket (Conversion › Landing pages › Redirect): everyone on `from` goes to `to`. SDK 0.7.0+. */\nexport interface RedirectConfig {\n /** A path of the site, like \"/old-pricing\". */\n from: string;\n /** A path or a full address. */\n to: string;\n}\n\nexport interface CockpitConfig {\n /** The product the public key belongs to. */\n project: string;\n experiments: ExperimentConfig[];\n /** The product's affiliate program: how many days a partner's referral is kept. Missing without a program. */\n referral?: { cookieDays: number };\n /** Session recordings: present only while the product records (Cockpit › Sessions). See `ReplayConfig`. */\n replay?: ReplayConfig;\n /** Feature flags of the product in Production (MRRocket › Build › Feature flags), evaluated by `useFlag` and `getFlag`. */\n flags?: FlagConfig[];\n /**\n * Published copy for texts the site marks (`<Text id>`, `data-ck-text`): a landing page's headline, subheadline and\n * button (`pricing.headline` …) and a paywall's (`paywall.<placement>.headline` …) as published in MRRocket. A\n * running test's variant changes them on top. Missing when nothing is published. SDK 0.5.0+.\n */\n texts?: Record<string, string>;\n /** The product's live cancel flow (MRRocket › Churn › Cancel flow), for `cancelFlow.open()`. SDK 0.7.0+. */\n cancelFlow?: CancelFlowConfig;\n /** The in-app steps of live onboarding flows (tours, checklists, modals). SDK 0.7.0+. */\n onboarding?: OnboardingFlow[];\n /** A live Lifecycle journey has in-app messages: signed-in people ask `/api/sdk/inapp` for theirs. SDK 0.7.0+. */\n messages?: boolean;\n /** Page redirects set in MRRocket; the SDK sends visitors on before the page shows (`redirectFor`). SDK 0.7.0+. */\n redirects?: RedirectConfig[];\n}\n\n/** One condition of a flag's audience, on a property of the person (`identify(id, props)`), the id or the page. */\nexport interface FlagCondition {\n /** \"Email\", \"Plan\", \"Country\", \"id\", \"path\" … Matched against the person's properties without regard to case. */\n property: string;\n op: \"is\" | \"is not\" | \"in\" | \"not in\" | \"≥\" | \"in cohort\";\n values: string[];\n}\n\n/** Users who match every condition, of whom `rollout` percent get the flag. No conditions: everyone. */\nexport interface FlagConditionSet {\n conditions: FlagCondition[];\n /** 0 to 100. */\n rollout: number;\n}\n\nexport interface FlagConfig {\n key: string;\n /** Off: everyone gets the off side. */\n on: boolean;\n /** The first set a person matches decides; none matched: off. */\n sets: FlagConditionSet[];\n /** Multivariate flags: the variants and their split in percent (control included). Missing for an on/off flag. */\n variants?: { key: string; split: number; payload?: string }[];\n}\n\n/** What a person is for flag conditions: their properties from identify(), their id and the page they are on. */\nexport type FlagPerson = Record<string, unknown>;\n\nconst propOf = (person: FlagPerson, name: string) => {\n const want = name.trim().toLowerCase();\n for (const [k, v] of Object.entries(person)) if (k.toLowerCase() === want) return v;\n return undefined;\n};\n\n/** `\"@acme.com\"` matches every address at that domain; anything else matches the whole value, without regard to case. */\nconst same = (actual: string, value: string) => {\n const a = actual.trim().toLowerCase();\n const v = value.trim().toLowerCase();\n return v.startsWith(\"@\") ? a.endsWith(v) : a === v;\n};\n\nconst matchesCondition = (c: FlagCondition, person: FlagPerson) => {\n // Cohorts live in MRRocket; a browser cannot know them, so a cohort condition never matches here.\n if (c.op === \"in cohort\") return false;\n const raw = propOf(person, c.property);\n const actual = raw === undefined || raw === null ? \"\" : String(raw);\n if (c.op === \"≥\") {\n const a = Number(actual);\n const b = Number(c.values[0]);\n return actual !== \"\" && Number.isFinite(a) && Number.isFinite(b) && a >= b;\n }\n const hit = actual !== \"\" && c.values.some((v) => same(actual, v));\n return c.op === \"is\" || c.op === \"in\" ? hit : !hit;\n};\n\n/**\n * The flag for one person: `false` (off), `true` (on, an on/off flag) or the variant's key (a multivariate flag). The\n * bucket is a stable hash of flag and person, so a person keeps their side as the rollout grows.\n */\nexport const evaluateFlag = (flag: FlagConfig, distinctId: string, person: FlagPerson = {}): boolean | string => {\n if (!flag.on) return false;\n const subject = { id: distinctId, ...person };\n const set = flag.sets.length ? flag.sets.find((s) => s.conditions.every((c) => matchesCondition(c, subject))) : { conditions: [], rollout: 100 };\n if (!set) return false;\n if (flagUnit(`${flag.key}:rollout:${distinctId}`) * 100 >= set.rollout) return false;\n if (!flag.variants?.length) return true;\n const u = flagUnit(`${flag.key}:variant:${distinctId}`) * 100;\n let acc = 0;\n for (const v of flag.variants) {\n acc += v.split;\n if (u < acc) return v.key;\n }\n return flag.variants[flag.variants.length - 1].key;\n};\n\n/** A flag of the config for this person; an unknown flag (or no config) is off. */\nexport const flagFor = (key: string, config: CockpitConfig | null, distinctId: string, person: FlagPerson = {}) => {\n const flag = config?.flags?.find((f) => f.key === key);\n return flag ? evaluateFlag(flag, distinctId, person) : false;\n};\n\n/** One JavaScript error as it goes over the wire to `POST /api/sdk/errors`. */\nexport interface ErrorReport {\n /** \"TypeError\", \"Error\" … */\n type: string;\n message: string;\n /** The raw stack, at most 8 KB. MRRocket parses it into frames. */\n stack?: string;\n /** \"onerror\" (uncaught), \"onunhandledrejection\" (a rejected promise nobody caught) or \"manual\" (captureError). */\n mechanism: \"onerror\" | \"onunhandledrejection\" | \"manual\";\n handled: boolean;\n url: string;\n ts: number;\n /** What happened just before: pages and clicks, newest last, at most 20. */\n crumbs?: { ts: number; type: \"navigation\" | \"click\" | \"lifecycle\"; message: string }[];\n /** Your release (a version or commit), from the provider's `release` prop. */\n release?: string;\n distinctId?: string;\n sessionId?: string;\n}\n\n/**\n * Session recordings of a product, from the config. Recording also needs the visitor's own replay consent\n * (`<CockpitProvider replay>` or `setConsent({ replay: true })`); without it nothing is recorded or buffered.\n */\nexport interface ReplayConfig {\n /**\n * Share of sessions uploaded from their start, 0 to 1 (default 0.1). The others keep only the last minute in\n * memory and upload it when something goes wrong: a JS error, a rage click, a `purchase…` or `checkout…` event.\n */\n sampleRate: number;\n}\n\n/**\n * What the SDK may send and store.\n * - \"cookieless\" (default): no cookie, no localStorage. Events go without a visitor id; the server counts visitors\n * with a hash of key, date, IP and user agent that changes every day.\n * - \"cookies\": a persistent `ck_id` cookie identifies the visitor across visits, `ck_sid` holds the session.\n * - \"off\": nothing is sent and nothing is stored.\n */\nexport type ConsentMode = \"cookieless\" | \"cookies\" | \"off\";\n\n/**\n * What `setConsent` takes: a mode as before, or the parts to change. `replay` is the separate consent for session\n * recordings (default false); it applies only while analytics is not \"off\".\n *\n * setConsent(\"cookies\");\n * setConsent({ replay: true });\n * setConsent({ analytics: \"cookieless\", replay: false });\n */\nexport type ConsentChoice = ConsentMode | { analytics?: ConsentMode; replay?: boolean };\n\n/** The campaign parameters the SDK reads from the landing URL. */\nexport const UTM_KEYS = [\"utm_source\", \"utm_medium\", \"utm_campaign\", \"utm_term\", \"utm_content\"] as const;\nexport type UtmKey = (typeof UTM_KEYS)[number];\n\n/** One event as it goes over the wire to `POST /api/sdk/events`. */\nexport interface CockpitEvent {\n event: string;\n /** The visitor. Missing in \"cookieless\" mode until identify(): the server then uses its daily visitor hash. */\n distinctId?: string;\n /** Ends after 30 minutes without an event. */\n sessionId?: string;\n props?: Record<string, unknown>;\n /** Milliseconds since the epoch. */\n ts: number;\n url?: string;\n /** `document.referrer` on the first page of a page load, the previous in-app URL after client-side navigation. */\n referrer?: string;\n utm_source?: string;\n utm_medium?: string;\n utm_campaign?: string;\n utm_term?: string;\n utm_content?: string;\n /** The screen size in CSS pixels (points on a phone). */\n screen?: { w: number; h: number };\n /** The app that sent it (React Native only): MRRocket stores it with the event and fills OS and device from it. */\n app?: AppContext;\n}\n\n/** Where a React Native event comes from. Every field is optional except the platform; nothing identifies the person. */\nexport interface AppContext {\n /** \"ios\" or \"android\" (React Native's `Platform.OS`). */\n platform: string;\n /** The app's version, like \"1.4.0\" (`expo-application`'s `nativeApplicationVersion`). */\n appVersion?: string;\n /** The build number, like \"42\". */\n build?: string;\n /** The OS version, like \"17.4\". */\n osVersion?: string;\n /** The device model, like \"iPhone 15 Pro\" (`expo-device`). */\n deviceModel?: string;\n /** \"phone\", \"tablet\", \"desktop\" or \"tv\" when `expo-device` knows it. */\n deviceType?: string;\n /** The first preferred locale, like \"de-DE\". */\n locale?: string;\n}\n\n/** The query parameter that forces variants, for previews in Cockpit: `?ck_preview=pricing-annual:a,home-hero:control`. */\nexport const PREVIEW_PARAM = \"ck_preview\";\n/** Set when Cockpit embeds the page: nothing is tracked, so previews never count as visits. */\nexport const EMBED_PARAM = \"ck_embed\";\n/** The cookie that keeps a visitor in the same variant across visits. Only written in \"cookies\" mode. */\nexport const ID_COOKIE = \"ck_id\";\n/** The cookie that holds the session id in \"cookies\" mode. */\nexport const SESSION_COOKIE = \"ck_sid\";\n/** A session ends after this long without an event. */\nexport const SESSION_TIMEOUT_MS = 30 * 60 * 1000;\n\n/** The UTM parameters from a parameter lookup (`URLSearchParams.get` in a browser). Empty values are left out. */\nexport const utmFrom = (get: (name: string) => string | null | undefined): Partial<Record<UtmKey, string>> => {\n const out: Partial<Record<UtmKey, string>> = {};\n for (const key of UTM_KEYS) {\n const value = get(key)?.trim();\n if (value) out[key] = value.slice(0, 150);\n }\n return out;\n};\n\n/** The UTM parameters of a query string, like `\"?utm_source=x\"`. Empty values are left out. */\nexport const readUtm = (search: string): Partial<Record<UtmKey, string>> => {\n const params = new URLSearchParams(search);\n return utmFrom((k) => params.get(k));\n};\n\n/**\n * The query parameters of a URL or query string, without `URLSearchParams` (React Native's is incomplete): `+` is a\n * space, values are percent-decoded, the first of a repeated name wins.\n */\nexport const parseQuery = (url: string): Record<string, string> => {\n const out: Record<string, string> = {};\n const q = url.split(\"#\")[0];\n const at = q.indexOf(\"?\");\n if (at < 0) return out;\n const dec = (s: string) => {\n try {\n return decodeURIComponent(s.replace(/\\+/g, \" \"));\n } catch {\n return s;\n }\n };\n for (const pair of q.slice(at + 1).split(\"&\")) {\n if (!pair) continue;\n const eq = pair.indexOf(\"=\");\n const k = dec(eq < 0 ? pair : pair.slice(0, eq));\n if (k && !(k in out)) out[k] = eq < 0 ? \"\" : dec(pair.slice(eq + 1));\n }\n return out;\n};\n\n/** FNV-1a, 32 bit, mapped to [0, 1). Stable across server and browser. */\nexport const hashUnit = (input: string) => {\n let h = 0x811c9dc5;\n for (let i = 0; i < input.length; i++) {\n h ^= input.charCodeAt(i);\n h = Math.imul(h, 0x01000193);\n }\n return (h >>> 0) / 4294967296;\n};\n\n/**\n * FNV-1a with murmur3's finalizer, mapped to [0, 1): ids that differ only at the end (\"user-1\", \"user-2\") still spread\n * evenly, so a 30 % rollout reaches about 30 % of people. Flags only; experiments keep `hashUnit` so nobody switches.\n */\nexport const flagUnit = (input: string) => {\n let h = 0x811c9dc5;\n for (let i = 0; i < input.length; i++) {\n h ^= input.charCodeAt(i);\n h = Math.imul(h, 0x01000193);\n }\n h ^= h >>> 16;\n h = Math.imul(h, 0x85ebca6b);\n h ^= h >>> 13;\n h = Math.imul(h, 0xc2b2ae35);\n h ^= h >>> 16;\n return (h >>> 0) / 4294967296;\n};\n\n/** The variant a visitor sees: a shipped winner, control while the experiment does not run, else a stable bucket by visitor. */\nexport const assign = (experiment: ExperimentConfig, distinctId: string): VariantId => {\n const control = experiment.variants[0]?.id ?? \"control\";\n if (experiment.winner) return experiment.winner;\n if (experiment.status !== \"running\") return control;\n const u = hashUnit(`${experiment.key}:${distinctId}`);\n let acc = 0;\n for (const v of experiment.variants) {\n acc += v.weight;\n if (u < acc) return v.id;\n }\n return experiment.variants[experiment.variants.length - 1]?.id ?? control;\n};\n\n/** `\"a:b,c:d\"` → `{ a: \"b\", c: \"d\" }`. Ignores anything malformed. */\nexport const parsePreview = (value: string | string[] | null | undefined): Record<string, VariantId> => {\n const raw = Array.isArray(value) ? value[0] : value;\n if (!raw) return {};\n const out: Record<string, VariantId> = {};\n for (const pair of raw.split(\",\")) {\n const [key, variant] = pair.split(\":\");\n if (key && variant) out[key.trim()] = variant.trim();\n }\n return out;\n};\n\n/** A link that shows one variant of a page, for example in Cockpit's preview frames. */\nexport const previewHref = (url: string, forced: Record<string, VariantId>, embed = true) => {\n const [path, query = \"\"] = url.split(\"?\");\n const params = new URLSearchParams(query);\n const value = Object.entries(forced)\n .map(([k, v]) => `${k}:${v}`)\n .join(\",\");\n if (value) params.set(PREVIEW_PARAM, value);\n if (embed) params.set(EMBED_PARAM, \"1\");\n return `${path}?${params.toString()}`;\n};\n\n/** The variant for one experiment, from a forced preview first, then the config. Unknown experiments get control. */\n/** The experiments whose variants change the text `id`: showing that text is an exposure to them. */\nexport const experimentsOfText = (config: CockpitConfig | null, id: string) =>\n (config?.experiments ?? []).filter((e) => e.variants.some((v) => v.changes && id in v.changes)).map((e) => e.key);\n\nexport const resolveVariant = (key: string, config: CockpitConfig | null, distinctId: string, preview: Record<string, VariantId>): VariantId => {\n if (preview[key]) return preview[key];\n const experiment = config?.experiments.find((e) => e.key === key);\n return experiment ? assign(experiment, distinctId) : \"control\";\n};\n\n/**\n * Every text change that applies to this visitor: the published texts first, then each experiment's in config order.\n * Control keeps the published (or the page's own) words, so only a non-control variant (assigned, forced in a preview,\n * or a shipped winner) changes them further.\n */\nexport const textChanges = (config: CockpitConfig | null, distinctId: string, preview: Record<string, VariantId>) => {\n const out: Record<string, string> = { ...(config?.texts ?? {}) };\n for (const experiment of config?.experiments ?? []) {\n const id = resolveVariant(experiment.key, config, distinctId, preview);\n if (id === (experiment.variants[0]?.id ?? \"control\")) continue;\n Object.assign(out, experiment.variants.find((v) => v.id === id)?.changes ?? {});\n }\n return out;\n};\n\n/**\n * The query parameter a redirect test adds to the variant's address (`ck_x=<test>:<variant>`): the page there records\n * the exposure and takes it out of the address bar. It also keeps that page from redirecting again.\n */\nexport const EXPOSURE_PARAM = \"ck_x\";\n\n/** What `redirectFor` decided: go to `to`, and/or the redirect test this page belongs to and the visitor's variant. */\nexport interface RedirectDecision {\n /** Where to send the visitor now. Missing for control, which stays on the page. */\n to?: string;\n /** The redirect test, while it runs: record `$exposure` for it (on this page for control, on `to` otherwise). */\n experiment?: string;\n variant?: VariantId;\n}\n\nconst pathOf = (p: string) => p.replace(/\\/+$/, \"\") || \"/\";\n\n/**\n * Where a page sends the visitor, before it shows: a page redirect set in MRRocket first, else a redirect test that runs\n * on this path (the visitor's variant, by the same stable assignment as every test). Null when nothing applies.\n *\n * - The new address keeps the page's query (UTM parameters) and its hash; a running test adds `ck_x=<test>:<variant>`,\n * so the page it lands on records the exposure (and never redirects again).\n * - `distinctId` null (no stable id, as on a server without the `ck_id` cookie): running tests are left to the browser,\n * page redirects and shipped winners still apply.\n * - A redirect to the page itself is ignored.\n */\nexport const redirectFor = (config: CockpitConfig | null, href: string, distinctId: string | null, preview: Record<string, VariantId> = {}): RedirectDecision | null => {\n if (!config) return null;\n let here: URL;\n try {\n here = new URL(href);\n } catch {\n return null;\n }\n if (here.searchParams.has(EXPOSURE_PARAM)) return null;\n const path = pathOf(here.pathname);\n const go = (to: string, mark?: string) => {\n let t: URL;\n try {\n t = new URL(to, here);\n } catch {\n return undefined;\n }\n if (!/^https?:$/.test(t.protocol) || (t.origin === here.origin && pathOf(t.pathname) === path)) return undefined;\n here.searchParams.forEach((v, k) => {\n if (!t.searchParams.has(k)) t.searchParams.set(k, v);\n });\n if (mark) t.searchParams.set(EXPOSURE_PARAM, mark);\n if (!t.hash) t.hash = here.hash;\n return t.href;\n };\n for (const r of config.redirects ?? []) {\n const to = r.from && pathOf(r.from) === path ? go(r.to) : undefined;\n if (to) return { to };\n }\n for (const e of config.experiments) {\n if (!e.path || pathOf(e.path) !== path) continue;\n const running = e.status === \"running\" && !e.winner;\n if (running && distinctId === null && !preview[e.key]) return null;\n const variant = resolveVariant(e.key, config, distinctId ?? \"\", preview);\n const url = e.variants.find((v) => v.id === variant)?.url;\n const to = url ? go(url, running ? `${e.key}:${variant}` : undefined) : undefined;\n return running ? { to, experiment: e.key, variant } : to ? { to } : null;\n }\n return null;\n};\n\n/** The test and variant a redirect test's `ck_x` names, when the config has them; null otherwise. */\nexport const exposureFrom = (config: CockpitConfig | null, value: string | null | undefined) => {\n const [key, variant] = (value ?? \"\").split(\":\");\n const e = config?.experiments.find((x) => x.key === key);\n return e && e.variants.some((v) => v.id === variant) ? { experiment: key, variant } : null;\n};\n\n/**\n * The MRRocket API host from `NEXT_PUBLIC_MRROCKET_HOST`. Written as a literal property access on purpose: Next.js\n * inlines `process.env.NEXT_PUBLIC_*` at build time, in the browser bundle and in the server bundle (also on\n * Cloudflare Workers), and only replaces the literal form. Without a bundler (or a `process`) it is undefined.\n */\nexport const envHost = (): string | undefined => {\n try {\n return process.env.NEXT_PUBLIC_MRROCKET_HOST || undefined;\n } catch {\n return undefined;\n }\n};\n\n/** The public key from `NEXT_PUBLIC_MRROCKET_KEY`, the default for the `apiKey` prop. Same literal-access rule as `envHost`. */\nexport const envKey = (): string | undefined => {\n try {\n return process.env.NEXT_PUBLIC_MRROCKET_KEY || undefined;\n } catch {\n return undefined;\n }\n};\n\n/**\n * Where the API lives: the `host` prop, else `NEXT_PUBLIC_MRROCKET_HOST`, else \"\" (the page's own origin, which is how\n * MRRocket's own demo pages call it). Without a trailing slash.\n */\nexport const resolveHost = (host?: string) => (host ?? envHost() ?? \"\").trim().replace(/\\/+$/, \"\");\n\n/** A random visitor id. */\nexport const newDistinctId = () =>\n typeof crypto !== \"undefined\" && \"randomUUID\" in crypto ? crypto.randomUUID() : `ck_${Math.random().toString(36).slice(2)}${Date.now().toString(36)}`;\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../src/core.ts"],"sourcesContent":["/**\n * The part of the SDK that runs anywhere: experiment config, variant assignment, preview links and event shapes.\n * No React, no browser APIs, so the server helper, the proxy helper and the client share it.\n */\nimport type { CancelFlowConfig, OnboardingFlow } from \"./flows\";\n\nexport type VariantId = string;\n\n/** This build of the SDK. Sent with the config request so MRRocket knows what the site runs (error tracking, flags). */\nexport const SDK_VERSION = \"0.13.0\";\n\n/** One variant of an experiment. `changes` replaces texts that the page marks with `<Text id>`. */\nexport interface VariantConfig {\n id: VariantId;\n /** Share of traffic, 0 to 1. The weights of an experiment add up to 1. */\n weight: number;\n changes?: Record<string, string>;\n /**\n * A redirect test: where this variant sends the visitor instead of the test's page (a path like \"/pricing-b\" or a\n * full address). Control has none and stays. SDK 0.7.0+.\n */\n url?: string;\n}\n\nexport interface ExperimentConfig {\n /** The experiment's key, the same as in Cockpit. */\n key: string;\n status: \"running\" | \"draft\" | \"stopped\";\n /** Control first. */\n variants: VariantConfig[];\n /** A winner that shipped: everyone gets it, and nobody is assigned anymore. */\n winner?: VariantId;\n /** A redirect test: the path of the page it runs on (\"/pricing\"). Visitors there are sent to their variant's `url`. */\n path?: string;\n}\n\n/** A page redirect set in MRRocket (Conversion › Landing pages › Redirect): everyone on `from` goes to `to`. SDK 0.7.0+. */\nexport interface RedirectConfig {\n /** A path of the site, like \"/old-pricing\". */\n from: string;\n /** A path or a full address. */\n to: string;\n}\n\nexport interface CockpitConfig {\n /** The product the public key belongs to. */\n project: string;\n experiments: ExperimentConfig[];\n /** The product's affiliate program: how many days a partner's referral is kept. Missing without a program. */\n referral?: { cookieDays: number };\n /** Session recordings: present only while the product records (Cockpit › Sessions). See `ReplayConfig`. */\n replay?: ReplayConfig;\n /** Feature flags of the product in Production (MRRocket › Build › Feature flags), evaluated by `useFlag` and `getFlag`. */\n flags?: FlagConfig[];\n /**\n * Published copy for texts the site marks (`<Text id>`, `data-ck-text`): a landing page's headline, subheadline and\n * button (`pricing.headline` …) and a paywall's (`paywall.<placement>.headline` …) as published in MRRocket. A\n * running test's variant changes them on top. Missing when nothing is published. SDK 0.5.0+.\n */\n texts?: Record<string, string>;\n /** The product's live cancel flow (MRRocket › Churn › Cancel flow), for `cancelFlow.open()`. SDK 0.7.0+. */\n cancelFlow?: CancelFlowConfig;\n /** The in-app steps of live onboarding flows (tours, checklists, modals). SDK 0.7.0+. */\n onboarding?: OnboardingFlow[];\n /** A live Lifecycle journey has in-app messages: signed-in people ask `/api/sdk/inapp` for theirs. SDK 0.7.0+. */\n messages?: boolean;\n /** Page redirects set in MRRocket; the SDK sends visitors on before the page shows (`redirectFor`). SDK 0.7.0+. */\n redirects?: RedirectConfig[];\n}\n\n/** One condition of a flag's audience, on a property of the person (`identify(id, props)`), the id or the page. */\nexport interface FlagCondition {\n /** \"Email\", \"Plan\", \"Country\", \"id\", \"path\" … Matched against the person's properties without regard to case. */\n property: string;\n op: \"is\" | \"is not\" | \"in\" | \"not in\" | \"≥\" | \"in cohort\";\n values: string[];\n}\n\n/** Users who match every condition, of whom `rollout` percent get the flag. No conditions: everyone. */\nexport interface FlagConditionSet {\n conditions: FlagCondition[];\n /** 0 to 100. */\n rollout: number;\n}\n\nexport interface FlagConfig {\n key: string;\n /** Off: everyone gets the off side. */\n on: boolean;\n /** The first set a person matches decides; none matched: off. */\n sets: FlagConditionSet[];\n /** Multivariate flags: the variants and their split in percent (control included). Missing for an on/off flag. */\n variants?: { key: string; split: number; payload?: string }[];\n}\n\n/** What a person is for flag conditions: their properties from identify(), their id and the page they are on. */\nexport type FlagPerson = Record<string, unknown>;\n\nconst propOf = (person: FlagPerson, name: string) => {\n const want = name.trim().toLowerCase();\n for (const [k, v] of Object.entries(person)) if (k.toLowerCase() === want) return v;\n return undefined;\n};\n\n/** `\"@acme.com\"` matches every address at that domain; anything else matches the whole value, without regard to case. */\nconst same = (actual: string, value: string) => {\n const a = actual.trim().toLowerCase();\n const v = value.trim().toLowerCase();\n return v.startsWith(\"@\") ? a.endsWith(v) : a === v;\n};\n\nconst matchesCondition = (c: FlagCondition, person: FlagPerson) => {\n // Cohorts live in MRRocket; a browser cannot know them, so a cohort condition never matches here.\n if (c.op === \"in cohort\") return false;\n const raw = propOf(person, c.property);\n const actual = raw === undefined || raw === null ? \"\" : String(raw);\n if (c.op === \"≥\") {\n const a = Number(actual);\n const b = Number(c.values[0]);\n return actual !== \"\" && Number.isFinite(a) && Number.isFinite(b) && a >= b;\n }\n const hit = actual !== \"\" && c.values.some((v) => same(actual, v));\n return c.op === \"is\" || c.op === \"in\" ? hit : !hit;\n};\n\n/**\n * The flag for one person: `false` (off), `true` (on, an on/off flag) or the variant's key (a multivariate flag). The\n * bucket is a stable hash of flag and person, so a person keeps their side as the rollout grows.\n */\nexport const evaluateFlag = (flag: FlagConfig, distinctId: string, person: FlagPerson = {}): boolean | string => {\n if (!flag.on) return false;\n const subject = { id: distinctId, ...person };\n const set = flag.sets.length ? flag.sets.find((s) => s.conditions.every((c) => matchesCondition(c, subject))) : { conditions: [], rollout: 100 };\n if (!set) return false;\n if (flagUnit(`${flag.key}:rollout:${distinctId}`) * 100 >= set.rollout) return false;\n if (!flag.variants?.length) return true;\n const u = flagUnit(`${flag.key}:variant:${distinctId}`) * 100;\n let acc = 0;\n for (const v of flag.variants) {\n acc += v.split;\n if (u < acc) return v.key;\n }\n return flag.variants[flag.variants.length - 1].key;\n};\n\n/** A flag of the config for this person; an unknown flag (or no config) is off. */\nexport const flagFor = (key: string, config: CockpitConfig | null, distinctId: string, person: FlagPerson = {}) => {\n const flag = config?.flags?.find((f) => f.key === key);\n return flag ? evaluateFlag(flag, distinctId, person) : false;\n};\n\n/** One JavaScript error as it goes over the wire to `POST /api/sdk/errors`. */\nexport interface ErrorReport {\n /** \"TypeError\", \"Error\" … */\n type: string;\n message: string;\n /** The raw stack, at most 8 KB. MRRocket parses it into frames. */\n stack?: string;\n /** \"onerror\" (uncaught), \"onunhandledrejection\" (a rejected promise nobody caught) or \"manual\" (captureError). */\n mechanism: \"onerror\" | \"onunhandledrejection\" | \"manual\";\n handled: boolean;\n url: string;\n ts: number;\n /** What happened just before: pages and clicks, newest last, at most 20. */\n crumbs?: { ts: number; type: \"navigation\" | \"click\" | \"lifecycle\"; message: string }[];\n /** Your release (a version or commit), from the provider's `release` prop. */\n release?: string;\n distinctId?: string;\n sessionId?: string;\n}\n\n/**\n * Session recordings of a product, from the config. Recording also needs the visitor's own replay consent\n * (`<CockpitProvider replay>` or `setConsent({ replay: true })`); without it nothing is recorded or buffered.\n */\nexport interface ReplayConfig {\n /**\n * Share of sessions uploaded from their start, 0 to 1 (default 0.1). The others keep only the last minute in\n * memory and upload it when something goes wrong: a JS error, a rage click, a `purchase…` or `checkout…` event.\n */\n sampleRate: number;\n}\n\n/**\n * What the SDK may send and store.\n * - \"cookieless\" (default): no cookie, no localStorage. Events go without a visitor id; the server counts visitors\n * with a hash of key, date, IP and user agent that changes every day.\n * - \"cookies\": a persistent `ck_id` cookie identifies the visitor across visits, `ck_sid` holds the session.\n * - \"off\": nothing is sent and nothing is stored.\n */\nexport type ConsentMode = \"cookieless\" | \"cookies\" | \"off\";\n\n/**\n * What `setConsent` takes: a mode as before, or the parts to change. `replay` is the separate consent for session\n * recordings (default false); it applies only while analytics is not \"off\".\n *\n * setConsent(\"cookies\");\n * setConsent({ replay: true });\n * setConsent({ analytics: \"cookieless\", replay: false });\n */\nexport type ConsentChoice = ConsentMode | { analytics?: ConsentMode; replay?: boolean };\n\n/** The campaign parameters the SDK reads from the landing URL. */\nexport const UTM_KEYS = [\"utm_source\", \"utm_medium\", \"utm_campaign\", \"utm_term\", \"utm_content\"] as const;\nexport type UtmKey = (typeof UTM_KEYS)[number];\n\n/** One event as it goes over the wire to `POST /api/sdk/events`. */\nexport interface CockpitEvent {\n event: string;\n /** The visitor. Missing in \"cookieless\" mode until identify(): the server then uses its daily visitor hash. */\n distinctId?: string;\n /** Ends after 30 minutes without an event. */\n sessionId?: string;\n props?: Record<string, unknown>;\n /** Milliseconds since the epoch. */\n ts: number;\n url?: string;\n /** `document.referrer` on the first page of a page load, the previous in-app URL after client-side navigation. */\n referrer?: string;\n utm_source?: string;\n utm_medium?: string;\n utm_campaign?: string;\n utm_term?: string;\n utm_content?: string;\n /** The screen size in CSS pixels (points on a phone). */\n screen?: { w: number; h: number };\n /** The app that sent it (React Native only): MRRocket stores it with the event and fills OS and device from it. */\n app?: AppContext;\n}\n\n/** Where a React Native event comes from. Every field is optional except the platform; nothing identifies the person. */\nexport interface AppContext {\n /** \"ios\" or \"android\" (React Native's `Platform.OS`). */\n platform: string;\n /** The app's version, like \"1.4.0\" (`expo-application`'s `nativeApplicationVersion`). */\n appVersion?: string;\n /** The build number, like \"42\". */\n build?: string;\n /** The OS version, like \"17.4\". */\n osVersion?: string;\n /** The device model, like \"iPhone 15 Pro\" (`expo-device`). */\n deviceModel?: string;\n /** \"phone\", \"tablet\", \"desktop\" or \"tv\" when `expo-device` knows it. */\n deviceType?: string;\n /** The first preferred locale, like \"de-DE\". */\n locale?: string;\n}\n\n/** The query parameter that forces variants, for previews in Cockpit: `?ck_preview=pricing-annual:a,home-hero:control`. */\nexport const PREVIEW_PARAM = \"ck_preview\";\n/** Set when Cockpit embeds the page: nothing is tracked, so previews never count as visits. */\nexport const EMBED_PARAM = \"ck_embed\";\n/** The cookie that keeps a visitor in the same variant across visits. Only written in \"cookies\" mode. */\nexport const ID_COOKIE = \"ck_id\";\n/** The cookie that holds the session id in \"cookies\" mode. */\nexport const SESSION_COOKIE = \"ck_sid\";\n/** A session ends after this long without an event. */\nexport const SESSION_TIMEOUT_MS = 30 * 60 * 1000;\n\n/** The UTM parameters from a parameter lookup (`URLSearchParams.get` in a browser). Empty values are left out. */\nexport const utmFrom = (get: (name: string) => string | null | undefined): Partial<Record<UtmKey, string>> => {\n const out: Partial<Record<UtmKey, string>> = {};\n for (const key of UTM_KEYS) {\n const value = get(key)?.trim();\n if (value) out[key] = value.slice(0, 150);\n }\n return out;\n};\n\n/** The UTM parameters of a query string, like `\"?utm_source=x\"`. Empty values are left out. */\nexport const readUtm = (search: string): Partial<Record<UtmKey, string>> => {\n const params = new URLSearchParams(search);\n return utmFrom((k) => params.get(k));\n};\n\n/**\n * The query parameters of a URL or query string, without `URLSearchParams` (React Native's is incomplete): `+` is a\n * space, values are percent-decoded, the first of a repeated name wins.\n */\nexport const parseQuery = (url: string): Record<string, string> => {\n const out: Record<string, string> = {};\n const q = url.split(\"#\")[0];\n const at = q.indexOf(\"?\");\n if (at < 0) return out;\n const dec = (s: string) => {\n try {\n return decodeURIComponent(s.replace(/\\+/g, \" \"));\n } catch {\n return s;\n }\n };\n for (const pair of q.slice(at + 1).split(\"&\")) {\n if (!pair) continue;\n const eq = pair.indexOf(\"=\");\n const k = dec(eq < 0 ? pair : pair.slice(0, eq));\n if (k && !(k in out)) out[k] = eq < 0 ? \"\" : dec(pair.slice(eq + 1));\n }\n return out;\n};\n\n/** FNV-1a, 32 bit, mapped to [0, 1). Stable across server and browser. */\nexport const hashUnit = (input: string) => {\n let h = 0x811c9dc5;\n for (let i = 0; i < input.length; i++) {\n h ^= input.charCodeAt(i);\n h = Math.imul(h, 0x01000193);\n }\n return (h >>> 0) / 4294967296;\n};\n\n/**\n * FNV-1a with murmur3's finalizer, mapped to [0, 1): ids that differ only at the end (\"user-1\", \"user-2\") still spread\n * evenly, so a 30 % rollout reaches about 30 % of people. Flags only; experiments keep `hashUnit` so nobody switches.\n */\nexport const flagUnit = (input: string) => {\n let h = 0x811c9dc5;\n for (let i = 0; i < input.length; i++) {\n h ^= input.charCodeAt(i);\n h = Math.imul(h, 0x01000193);\n }\n h ^= h >>> 16;\n h = Math.imul(h, 0x85ebca6b);\n h ^= h >>> 13;\n h = Math.imul(h, 0xc2b2ae35);\n h ^= h >>> 16;\n return (h >>> 0) / 4294967296;\n};\n\n/** The variant a visitor sees: a shipped winner, control while the experiment does not run, else a stable bucket by visitor. */\nexport const assign = (experiment: ExperimentConfig, distinctId: string): VariantId => {\n const control = experiment.variants[0]?.id ?? \"control\";\n if (experiment.winner) return experiment.winner;\n if (experiment.status !== \"running\") return control;\n const u = hashUnit(`${experiment.key}:${distinctId}`);\n let acc = 0;\n for (const v of experiment.variants) {\n acc += v.weight;\n if (u < acc) return v.id;\n }\n return experiment.variants[experiment.variants.length - 1]?.id ?? control;\n};\n\n/** `\"a:b,c:d\"` → `{ a: \"b\", c: \"d\" }`. Ignores anything malformed. */\nexport const parsePreview = (value: string | string[] | null | undefined): Record<string, VariantId> => {\n const raw = Array.isArray(value) ? value[0] : value;\n if (!raw) return {};\n const out: Record<string, VariantId> = {};\n for (const pair of raw.split(\",\")) {\n const [key, variant] = pair.split(\":\");\n if (key && variant) out[key.trim()] = variant.trim();\n }\n return out;\n};\n\n/** A link that shows one variant of a page, for example in Cockpit's preview frames. */\nexport const previewHref = (url: string, forced: Record<string, VariantId>, embed = true) => {\n const [path, query = \"\"] = url.split(\"?\");\n const params = new URLSearchParams(query);\n const value = Object.entries(forced)\n .map(([k, v]) => `${k}:${v}`)\n .join(\",\");\n if (value) params.set(PREVIEW_PARAM, value);\n if (embed) params.set(EMBED_PARAM, \"1\");\n return `${path}?${params.toString()}`;\n};\n\n/** The variant for one experiment, from a forced preview first, then the config. Unknown experiments get control. */\n/** The experiments whose variants change the text `id`: showing that text is an exposure to them. */\nexport const experimentsOfText = (config: CockpitConfig | null, id: string) =>\n (config?.experiments ?? []).filter((e) => e.variants.some((v) => v.changes && id in v.changes)).map((e) => e.key);\n\nexport const resolveVariant = (key: string, config: CockpitConfig | null, distinctId: string, preview: Record<string, VariantId>): VariantId => {\n if (preview[key]) return preview[key];\n const experiment = config?.experiments.find((e) => e.key === key);\n return experiment ? assign(experiment, distinctId) : \"control\";\n};\n\n/**\n * Every text change that applies to this visitor: the published texts first, then each experiment's in config order.\n * Control keeps the published (or the page's own) words, so only a non-control variant (assigned, forced in a preview,\n * or a shipped winner) changes them further.\n */\nexport const textChanges = (config: CockpitConfig | null, distinctId: string, preview: Record<string, VariantId>) => {\n const out: Record<string, string> = { ...(config?.texts ?? {}) };\n for (const experiment of config?.experiments ?? []) {\n const id = resolveVariant(experiment.key, config, distinctId, preview);\n if (id === (experiment.variants[0]?.id ?? \"control\")) continue;\n Object.assign(out, experiment.variants.find((v) => v.id === id)?.changes ?? {});\n }\n return out;\n};\n\n/**\n * The query parameter a redirect test adds to the variant's address (`ck_x=<test>:<variant>`): the page there records\n * the exposure and takes it out of the address bar. It also keeps that page from redirecting again.\n */\nexport const EXPOSURE_PARAM = \"ck_x\";\n\n/** What `redirectFor` decided: go to `to`, and/or the redirect test this page belongs to and the visitor's variant. */\nexport interface RedirectDecision {\n /** Where to send the visitor now. Missing for control, which stays on the page. */\n to?: string;\n /** The redirect test, while it runs: record `$exposure` for it (on this page for control, on `to` otherwise). */\n experiment?: string;\n variant?: VariantId;\n}\n\nconst pathOf = (p: string) => p.replace(/\\/+$/, \"\") || \"/\";\n\n/**\n * Where a page sends the visitor, before it shows: a page redirect set in MRRocket first, else a redirect test that runs\n * on this path (the visitor's variant, by the same stable assignment as every test). Null when nothing applies.\n *\n * - The new address keeps the page's query (UTM parameters) and its hash; a running test adds `ck_x=<test>:<variant>`,\n * so the page it lands on records the exposure (and never redirects again).\n * - `distinctId` null (no stable id, as on a server without the `ck_id` cookie): running tests are left to the browser,\n * page redirects and shipped winners still apply.\n * - A redirect to the page itself is ignored.\n */\nexport const redirectFor = (config: CockpitConfig | null, href: string, distinctId: string | null, preview: Record<string, VariantId> = {}): RedirectDecision | null => {\n if (!config) return null;\n let here: URL;\n try {\n here = new URL(href);\n } catch {\n return null;\n }\n if (here.searchParams.has(EXPOSURE_PARAM)) return null;\n const path = pathOf(here.pathname);\n const go = (to: string, mark?: string) => {\n let t: URL;\n try {\n t = new URL(to, here);\n } catch {\n return undefined;\n }\n if (!/^https?:$/.test(t.protocol) || (t.origin === here.origin && pathOf(t.pathname) === path)) return undefined;\n here.searchParams.forEach((v, k) => {\n if (!t.searchParams.has(k)) t.searchParams.set(k, v);\n });\n if (mark) t.searchParams.set(EXPOSURE_PARAM, mark);\n if (!t.hash) t.hash = here.hash;\n return t.href;\n };\n for (const r of config.redirects ?? []) {\n const to = r.from && pathOf(r.from) === path ? go(r.to) : undefined;\n if (to) return { to };\n }\n for (const e of config.experiments) {\n if (!e.path || pathOf(e.path) !== path) continue;\n const running = e.status === \"running\" && !e.winner;\n if (running && distinctId === null && !preview[e.key]) return null;\n const variant = resolveVariant(e.key, config, distinctId ?? \"\", preview);\n const url = e.variants.find((v) => v.id === variant)?.url;\n const to = url ? go(url, running ? `${e.key}:${variant}` : undefined) : undefined;\n return running ? { to, experiment: e.key, variant } : to ? { to } : null;\n }\n return null;\n};\n\n/** The test and variant a redirect test's `ck_x` names, when the config has them; null otherwise. */\nexport const exposureFrom = (config: CockpitConfig | null, value: string | null | undefined) => {\n const [key, variant] = (value ?? \"\").split(\":\");\n const e = config?.experiments.find((x) => x.key === key);\n return e && e.variants.some((v) => v.id === variant) ? { experiment: key, variant } : null;\n};\n\n/**\n * The MRRocket API host from `NEXT_PUBLIC_MRROCKET_HOST`. Written as a literal property access on purpose: Next.js\n * inlines `process.env.NEXT_PUBLIC_*` at build time, in the browser bundle and in the server bundle (also on\n * Cloudflare Workers), and only replaces the literal form. Without a bundler (or a `process`) it is undefined.\n */\nexport const envHost = (): string | undefined => {\n try {\n return process.env.NEXT_PUBLIC_MRROCKET_HOST || undefined;\n } catch {\n return undefined;\n }\n};\n\n/** The public key from `NEXT_PUBLIC_MRROCKET_KEY`, the default for the `apiKey` prop. Same literal-access rule as `envHost`. */\nexport const envKey = (): string | undefined => {\n try {\n return process.env.NEXT_PUBLIC_MRROCKET_KEY || undefined;\n } catch {\n return undefined;\n }\n};\n\n/**\n * Where the API lives: the `host` prop, else `NEXT_PUBLIC_MRROCKET_HOST`, else \"\" (the page's own origin, which is how\n * MRRocket's own demo pages call it). Without a trailing slash.\n */\nexport const resolveHost = (host?: string) => (host ?? envHost() ?? \"\").trim().replace(/\\/+$/, \"\");\n\n/** A random visitor id. */\nexport const newDistinctId = () =>\n typeof crypto !== \"undefined\" && \"randomUUID\" in crypto ? crypto.randomUUID() : `ck_${Math.random().toString(36).slice(2)}${Date.now().toString(36)}`;\n"],"mappings":";AASO,IAAM,cAAc;AAyF3B,IAAM,SAAS,CAAC,QAAoB,SAAiB;AACjD,QAAM,OAAO,KAAK,KAAK,EAAE,YAAY;AACrC,aAAW,CAAC,GAAG,CAAC,KAAK,OAAO,QAAQ,MAAM,EAAG,KAAI,EAAE,YAAY,MAAM,KAAM,QAAO;AAClF,SAAO;AACX;AAGA,IAAM,OAAO,CAAC,QAAgB,UAAkB;AAC5C,QAAM,IAAI,OAAO,KAAK,EAAE,YAAY;AACpC,QAAM,IAAI,MAAM,KAAK,EAAE,YAAY;AACnC,SAAO,EAAE,WAAW,GAAG,IAAI,EAAE,SAAS,CAAC,IAAI,MAAM;AACrD;AAEA,IAAM,mBAAmB,CAAC,GAAkB,WAAuB;AAE/D,MAAI,EAAE,OAAO,YAAa,QAAO;AACjC,QAAM,MAAM,OAAO,QAAQ,EAAE,QAAQ;AACrC,QAAM,SAAS,QAAQ,UAAa,QAAQ,OAAO,KAAK,OAAO,GAAG;AAClE,MAAI,EAAE,OAAO,UAAK;AACd,UAAM,IAAI,OAAO,MAAM;AACvB,UAAM,IAAI,OAAO,EAAE,OAAO,CAAC,CAAC;AAC5B,WAAO,WAAW,MAAM,OAAO,SAAS,CAAC,KAAK,OAAO,SAAS,CAAC,KAAK,KAAK;AAAA,EAC7E;AACA,QAAM,MAAM,WAAW,MAAM,EAAE,OAAO,KAAK,CAAC,MAAM,KAAK,QAAQ,CAAC,CAAC;AACjE,SAAO,EAAE,OAAO,QAAQ,EAAE,OAAO,OAAO,MAAM,CAAC;AACnD;AAMO,IAAM,eAAe,CAAC,MAAkB,YAAoB,SAAqB,CAAC,MAAwB;AAC7G,MAAI,CAAC,KAAK,GAAI,QAAO;AACrB,QAAM,UAAU,EAAE,IAAI,YAAY,GAAG,OAAO;AAC5C,QAAM,MAAM,KAAK,KAAK,SAAS,KAAK,KAAK,KAAK,CAAC,MAAM,EAAE,WAAW,MAAM,CAAC,MAAM,iBAAiB,GAAG,OAAO,CAAC,CAAC,IAAI,EAAE,YAAY,CAAC,GAAG,SAAS,IAAI;AAC/I,MAAI,CAAC,IAAK,QAAO;AACjB,MAAI,SAAS,GAAG,KAAK,GAAG,YAAY,UAAU,EAAE,IAAI,OAAO,IAAI,QAAS,QAAO;AAC/E,MAAI,CAAC,KAAK,UAAU,OAAQ,QAAO;AACnC,QAAM,IAAI,SAAS,GAAG,KAAK,GAAG,YAAY,UAAU,EAAE,IAAI;AAC1D,MAAI,MAAM;AACV,aAAW,KAAK,KAAK,UAAU;AAC3B,WAAO,EAAE;AACT,QAAI,IAAI,IAAK,QAAO,EAAE;AAAA,EAC1B;AACA,SAAO,KAAK,SAAS,KAAK,SAAS,SAAS,CAAC,EAAE;AACnD;AAGO,IAAM,UAAU,CAAC,KAAa,QAA8B,YAAoB,SAAqB,CAAC,MAAM;AAC/G,QAAM,OAAO,QAAQ,OAAO,KAAK,CAAC,MAAM,EAAE,QAAQ,GAAG;AACrD,SAAO,OAAO,aAAa,MAAM,YAAY,MAAM,IAAI;AAC3D;AAsDO,IAAM,WAAW,CAAC,cAAc,cAAc,gBAAgB,YAAY,aAAa;AA8CvF,IAAM,gBAAgB;AAEtB,IAAM,cAAc;AAEpB,IAAM,YAAY;AAElB,IAAM,iBAAiB;AAEvB,IAAM,qBAAqB,KAAK,KAAK;AAGrC,IAAM,UAAU,CAAC,QAAsF;AAC1G,QAAM,MAAuC,CAAC;AAC9C,aAAW,OAAO,UAAU;AACxB,UAAM,QAAQ,IAAI,GAAG,GAAG,KAAK;AAC7B,QAAI,MAAO,KAAI,GAAG,IAAI,MAAM,MAAM,GAAG,GAAG;AAAA,EAC5C;AACA,SAAO;AACX;AAGO,IAAM,UAAU,CAAC,WAAoD;AACxE,QAAM,SAAS,IAAI,gBAAgB,MAAM;AACzC,SAAO,QAAQ,CAAC,MAAM,OAAO,IAAI,CAAC,CAAC;AACvC;AAMO,IAAM,aAAa,CAAC,QAAwC;AAC/D,QAAM,MAA8B,CAAC;AACrC,QAAM,IAAI,IAAI,MAAM,GAAG,EAAE,CAAC;AAC1B,QAAM,KAAK,EAAE,QAAQ,GAAG;AACxB,MAAI,KAAK,EAAG,QAAO;AACnB,QAAM,MAAM,CAAC,MAAc;AACvB,QAAI;AACA,aAAO,mBAAmB,EAAE,QAAQ,OAAO,GAAG,CAAC;AAAA,IACnD,QAAQ;AACJ,aAAO;AAAA,IACX;AAAA,EACJ;AACA,aAAW,QAAQ,EAAE,MAAM,KAAK,CAAC,EAAE,MAAM,GAAG,GAAG;AAC3C,QAAI,CAAC,KAAM;AACX,UAAM,KAAK,KAAK,QAAQ,GAAG;AAC3B,UAAM,IAAI,IAAI,KAAK,IAAI,OAAO,KAAK,MAAM,GAAG,EAAE,CAAC;AAC/C,QAAI,KAAK,EAAE,KAAK,KAAM,KAAI,CAAC,IAAI,KAAK,IAAI,KAAK,IAAI,KAAK,MAAM,KAAK,CAAC,CAAC;AAAA,EACvE;AACA,SAAO;AACX;AAGO,IAAM,WAAW,CAAC,UAAkB;AACvC,MAAI,IAAI;AACR,WAAS,IAAI,GAAG,IAAI,MAAM,QAAQ,KAAK;AACnC,SAAK,MAAM,WAAW,CAAC;AACvB,QAAI,KAAK,KAAK,GAAG,QAAU;AAAA,EAC/B;AACA,UAAQ,MAAM,KAAK;AACvB;AAMO,IAAM,WAAW,CAAC,UAAkB;AACvC,MAAI,IAAI;AACR,WAAS,IAAI,GAAG,IAAI,MAAM,QAAQ,KAAK;AACnC,SAAK,MAAM,WAAW,CAAC;AACvB,QAAI,KAAK,KAAK,GAAG,QAAU;AAAA,EAC/B;AACA,OAAK,MAAM;AACX,MAAI,KAAK,KAAK,GAAG,UAAU;AAC3B,OAAK,MAAM;AACX,MAAI,KAAK,KAAK,GAAG,UAAU;AAC3B,OAAK,MAAM;AACX,UAAQ,MAAM,KAAK;AACvB;AAGO,IAAM,SAAS,CAAC,YAA8B,eAAkC;AACnF,QAAM,UAAU,WAAW,SAAS,CAAC,GAAG,MAAM;AAC9C,MAAI,WAAW,OAAQ,QAAO,WAAW;AACzC,MAAI,WAAW,WAAW,UAAW,QAAO;AAC5C,QAAM,IAAI,SAAS,GAAG,WAAW,GAAG,IAAI,UAAU,EAAE;AACpD,MAAI,MAAM;AACV,aAAW,KAAK,WAAW,UAAU;AACjC,WAAO,EAAE;AACT,QAAI,IAAI,IAAK,QAAO,EAAE;AAAA,EAC1B;AACA,SAAO,WAAW,SAAS,WAAW,SAAS,SAAS,CAAC,GAAG,MAAM;AACtE;AAGO,IAAM,eAAe,CAAC,UAA2E;AACpG,QAAM,MAAM,MAAM,QAAQ,KAAK,IAAI,MAAM,CAAC,IAAI;AAC9C,MAAI,CAAC,IAAK,QAAO,CAAC;AAClB,QAAM,MAAiC,CAAC;AACxC,aAAW,QAAQ,IAAI,MAAM,GAAG,GAAG;AAC/B,UAAM,CAAC,KAAK,OAAO,IAAI,KAAK,MAAM,GAAG;AACrC,QAAI,OAAO,QAAS,KAAI,IAAI,KAAK,CAAC,IAAI,QAAQ,KAAK;AAAA,EACvD;AACA,SAAO;AACX;AAGO,IAAM,cAAc,CAAC,KAAa,QAAmC,QAAQ,SAAS;AACzF,QAAM,CAAC,MAAM,QAAQ,EAAE,IAAI,IAAI,MAAM,GAAG;AACxC,QAAM,SAAS,IAAI,gBAAgB,KAAK;AACxC,QAAM,QAAQ,OAAO,QAAQ,MAAM,EAC9B,IAAI,CAAC,CAAC,GAAG,CAAC,MAAM,GAAG,CAAC,IAAI,CAAC,EAAE,EAC3B,KAAK,GAAG;AACb,MAAI,MAAO,QAAO,IAAI,eAAe,KAAK;AAC1C,MAAI,MAAO,QAAO,IAAI,aAAa,GAAG;AACtC,SAAO,GAAG,IAAI,IAAI,OAAO,SAAS,CAAC;AACvC;AAIO,IAAM,oBAAoB,CAAC,QAA8B,QAC3D,QAAQ,eAAe,CAAC,GAAG,OAAO,CAAC,MAAM,EAAE,SAAS,KAAK,CAAC,MAAM,EAAE,WAAW,MAAM,EAAE,OAAO,CAAC,EAAE,IAAI,CAAC,MAAM,EAAE,GAAG;AAE7G,IAAM,iBAAiB,CAAC,KAAa,QAA8B,YAAoB,YAAkD;AAC5I,MAAI,QAAQ,GAAG,EAAG,QAAO,QAAQ,GAAG;AACpC,QAAM,aAAa,QAAQ,YAAY,KAAK,CAAC,MAAM,EAAE,QAAQ,GAAG;AAChE,SAAO,aAAa,OAAO,YAAY,UAAU,IAAI;AACzD;AAOO,IAAM,cAAc,CAAC,QAA8B,YAAoB,YAAuC;AACjH,QAAM,MAA8B,EAAE,GAAI,QAAQ,SAAS,CAAC,EAAG;AAC/D,aAAW,cAAc,QAAQ,eAAe,CAAC,GAAG;AAChD,UAAM,KAAK,eAAe,WAAW,KAAK,QAAQ,YAAY,OAAO;AACrE,QAAI,QAAQ,WAAW,SAAS,CAAC,GAAG,MAAM,WAAY;AACtD,WAAO,OAAO,KAAK,WAAW,SAAS,KAAK,CAAC,MAAM,EAAE,OAAO,EAAE,GAAG,WAAW,CAAC,CAAC;AAAA,EAClF;AACA,SAAO;AACX;AAMO,IAAM,iBAAiB;AAW9B,IAAM,SAAS,CAAC,MAAc,EAAE,QAAQ,QAAQ,EAAE,KAAK;AAYhD,IAAM,cAAc,CAAC,QAA8B,MAAc,YAA2B,UAAqC,CAAC,MAA+B;AACpK,MAAI,CAAC,OAAQ,QAAO;AACpB,MAAI;AACJ,MAAI;AACA,WAAO,IAAI,IAAI,IAAI;AAAA,EACvB,QAAQ;AACJ,WAAO;AAAA,EACX;AACA,MAAI,KAAK,aAAa,IAAI,cAAc,EAAG,QAAO;AAClD,QAAM,OAAO,OAAO,KAAK,QAAQ;AACjC,QAAM,KAAK,CAAC,IAAY,SAAkB;AACtC,QAAI;AACJ,QAAI;AACA,UAAI,IAAI,IAAI,IAAI,IAAI;AAAA,IACxB,QAAQ;AACJ,aAAO;AAAA,IACX;AACA,QAAI,CAAC,YAAY,KAAK,EAAE,QAAQ,KAAM,EAAE,WAAW,KAAK,UAAU,OAAO,EAAE,QAAQ,MAAM,KAAO,QAAO;AACvG,SAAK,aAAa,QAAQ,CAAC,GAAG,MAAM;AAChC,UAAI,CAAC,EAAE,aAAa,IAAI,CAAC,EAAG,GAAE,aAAa,IAAI,GAAG,CAAC;AAAA,IACvD,CAAC;AACD,QAAI,KAAM,GAAE,aAAa,IAAI,gBAAgB,IAAI;AACjD,QAAI,CAAC,EAAE,KAAM,GAAE,OAAO,KAAK;AAC3B,WAAO,EAAE;AAAA,EACb;AACA,aAAW,KAAK,OAAO,aAAa,CAAC,GAAG;AACpC,UAAM,KAAK,EAAE,QAAQ,OAAO,EAAE,IAAI,MAAM,OAAO,GAAG,EAAE,EAAE,IAAI;AAC1D,QAAI,GAAI,QAAO,EAAE,GAAG;AAAA,EACxB;AACA,aAAW,KAAK,OAAO,aAAa;AAChC,QAAI,CAAC,EAAE,QAAQ,OAAO,EAAE,IAAI,MAAM,KAAM;AACxC,UAAM,UAAU,EAAE,WAAW,aAAa,CAAC,EAAE;AAC7C,QAAI,WAAW,eAAe,QAAQ,CAAC,QAAQ,EAAE,GAAG,EAAG,QAAO;AAC9D,UAAM,UAAU,eAAe,EAAE,KAAK,QAAQ,cAAc,IAAI,OAAO;AACvE,UAAM,MAAM,EAAE,SAAS,KAAK,CAAC,MAAM,EAAE,OAAO,OAAO,GAAG;AACtD,UAAM,KAAK,MAAM,GAAG,KAAK,UAAU,GAAG,EAAE,GAAG,IAAI,OAAO,KAAK,MAAS,IAAI;AACxE,WAAO,UAAU,EAAE,IAAI,YAAY,EAAE,KAAK,QAAQ,IAAI,KAAK,EAAE,GAAG,IAAI;AAAA,EACxE;AACA,SAAO;AACX;AAGO,IAAM,eAAe,CAAC,QAA8B,UAAqC;AAC5F,QAAM,CAAC,KAAK,OAAO,KAAK,SAAS,IAAI,MAAM,GAAG;AAC9C,QAAM,IAAI,QAAQ,YAAY,KAAK,CAAC,MAAM,EAAE,QAAQ,GAAG;AACvD,SAAO,KAAK,EAAE,SAAS,KAAK,CAAC,MAAM,EAAE,OAAO,OAAO,IAAI,EAAE,YAAY,KAAK,QAAQ,IAAI;AAC1F;AAOO,IAAM,UAAU,MAA0B;AAC7C,MAAI;AACA,WAAO,QAAQ,IAAI,6BAA6B;AAAA,EACpD,QAAQ;AACJ,WAAO;AAAA,EACX;AACJ;AAGO,IAAM,SAAS,MAA0B;AAC5C,MAAI;AACA,WAAO,QAAQ,IAAI,4BAA4B;AAAA,EACnD,QAAQ;AACJ,WAAO;AAAA,EACX;AACJ;AAMO,IAAM,cAAc,CAAC,UAAmB,QAAQ,QAAQ,KAAK,IAAI,KAAK,EAAE,QAAQ,QAAQ,EAAE;AAG1F,IAAM,gBAAgB,MACzB,OAAO,WAAW,eAAe,gBAAgB,SAAS,OAAO,WAAW,IAAI,MAAM,KAAK,OAAO,EAAE,SAAS,EAAE,EAAE,MAAM,CAAC,CAAC,GAAG,KAAK,IAAI,EAAE,SAAS,EAAE,CAAC;","names":[]}
|
package/dist/proxy.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { type CockpitConfig } from "./core.js";
|
|
2
2
|
export { getCockpitConfig } from "./server.js";
|
|
3
|
+
export { type SecurityOptions, reportSuspiciousRequest, watchRequest } from "./security.js";
|
|
3
4
|
/** What `request.cookies` (Next's `RequestCookies`) offers: read, and set so that the page of this request sees the id. */
|
|
4
5
|
interface IncomingCookies {
|
|
5
6
|
get(name: string): {
|
package/dist/proxy.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
// src/proxy.ts
|
|
2
2
|
import { ID_COOKIE, PREVIEW_PARAM, newDistinctId, parsePreview, redirectFor } from "./core.js";
|
|
3
3
|
import { getCockpitConfig } from "./server.js";
|
|
4
|
+
import { reportSuspiciousRequest, watchRequest } from "./security.js";
|
|
4
5
|
var withCockpitId = (incoming, outgoing) => {
|
|
5
6
|
if (incoming.get(ID_COOKIE)) return;
|
|
6
7
|
const id = newDistinctId();
|
|
@@ -20,6 +21,8 @@ var experimentRedirect = (request, config, options = {}) => {
|
|
|
20
21
|
export {
|
|
21
22
|
experimentRedirect,
|
|
22
23
|
getCockpitConfig,
|
|
24
|
+
reportSuspiciousRequest,
|
|
25
|
+
watchRequest,
|
|
23
26
|
withCockpitId
|
|
24
27
|
};
|
|
25
28
|
//# sourceMappingURL=proxy.js.map
|
package/dist/proxy.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/proxy.ts"],"sourcesContent":["import { type CockpitConfig, ID_COOKIE, PREVIEW_PARAM, newDistinctId, parsePreview, redirectFor } from \"./core\";\n\nexport { getCockpitConfig } from \"./server\";\n\n/** What `request.cookies` (Next's `RequestCookies`) offers: read, and set so that the page of this request sees the id. */\ninterface IncomingCookies {\n get(name: string): { value: string } | undefined;\n set(name: string, value: string): unknown;\n}\n\n/** What `response.cookies` (Next's `ResponseCookies`) offers: set with options. */\ninterface OutgoingCookies {\n set(name: string, value: string, options?: { path?: string; maxAge?: number; sameSite?: \"lax\" | \"strict\" | \"none\" }): unknown;\n}\n\n/**\n * For `proxy.ts`: gives a first-time visitor their `ck_id` before the page renders, so the server already assigns\n * the variant they will keep. It writes a cookie, so only use it with `consent=\"cookies\"` and only once the visitor\n * agreed; in the default cookieless mode leave it out.\n *\n * export function proxy(request: NextRequest) {\n * const response = NextResponse.next();\n * withCockpitId(request.cookies, response.cookies);\n * return response;\n * }\n */\nexport const withCockpitId = (incoming: IncomingCookies, outgoing: OutgoingCookies) => {\n if (incoming.get(ID_COOKIE)) return;\n const id = newDistinctId();\n incoming.set(ID_COOKIE, id);\n outgoing.set(ID_COOKIE, id, { path: \"/\", maxAge: 60 * 60 * 24 * 365, sameSite: \"lax\" });\n};\n\n/**\n * For `proxy.ts`: where to send this request, or null to render it. Page redirects set in MRRocket (Landing pages ›\n * Redirect) and redirect tests (Experiments) answer before the page renders, so nobody sees the old page flash. The\n * page the visitor lands on records the test's exposure (`<CockpitProvider>` reads `ck_x` there); control renders as\n * usual and the provider records it.\n *\n * A running test needs a stable visitor id to keep each person in one variant: the `ck_id` cookie (consent \"cookies\",\n * with `withCockpitId` before this), or your signed-in user id as `distinctId`. Without either, the test is left to the\n * browser (the provider redirects after the first paint); page redirects and shipped winners still answer here.\n *\n * import { NextResponse, type NextRequest } from \"next/server\";\n * import { experimentRedirect, getCockpitConfig } from \"@mrrocket/sdk/next/proxy\";\n *\n * export async function proxy(request: NextRequest) {\n * const to = experimentRedirect(request, await getCockpitConfig({ timeoutMs: 800 }));\n * return to ? NextResponse.redirect(to, 307) : NextResponse.next();\n * }\n * export const config = { matcher: [\"/pricing\", \"/old-pricing\"] };\n *\n * Keep the matcher to the pages with a redirect or a test: each matched request waits for the config (at most\n * `timeoutMs`; MRRocket unreachable means no redirect).\n */\nexport const experimentRedirect = (\n request: { url: string; cookies?: { get(name: string): { value: string } | undefined } },\n config: CockpitConfig | null,\n options: { distinctId?: string } = {},\n): string | null => {\n const id = options.distinctId ?? request.cookies?.get(ID_COOKIE)?.value ?? null;\n let preview = {};\n try {\n preview = parsePreview(new URL(request.url).searchParams.get(PREVIEW_PARAM));\n } catch {\n return null;\n }\n return redirectFor(config, request.url, id, preview)?.to ?? null;\n};\n"],"mappings":";AAAA,SAA6B,WAAW,eAAe,eAAe,cAAc,mBAAmB;AAEvG,SAAS,wBAAwB;
|
|
1
|
+
{"version":3,"sources":["../src/proxy.ts"],"sourcesContent":["import { type CockpitConfig, ID_COOKIE, PREVIEW_PARAM, newDistinctId, parsePreview, redirectFor } from \"./core\";\n\nexport { getCockpitConfig } from \"./server\";\nexport { type SecurityOptions, reportSuspiciousRequest, watchRequest } from \"./security\";\n\n/** What `request.cookies` (Next's `RequestCookies`) offers: read, and set so that the page of this request sees the id. */\ninterface IncomingCookies {\n get(name: string): { value: string } | undefined;\n set(name: string, value: string): unknown;\n}\n\n/** What `response.cookies` (Next's `ResponseCookies`) offers: set with options. */\ninterface OutgoingCookies {\n set(name: string, value: string, options?: { path?: string; maxAge?: number; sameSite?: \"lax\" | \"strict\" | \"none\" }): unknown;\n}\n\n/**\n * For `proxy.ts`: gives a first-time visitor their `ck_id` before the page renders, so the server already assigns\n * the variant they will keep. It writes a cookie, so only use it with `consent=\"cookies\"` and only once the visitor\n * agreed; in the default cookieless mode leave it out.\n *\n * export function proxy(request: NextRequest) {\n * const response = NextResponse.next();\n * withCockpitId(request.cookies, response.cookies);\n * return response;\n * }\n */\nexport const withCockpitId = (incoming: IncomingCookies, outgoing: OutgoingCookies) => {\n if (incoming.get(ID_COOKIE)) return;\n const id = newDistinctId();\n incoming.set(ID_COOKIE, id);\n outgoing.set(ID_COOKIE, id, { path: \"/\", maxAge: 60 * 60 * 24 * 365, sameSite: \"lax\" });\n};\n\n/**\n * For `proxy.ts`: where to send this request, or null to render it. Page redirects set in MRRocket (Landing pages ›\n * Redirect) and redirect tests (Experiments) answer before the page renders, so nobody sees the old page flash. The\n * page the visitor lands on records the test's exposure (`<CockpitProvider>` reads `ck_x` there); control renders as\n * usual and the provider records it.\n *\n * A running test needs a stable visitor id to keep each person in one variant: the `ck_id` cookie (consent \"cookies\",\n * with `withCockpitId` before this), or your signed-in user id as `distinctId`. Without either, the test is left to the\n * browser (the provider redirects after the first paint); page redirects and shipped winners still answer here.\n *\n * import { NextResponse, type NextRequest } from \"next/server\";\n * import { experimentRedirect, getCockpitConfig } from \"@mrrocket/sdk/next/proxy\";\n *\n * export async function proxy(request: NextRequest) {\n * const to = experimentRedirect(request, await getCockpitConfig({ timeoutMs: 800 }));\n * return to ? NextResponse.redirect(to, 307) : NextResponse.next();\n * }\n * export const config = { matcher: [\"/pricing\", \"/old-pricing\"] };\n *\n * Keep the matcher to the pages with a redirect or a test: each matched request waits for the config (at most\n * `timeoutMs`; MRRocket unreachable means no redirect).\n */\nexport const experimentRedirect = (\n request: { url: string; cookies?: { get(name: string): { value: string } | undefined } },\n config: CockpitConfig | null,\n options: { distinctId?: string } = {},\n): string | null => {\n const id = options.distinctId ?? request.cookies?.get(ID_COOKIE)?.value ?? null;\n let preview = {};\n try {\n preview = parsePreview(new URL(request.url).searchParams.get(PREVIEW_PARAM));\n } catch {\n return null;\n }\n return redirectFor(config, request.url, id, preview)?.to ?? null;\n};\n"],"mappings":";AAAA,SAA6B,WAAW,eAAe,eAAe,cAAc,mBAAmB;AAEvG,SAAS,wBAAwB;AACjC,SAA+B,yBAAyB,oBAAoB;AAwBrE,IAAM,gBAAgB,CAAC,UAA2B,aAA8B;AACnF,MAAI,SAAS,IAAI,SAAS,EAAG;AAC7B,QAAM,KAAK,cAAc;AACzB,WAAS,IAAI,WAAW,EAAE;AAC1B,WAAS,IAAI,WAAW,IAAI,EAAE,MAAM,KAAK,QAAQ,KAAK,KAAK,KAAK,KAAK,UAAU,MAAM,CAAC;AAC1F;AAwBO,IAAM,qBAAqB,CAC9B,SACA,QACA,UAAmC,CAAC,MACpB;AAChB,QAAM,KAAK,QAAQ,cAAc,QAAQ,SAAS,IAAI,SAAS,GAAG,SAAS;AAC3E,MAAI,UAAU,CAAC;AACf,MAAI;AACA,cAAU,aAAa,IAAI,IAAI,QAAQ,GAAG,EAAE,aAAa,IAAI,aAAa,CAAC;AAAA,EAC/E,QAAQ;AACJ,WAAO;AAAA,EACX;AACA,SAAO,YAAY,QAAQ,QAAQ,KAAK,IAAI,OAAO,GAAG,MAAM;AAChE;","names":[]}
|
|
@@ -421,7 +421,7 @@ interface FlowsController {
|
|
|
421
421
|
|
|
422
422
|
type VariantId = string;
|
|
423
423
|
/** This build of the SDK. Sent with the config request so MRRocket knows what the site runs (error tracking, flags). */
|
|
424
|
-
declare const SDK_VERSION = "0.
|
|
424
|
+
declare const SDK_VERSION = "0.13.0";
|
|
425
425
|
/** One variant of an experiment. `changes` replaces texts that the page marks with `<Text id>`. */
|
|
426
426
|
interface VariantConfig {
|
|
427
427
|
id: VariantId;
|
|
@@ -421,7 +421,7 @@ interface FlowsController {
|
|
|
421
421
|
|
|
422
422
|
type VariantId = string;
|
|
423
423
|
/** This build of the SDK. Sent with the config request so MRRocket knows what the site runs (error tracking, flags). */
|
|
424
|
-
declare const SDK_VERSION = "0.
|
|
424
|
+
declare const SDK_VERSION = "0.13.0";
|
|
425
425
|
/** One variant of an experiment. `changes` replaces texts that the page marks with `<Text id>`. */
|
|
426
426
|
interface VariantConfig {
|
|
427
427
|
id: VariantId;
|
|
@@ -49,7 +49,7 @@ module.exports = __toCommonJS(native_exports);
|
|
|
49
49
|
var import_react3 = require("react");
|
|
50
50
|
|
|
51
51
|
// src/core.ts
|
|
52
|
-
var SDK_VERSION = "0.
|
|
52
|
+
var SDK_VERSION = "0.13.0";
|
|
53
53
|
var propOf = (person, name) => {
|
|
54
54
|
const want = name.trim().toLowerCase();
|
|
55
55
|
for (const [k, v] of Object.entries(person)) if (k.toLowerCase() === want) return v;
|