@gurulu/web 0.1.0 → 1.0.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.
@@ -0,0 +1,26 @@
1
+ import { type TransportConfig } from './transport.ts';
2
+ import type { OutgoingEvent } from './types.ts';
3
+ export interface QueueOptions {
4
+ transport: TransportConfig;
5
+ flushIntervalMs: number;
6
+ maxQueueSize: number;
7
+ debug?: boolean;
8
+ }
9
+ export declare class EventQueue {
10
+ private readonly opts;
11
+ private buffer;
12
+ private timer;
13
+ private flushing;
14
+ constructor(opts: QueueOptions);
15
+ enqueue(event: OutgoingEvent): void;
16
+ size(): number;
17
+ /** Manual flush — caller may await. */
18
+ flush(): Promise<void>;
19
+ /** Unload-path: synchronous best-effort via sendBeacon. */
20
+ flushBeacon(): void;
21
+ private scheduleNext;
22
+ private scheduleImmediate;
23
+ private sendWithRetry;
24
+ private installUnloadHooks;
25
+ }
26
+ //# sourceMappingURL=queue.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"queue.d.ts","sourceRoot":"","sources":["../src/queue.ts"],"names":[],"mappings":"AAIA,OAAO,EAA8B,KAAK,eAAe,EAAE,MAAM,gBAAgB,CAAC;AAClF,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AA8BhD,MAAM,WAAW,YAAY;IAC3B,SAAS,EAAE,eAAe,CAAC;IAC3B,eAAe,EAAE,MAAM,CAAC;IACxB,YAAY,EAAE,MAAM,CAAC;IACrB,KAAK,CAAC,EAAE,OAAO,CAAC;CACjB;AAED,qBAAa,UAAU;IACrB,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAe;IACpC,OAAO,CAAC,MAAM,CAAkB;IAChC,OAAO,CAAC,KAAK,CAA8C;IAC3D,OAAO,CAAC,QAAQ,CAAS;gBAEb,IAAI,EAAE,YAAY;IAS9B,OAAO,CAAC,KAAK,EAAE,aAAa,GAAG,IAAI;IAUnC,IAAI,IAAI,MAAM;IAId,uCAAuC;IACjC,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAmB5B,2DAA2D;IAC3D,WAAW,IAAI,IAAI;IASnB,OAAO,CAAC,YAAY;IAQpB,OAAO,CAAC,iBAAiB;YAWX,aAAa;IAgB3B,OAAO,CAAC,kBAAkB;CAY3B"}
@@ -0,0 +1,15 @@
1
+ import type { ClickIdContext, SourceTouch, UTMContext } from './types.ts';
2
+ export interface SessionState {
3
+ session_id: string;
4
+ session_started_at: number;
5
+ is_new: boolean;
6
+ }
7
+ export declare function resolveSession(now?: number): SessionState;
8
+ export declare function newSession(now?: number): SessionState;
9
+ export declare function parseUrlContext(href: string | undefined, referrer: string | undefined): {
10
+ utm: UTMContext;
11
+ click_id: ClickIdContext;
12
+ };
13
+ export declare function preserveFirstSource(touch: SourceTouch): void;
14
+ export declare function getFirstSource(): SourceTouch | null;
15
+ //# sourceMappingURL=session.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"session.d.ts","sourceRoot":"","sources":["../src/session.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,cAAc,EAAE,WAAW,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAqC1E,MAAM,WAAW,YAAY;IAC3B,UAAU,EAAE,MAAM,CAAC;IACnB,kBAAkB,EAAE,MAAM,CAAC;IAC3B,MAAM,EAAE,OAAO,CAAC;CACjB;AAED,wBAAgB,cAAc,CAAC,GAAG,SAAa,GAAG,YAAY,CAc7D;AAED,wBAAgB,UAAU,CAAC,GAAG,SAAa,GAAG,YAAY,CAMzD;AAED,wBAAgB,eAAe,CAC7B,IAAI,EAAE,MAAM,GAAG,SAAS,EACxB,QAAQ,EAAE,MAAM,GAAG,SAAS,GAC3B;IACD,GAAG,EAAE,UAAU,CAAC;IAChB,QAAQ,EAAE,cAAc,CAAC;CAC1B,CA+BA;AAED,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,WAAW,GAAG,IAAI,CAG5D;AAED,wBAAgB,cAAc,IAAI,WAAW,GAAG,IAAI,CAQnD"}
package/dist/t.js ADDED
@@ -0,0 +1,10 @@
1
+ class M{workspaceId;apiUrl;fetchImpl;storage;autoBanner;locale;anonymousIdValue;bannerConfigCache=null;bannerEl=null;constructor(u){this.workspaceId=u.workspaceId,this.apiUrl=(u.apiUrl??"https://api.gurulu.io").replace(/\/+$/,""),this.fetchImpl=u.fetchImpl??(typeof fetch<"u"?fetch.bind(globalThis):Nu),this.storage=u.storage??(typeof globalThis<"u"&&"localStorage"in globalThis?globalThis.localStorage:null),this.autoBanner=u.autoBanner??!0,this.locale=u.locale??Eu(),this.anonymousIdValue=u.anonymousId??this.loadOrCreateAnonId()}async init(){if(typeof window>"u")return;try{let m=await this.fetchImpl(`${this.apiUrl}/v1/consent/banner-config?workspace_id=${encodeURIComponent(this.workspaceId)}`,{method:"GET",credentials:"omit"});if(!m.ok)return;this.bannerConfigCache=await m.json()}catch{return}if(!this.getState()&&this.autoBanner&&this.bannerConfigCache?.mode==="banner_required")this.showBanner()}getState(){if(!this.storage)return null;try{let u=this.storage.getItem(this.storageKey());if(!u)return null;let m=JSON.parse(u);if(m.expiresAt&&new Date(m.expiresAt).getTime()<Date.now())return this.storage.removeItem(this.storageKey()),null;return m}catch{return null}}async setState(u){let m=this.getState()?.categories,n={necessary:!0,analytics:u.analytics??m?.analytics??!1,marketing:u.marketing??m?.marketing??!1,functional:u.functional??m?.functional??!1,personalization:u.personalization??m?.personalization??!1},_=new Date,A=this.bannerConfigCache?.renewal_months??13,l=new Date(_);l.setMonth(l.getMonth()+A);let C={workspaceId:this.workspaceId,anonymousId:this.anonymousIdValue,categories:n,grantedAt:_.toISOString(),expiresAt:l.toISOString(),source:"sdk_api"};if(this.storage)try{this.storage.setItem(this.storageKey(),JSON.stringify(C))}catch{}try{await this.fetchImpl(`${this.apiUrl}/v1/consent/state`,{method:"POST",credentials:"omit",headers:{"content-type":"application/json"},body:JSON.stringify({workspace_id:this.workspaceId,anonymous_id:this.anonymousIdValue,necessary:n.necessary,analytics:n.analytics,marketing:n.marketing,functional:n.functional,personalization:n.personalization,source:"sdk_api"})})}catch{}}showBanner(){if(typeof document>"u")return;if(this.bannerEl)return;let u=this.bannerConfigCache?.banner_config??{},m=this.bannerText(),n=u.brand?.primary_color??"#fafafa",_=u.position??"bottom",A=document.createElement("div");A.setAttribute("data-gurulu-consent-banner",""),A.setAttribute("role","dialog"),A.setAttribute("aria-label",m.heading),A.style.cssText=hu(_),A.innerHTML=qu(m,n);let l=A.querySelector("[data-gurulu-accept]"),C=A.querySelector("[data-gurulu-reject]");l?.addEventListener("click",()=>{this.setState({analytics:!0,marketing:!0,functional:!0,personalization:!0}),this.hideBanner()}),C?.addEventListener("click",()=>{this.setState({analytics:!1,marketing:!1,functional:!1,personalization:!1}),this.hideBanner()}),document.body.appendChild(A),this.bannerEl=A}hideBanner(){if(this.bannerEl?.parentNode)this.bannerEl.parentNode.removeChild(this.bannerEl);this.bannerEl=null}buildIngestHeader(){let u=this.getState();if(!u)return JSON.stringify({necessary:!0});return JSON.stringify(u.categories)}getAnonymousId(){return this.anonymousIdValue}storageKey(){return`gurulu_consent_${this.workspaceId}`}loadOrCreateAnonId(){if(!this.storage)return B();try{let u=this.storage.getItem("gurulu_anon_id");if(u)return u;let m=B();return this.storage.setItem("gurulu_anon_id",m),m}catch{return B()}}bannerText(){let m=this.bannerConfigCache?.banner_config?.text_overrides?.[this.locale];if(this.locale==="tr")return{heading:m?.heading??"Çerezleri tercih et",body:m?.body??"Deneyimini iyileştirmek için analytics + pazarlama çerezleri kullanıyoruz.",accept:m?.accept??"Tümünü kabul et",reject:m?.reject??"Sadece gerekli olanlar"};return{heading:m?.heading??"Manage cookies",body:m?.body??"We use analytics and marketing cookies to improve your experience.",accept:m?.accept??"Accept all",reject:m?.reject??"Necessary only"}}}function hu(u){switch(u){case"top":return"position:fixed;z-index:2147483647;background:#141414;color:#fafafa;border:1px solid #262626;border-radius:8px;padding:16px;font-family:-apple-system,Segoe UI,Roboto,sans-serif;font-size:14px;line-height:1.5;box-shadow:0 4px 24px rgba(0,0,0,0.4);max-width:480px;top:16px;left:50%;transform:translateX(-50%);";case"bottom-left":return"position:fixed;z-index:2147483647;background:#141414;color:#fafafa;border:1px solid #262626;border-radius:8px;padding:16px;font-family:-apple-system,Segoe UI,Roboto,sans-serif;font-size:14px;line-height:1.5;box-shadow:0 4px 24px rgba(0,0,0,0.4);max-width:480px;bottom:16px;left:16px;";case"bottom-right":return"position:fixed;z-index:2147483647;background:#141414;color:#fafafa;border:1px solid #262626;border-radius:8px;padding:16px;font-family:-apple-system,Segoe UI,Roboto,sans-serif;font-size:14px;line-height:1.5;box-shadow:0 4px 24px rgba(0,0,0,0.4);max-width:480px;bottom:16px;right:16px;";case"modal":return"position:fixed;z-index:2147483647;background:#141414;color:#fafafa;border:1px solid #262626;border-radius:8px;padding:16px;font-family:-apple-system,Segoe UI,Roboto,sans-serif;font-size:14px;line-height:1.5;box-shadow:0 4px 24px rgba(0,0,0,0.4);max-width:480px;top:50%;left:50%;transform:translate(-50%,-50%);";default:return"position:fixed;z-index:2147483647;background:#141414;color:#fafafa;border:1px solid #262626;border-radius:8px;padding:16px;font-family:-apple-system,Segoe UI,Roboto,sans-serif;font-size:14px;line-height:1.5;box-shadow:0 4px 24px rgba(0,0,0,0.4);max-width:480px;bottom:16px;left:50%;transform:translateX(-50%);"}}function qu(u,m){return`
2
+ <div style="margin-bottom:12px;font-weight:600;">${J(u.heading)}</div>
3
+ <div style="margin-bottom:16px;color:#a3a3a3;">${J(u.body)}</div>
4
+ <div style="display:flex;gap:8px;flex-wrap:wrap;">
5
+ <button data-gurulu-accept type="button" style="background:${tu(m)};color:#0a0a0a;border:none;padding:8px 16px;border-radius:6px;font-weight:600;cursor:pointer;">${J(u.accept)}</button>
6
+ <button data-gurulu-reject type="button" style="background:transparent;color:#fafafa;border:1px solid #404040;padding:8px 16px;border-radius:6px;cursor:pointer;">${J(u.reject)}</button>
7
+ </div>
8
+ `}function J(u){return u.replace(/[&<>"']/g,(m)=>{switch(m){case"&":return"&amp;";case"<":return"&lt;";case">":return"&gt;";case'"':return"&quot;";case"'":return"&#39;";default:return m}})}function tu(u){return u.replace(/[^a-zA-Z0-9#()_\-., ]/g,"")}function Eu(){if(typeof navigator>"u")return"en";let u=(navigator.language??"").toLowerCase();if(u.startsWith("tr"))return"tr";if(u.startsWith("zh"))return"zh";if(u.startsWith("ar"))return"ar";return"en"}function B(){if(typeof crypto<"u"&&typeof crypto.randomUUID==="function")return`anon_${crypto.randomUUID()}`;return`anon_${Math.random().toString(36).slice(2)}${Date.now().toString(36)}`}async function Nu(){throw Error("fetch not available — provide opts.fetchImpl")}var Vu=new Set(["A","BUTTON"]),Xu=/\.(pdf|zip|dmg|exe|tar|gz|rar|7z|mp3|mp4|csv|xlsx?|docx?|pptx?)(\?|$)/i;function $u(u){let m=u;while(m){if(m.hasAttribute?.("data-gurulu-no-track"))return!1;m=m.parentElement}return!0}function Ju(u){let m=u;while(m){if(!m.tagName){m=m.parentElement;continue}if(Vu.has(m.tagName))return m;if(m.getAttribute?.("role")==="button")return m;if(m.tagName==="INPUT"){let n=m.type?.toLowerCase();if(n==="submit"||n==="button")return m}m=m.parentElement}return null}function Mu(u){let m={};if(!u.attributes)return m;for(let n=0;n<u.attributes.length;n+=1){let _=u.attributes.item(n);if(!_)continue;if(_.name.startsWith("data-gurulu-prop-")){let A=_.name.slice(17);m[A]=_.value}}return m}function d(u){if(typeof document>"u")return{stop:()=>{return}};let m=(n)=>{let _=Ju(n.target);if(!_||!$u(_))return;let A={element_tag:_.tagName.toLowerCase()};if(_.id)A.element_id=_.id;if(_.className&&typeof _.className==="string")A.element_class=_.className.slice(0,256);let l=(_.textContent??"").trim().slice(0,200);if(l)A.element_text=l;if(_.tagName==="A"){let c=_.href;if(c){A.href=c;try{let D=new URL(c).hostname;if(typeof window<"u"&&D!==window.location.hostname)A.is_outbound=!0}catch{}if(Xu.test(c))A.is_download=!0}}let C=_.getAttribute?.("data-gurulu-event");if(C)A.custom_event=C,A.custom_props=Mu(_);u(A)};return document.addEventListener("click",m,!0),{stop(){document.removeEventListener("click",m,!0)}}}var Qu=new Set(["password","tel"]);function f(u){if(u.tagName!=="INPUT")return!1;let m=u,n=(m.type??"").toLowerCase();if(Qu.has(n))return!0;if((m.autocomplete??"").toLowerCase().startsWith("cc-"))return!0;return!1}function i(u){let m=u;while(m){if(m.hasAttribute?.("data-gurulu-no-track"))return!1;m=m.parentElement}return!0}function Zu(u){let m=u;while(m){if(m.tagName==="FORM")return m;m=m.parentElement}return null}function x(u){let m={count:u.elements?.length??0};if(u.id)m.id=u.id;if(u.name)m.name=u.name;return m}function w(u){if(typeof document>"u")return{stop:()=>{return}};let m=new WeakSet,n=(A)=>{let l=A.target;if(!l)return;if(f(l))return;if(!i(l))return;let C=Zu(l);if(!C||m.has(C))return;m.add(C);let c=x(C),D={field_count:c.count};if(c.id)D.form_id=c.id;if(c.name)D.form_name=c.name;u.onStart(D)},_=(A)=>{let l=A.target;if(!l||l.tagName!=="FORM")return;if(!i(l))return;let C=x(l),c=[],D=l.elements;for(let z=0;z<D.length;z+=1){let H=D[z];if(!H)continue;if(f(H))continue;if(!("value"in H))continue;if(typeof H.value==="string"&&H.value.length>0&&H.name)c.push(H.name)}let j={field_count:C.count};if(C.id)j.form_id=C.id;if(C.name)j.form_name=C.name;if(c.length>0)j.filled_fields=c;u.onSubmit(j)};return document.addEventListener("focus",n,!0),document.addEventListener("submit",_,!0),{stop(){document.removeEventListener("focus",n,!0),document.removeEventListener("submit",_,!0)}}}function g(u){if(typeof window>"u"||typeof document>"u")return{stop:()=>{return}};let m=window.location.pathname+window.location.search;function n(){let C=window.location.pathname+window.location.search;if(C===m)return;m=C,u(window.location.href,document.title,document.referrer)}u(window.location.href,document.title,document.referrer);let _=()=>n();window.addEventListener("popstate",_);let A=history.pushState.bind(history),l=history.replaceState.bind(history);return history.pushState=function(...c){let D=A(...c);return queueMicrotask(n),D},history.replaceState=function(...c){let D=l(...c);return queueMicrotask(n),D},{stop(){window.removeEventListener("popstate",_),history.pushState=A,history.replaceState=l}}}var Fu=[25,50,75,90];function y(u){if(typeof window>"u"||typeof document>"u")return{stop:()=>{return}};let m=new Set,n=0,_=()=>{n=0;let{documentElement:l,body:C}=document;if(!l||!C)return;let c=window.scrollY??l.scrollTop??0,D=window.innerHeight??l.clientHeight??0,j=Math.max(C.scrollHeight??0,l.scrollHeight??0);if(j<=D)return;let z=(c+D)/j*100;for(let H of Fu)if(z>=H&&!m.has(H))m.add(H),u({depth_percent:H})},A=()=>{if(n!==0)return;n=requestAnimationFrame(_)};return window.addEventListener("scroll",A,{passive:!0}),{stop(){if(window.removeEventListener("scroll",A),n!==0)cancelAnimationFrame(n)}}}var Bu={LCP:[2500,4000],FID:[100,300],INP:[200,500],CLS:[0.1,0.25],TTFB:[800,1800],FCP:[1800,3000]};function E(u,m){let[n,_]=Bu[u];if(m<=n)return"good";if(m<=_)return"needs-improvement";return"poor"}function Q(u,m,n){if(typeof PerformanceObserver>"u")return null;try{let _=new PerformanceObserver((A)=>n(A.getEntries()));return _.observe({type:u,buffered:m}),_}catch{return null}}function O(u){if(typeof window>"u")return{stop:()=>{return}};let m=[];try{let S=performance.getEntriesByType("navigation")[0];if(S&&S.responseStart>0)u({metric:"TTFB",value:S.responseStart,rating:E("TTFB",S.responseStart)});let T=performance.getEntriesByName("first-contentful-paint")[0];if(T)u({metric:"FCP",value:T.startTime,rating:E("FCP",T.startTime)})}catch{}let n=0,_=Q("largest-contentful-paint",!0,(S)=>{let T=S[S.length-1];if(T)n=T.startTime});if(_)m.push(_);let A=Q("first-input",!0,(S)=>{let T=S[0];if(T){let b=T.processingStart-T.startTime;u({metric:"FID",value:b,rating:E("FID",b)})}});if(A)m.push(A);let l=0,C=Q("event",!0,(S)=>{for(let T of S){let b=T.duration;if(b>l)l=b}});if(C)m.push(C);let c=0,D=Q("layout-shift",!0,(S)=>{for(let T of S){let b=T;if(!b.hadRecentInput)c+=b.value}});if(D)m.push(D);let j=()=>{if(n>0)u({metric:"LCP",value:n,rating:E("LCP",n)});if(l>0)u({metric:"INP",value:l,rating:E("INP",l)});if(c>0)u({metric:"CLS",value:c,rating:E("CLS",c)});n=0,l=0,c=0},z=null,H=null;if(typeof document<"u")z=()=>{if(document.visibilityState==="hidden")j()},H=j,document.addEventListener("visibilitychange",z),window.addEventListener("pagehide",H);return{stop(){for(let S of m)try{S.disconnect()}catch{}if(typeof document<"u"&&z)document.removeEventListener("visibilitychange",z);if(typeof window<"u"&&H)window.removeEventListener("pagehide",H)}}}var Gu={page_view:!0,click:!0,form_started:!0,form_submitted:!0,scroll_depth:!0,web_vitals:!0,outbound_link_click:!0,download_click:!0,js_error:!1,console_error:!1,network_error:!1};function k(u,m){if(u===!1)return{stopAll:()=>{return}};let n={...Gu,...u??{}},_=[];if(n.page_view)_.push(g(m.pageView));if(n.click)_.push(d(m.click));if(n.form_started||n.form_submitted)_.push(w({onStart:n.form_started?m.formStarted:()=>{return},onSubmit:n.form_submitted?m.formSubmitted:()=>{return}}));if(n.scroll_depth)_.push(y(m.scrollDepth));if(n.web_vitals)_.push(O(m.webVital));return{stopAll(){for(let A of _)try{A.stop()}catch{}}}}function N(){return typeof window<"u"&&typeof document<"u"}function R(){if(typeof crypto<"u"&&typeof crypto.randomUUID==="function")return crypto.randomUUID();return`${Date.now().toString(36)}-${Math.random().toString(36).slice(2)}`}function I(u){if(!N())return null;try{return window.localStorage.getItem(u)}catch{return null}}function Z(u,m){if(!N())return;try{window.localStorage.setItem(u,m)}catch{}}function r(u){if(!N())return;try{window.localStorage.removeItem(u)}catch{}}function Ru(u){if(!N())return null;let m=document.cookie??"";for(let n of m.split(";")){let[_,A]=n.trim().split("=");if(_===u&&A!==void 0)return decodeURIComponent(A)}return null}function G(u,m,n=365){if(!N())return;let _=new Date(Date.now()+n*86400000).toUTCString(),A=window.location.hostname,l=A.split("."),C=l.length>=2?`.${l.slice(-2).join(".")}`:A;try{document.cookie=`${u}=${encodeURIComponent(m)}; path=/; domain=${C}; expires=${_}; SameSite=Lax`}catch{}}function v(){let u=I("gurulu_aid")??Ru("gurulu_aid_mirror");if(u)return Z("gurulu_aid",u),G("gurulu_aid_mirror",u),u;let m=R();return Z("gurulu_aid",m),G("gurulu_aid_mirror",m),m}function p(u){Z("gurulu_pid",u)}function a(){return I("gurulu_pid")}function e(){r("gurulu_pid"),r("gurulu_aid");let u=R();return Z("gurulu_aid",u),G("gurulu_aid_mirror",u),u}function s(){return R()}class q extends Error{code;constructor(u,m){super(m);this.code=u,this.name="SDKError"}}class uu extends q{constructor(u){super("SDK_INIT",u);this.name="InitError"}}class t extends q{status;constructor(u,m){super("SDK_NETWORK",u);if(this.name="NetworkError",m!==void 0)this.status=m}}class mu extends q{constructor(u){super("SDK_QUEUE_FULL",u);this.name="QueueFullError"}}class _u extends q{constructor(){super("SDK_OPTED_OUT","tracking disabled — gurulu.optOut() in effect");this.name="OptedOutError"}}class nu extends q{constructor(){super("SDK_CONSENT_BLOCKED","event queued — awaiting consent grant");this.name="ConsentBlockedError"}}var Wu=65536;function Au(u,m){let n=new Headers({"content-type":"application/json",authorization:`Bearer ${u.workspaceKey}`,"x-gurulu-sdk":`@gurulu/web@${u.sdkVersion}`});if(m)for(let[_,A]of Object.entries(m))n.set(_,A);return n}async function lu(u,m,n){let _=u.fetchImpl??(typeof fetch<"u"?fetch.bind(globalThis):null);if(!_)throw new t("fetch unavailable");let A=`${u.endpoint}/v1/ingest/batch`,l=await _(A,{method:"POST",keepalive:!0,credentials:"omit",headers:Au(u,n),body:JSON.stringify(m)});if(!l.ok)throw new t(`ingest ${l.status}`,l.status)}function Cu(u,m){if(typeof navigator>"u"||typeof navigator.sendBeacon!=="function")return!1;let n=`${u.endpoint}/v1/ingest/batch?wk=${encodeURIComponent(u.workspaceKey)}&sdk=${encodeURIComponent(u.sdkVersion)}`;try{let _=JSON.stringify(m);if(_.length>=Wu)return!1;let A=new Blob([_],{type:"application/json"});return navigator.sendBeacon(n,A)}catch{return!1}}async function cu(u,m){let n=u.fetchImpl??(typeof fetch<"u"?fetch.bind(globalThis):null);if(!n)throw new t("fetch unavailable");let _=await n(`${u.endpoint}/v1/ingest/identify`,{method:"POST",keepalive:!0,credentials:"omit",headers:Au(u),body:JSON.stringify(m)});if(!_.ok)throw new t(`identify ${_.status}`,_.status)}var Y="gurulu_queue";function L(){return typeof window<"u"}function Yu(){if(!L())return[];try{let u=window.localStorage.getItem(Y);if(!u)return[];let m=JSON.parse(u);return Array.isArray(m)?m:[]}catch{return[]}}function W(u){if(!L())return;try{if(u.length===0)window.localStorage.removeItem(Y);else window.localStorage.setItem(Y,JSON.stringify(u))}catch{}}class P{opts;buffer;timer=null;flushing=!1;constructor(u){if(this.opts=u,this.buffer=Yu(),this.scheduleNext(),this.installUnloadHooks(),this.buffer.length>0)this.scheduleImmediate()}enqueue(u){if(this.buffer.push(u),W(this.buffer),this.buffer.length>=this.opts.maxQueueSize)this.flush();else this.scheduleNext()}size(){return this.buffer.length}async flush(){if(this.flushing||this.buffer.length===0)return;this.flushing=!0;let u=this.buffer.slice(0);try{await this.sendWithRetry(u),this.buffer=this.buffer.slice(u.length),W(this.buffer)}catch(m){if(this.opts.debug&&typeof console<"u")console.warn("[gurulu] flush failed, retain queue",m)}finally{this.flushing=!1}}flushBeacon(){if(this.buffer.length===0)return;if(Cu(this.opts.transport,{events:this.buffer}))this.buffer=[],W(this.buffer)}scheduleNext(){if(this.timer!==null)return;this.timer=setTimeout(()=>{this.timer=null,this.flush()},this.opts.flushIntervalMs)}scheduleImmediate(){if(this.timer!==null)clearTimeout(this.timer),this.timer=null;this.timer=setTimeout(()=>{this.timer=null,this.flush()},50)}async sendWithRetry(u){let m=0,n=3;while(m<n)try{await lu(this.opts.transport,{events:u});return}catch(_){if(m+=1,m>=n)throw _;let A=2**m*1000;await new Promise((l)=>setTimeout(l,A))}}installUnloadHooks(){if(!L()||typeof document>"u")return;let u=()=>{if(document.visibilityState==="hidden")this.flushBeacon()};try{document.addEventListener("visibilitychange",u),window.addEventListener("pagehide",()=>this.flushBeacon())}catch{}}}function Du(){return typeof window<"u"}function Lu(){if(typeof crypto<"u"&&typeof crypto.randomUUID==="function")return crypto.randomUUID();return`${Date.now().toString(36)}-${Math.random().toString(36).slice(2)}`}function X(u){if(!Du())return null;try{return window.localStorage.getItem(u)}catch{return null}}function V(u,m){if(!Du())return;try{window.localStorage.setItem(u,m)}catch{}}function F(u=Date.now()){let m=X("gurulu_sid"),n=Number(X("gurulu_session_started_at")??"0"),_=Number(X("gurulu_last_event_at")??"0");if(m&&n&&u-_<1800000)return V("gurulu_last_event_at",String(u)),{session_id:m,session_started_at:n,is_new:!1};let A=Lu();return V("gurulu_sid",A),V("gurulu_session_started_at",String(u)),V("gurulu_last_event_at",String(u)),{session_id:A,session_started_at:u,is_new:!0}}function Hu(u,m){let n={},_={};if(!u)return{utm:n,click_id:_};let A;try{A=new URL(u)}catch{return{utm:n,click_id:_}}let l=A.searchParams,C=l.get("utm_source");if(C)n.source=C;let c=l.get("utm_medium");if(c)n.medium=c;let D=l.get("utm_campaign");if(D)n.campaign=D;let j=l.get("utm_term");if(j)n.term=j;let z=l.get("utm_content");if(z)n.content=z;let H=l.get("gclid");if(H)_.gclid=H;let S=l.get("fbclid");if(S)_.fbclid=S;let T=l.get("ttclid");if(T)_.ttclid=T;let b=l.get("li_fat_id");if(b)_.li_fat_id=b;return{utm:n,click_id:_}}function Su(u){if(X("gurulu_first_source"))return;V("gurulu_first_source",JSON.stringify(u))}function Tu(){let u=X("gurulu_first_source");if(!u)return null;try{return JSON.parse(u)}catch{return null}}var K="0.1.0",ju="https://ingest.gurulu.io",Pu="https://api.gurulu.io",o="gurulu_opt_out";function zu(){if(typeof window>"u")return!1;try{return window.localStorage.getItem(o)==="1"}catch{return!1}}function bu(u){if(typeof window>"u")return;try{if(u)window.localStorage.setItem(o,"1");else window.localStorage.removeItem(o)}catch{}}function Ku(){return new Date().toISOString()}class ${initialized=!1;opts=null;queue=null;session=null;anonymousId=null;autocaptureHandle=null;consent;init(u){if(this.initialized){if(u.debug&&typeof console<"u")console.warn("[gurulu] init() already called — ignoring");return}if(typeof window>"u")return;this.opts=u,this.anonymousId=v(),this.session=F(),this.consent=new M({workspaceId:u.workspaceKey,apiUrl:u.apiUrl??Pu,anonymousId:this.anonymousId,autoBanner:u.consent_mode==="banner_required"}),this.consent.init();let m={endpoint:u.endpoint??ju,workspaceKey:u.workspaceKey,sdkVersion:K};this.queue=new P({transport:m,flushIntervalMs:u.flush_interval_ms??5000,maxQueueSize:u.max_queue_size??50,...u.debug?{debug:!0}:{}});let n=Hu(window.location.href,document.referrer);if(Object.keys(n.utm).length>0||Object.keys(n.click_id).length>0){let _={utm:n.utm,click_id:n.click_id,referrer:document.referrer,landing_url:window.location.href,captured_at:Date.now()};Su(_)}this.autocaptureHandle=k(u.autocapture,{pageView:(_,A,l)=>this.queueEvent("page_view","interaction",{url:_,title:A,referrer:l}),click:(_)=>{let A=_.custom_event??"element_clicked",l={element_tag:_.element_tag,..._.element_id?{element_id:_.element_id}:{},..._.element_class?{element_class:_.element_class}:{},..._.element_text?{element_text:_.element_text}:{},..._.href?{href:_.href}:{},..._.is_outbound?{is_outbound:!0}:{},..._.is_download?{is_download:!0}:{},..._.custom_props??{}};this.queueEvent(A,"interaction",l)},formStarted:(_)=>this.queueEvent("form_started","interaction",_),formSubmitted:(_)=>this.queueEvent("form_submitted","interaction",_),scrollDepth:(_)=>this.queueEvent("scroll_depth","interaction",_),webVital:(_)=>this.queueEvent("web_vital","interaction",_)}),this.initialized=!0}track(u,m){this.queueEvent(u,"interaction",m)}page(u){if(!this.initialized||typeof window>"u")return;let m=u?.url??window.location.href,n=u?.title??document.title,_=u?.referrer??document.referrer;this.queueEvent("page_view","interaction",{url:m,title:n,referrer:_})}async identify(u,m){if(!this.initialized||zu())return;if(p(u),!this.opts)return;try{await cu({endpoint:this.opts.endpoint??ju,workspaceKey:this.opts.workspaceKey,sdkVersion:K},{anonymous_id:this.anonymousId,external_user_id:u,...m?.email?{email:m.email}:{},...m?.phone?{phone:m.phone}:{},...m?{traits:m}:{}})}catch(n){if(this.opts?.debug&&typeof console<"u")console.warn("[gurulu] identify failed",n)}}reset(){if(this.anonymousId=e(),typeof window<"u")try{window.localStorage.removeItem("gurulu_sid"),window.localStorage.removeItem("gurulu_session_started_at"),window.localStorage.removeItem("gurulu_last_event_at"),window.localStorage.removeItem("gurulu_first_source")}catch{}this.session=F()}async flush(){await this.queue?.flush()}optOut(){bu(!0)}optIn(){bu(!1)}queueEvent(u,m,n){if(!this.initialized||!this.queue||zu())return;if(!this.consentAllowsAnalytics()&&this.opts?.consent_mode==="banner_required")return;this.session=F();let _={anonymous_id:this.anonymousId??"anon_unknown",event_id:s(),event_key:u,event_type:m,occurred_at:Ku(),producer:"script",producer_version:K,...this.session?{session_id:this.session.session_id}:{}},A=a();if(A)_.person_id=A;if(n&&Object.keys(n).length>0)_.properties=n;let l=this.buildContext();if(l)_.context=l;let C=this.snapshotConsent();if(C)_.consent_state=C;this.queue.enqueue(_)}snapshotConsent(){let u=this.consent?.getState();if(!u)return;return{necessary:u.categories.necessary,analytics:u.categories.analytics,marketing:u.categories.marketing,functional:u.categories.functional,personalization:u.categories.personalization}}consentAllowsAnalytics(){let u=this.consent?.getState();if(!u)return!1;return u.categories.analytics===!0}buildContext(){if(typeof window>"u")return;let u={url:window.location.href,referrer:document.referrer,page_title:document.title,user_agent:navigator.userAgent,domain:window.location.hostname},m=Tu();if(m?.utm&&Object.keys(m.utm).length>0)u.utm=m.utm;if(m?.click_id&&Object.keys(m.click_id).length>0)u.click_id=m.click_id;return u}}var ou="0.1.0",h=new $;function Tm(){return new $}var U={init:(u)=>h.init(u),track:(u,m)=>h.track(u,m),identify:(u,m)=>h.identify(u,m),page:(u)=>h.page(u),reset:()=>h.reset(),flush:()=>h.flush(),optOut:()=>h.optOut(),optIn:()=>h.optIn(),get consent(){return h.consent},VERSION:ou};function Uu(){if(typeof document>"u")return;let u=document.currentScript,m=!u?document.querySelector("script[data-workspace]"):u;if(!m)return;let n=m.getAttribute("data-workspace");if(!n)return;let _={workspaceKey:n},A=m.getAttribute("data-endpoint");if(A)_.endpoint=A;let l=m.getAttribute("data-api-url");if(l)_.apiUrl=l;let C=m.getAttribute("data-consent");if(C==="banner_required"||C==="allow_by_default")_.consent_mode=C;let c=m.getAttribute("data-allowlist");if(c)_.cross_domain_allowlist=c.split(",").map((D)=>D.trim());if(U.init(_),typeof window<"u")window.gurulu=U}Uu();var qm=U;export{qm as default,Tm as createGurulu,Uu as autoBootstrap,ou as VERSION,q as SDKError,mu as QueueFullError,_u as OptedOutError,t as NetworkError,uu as InitError,M as GuruluConsent,$ as Gurulu,nu as ConsentBlockedError};
9
+
10
+ //# debugId=115493C2BAC1CDE364756E2164756E21
package/dist/t.js.map ADDED
@@ -0,0 +1,23 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../src/consent.ts", "../src/autocapture/click.ts", "../src/autocapture/form.ts", "../src/autocapture/page.ts", "../src/autocapture/scroll.ts", "../src/autocapture/web-vitals.ts", "../src/autocapture/index.ts", "../src/identity.ts", "../src/errors.ts", "../src/transport.ts", "../src/queue.ts", "../src/session.ts", "../src/core.ts", "../src/index.ts"],
4
+ "sourcesContent": [
5
+ "// @gurulu/web — browser SDK consent hook (Sprint D, M11 K28 zero-runtime-dep).\n//\n// API:\n// const consent = new GuruluConsent({ workspaceId, apiUrl: 'https://api.gurulu.io' });\n// await consent.init(); // banner-config fetch + auto-show if needed\n// consent.getState(); // localStorage snapshot\n// await consent.setState({ analytics: true }); // POST /v1/consent/state + cache\n// consent.showBanner() / hideBanner();\n// consent.buildIngestHeader(); // gurulu.track() için header\n//\n// Tasarım:\n// - LocalStorage key: gurulu_consent_{workspaceId}\n// - Fetch credentials: 'omit' (3rd-party context — cookie istemiyoruz)\n// - Banner UI: minimal positioned <div>, inline styles (CSP friendly).\n// - SSR-safe — window/document undefined check'leri.\n//\n// PostHog/Mixpanel/GA4 karşılaştırma:\n// - GA4 Consent Mode: gtag('consent', 'update', {...}) — Gurulu API'mizden de\n// set edilen state'i `dataLayer`a kopyalama optional helper (Faz 1.x).\n// - PostHog: cookie-based, banner kendi UI yok — Gurulu inline minimal banner.\n// - Mixpanel: opt_out_tracking() — Gurulu `setState({analytics:false})` eşdeğer.\n\nexport interface ConsentCategories {\n necessary: boolean;\n analytics: boolean;\n marketing: boolean;\n functional: boolean;\n personalization: boolean;\n}\n\nexport interface ConsentSnapshot {\n workspaceId: string;\n anonymousId: string;\n categories: ConsentCategories;\n /** ISO 8601. */\n grantedAt: string;\n /** ISO 8601. */\n expiresAt: string;\n /** Hangi kaynaktan geldi — 'banner' | 'settings_page' | 'sdk_api' | 'remote'. */\n source: 'banner' | 'settings_page' | 'sdk_api' | 'remote';\n}\n\nexport interface BannerConfig {\n position?: 'bottom' | 'top' | 'bottom-left' | 'bottom-right' | 'modal';\n theme?: 'auto' | 'light' | 'dark';\n buttons?: {\n accept?: boolean;\n reject?: boolean;\n customize?: boolean;\n };\n brand?: {\n logo_url?: string;\n primary_color?: string;\n };\n text_overrides?: Record<string, Record<string, string>>;\n}\n\nexport interface BannerConfigResponse {\n workspace_id: string;\n mode: 'allow_by_default' | 'banner_required' | 'strict_gdpr';\n categories: {\n necessary: { required: true; default: true };\n analytics: { default: boolean };\n marketing: { default: boolean };\n functional: { default: boolean };\n personalization: { default: boolean };\n };\n banner_config: BannerConfig;\n renewal_months: number;\n eu_detected: {\n country: string;\n region: string;\n gdpr_applicable: boolean;\n };\n}\n\nexport interface GuruluConsentOptions {\n workspaceId: string;\n /** Default 'https://api.gurulu.io'. Local dev: 'http://localhost:3000'. */\n apiUrl?: string;\n /** Browser locale (banner copy fallback). Default navigator.language. */\n locale?: 'tr' | 'en' | 'zh' | 'ar';\n /** Anonymous ID (otherwise generated). */\n anonymousId?: string;\n /** Test injection. */\n fetchImpl?: typeof fetch;\n /** Test injection. */\n storage?: Storage;\n /** Auto-call showBanner() in init() if mode requires. Default true. */\n autoBanner?: boolean;\n}\n\nconst DEFAULT_API_URL = 'https://api.gurulu.io';\nconst STORAGE_PREFIX = 'gurulu_consent_';\nconst ANON_STORAGE_KEY = 'gurulu_anon_id';\n\n/**\n * Browser SDK consent manager.\n * Zero runtime dep (M11 K28). SSR-safe (init no-op if window undefined).\n */\nexport class GuruluConsent {\n private readonly workspaceId: string;\n private readonly apiUrl: string;\n private readonly fetchImpl: typeof fetch;\n private readonly storage: Storage | null;\n private readonly autoBanner: boolean;\n private readonly locale: 'tr' | 'en' | 'zh' | 'ar';\n private anonymousIdValue: string;\n private bannerConfigCache: BannerConfigResponse | null = null;\n private bannerEl: HTMLElement | null = null;\n\n constructor(opts: GuruluConsentOptions) {\n this.workspaceId = opts.workspaceId;\n this.apiUrl = (opts.apiUrl ?? DEFAULT_API_URL).replace(/\\/+$/, '');\n this.fetchImpl =\n opts.fetchImpl ??\n (typeof fetch !== 'undefined'\n ? (fetch.bind(globalThis) as typeof fetch)\n : (noFetch as unknown as typeof fetch));\n this.storage =\n opts.storage ??\n (typeof globalThis !== 'undefined' && 'localStorage' in globalThis\n ? (globalThis as unknown as { localStorage: Storage }).localStorage\n : null);\n this.autoBanner = opts.autoBanner ?? true;\n this.locale = opts.locale ?? detectLocale();\n this.anonymousIdValue = opts.anonymousId ?? this.loadOrCreateAnonId();\n }\n\n /**\n * Banner config fetch + auto-show. Idempotent — tekrar çağırılabilir.\n */\n async init(): Promise<void> {\n if (typeof window === 'undefined') return; // SSR no-op\n\n try {\n const res = await this.fetchImpl(\n `${this.apiUrl}/v1/consent/banner-config?workspace_id=${encodeURIComponent(this.workspaceId)}`,\n { method: 'GET', credentials: 'omit' },\n );\n if (!res.ok) return;\n this.bannerConfigCache = (await res.json()) as BannerConfigResponse;\n } catch {\n // Network fail — caller decides (state lookup local fallback).\n return;\n }\n\n const local = this.getState();\n if (!local && this.autoBanner && this.bannerConfigCache?.mode === 'banner_required') {\n this.showBanner();\n }\n }\n\n /** Local consent snapshot — `null` if visitor henüz consent vermemiş. */\n getState(): ConsentSnapshot | null {\n if (!this.storage) return null;\n try {\n const raw = this.storage.getItem(this.storageKey());\n if (!raw) return null;\n const parsed = JSON.parse(raw) as ConsentSnapshot;\n // Expiry check.\n if (parsed.expiresAt && new Date(parsed.expiresAt).getTime() < Date.now()) {\n this.storage.removeItem(this.storageKey());\n return null;\n }\n return parsed;\n } catch {\n return null;\n }\n }\n\n /**\n * Consent state set + remote sync (POST /v1/consent/state).\n * `necessary` her zaman true (forced).\n */\n async setState(categories: Partial<ConsentCategories>): Promise<void> {\n const current = this.getState()?.categories;\n const next: ConsentCategories = {\n necessary: true,\n analytics: categories.analytics ?? current?.analytics ?? false,\n marketing: categories.marketing ?? current?.marketing ?? false,\n functional: categories.functional ?? current?.functional ?? false,\n personalization: categories.personalization ?? current?.personalization ?? false,\n };\n\n const grantedAt = new Date();\n const renewalMonths = this.bannerConfigCache?.renewal_months ?? 13;\n const expiresAt = new Date(grantedAt);\n expiresAt.setMonth(expiresAt.getMonth() + renewalMonths);\n\n const snapshot: ConsentSnapshot = {\n workspaceId: this.workspaceId,\n anonymousId: this.anonymousIdValue,\n categories: next,\n grantedAt: grantedAt.toISOString(),\n expiresAt: expiresAt.toISOString(),\n source: 'sdk_api',\n };\n\n // Local-first — Faz 1: 3rd-party context cookie-less.\n if (this.storage) {\n try {\n this.storage.setItem(this.storageKey(), JSON.stringify(snapshot));\n } catch {\n /* quota / private mode */\n }\n }\n\n // Remote sync — best-effort.\n try {\n await this.fetchImpl(`${this.apiUrl}/v1/consent/state`, {\n method: 'POST',\n credentials: 'omit',\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify({\n workspace_id: this.workspaceId,\n anonymous_id: this.anonymousIdValue,\n necessary: next.necessary,\n analytics: next.analytics,\n marketing: next.marketing,\n functional: next.functional,\n personalization: next.personalization,\n source: 'sdk_api',\n }),\n });\n } catch {\n // Offline — local state geçerli, next init() sync edecek.\n }\n }\n\n /** Banner DOM elemanını oluştur + body'ye ekle. Idempotent. */\n showBanner(): void {\n if (typeof document === 'undefined') return;\n if (this.bannerEl) return;\n\n const cfg = this.bannerConfigCache?.banner_config ?? {};\n const text = this.bannerText();\n const accentColor = cfg.brand?.primary_color ?? '#fafafa';\n const position = cfg.position ?? 'bottom';\n\n const el = document.createElement('div');\n el.setAttribute('data-gurulu-consent-banner', '');\n el.setAttribute('role', 'dialog');\n el.setAttribute('aria-label', text.heading);\n el.style.cssText = bannerCss(position);\n el.innerHTML = bannerHtml(text, accentColor);\n\n const acceptBtn = el.querySelector<HTMLButtonElement>('[data-gurulu-accept]');\n const rejectBtn = el.querySelector<HTMLButtonElement>('[data-gurulu-reject]');\n acceptBtn?.addEventListener('click', () => {\n void this.setState({\n analytics: true,\n marketing: true,\n functional: true,\n personalization: true,\n });\n this.hideBanner();\n });\n rejectBtn?.addEventListener('click', () => {\n void this.setState({\n analytics: false,\n marketing: false,\n functional: false,\n personalization: false,\n });\n this.hideBanner();\n });\n\n document.body.appendChild(el);\n this.bannerEl = el;\n }\n\n hideBanner(): void {\n if (this.bannerEl?.parentNode) {\n this.bannerEl.parentNode.removeChild(this.bannerEl);\n }\n this.bannerEl = null;\n }\n\n /**\n * gurulu.track() için header — `X-Gurulu-Consent: {categories JSON}`.\n * Ingest endpoint K17 event snapshot için bu header'ı okur.\n */\n buildIngestHeader(): string {\n const snap = this.getState();\n if (!snap) return JSON.stringify({ necessary: true });\n return JSON.stringify(snap.categories);\n }\n\n /** Test util — current anon id. */\n getAnonymousId(): string {\n return this.anonymousIdValue;\n }\n\n // ─── internal ──────────────────────────────────────────────────────────\n\n private storageKey(): string {\n return `${STORAGE_PREFIX}${this.workspaceId}`;\n }\n\n private loadOrCreateAnonId(): string {\n if (!this.storage) return generateAnonId();\n try {\n const existing = this.storage.getItem(ANON_STORAGE_KEY);\n if (existing) return existing;\n const fresh = generateAnonId();\n this.storage.setItem(ANON_STORAGE_KEY, fresh);\n return fresh;\n } catch {\n return generateAnonId();\n }\n }\n\n private bannerText(): { heading: string; body: string; accept: string; reject: string } {\n // Per-locale text — backend banner_config text_overrides override eder.\n const cfg = this.bannerConfigCache?.banner_config;\n const localeOverride = cfg?.text_overrides?.[this.locale];\n if (this.locale === 'tr') {\n return {\n heading: localeOverride?.heading ?? 'Çerezleri tercih et',\n body:\n localeOverride?.body ??\n 'Deneyimini iyileştirmek için analytics + pazarlama çerezleri kullanıyoruz.',\n accept: localeOverride?.accept ?? 'Tümünü kabul et',\n reject: localeOverride?.reject ?? 'Sadece gerekli olanlar',\n };\n }\n return {\n heading: localeOverride?.heading ?? 'Manage cookies',\n body:\n localeOverride?.body ??\n 'We use analytics and marketing cookies to improve your experience.',\n accept: localeOverride?.accept ?? 'Accept all',\n reject: localeOverride?.reject ?? 'Necessary only',\n };\n }\n}\n\nfunction bannerCss(position: NonNullable<BannerConfig['position']>): string {\n const base =\n 'position:fixed;z-index:2147483647;background:#141414;color:#fafafa;border:1px solid #262626;border-radius:8px;padding:16px;font-family:-apple-system,Segoe UI,Roboto,sans-serif;font-size:14px;line-height:1.5;box-shadow:0 4px 24px rgba(0,0,0,0.4);max-width:480px;';\n switch (position) {\n case 'top':\n return `${base}top:16px;left:50%;transform:translateX(-50%);`;\n case 'bottom-left':\n return `${base}bottom:16px;left:16px;`;\n case 'bottom-right':\n return `${base}bottom:16px;right:16px;`;\n case 'modal':\n return `${base}top:50%;left:50%;transform:translate(-50%,-50%);`;\n default:\n return `${base}bottom:16px;left:50%;transform:translateX(-50%);`;\n }\n}\n\nfunction bannerHtml(\n text: { heading: string; body: string; accept: string; reject: string },\n accent: string,\n): string {\n // Inline button colors — accent ile primary, gri secondary.\n return `\n <div style=\"margin-bottom:12px;font-weight:600;\">${escapeHtml(text.heading)}</div>\n <div style=\"margin-bottom:16px;color:#a3a3a3;\">${escapeHtml(text.body)}</div>\n <div style=\"display:flex;gap:8px;flex-wrap:wrap;\">\n <button data-gurulu-accept type=\"button\" style=\"background:${escapeAttr(accent)};color:#0a0a0a;border:none;padding:8px 16px;border-radius:6px;font-weight:600;cursor:pointer;\">${escapeHtml(text.accept)}</button>\n <button data-gurulu-reject type=\"button\" style=\"background:transparent;color:#fafafa;border:1px solid #404040;padding:8px 16px;border-radius:6px;cursor:pointer;\">${escapeHtml(text.reject)}</button>\n </div>\n `;\n}\n\nfunction escapeHtml(s: string): string {\n return s.replace(/[&<>\"']/g, (c) => {\n switch (c) {\n case '&':\n return '&amp;';\n case '<':\n return '&lt;';\n case '>':\n return '&gt;';\n case '\"':\n return '&quot;';\n case \"'\":\n return '&#39;';\n default:\n return c;\n }\n });\n}\n\nfunction escapeAttr(s: string): string {\n return s.replace(/[^a-zA-Z0-9#()_\\-., ]/g, '');\n}\n\nfunction detectLocale(): 'tr' | 'en' | 'zh' | 'ar' {\n if (typeof navigator === 'undefined') return 'en';\n const lang = (navigator.language ?? '').toLowerCase();\n if (lang.startsWith('tr')) return 'tr';\n if (lang.startsWith('zh')) return 'zh';\n if (lang.startsWith('ar')) return 'ar';\n return 'en';\n}\n\nfunction generateAnonId(): string {\n // RFC 4122 v4-ish; crypto.randomUUID varsa kullan.\n if (typeof crypto !== 'undefined' && typeof crypto.randomUUID === 'function') {\n return `anon_${crypto.randomUUID()}`;\n }\n return `anon_${Math.random().toString(36).slice(2)}${Date.now().toString(36)}`;\n}\n\nasync function noFetch(): Promise<Response> {\n throw new Error('fetch not available — provide opts.fetchImpl');\n}\n",
6
+ "// @gurulu/web autocapture — click on <a>/<button>/[role=button]/<input type=submit|button>.\n//\n// Privacy: traverse up — bail if any ancestor has `data-gurulu-no-track`.\n// Skip elements inside password/payment <input> (auto-redact category).\n//\n// Spec § 2.5 + § 4.3.\n\nexport interface ClickPayload {\n element_tag: string;\n element_id?: string;\n element_class?: string;\n element_text?: string;\n href?: string;\n is_outbound?: boolean;\n is_download?: boolean;\n custom_event?: string;\n custom_props?: Record<string, string>;\n}\n\nexport type ClickTracker = (payload: ClickPayload) => void;\n\nconst CLICKABLE_TAGS = new Set(['A', 'BUTTON']);\nconst DOWNLOAD_EXT = /\\.(pdf|zip|dmg|exe|tar|gz|rar|7z|mp3|mp4|csv|xlsx?|docx?|pptx?)(\\?|$)/i;\n\nfunction isTrackable(el: HTMLElement): boolean {\n let node: HTMLElement | null = el;\n while (node) {\n if (node.hasAttribute?.('data-gurulu-no-track')) return false;\n node = node.parentElement;\n }\n return true;\n}\n\nfunction findClickable(target: EventTarget | null): HTMLElement | null {\n let node = target as HTMLElement | null;\n while (node) {\n if (!node.tagName) {\n node = node.parentElement;\n continue;\n }\n if (CLICKABLE_TAGS.has(node.tagName)) return node;\n if (node.getAttribute?.('role') === 'button') return node;\n if (node.tagName === 'INPUT') {\n const t = (node as HTMLInputElement).type?.toLowerCase();\n if (t === 'submit' || t === 'button') return node;\n }\n node = node.parentElement;\n }\n return null;\n}\n\nfunction readCustomProps(el: HTMLElement): Record<string, string> {\n const out: Record<string, string> = {};\n if (!el.attributes) return out;\n for (let i = 0; i < el.attributes.length; i += 1) {\n const attr = el.attributes.item(i);\n if (!attr) continue;\n if (attr.name.startsWith('data-gurulu-prop-')) {\n const k = attr.name.slice('data-gurulu-prop-'.length);\n out[k] = attr.value;\n }\n }\n return out;\n}\n\nexport interface ClickAutocaptureHandle {\n stop(): void;\n}\n\nexport function startClickAutocapture(track: ClickTracker): ClickAutocaptureHandle {\n if (typeof document === 'undefined') return { stop: () => undefined };\n\n const handler = (ev: Event): void => {\n const found = findClickable(ev.target);\n if (!found || !isTrackable(found)) return;\n\n const payload: ClickPayload = {\n element_tag: found.tagName.toLowerCase(),\n };\n if (found.id) payload.element_id = found.id;\n if (found.className && typeof found.className === 'string') {\n payload.element_class = found.className.slice(0, 256);\n }\n const text = (found.textContent ?? '').trim().slice(0, 200);\n if (text) payload.element_text = text;\n\n if (found.tagName === 'A') {\n const href = (found as HTMLAnchorElement).href;\n if (href) {\n payload.href = href;\n try {\n const host = new URL(href).hostname;\n if (typeof window !== 'undefined' && host !== window.location.hostname) {\n payload.is_outbound = true;\n }\n } catch {\n /* invalid href */\n }\n if (DOWNLOAD_EXT.test(href)) payload.is_download = true;\n }\n }\n\n const customEvent = found.getAttribute?.('data-gurulu-event');\n if (customEvent) {\n payload.custom_event = customEvent;\n payload.custom_props = readCustomProps(found);\n }\n\n track(payload);\n };\n\n document.addEventListener('click', handler, true);\n\n return {\n stop(): void {\n document.removeEventListener('click', handler, true);\n },\n };\n}\n",
7
+ "// @gurulu/web autocapture — form_started (first focus) + form_submitted.\n//\n// Privacy auto-skip: <input type=\"password|tel|email\">, [autocomplete*=\"cc-\"],\n// and any field inside [data-gurulu-no-track].\n//\n// Spec § 2.5 + § 7 Privacy.\n\nexport interface FormStartedPayload {\n form_id?: string;\n form_name?: string;\n field_count: number;\n}\n\nexport interface FormSubmittedPayload {\n form_id?: string;\n form_name?: string;\n field_count: number;\n /** Names of fields that had user input (PII-safe — no values). */\n filled_fields?: string[];\n}\n\nexport type FormTracker = {\n onStart: (p: FormStartedPayload) => void;\n onSubmit: (p: FormSubmittedPayload) => void;\n};\n\nconst SENSITIVE_INPUT_TYPES = new Set(['password', 'tel']);\n\nfunction isSensitiveField(el: HTMLElement): boolean {\n if (el.tagName !== 'INPUT') return false;\n const input = el as HTMLInputElement;\n const type = (input.type ?? '').toLowerCase();\n if (SENSITIVE_INPUT_TYPES.has(type)) return true;\n const autocomplete = (input.autocomplete ?? '').toLowerCase();\n if (autocomplete.startsWith('cc-')) return true;\n return false;\n}\n\nfunction isTrackable(el: HTMLElement): boolean {\n let node: HTMLElement | null = el;\n while (node) {\n if (node.hasAttribute?.('data-gurulu-no-track')) return false;\n node = node.parentElement;\n }\n return true;\n}\n\nfunction findForm(target: EventTarget | null): HTMLFormElement | null {\n let node = target as HTMLElement | null;\n while (node) {\n if (node.tagName === 'FORM') return node as HTMLFormElement;\n node = node.parentElement;\n }\n return null;\n}\n\nfunction formMeta(form: HTMLFormElement): { id?: string; name?: string; count: number } {\n const out: { id?: string; name?: string; count: number } = {\n count: form.elements?.length ?? 0,\n };\n if (form.id) out.id = form.id;\n if (form.name) out.name = form.name;\n return out;\n}\n\nexport interface FormAutocaptureHandle {\n stop(): void;\n}\n\nexport function startFormAutocapture(track: FormTracker): FormAutocaptureHandle {\n if (typeof document === 'undefined') return { stop: () => undefined };\n\n const startedForms = new WeakSet<HTMLFormElement>();\n\n const onFocus = (ev: FocusEvent): void => {\n const t = ev.target as HTMLElement | null;\n if (!t) return;\n if (isSensitiveField(t)) return;\n if (!isTrackable(t)) return;\n const form = findForm(t);\n if (!form || startedForms.has(form)) return;\n startedForms.add(form);\n const meta = formMeta(form);\n const p: FormStartedPayload = { field_count: meta.count };\n if (meta.id) p.form_id = meta.id;\n if (meta.name) p.form_name = meta.name;\n track.onStart(p);\n };\n\n const onSubmit = (ev: Event): void => {\n const form = ev.target as HTMLFormElement | null;\n if (!form || form.tagName !== 'FORM') return;\n if (!isTrackable(form)) return;\n const meta = formMeta(form);\n const filled: string[] = [];\n const els = form.elements;\n for (let i = 0; i < els.length; i += 1) {\n const el = els[i] as HTMLInputElement | undefined;\n if (!el) continue;\n if (isSensitiveField(el)) continue;\n if (!('value' in el)) continue;\n if (typeof el.value === 'string' && el.value.length > 0 && el.name) {\n filled.push(el.name);\n }\n }\n const p: FormSubmittedPayload = { field_count: meta.count };\n if (meta.id) p.form_id = meta.id;\n if (meta.name) p.form_name = meta.name;\n if (filled.length > 0) p.filled_fields = filled;\n track.onSubmit(p);\n };\n\n document.addEventListener('focus', onFocus, true);\n document.addEventListener('submit', onSubmit, true);\n\n return {\n stop(): void {\n document.removeEventListener('focus', onFocus, true);\n document.removeEventListener('submit', onSubmit, true);\n },\n };\n}\n",
8
+ "// @gurulu/web autocapture — page_view + SPA route change.\n//\n// Spec § 2.5 — auto on init + popstate + history.pushState/replaceState patch.\n\nexport type PageTracker = (path: string, title: string, referrer: string) => void;\n\nexport interface PageAutocaptureHandle {\n stop(): void;\n}\n\n/**\n * Patch history & listen popstate. Emits once on init for initial page_view.\n */\nexport function startPageAutocapture(track: PageTracker): PageAutocaptureHandle {\n if (typeof window === 'undefined' || typeof document === 'undefined') {\n return { stop: () => undefined };\n }\n\n let lastPath = window.location.pathname + window.location.search;\n\n function emit(): void {\n const current = window.location.pathname + window.location.search;\n if (current === lastPath) return;\n lastPath = current;\n track(window.location.href, document.title, document.referrer);\n }\n\n // Initial page_view.\n track(window.location.href, document.title, document.referrer);\n\n const onPop = (): void => emit();\n window.addEventListener('popstate', onPop);\n\n const origPush = history.pushState.bind(history);\n const origReplace = history.replaceState.bind(history);\n\n history.pushState = function patchedPush(...args) {\n const ret = origPush(...args);\n queueMicrotask(emit);\n return ret;\n } as History['pushState'];\n\n history.replaceState = function patchedReplace(...args) {\n const ret = origReplace(...args);\n queueMicrotask(emit);\n return ret;\n } as History['replaceState'];\n\n return {\n stop(): void {\n window.removeEventListener('popstate', onPop);\n history.pushState = origPush;\n history.replaceState = origReplace;\n },\n };\n}\n",
9
+ "// @gurulu/web autocapture — scroll_depth (25/50/75/90%) via scroll listener.\n//\n// Spec § 2.5 — modal scroll depth thresholds, fired once each per session.\n// Note: IntersectionObserver would only work with sentinel injection — using a\n// throttled scroll listener is simpler and SSR-safe.\n\nexport interface ScrollDepthPayload {\n depth_percent: 25 | 50 | 75 | 90;\n}\n\nexport type ScrollTracker = (payload: ScrollDepthPayload) => void;\n\nconst THRESHOLDS: Array<25 | 50 | 75 | 90> = [25, 50, 75, 90];\n\nexport interface ScrollAutocaptureHandle {\n stop(): void;\n}\n\nexport function startScrollAutocapture(track: ScrollTracker): ScrollAutocaptureHandle {\n if (typeof window === 'undefined' || typeof document === 'undefined') {\n return { stop: () => undefined };\n }\n\n const fired = new Set<number>();\n let raf = 0;\n\n const compute = (): void => {\n raf = 0;\n const doc = document.documentElement;\n const body = document.body;\n if (!doc || !body) return;\n const scrollTop = window.scrollY ?? doc.scrollTop ?? 0;\n const viewport = window.innerHeight ?? doc.clientHeight ?? 0;\n const docHeight = Math.max(body.scrollHeight ?? 0, doc.scrollHeight ?? 0);\n if (docHeight <= viewport) return;\n const percent = ((scrollTop + viewport) / docHeight) * 100;\n for (const t of THRESHOLDS) {\n if (percent >= t && !fired.has(t)) {\n fired.add(t);\n track({ depth_percent: t });\n }\n }\n };\n\n const onScroll = (): void => {\n if (raf !== 0) return;\n raf = requestAnimationFrame(compute);\n };\n\n window.addEventListener('scroll', onScroll, { passive: true });\n\n return {\n stop(): void {\n window.removeEventListener('scroll', onScroll);\n if (raf !== 0) cancelAnimationFrame(raf);\n },\n };\n}\n",
10
+ "// @gurulu/web autocapture — Core Web Vitals via native PerformanceObserver.\n//\n// Spec § 2.5 — LCP / FID / CLS / INP / TTFB / FCP. No `web-vitals` package\n// (zero runtime dep). Approximations rather than the exact w3c-spec formulas\n// — good enough for product analytics; full fidelity defers to Faz 2 errors+.\n\nexport interface WebVitalPayload {\n metric: 'LCP' | 'FID' | 'INP' | 'CLS' | 'TTFB' | 'FCP';\n value: number;\n rating?: 'good' | 'needs-improvement' | 'poor';\n}\n\nexport type WebVitalTracker = (payload: WebVitalPayload) => void;\n\nconst RATINGS: Record<WebVitalPayload['metric'], [number, number]> = {\n LCP: [2500, 4000],\n FID: [100, 300],\n INP: [200, 500],\n CLS: [0.1, 0.25],\n TTFB: [800, 1800],\n FCP: [1800, 3000],\n};\n\nfunction rate(metric: WebVitalPayload['metric'], value: number): WebVitalPayload['rating'] {\n const [good, poor] = RATINGS[metric];\n if (value <= good) return 'good';\n if (value <= poor) return 'needs-improvement';\n return 'poor';\n}\n\nfunction safeObserve(\n entryType: string,\n buffered: boolean,\n cb: (entries: PerformanceEntry[]) => void,\n): PerformanceObserver | null {\n if (typeof PerformanceObserver === 'undefined') return null;\n try {\n const po = new PerformanceObserver((list) => cb(list.getEntries()));\n po.observe({ type: entryType, buffered } as PerformanceObserverInit);\n return po;\n } catch {\n return null;\n }\n}\n\nexport interface WebVitalsHandle {\n stop(): void;\n}\n\nexport function startWebVitalsAutocapture(track: WebVitalTracker): WebVitalsHandle {\n if (typeof window === 'undefined') return { stop: () => undefined };\n\n const observers: PerformanceObserver[] = [];\n\n // TTFB + FCP — read from `navigation`/`paint` entries.\n try {\n const nav = performance.getEntriesByType('navigation')[0] as\n | PerformanceNavigationTiming\n | undefined;\n if (nav && nav.responseStart > 0) {\n track({ metric: 'TTFB', value: nav.responseStart, rating: rate('TTFB', nav.responseStart) });\n }\n const fcp = performance.getEntriesByName('first-contentful-paint')[0];\n if (fcp) {\n track({ metric: 'FCP', value: fcp.startTime, rating: rate('FCP', fcp.startTime) });\n }\n } catch {\n /* timing API missing */\n }\n\n // LCP — largest entry wins; report on visibility hidden.\n let lastLcp = 0;\n const lcpPo = safeObserve('largest-contentful-paint', true, (entries) => {\n const last = entries[entries.length - 1];\n if (last) lastLcp = (last as PerformanceEntry & { startTime: number }).startTime;\n });\n if (lcpPo) observers.push(lcpPo);\n\n // FID — first-input.\n const fidPo = safeObserve('first-input', true, (entries) => {\n const first = entries[0] as\n | (PerformanceEntry & { processingStart: number; startTime: number })\n | undefined;\n if (first) {\n const value = first.processingStart - first.startTime;\n track({ metric: 'FID', value, rating: rate('FID', value) });\n }\n });\n if (fidPo) observers.push(fidPo);\n\n // INP — approximated via slowest event duration > 40ms (interaction proxy).\n let worstInp = 0;\n const eventPo = safeObserve('event', true, (entries) => {\n for (const e of entries) {\n const dur = (e as PerformanceEntry & { duration: number }).duration;\n if (dur > worstInp) worstInp = dur;\n }\n });\n if (eventPo) observers.push(eventPo);\n\n // CLS — sum of layout-shifts without recent input.\n let cls = 0;\n const clsPo = safeObserve('layout-shift', true, (entries) => {\n for (const e of entries) {\n const ls = e as PerformanceEntry & { value: number; hadRecentInput: boolean };\n if (!ls.hadRecentInput) cls += ls.value;\n }\n });\n if (clsPo) observers.push(clsPo);\n\n const flush = (): void => {\n if (lastLcp > 0) track({ metric: 'LCP', value: lastLcp, rating: rate('LCP', lastLcp) });\n if (worstInp > 0) track({ metric: 'INP', value: worstInp, rating: rate('INP', worstInp) });\n if (cls > 0) track({ metric: 'CLS', value: cls, rating: rate('CLS', cls) });\n lastLcp = 0;\n worstInp = 0;\n cls = 0;\n };\n\n // Report on visibility hidden / pagehide.\n let onHidden: (() => void) | null = null;\n let onPagehide: (() => void) | null = null;\n if (typeof document !== 'undefined') {\n onHidden = (): void => {\n if (document.visibilityState === 'hidden') flush();\n };\n onPagehide = flush;\n document.addEventListener('visibilitychange', onHidden);\n window.addEventListener('pagehide', onPagehide);\n }\n\n return {\n stop(): void {\n for (const po of observers) {\n try {\n po.disconnect();\n } catch {\n /* noop */\n }\n }\n if (typeof document !== 'undefined' && onHidden) {\n document.removeEventListener('visibilitychange', onHidden);\n }\n if (typeof window !== 'undefined' && onPagehide) {\n window.removeEventListener('pagehide', onPagehide);\n }\n },\n };\n}\n",
11
+ "// @gurulu/web autocapture orchestrator — wires page/click/form/scroll/web-vitals\n// observers to a single track callback.\n//\n// Default policy: all five default-on; opt-in features (js_error/console_error/\n// network_error) wired Faz 2.\n\nimport type { AutocaptureConfig } from '../types.ts';\nimport { type ClickPayload, startClickAutocapture } from './click.ts';\nimport {\n type FormStartedPayload,\n type FormSubmittedPayload,\n startFormAutocapture,\n} from './form.ts';\nimport { startPageAutocapture } from './page.ts';\nimport { type ScrollDepthPayload, startScrollAutocapture } from './scroll.ts';\nimport { startWebVitalsAutocapture, type WebVitalPayload } from './web-vitals.ts';\n\nexport interface AutocaptureSinks {\n pageView: (url: string, title: string, referrer: string) => void;\n click: (payload: ClickPayload) => void;\n formStarted: (payload: FormStartedPayload) => void;\n formSubmitted: (payload: FormSubmittedPayload) => void;\n scrollDepth: (payload: ScrollDepthPayload) => void;\n webVital: (payload: WebVitalPayload) => void;\n}\n\nexport interface AutocaptureHandle {\n stopAll(): void;\n}\n\nconst DEFAULTS: Required<Omit<AutocaptureConfig, 'js_error' | 'console_error' | 'network_error'>> &\n Pick<AutocaptureConfig, 'js_error' | 'console_error' | 'network_error'> = {\n page_view: true,\n click: true,\n form_started: true,\n form_submitted: true,\n scroll_depth: true,\n web_vitals: true,\n outbound_link_click: true,\n download_click: true,\n js_error: false,\n console_error: false,\n network_error: false,\n};\n\nexport function startAutocapture(\n cfg: AutocaptureConfig | false | undefined,\n sinks: AutocaptureSinks,\n): AutocaptureHandle {\n if (cfg === false) return { stopAll: () => undefined };\n const merged = { ...DEFAULTS, ...(cfg ?? {}) };\n\n const handles: Array<{ stop(): void }> = [];\n\n if (merged.page_view) handles.push(startPageAutocapture(sinks.pageView));\n if (merged.click) handles.push(startClickAutocapture(sinks.click));\n if (merged.form_started || merged.form_submitted) {\n handles.push(\n startFormAutocapture({\n onStart: merged.form_started ? sinks.formStarted : () => undefined,\n onSubmit: merged.form_submitted ? sinks.formSubmitted : () => undefined,\n }),\n );\n }\n if (merged.scroll_depth) handles.push(startScrollAutocapture(sinks.scrollDepth));\n if (merged.web_vitals) handles.push(startWebVitalsAutocapture(sinks.webVital));\n\n return {\n stopAll(): void {\n for (const h of handles) {\n try {\n h.stop();\n } catch {\n /* noop */\n }\n }\n },\n };\n}\n",
12
+ "// @gurulu/web identity layer — anonymous_id + person_id + 1st-party cookie mirror.\n//\n// Spec § 3.1 / 3.2: localStorage backed with cookie mirror for cross-subdomain\n// continuity. UUID via native crypto.randomUUID (K28 — native Web API only).\n\nconst ANON_KEY = 'gurulu_aid';\nconst ANON_COOKIE = 'gurulu_aid_mirror';\nconst PERSON_KEY = 'gurulu_pid';\n\nfunction hasWindow(): boolean {\n return typeof window !== 'undefined' && typeof document !== 'undefined';\n}\n\nfunction uuid(): string {\n if (typeof crypto !== 'undefined' && typeof crypto.randomUUID === 'function') {\n return crypto.randomUUID();\n }\n // Fallback (very rare browser); not RFC v7 but unique enough for queue keying.\n return `${Date.now().toString(36)}-${Math.random().toString(36).slice(2)}`;\n}\n\nfunction readLocalStorage(key: string): string | null {\n if (!hasWindow()) return null;\n try {\n return window.localStorage.getItem(key);\n } catch {\n return null;\n }\n}\n\nfunction writeLocalStorage(key: string, value: string): void {\n if (!hasWindow()) return;\n try {\n window.localStorage.setItem(key, value);\n } catch {\n /* private mode / quota */\n }\n}\n\nfunction deleteLocalStorage(key: string): void {\n if (!hasWindow()) return;\n try {\n window.localStorage.removeItem(key);\n } catch {\n /* noop */\n }\n}\n\nfunction readCookie(name: string): string | null {\n if (!hasWindow()) return null;\n const cookieStr = document.cookie ?? '';\n for (const part of cookieStr.split(';')) {\n const [k, v] = part.trim().split('=');\n if (k === name && v !== undefined) return decodeURIComponent(v);\n }\n return null;\n}\n\nfunction writeCookie(name: string, value: string, days = 365): void {\n if (!hasWindow()) return;\n const expires = new Date(Date.now() + days * 86400000).toUTCString();\n const host = window.location.hostname;\n // Try to set on registrable-domain (.domain.com) — best-effort.\n const parts = host.split('.');\n const domain = parts.length >= 2 ? `.${parts.slice(-2).join('.')}` : host;\n try {\n document.cookie = `${name}=${encodeURIComponent(value)}; path=/; domain=${domain}; expires=${expires}; SameSite=Lax`;\n } catch {\n /* cross-origin / file:// */\n }\n}\n\nexport function getOrCreateAnonymousId(): string {\n const existing = readLocalStorage(ANON_KEY) ?? readCookie(ANON_COOKIE);\n if (existing) {\n // Re-mirror to both stores (recovery from one-side eviction).\n writeLocalStorage(ANON_KEY, existing);\n writeCookie(ANON_COOKIE, existing);\n return existing;\n }\n const fresh = uuid();\n writeLocalStorage(ANON_KEY, fresh);\n writeCookie(ANON_COOKIE, fresh);\n return fresh;\n}\n\nexport function setPersonId(personId: string): void {\n writeLocalStorage(PERSON_KEY, personId);\n}\n\nexport function getPersonId(): string | null {\n return readLocalStorage(PERSON_KEY);\n}\n\nexport function clearIdentity(): string {\n deleteLocalStorage(PERSON_KEY);\n deleteLocalStorage(ANON_KEY);\n const fresh = uuid();\n writeLocalStorage(ANON_KEY, fresh);\n writeCookie(ANON_COOKIE, fresh);\n return fresh;\n}\n\nexport function newEventId(): string {\n return uuid();\n}\n",
13
+ "// @gurulu/web — minimal error class hierarchy.\n// Note: SDK runtime never throws to user code — these are internal markers\n// surfaced through debug logging.\n\nexport class SDKError extends Error {\n readonly code: string;\n constructor(code: string, message: string) {\n super(message);\n this.code = code;\n this.name = 'SDKError';\n }\n}\n\nexport class InitError extends SDKError {\n constructor(message: string) {\n super('SDK_INIT', message);\n this.name = 'InitError';\n }\n}\n\nexport class NetworkError extends SDKError {\n readonly status?: number;\n constructor(message: string, status?: number) {\n super('SDK_NETWORK', message);\n this.name = 'NetworkError';\n if (status !== undefined) this.status = status;\n }\n}\n\nexport class QueueFullError extends SDKError {\n constructor(message: string) {\n super('SDK_QUEUE_FULL', message);\n this.name = 'QueueFullError';\n }\n}\n\nexport class OptedOutError extends SDKError {\n constructor() {\n super('SDK_OPTED_OUT', 'tracking disabled — gurulu.optOut() in effect');\n this.name = 'OptedOutError';\n }\n}\n\nexport class ConsentBlockedError extends SDKError {\n constructor() {\n super('SDK_CONSENT_BLOCKED', 'event queued — awaiting consent grant');\n this.name = 'ConsentBlockedError';\n }\n}\n",
14
+ "// @gurulu/web transport — fetch keepalive (primary) + sendBeacon (unload fallback).\n// 3rd-party context: credentials: 'omit', custom auth via Bearer header.\n//\n// Spec § 4.5.\n\nimport { NetworkError } from './errors.ts';\n\nconst BEACON_SIZE_LIMIT = 65536; // 64KB\n\nexport interface TransportConfig {\n endpoint: string;\n workspaceKey: string;\n sdkVersion: string;\n /** Optional override (tests). */\n fetchImpl?: typeof fetch;\n}\n\nfunction buildHeaders(cfg: TransportConfig, extra?: Record<string, string>): Headers {\n const h = new Headers({\n 'content-type': 'application/json',\n authorization: `Bearer ${cfg.workspaceKey}`,\n 'x-gurulu-sdk': `@gurulu/web@${cfg.sdkVersion}`,\n });\n if (extra) {\n for (const [k, v] of Object.entries(extra)) h.set(k, v);\n }\n return h;\n}\n\n/**\n * Send batch to /v1/ingest/batch via fetch keepalive.\n * Throws NetworkError on non-2xx. Caller handles retry/backoff.\n */\nexport async function sendBatch(\n cfg: TransportConfig,\n body: unknown,\n extraHeaders?: Record<string, string>,\n): Promise<void> {\n const f = cfg.fetchImpl ?? (typeof fetch !== 'undefined' ? fetch.bind(globalThis) : null);\n if (!f) throw new NetworkError('fetch unavailable');\n const url = `${cfg.endpoint}/v1/ingest/batch`;\n const res = await f(url, {\n method: 'POST',\n keepalive: true,\n credentials: 'omit',\n headers: buildHeaders(cfg, extraHeaders),\n body: JSON.stringify(body),\n });\n if (!res.ok) throw new NetworkError(`ingest ${res.status}`, res.status);\n}\n\n/** sendBeacon for `pagehide`/`beforeunload`. Sync best-effort. */\nexport function sendBeaconBatch(cfg: TransportConfig, body: unknown): boolean {\n if (typeof navigator === 'undefined' || typeof navigator.sendBeacon !== 'function') return false;\n const url = `${cfg.endpoint}/v1/ingest/batch?wk=${encodeURIComponent(cfg.workspaceKey)}&sdk=${encodeURIComponent(cfg.sdkVersion)}`;\n try {\n const payload = JSON.stringify(body);\n if (payload.length >= BEACON_SIZE_LIMIT) return false;\n const blob = new Blob([payload], { type: 'application/json' });\n return navigator.sendBeacon(url, blob);\n } catch {\n return false;\n }\n}\n\n/** Identify call → /v1/ingest/identify. */\nexport async function sendIdentify(cfg: TransportConfig, body: unknown): Promise<void> {\n const f = cfg.fetchImpl ?? (typeof fetch !== 'undefined' ? fetch.bind(globalThis) : null);\n if (!f) throw new NetworkError('fetch unavailable');\n const res = await f(`${cfg.endpoint}/v1/ingest/identify`, {\n method: 'POST',\n keepalive: true,\n credentials: 'omit',\n headers: buildHeaders(cfg),\n body: JSON.stringify(body),\n });\n if (!res.ok) throw new NetworkError(`identify ${res.status}`, res.status);\n}\n",
15
+ "// @gurulu/web batch queue — localStorage backed + 3x exponential retry.\n//\n// Spec § 4.5 — max 50 events, 5s auto-flush, sendBeacon fallback on unload.\n\nimport { sendBatch, sendBeaconBatch, type TransportConfig } from './transport.ts';\nimport type { OutgoingEvent } from './types.ts';\n\nconst QUEUE_KEY = 'gurulu_queue';\n\nfunction hasWindow(): boolean {\n return typeof window !== 'undefined';\n}\n\nfunction loadPersisted(): OutgoingEvent[] {\n if (!hasWindow()) return [];\n try {\n const raw = window.localStorage.getItem(QUEUE_KEY);\n if (!raw) return [];\n const arr = JSON.parse(raw);\n return Array.isArray(arr) ? (arr as OutgoingEvent[]) : [];\n } catch {\n return [];\n }\n}\n\nfunction persist(events: OutgoingEvent[]): void {\n if (!hasWindow()) return;\n try {\n if (events.length === 0) window.localStorage.removeItem(QUEUE_KEY);\n else window.localStorage.setItem(QUEUE_KEY, JSON.stringify(events));\n } catch {\n /* quota — silently drop persist */\n }\n}\n\nexport interface QueueOptions {\n transport: TransportConfig;\n flushIntervalMs: number;\n maxQueueSize: number;\n debug?: boolean;\n}\n\nexport class EventQueue {\n private readonly opts: QueueOptions;\n private buffer: OutgoingEvent[];\n private timer: ReturnType<typeof setTimeout> | null = null;\n private flushing = false;\n\n constructor(opts: QueueOptions) {\n this.opts = opts;\n this.buffer = loadPersisted();\n this.scheduleNext();\n this.installUnloadHooks();\n // Best-effort drain any persisted backlog on startup.\n if (this.buffer.length > 0) this.scheduleImmediate();\n }\n\n enqueue(event: OutgoingEvent): void {\n this.buffer.push(event);\n persist(this.buffer);\n if (this.buffer.length >= this.opts.maxQueueSize) {\n void this.flush();\n } else {\n this.scheduleNext();\n }\n }\n\n size(): number {\n return this.buffer.length;\n }\n\n /** Manual flush — caller may await. */\n async flush(): Promise<void> {\n if (this.flushing || this.buffer.length === 0) return;\n this.flushing = true;\n const batch = this.buffer.slice(0);\n try {\n await this.sendWithRetry(batch);\n this.buffer = this.buffer.slice(batch.length);\n persist(this.buffer);\n } catch (err) {\n // Persisted in `buffer` — next flush re-attempts.\n if (this.opts.debug && typeof console !== 'undefined') {\n // eslint-disable-next-line no-console\n console.warn('[gurulu] flush failed, retain queue', err);\n }\n } finally {\n this.flushing = false;\n }\n }\n\n /** Unload-path: synchronous best-effort via sendBeacon. */\n flushBeacon(): void {\n if (this.buffer.length === 0) return;\n const sent = sendBeaconBatch(this.opts.transport, { events: this.buffer });\n if (sent) {\n this.buffer = [];\n persist(this.buffer);\n }\n }\n\n private scheduleNext(): void {\n if (this.timer !== null) return;\n this.timer = setTimeout(() => {\n this.timer = null;\n void this.flush();\n }, this.opts.flushIntervalMs);\n }\n\n private scheduleImmediate(): void {\n if (this.timer !== null) {\n clearTimeout(this.timer);\n this.timer = null;\n }\n this.timer = setTimeout(() => {\n this.timer = null;\n void this.flush();\n }, 50);\n }\n\n private async sendWithRetry(batch: OutgoingEvent[]): Promise<void> {\n let attempt = 0;\n const max = 3;\n while (attempt < max) {\n try {\n await sendBatch(this.opts.transport, { events: batch });\n return;\n } catch (err) {\n attempt += 1;\n if (attempt >= max) throw err;\n const backoff = 2 ** attempt * 1000;\n await new Promise((r) => setTimeout(r, backoff));\n }\n }\n }\n\n private installUnloadHooks(): void {\n if (!hasWindow() || typeof document === 'undefined') return;\n const onHidden = (): void => {\n if (document.visibilityState === 'hidden') this.flushBeacon();\n };\n try {\n document.addEventListener('visibilitychange', onHidden);\n window.addEventListener('pagehide', () => this.flushBeacon());\n } catch {\n /* DOM unavailable */\n }\n }\n}\n",
16
+ "// @gurulu/web session resolution — 30 min idle timeout + UTM/click_id first-touch.\n//\n// Spec § 3.1 — gurulu_sid + gurulu_session_started_at + gurulu_first_source.\n\nimport type { ClickIdContext, SourceTouch, UTMContext } from './types.ts';\n\nconst SESSION_KEY = 'gurulu_sid';\nconst SESSION_STARTED_KEY = 'gurulu_session_started_at';\nconst LAST_EVENT_KEY = 'gurulu_last_event_at';\nconst FIRST_SOURCE_KEY = 'gurulu_first_source';\nconst SESSION_TIMEOUT_MS = 30 * 60 * 1000;\n\nfunction hasWindow(): boolean {\n return typeof window !== 'undefined';\n}\n\nfunction uuid(): string {\n if (typeof crypto !== 'undefined' && typeof crypto.randomUUID === 'function') {\n return crypto.randomUUID();\n }\n return `${Date.now().toString(36)}-${Math.random().toString(36).slice(2)}`;\n}\n\nfunction readLs(key: string): string | null {\n if (!hasWindow()) return null;\n try {\n return window.localStorage.getItem(key);\n } catch {\n return null;\n }\n}\n\nfunction writeLs(key: string, value: string): void {\n if (!hasWindow()) return;\n try {\n window.localStorage.setItem(key, value);\n } catch {\n /* noop */\n }\n}\n\nexport interface SessionState {\n session_id: string;\n session_started_at: number;\n is_new: boolean;\n}\n\nexport function resolveSession(now = Date.now()): SessionState {\n const existing = readLs(SESSION_KEY);\n const startedAt = Number(readLs(SESSION_STARTED_KEY) ?? '0');\n const lastEvent = Number(readLs(LAST_EVENT_KEY) ?? '0');\n\n if (existing && startedAt && now - lastEvent < SESSION_TIMEOUT_MS) {\n writeLs(LAST_EVENT_KEY, String(now));\n return { session_id: existing, session_started_at: startedAt, is_new: false };\n }\n const fresh = uuid();\n writeLs(SESSION_KEY, fresh);\n writeLs(SESSION_STARTED_KEY, String(now));\n writeLs(LAST_EVENT_KEY, String(now));\n return { session_id: fresh, session_started_at: now, is_new: true };\n}\n\nexport function newSession(now = Date.now()): SessionState {\n const fresh = uuid();\n writeLs(SESSION_KEY, fresh);\n writeLs(SESSION_STARTED_KEY, String(now));\n writeLs(LAST_EVENT_KEY, String(now));\n return { session_id: fresh, session_started_at: now, is_new: true };\n}\n\nexport function parseUrlContext(\n href: string | undefined,\n referrer: string | undefined,\n): {\n utm: UTMContext;\n click_id: ClickIdContext;\n} {\n const utm: UTMContext = {};\n const clickId: ClickIdContext = {};\n if (!href) return { utm, click_id: clickId };\n let url: URL;\n try {\n url = new URL(href);\n } catch {\n return { utm, click_id: clickId };\n }\n const p = url.searchParams;\n const s = p.get('utm_source');\n if (s) utm.source = s;\n const m = p.get('utm_medium');\n if (m) utm.medium = m;\n const c = p.get('utm_campaign');\n if (c) utm.campaign = c;\n const t = p.get('utm_term');\n if (t) utm.term = t;\n const ct = p.get('utm_content');\n if (ct) utm.content = ct;\n const gclid = p.get('gclid');\n if (gclid) clickId.gclid = gclid;\n const fbclid = p.get('fbclid');\n if (fbclid) clickId.fbclid = fbclid;\n const ttclid = p.get('ttclid');\n if (ttclid) clickId.ttclid = ttclid;\n const li = p.get('li_fat_id');\n if (li) clickId.li_fat_id = li;\n void referrer; // reserved for referrer-source classifier (future)\n return { utm, click_id: clickId };\n}\n\nexport function preserveFirstSource(touch: SourceTouch): void {\n if (readLs(FIRST_SOURCE_KEY)) return;\n writeLs(FIRST_SOURCE_KEY, JSON.stringify(touch));\n}\n\nexport function getFirstSource(): SourceTouch | null {\n const raw = readLs(FIRST_SOURCE_KEY);\n if (!raw) return null;\n try {\n return JSON.parse(raw) as SourceTouch;\n } catch {\n return null;\n }\n}\n",
17
+ "// @gurulu/web core — main Gurulu SDK class.\n// Spec: docs/specs/07-sdk-web.md\n//\n// Lifecycle:\n// const sdk = createGurulu();\n// sdk.init({ workspaceKey: 'pk_xxx' });\n// sdk.track('button_clicked', { ... });\n// sdk.identify('user_42', { email: '...' });\n// sdk.reset(); // logout\n// sdk.flush(); // unload edge\n//\n// Zero runtime dep — all transport via native fetch + sendBeacon.\n\nimport { startAutocapture } from './autocapture/index.ts';\nimport { GuruluConsent } from './consent.ts';\nimport {\n clearIdentity,\n getOrCreateAnonymousId,\n getPersonId,\n newEventId,\n setPersonId,\n} from './identity.ts';\nimport { EventQueue } from './queue.ts';\nimport {\n getFirstSource,\n parseUrlContext,\n preserveFirstSource,\n resolveSession,\n type SessionState,\n} from './session.ts';\nimport { sendIdentify, type TransportConfig } from './transport.ts';\nimport type {\n ConsentCategoriesState,\n EventContext,\n EventType,\n InitOptions,\n OutgoingEvent,\n} from './types.ts';\n\nconst SDK_VERSION = '0.1.0';\nconst DEFAULT_ENDPOINT = 'https://ingest.gurulu.io';\nconst DEFAULT_API_URL = 'https://api.gurulu.io';\nconst OPT_OUT_KEY = 'gurulu_opt_out';\n\nfunction isOptedOut(): boolean {\n if (typeof window === 'undefined') return false;\n try {\n return window.localStorage.getItem(OPT_OUT_KEY) === '1';\n } catch {\n return false;\n }\n}\n\nfunction setOptOut(flag: boolean): void {\n if (typeof window === 'undefined') return;\n try {\n if (flag) window.localStorage.setItem(OPT_OUT_KEY, '1');\n else window.localStorage.removeItem(OPT_OUT_KEY);\n } catch {\n /* noop */\n }\n}\n\nfunction nowIso(): string {\n return new Date().toISOString();\n}\n\nexport interface PageOverride {\n url?: string;\n title?: string;\n referrer?: string;\n}\n\nexport class Gurulu {\n private initialized = false;\n private opts: InitOptions | null = null;\n private queue: EventQueue | null = null;\n private session: SessionState | null = null;\n private anonymousId: string | null = null;\n private autocaptureHandle: { stopAll: () => void } | null = null;\n consent!: GuruluConsent;\n\n /**\n * 1-line init. Idempotent — second call no-ops with debug warning.\n */\n init(opts: InitOptions): void {\n if (this.initialized) {\n if (opts.debug && typeof console !== 'undefined') {\n // eslint-disable-next-line no-console\n console.warn('[gurulu] init() already called — ignoring');\n }\n return;\n }\n if (typeof window === 'undefined') return; // SSR no-op\n\n this.opts = opts;\n this.anonymousId = getOrCreateAnonymousId();\n this.session = resolveSession();\n this.consent = new GuruluConsent({\n workspaceId: opts.workspaceKey,\n apiUrl: opts.apiUrl ?? DEFAULT_API_URL,\n anonymousId: this.anonymousId,\n autoBanner: opts.consent_mode === 'banner_required',\n });\n // Fire consent banner-config (best-effort, async).\n void this.consent.init();\n\n const transport: TransportConfig = {\n endpoint: opts.endpoint ?? DEFAULT_ENDPOINT,\n workspaceKey: opts.workspaceKey,\n sdkVersion: SDK_VERSION,\n };\n this.queue = new EventQueue({\n transport,\n flushIntervalMs: opts.flush_interval_ms ?? 5000,\n maxQueueSize: opts.max_queue_size ?? 50,\n ...(opts.debug ? { debug: true } : {}),\n });\n\n // First-touch UTM preserve.\n const ctx = parseUrlContext(window.location.href, document.referrer);\n if (Object.keys(ctx.utm).length > 0 || Object.keys(ctx.click_id).length > 0) {\n const touch = {\n utm: ctx.utm,\n click_id: ctx.click_id,\n referrer: document.referrer,\n landing_url: window.location.href,\n captured_at: Date.now(),\n };\n preserveFirstSource(touch);\n }\n\n // Wire autocapture.\n this.autocaptureHandle = startAutocapture(opts.autocapture, {\n pageView: (url, title, referrer) =>\n this.queueEvent('page_view', 'interaction', { url, title, referrer }),\n click: (payload) => {\n const ev = payload.custom_event ?? 'element_clicked';\n const props: Record<string, unknown> = {\n element_tag: payload.element_tag,\n ...(payload.element_id ? { element_id: payload.element_id } : {}),\n ...(payload.element_class ? { element_class: payload.element_class } : {}),\n ...(payload.element_text ? { element_text: payload.element_text } : {}),\n ...(payload.href ? { href: payload.href } : {}),\n ...(payload.is_outbound ? { is_outbound: true } : {}),\n ...(payload.is_download ? { is_download: true } : {}),\n ...(payload.custom_props ?? {}),\n };\n this.queueEvent(ev, 'interaction', props);\n },\n formStarted: (p) =>\n this.queueEvent('form_started', 'interaction', p as unknown as Record<string, unknown>),\n formSubmitted: (p) =>\n this.queueEvent('form_submitted', 'interaction', p as unknown as Record<string, unknown>),\n scrollDepth: (p) =>\n this.queueEvent('scroll_depth', 'interaction', p as unknown as Record<string, unknown>),\n webVital: (p) =>\n this.queueEvent('web_vital', 'interaction', p as unknown as Record<string, unknown>),\n });\n\n this.initialized = true;\n }\n\n /** Manual `track()` — typed via codegen (`@gurulu/web/generated`). */\n track(eventKey: string, properties?: Record<string, unknown>): void {\n this.queueEvent(eventKey, 'interaction', properties);\n }\n\n /** Manual `page()` — SPA route helper for non-history flows. */\n page(override?: PageOverride): void {\n if (!this.initialized || typeof window === 'undefined') return;\n const url = override?.url ?? window.location.href;\n const title = override?.title ?? document.title;\n const referrer = override?.referrer ?? document.referrer;\n this.queueEvent('page_view', 'interaction', { url, title, referrer });\n }\n\n /**\n * Identify — anonymous → person merge.\n * Stores person_id locally + best-effort POST /v1/ingest/identify.\n */\n async identify(personId: string, traits?: Record<string, unknown>): Promise<void> {\n if (!this.initialized || isOptedOut()) return;\n setPersonId(personId);\n if (!this.opts) return;\n try {\n await sendIdentify(\n {\n endpoint: this.opts.endpoint ?? DEFAULT_ENDPOINT,\n workspaceKey: this.opts.workspaceKey,\n sdkVersion: SDK_VERSION,\n },\n {\n anonymous_id: this.anonymousId,\n external_user_id: personId,\n ...(traits?.email ? { email: traits.email } : {}),\n ...(traits?.phone ? { phone: traits.phone } : {}),\n ...(traits ? { traits } : {}),\n },\n );\n } catch (err) {\n if (this.opts?.debug && typeof console !== 'undefined') {\n // eslint-disable-next-line no-console\n console.warn('[gurulu] identify failed', err);\n }\n }\n }\n\n /** Logout — wipes person_id, rotates anonymous_id, new session. */\n reset(): void {\n this.anonymousId = clearIdentity();\n if (typeof window !== 'undefined') {\n try {\n window.localStorage.removeItem('gurulu_sid');\n window.localStorage.removeItem('gurulu_session_started_at');\n window.localStorage.removeItem('gurulu_last_event_at');\n window.localStorage.removeItem('gurulu_first_source');\n } catch {\n /* noop */\n }\n }\n this.session = resolveSession();\n }\n\n /** Manual flush — caller may await. */\n async flush(): Promise<void> {\n await this.queue?.flush();\n }\n\n /** Tracking opt-out — all subsequent track/identify no-op. */\n optOut(): void {\n setOptOut(true);\n }\n\n /** Re-enable tracking. */\n optIn(): void {\n setOptOut(false);\n }\n\n // ───────────────────────── internal ──────────────────────────────\n\n private queueEvent(\n eventKey: string,\n eventType: EventType,\n properties?: Record<string, unknown>,\n ): void {\n if (!this.initialized || !this.queue || isOptedOut()) return;\n if (!this.consentAllowsAnalytics() && this.opts?.consent_mode === 'banner_required') {\n // Drop pre-consent in banner_required mode; SDK queue still captures\n // necessary events post-consent.\n return;\n }\n this.session = resolveSession();\n const event: OutgoingEvent = {\n anonymous_id: this.anonymousId ?? 'anon_unknown',\n event_id: newEventId(),\n event_key: eventKey,\n event_type: eventType,\n occurred_at: nowIso(),\n producer: 'script',\n producer_version: SDK_VERSION,\n ...(this.session ? { session_id: this.session.session_id } : {}),\n };\n const pid = getPersonId();\n if (pid) event.person_id = pid;\n if (properties && Object.keys(properties).length > 0) event.properties = properties;\n const ctx = this.buildContext();\n if (ctx) event.context = ctx;\n const consent = this.snapshotConsent();\n if (consent) event.consent_state = consent;\n this.queue.enqueue(event);\n }\n\n private snapshotConsent(): ConsentCategoriesState | undefined {\n const snap = this.consent?.getState();\n if (!snap) return undefined;\n return {\n necessary: snap.categories.necessary,\n analytics: snap.categories.analytics,\n marketing: snap.categories.marketing,\n functional: snap.categories.functional,\n personalization: snap.categories.personalization,\n };\n }\n\n private consentAllowsAnalytics(): boolean {\n const snap = this.consent?.getState();\n if (!snap) return false;\n return snap.categories.analytics === true;\n }\n\n private buildContext(): EventContext | undefined {\n if (typeof window === 'undefined') return undefined;\n const ctx: EventContext = {\n url: window.location.href,\n referrer: document.referrer,\n page_title: document.title,\n user_agent: navigator.userAgent,\n domain: window.location.hostname,\n };\n const first = getFirstSource();\n if (first?.utm && Object.keys(first.utm).length > 0) ctx.utm = first.utm;\n if (first?.click_id && Object.keys(first.click_id).length > 0) ctx.click_id = first.click_id;\n return ctx;\n }\n}\n",
18
+ "// @gurulu/web — Modül 11 browser SDK. PUBLIC NPM + CDN `t.js` (apps/cdn).\n//\n// Spec: docs/specs/07-sdk-web.md\n// Install (1-line script):\n// <script src=\"https://cdn.gurulu.io/t.js\" data-workspace=\"pk_xxx\" defer></script>\n//\n// npm:\n// import gurulu, { GuruluEvents } from '@gurulu/web';\n// gurulu.init({ workspaceKey: 'pk_xxx' });\n// gurulu.track('button_clicked', { ... });\n//\n// Sprint F1.3: core + queue + transport + identity + session + autocapture\n// (page/click/form/scroll/web-vitals) + script-tag bootstrap. Zero runtime\n// dep (M11 K28). SSR-safe.\n\nexport const VERSION = '0.1.0';\n\nexport {\n type BannerConfig,\n type BannerConfigResponse,\n type ConsentCategories,\n type ConsentSnapshot,\n GuruluConsent,\n type GuruluConsentOptions,\n} from './consent.ts';\n\nexport { Gurulu, type PageOverride } from './core.ts';\nexport {\n ConsentBlockedError,\n InitError,\n NetworkError,\n OptedOutError,\n QueueFullError,\n SDKError,\n} from './errors.ts';\nexport type {\n AutocaptureConfig,\n ClickIdContext,\n ConsentCategoriesState,\n EventContext,\n EventProducer,\n EventType,\n InitOptions,\n OutgoingEvent,\n UTMContext,\n} from './types.ts';\n\nimport { Gurulu } from './core.ts';\nimport type { InitOptions } from './types.ts';\n\n/** Singleton — matches `window.gurulu` global behaviour. */\nconst singleton = new Gurulu();\n\nexport interface GuruluPublicSDK {\n init: (opts: InitOptions) => void;\n track: (eventKey: string, properties?: Record<string, unknown>) => void;\n identify: (personId: string, traits?: Record<string, unknown>) => Promise<void>;\n page: (override?: Parameters<Gurulu['page']>[0]) => void;\n reset: () => void;\n flush: () => Promise<void>;\n optOut: () => void;\n optIn: () => void;\n consent: Gurulu['consent'];\n VERSION: string;\n}\n\n/**\n * Factory — `createGurulu()` returns an isolated instance (multi-tenant SDK or\n * test harness). For 99% of consumers, prefer the default singleton via the\n * default export below.\n */\nexport function createGurulu(): Gurulu {\n return new Gurulu();\n}\n\nconst publicSdk = {\n init: (opts: InitOptions) => singleton.init(opts),\n track: (eventKey: string, properties?: Record<string, unknown>) =>\n singleton.track(eventKey, properties),\n identify: (personId: string, traits?: Record<string, unknown>) =>\n singleton.identify(personId, traits),\n page: (override?: Parameters<Gurulu['page']>[0]) => singleton.page(override),\n reset: () => singleton.reset(),\n flush: () => singleton.flush(),\n optOut: () => singleton.optOut(),\n optIn: () => singleton.optIn(),\n get consent() {\n return singleton.consent;\n },\n VERSION,\n};\n\n/**\n * Script-tag auto-init — reads `<script data-workspace=\"pk_xxx\" ...>` and\n * runs `init()` automatically. No-op in non-browser environments and when\n * the script is loaded via ESM import (no `data-workspace` attr).\n */\nexport function autoBootstrap(): void {\n if (typeof document === 'undefined') return;\n const script = document.currentScript as HTMLScriptElement | null;\n const fallback = !script\n ? (document.querySelector('script[data-workspace]') as HTMLScriptElement | null)\n : script;\n if (!fallback) return;\n const workspaceKey = fallback.getAttribute('data-workspace');\n if (!workspaceKey) return;\n const opts: InitOptions = { workspaceKey };\n const endpoint = fallback.getAttribute('data-endpoint');\n if (endpoint) opts.endpoint = endpoint;\n const apiUrl = fallback.getAttribute('data-api-url');\n if (apiUrl) opts.apiUrl = apiUrl;\n const consentMode = fallback.getAttribute('data-consent');\n if (consentMode === 'banner_required' || consentMode === 'allow_by_default') {\n opts.consent_mode = consentMode;\n }\n const allowlist = fallback.getAttribute('data-allowlist');\n if (allowlist) opts.cross_domain_allowlist = allowlist.split(',').map((s) => s.trim());\n publicSdk.init(opts);\n if (typeof window !== 'undefined') {\n (window as unknown as { gurulu: typeof publicSdk }).gurulu = publicSdk;\n }\n}\n\n// Side-effect: auto-bootstrap when running as the CDN `t.js` build target.\n// In ESM consumer mode (`import gurulu from '@gurulu/web'`), `currentScript`\n// is null + no `data-workspace` attribute, so this is a safe no-op.\nautoBootstrap();\n\nexport default publicSdk;\n"
19
+ ],
20
+ "mappings": "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",
21
+ "debugId": "115493C2BAC1CDE364756E2164756E21",
22
+ "names": []
23
+ }
@@ -0,0 +1,17 @@
1
+ export interface TransportConfig {
2
+ endpoint: string;
3
+ workspaceKey: string;
4
+ sdkVersion: string;
5
+ /** Optional override (tests). */
6
+ fetchImpl?: typeof fetch;
7
+ }
8
+ /**
9
+ * Send batch to /v1/ingest/batch via fetch keepalive.
10
+ * Throws NetworkError on non-2xx. Caller handles retry/backoff.
11
+ */
12
+ export declare function sendBatch(cfg: TransportConfig, body: unknown, extraHeaders?: Record<string, string>): Promise<void>;
13
+ /** sendBeacon for `pagehide`/`beforeunload`. Sync best-effort. */
14
+ export declare function sendBeaconBatch(cfg: TransportConfig, body: unknown): boolean;
15
+ /** Identify call → /v1/ingest/identify. */
16
+ export declare function sendIdentify(cfg: TransportConfig, body: unknown): Promise<void>;
17
+ //# sourceMappingURL=transport.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"transport.d.ts","sourceRoot":"","sources":["../src/transport.ts"],"names":[],"mappings":"AASA,MAAM,WAAW,eAAe;IAC9B,QAAQ,EAAE,MAAM,CAAC;IACjB,YAAY,EAAE,MAAM,CAAC;IACrB,UAAU,EAAE,MAAM,CAAC;IACnB,iCAAiC;IACjC,SAAS,CAAC,EAAE,OAAO,KAAK,CAAC;CAC1B;AAcD;;;GAGG;AACH,wBAAsB,SAAS,CAC7B,GAAG,EAAE,eAAe,EACpB,IAAI,EAAE,OAAO,EACb,YAAY,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GACpC,OAAO,CAAC,IAAI,CAAC,CAYf;AAED,kEAAkE;AAClE,wBAAgB,eAAe,CAAC,GAAG,EAAE,eAAe,EAAE,IAAI,EAAE,OAAO,GAAG,OAAO,CAW5E;AAED,2CAA2C;AAC3C,wBAAsB,YAAY,CAAC,GAAG,EAAE,eAAe,EAAE,IAAI,EAAE,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC,CAWrF"}
@@ -0,0 +1,92 @@
1
+ export type EventType = 'interaction' | 'intent' | 'outcome';
2
+ export type EventProducer = 'script' | 'sdk_web' | 'sdk_server' | 'agent' | 'cli';
3
+ export interface ConsentCategoriesState {
4
+ necessary: boolean;
5
+ analytics: boolean;
6
+ marketing: boolean;
7
+ functional: boolean;
8
+ personalization: boolean;
9
+ }
10
+ export interface UTMContext {
11
+ source?: string;
12
+ medium?: string;
13
+ campaign?: string;
14
+ term?: string;
15
+ content?: string;
16
+ }
17
+ export interface ClickIdContext {
18
+ gclid?: string;
19
+ fbclid?: string;
20
+ ttclid?: string;
21
+ li_fat_id?: string;
22
+ }
23
+ export interface EventContext {
24
+ url?: string;
25
+ referrer?: string;
26
+ page_title?: string;
27
+ user_agent?: string;
28
+ utm?: UTMContext;
29
+ click_id?: ClickIdContext;
30
+ domain?: string;
31
+ }
32
+ /** Outgoing event payload — `/v1/ingest/event` + `/batch`. */
33
+ export interface OutgoingEvent {
34
+ anonymous_id: string;
35
+ person_id?: string;
36
+ session_id?: string;
37
+ event_id?: string;
38
+ event_key: string;
39
+ event_type: EventType;
40
+ occurred_at: string;
41
+ producer: EventProducer;
42
+ producer_version?: string;
43
+ properties?: Record<string, unknown>;
44
+ context?: EventContext;
45
+ consent_state?: ConsentCategoriesState;
46
+ }
47
+ /** Autocapture togglemap — defaults to "all on" except opt-in fields. */
48
+ export interface AutocaptureConfig {
49
+ page_view?: boolean;
50
+ click?: boolean;
51
+ form_started?: boolean;
52
+ form_submitted?: boolean;
53
+ scroll_depth?: boolean;
54
+ web_vitals?: boolean;
55
+ outbound_link_click?: boolean;
56
+ download_click?: boolean;
57
+ js_error?: boolean;
58
+ console_error?: boolean;
59
+ network_error?: boolean;
60
+ }
61
+ export interface InitOptions {
62
+ workspaceKey: string;
63
+ /** Default: `https://ingest.gurulu.io`. */
64
+ endpoint?: string;
65
+ /** Default: `https://api.gurulu.io` (consent banner-config). */
66
+ apiUrl?: string;
67
+ autocapture?: AutocaptureConfig | false;
68
+ /** Default: 'allow_by_default' — banner_required → init queues events until consent. */
69
+ consent_mode?: 'banner_required' | 'allow_by_default';
70
+ /** Flush throttle. Default 5000ms. */
71
+ flush_interval_ms?: number;
72
+ /** Max queue size before flush. Default 50. */
73
+ max_queue_size?: number;
74
+ /** Cross-domain anonymous_id share allowlist. */
75
+ cross_domain_allowlist?: string[];
76
+ /** Debug logger (no-op by default). */
77
+ debug?: boolean;
78
+ }
79
+ export interface IdentityState {
80
+ anonymous_id: string;
81
+ person_id: string | null;
82
+ session_id: string;
83
+ session_started_at: number;
84
+ }
85
+ export interface SourceTouch {
86
+ utm?: UTMContext;
87
+ click_id?: ClickIdContext;
88
+ referrer?: string;
89
+ landing_url?: string;
90
+ captured_at: number;
91
+ }
92
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,39 +1,49 @@
1
1
  {
2
2
  "name": "@gurulu/web",
3
- "version": "0.1.0",
4
- "description": "Gurulu Web SDK — zero-config analytics, error tracking, and session replay",
5
- "main": "dist/index.js",
6
- "types": "dist/index.d.ts",
3
+ "version": "1.0.0",
4
+ "private": false,
5
+ "license": "MIT",
6
+ "publishConfig": {
7
+ "access": "public"
8
+ },
9
+ "type": "module",
10
+ "description": "Gurulu browser SDK. Autocapture + identity hooks + registry-bound contract runtime + web-vitals. 1-line install.",
11
+ "homepage": "https://gurulu.io",
12
+ "repository": {
13
+ "type": "git",
14
+ "url": "https://github.com/Preatan/gurulu.io.git",
15
+ "directory": "packages/sdk-web"
16
+ },
17
+ "files": [
18
+ "dist",
19
+ "README.md",
20
+ "LICENSE"
21
+ ],
22
+ "main": "./dist/index.js",
23
+ "module": "./dist/index.js",
24
+ "types": "./dist/index.d.ts",
7
25
  "exports": {
8
26
  ".": {
9
- "require": "./dist/index.js",
10
- "types": "./dist/index.d.ts"
27
+ "types": "./dist/index.d.ts",
28
+ "import": "./dist/index.js",
29
+ "default": "./dist/index.js"
11
30
  },
12
- "./react": {
13
- "require": "./dist/react.js",
14
- "types": "./dist/react.d.ts"
15
- }
31
+ "./t.js": "./dist/t.js",
32
+ "./package.json": "./package.json"
16
33
  },
17
- "files": ["dist"],
18
34
  "scripts": {
19
- "build": "tsc",
20
- "dev": "tsc --watch"
21
- },
22
- "peerDependencies": {
23
- "react": ">=17.0.0"
24
- },
25
- "peerDependenciesMeta": {
26
- "react": { "optional": true }
35
+ "build": "rm -rf dist && bun run build:esm && bun run build:cdn && bun run build:types",
36
+ "build:esm": "bun build ./src/index.ts --outdir ./dist --target browser --format=esm",
37
+ "build:cdn": "bun build ./src/index.ts --outdir ./dist --entry-naming t.js --target browser --minify --sourcemap=external",
38
+ "build:types": "tsc -p tsconfig.build.json",
39
+ "build:dev": "bun build ./src/index.ts --outdir ./dist --target browser",
40
+ "typecheck": "tsc --noEmit",
41
+ "test": "bun test",
42
+ "prepublishOnly": "bun run build",
43
+ "clean": "rm -rf dist .turbo"
27
44
  },
45
+ "dependencies": {},
28
46
  "devDependencies": {
29
- "tsup": "^8.0.0",
30
- "typescript": "^5.3.0",
31
- "@types/node": "^20.0.0",
32
- "@types/react": "^18.0.0"
33
- },
34
- "engines": {
35
- "node": ">=16.0.0"
36
- },
37
- "license": "MIT",
38
- "keywords": ["analytics", "tracking", "error-tracking", "session-replay", "gurulu"]
47
+ "typescript": "^5.6.0"
48
+ }
39
49
  }
package/dist/react.d.ts DELETED
@@ -1,55 +0,0 @@
1
- /**
2
- * @gurulu/web/react — React/Next.js component for Gurulu analytics.
3
- *
4
- * Usage:
5
- * import { GuruluProvider } from '@gurulu/web/react';
6
- *
7
- * // In your root layout:
8
- * <GuruluProvider siteId="xxx" token="yyy" features={['errors', 'replay']} />
9
- */
10
- import { track, identify, getInstance } from './index';
11
- import type { GuruluConfig, GuruluInstance } from './index';
12
- export type { GuruluConfig, GuruluInstance };
13
- export { track, identify, getInstance };
14
- export interface GuruluProviderProps {
15
- siteId: string;
16
- token: string;
17
- endpoint?: string;
18
- features?: Array<'errors' | 'replay' | 'vitals'>;
19
- autoTrack?: boolean;
20
- consent?: 'full' | 'analytics' | 'necessary' | 'none';
21
- debug?: boolean;
22
- /** Auto-identify the current user (pass userId + email after auth) */
23
- userId?: string;
24
- userEmail?: string;
25
- }
26
- /**
27
- * Drop-in React component that initializes Gurulu tracking.
28
- * Renders nothing — just injects the tracker script and optionally identifies the user.
29
- *
30
- * ```tsx
31
- * // app/layout.tsx
32
- * import { GuruluProvider } from '@gurulu/web/react';
33
- *
34
- * export default function Layout({ children }) {
35
- * return (
36
- * <html>
37
- * <body>
38
- * {children}
39
- * <GuruluProvider
40
- * siteId="your-site-id"
41
- * token="your-token"
42
- * features={['errors', 'replay']}
43
- * />
44
- * </body>
45
- * </html>
46
- * );
47
- * }
48
- * ```
49
- */
50
- export declare function GuruluProvider({ siteId, token, endpoint, features, autoTrack, consent, debug, userId, userEmail, }: GuruluProviderProps): null;
51
- /**
52
- * Hook to get the Gurulu tracker instance.
53
- * Returns null until the tracker script has loaded.
54
- */
55
- export declare function useGurulu(): GuruluInstance | null;