@kryptonhq/analytics 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md ADDED
@@ -0,0 +1,103 @@
1
+ # @kryptonhq/analytics
2
+
3
+ Krypton Analytics JavaScript SDK.
4
+
5
+ ## Install (npm)
6
+
7
+ ```bash
8
+ npm install @kryptonhq/analytics
9
+ ```
10
+
11
+ ```ts
12
+ import { Krypton } from "@kryptonhq/analytics";
13
+
14
+ const krypton = new Krypton({
15
+ apiKey: "kapi_xxx",
16
+ endpoint: "https://ingest.yourdomain.com",
17
+ autoPageview: true,
18
+ heatmap: true,
19
+ });
20
+
21
+ krypton.track("signup_clicked", { plan: "pro" });
22
+ ```
23
+
24
+ ## Install (CDN, script tag)
25
+
26
+ Use jsDelivr:
27
+
28
+ ```html
29
+ <script defer src="https://cdn.jsdelivr.net/npm/@kryptonhq/analytics@latest/dist/browser.global.js"></script>
30
+ <script>
31
+ window.addEventListener("DOMContentLoaded", function () {
32
+ var krypton = window.KryptonAnalytics.init({
33
+ apiKey: "kapi_xxx",
34
+ endpoint: "https://ingest.yourdomain.com",
35
+ autoPageview: true,
36
+ heatmap: true
37
+ });
38
+
39
+ krypton.track("page_loaded");
40
+ });
41
+ </script>
42
+ ```
43
+
44
+ The CDN build exposes a global: `window.KryptonAnalytics`.
45
+
46
+ ### GA-style bootstrap snippet (single paste)
47
+
48
+ ```html
49
+ <script>
50
+ (function (w, d, s, u, n) {
51
+ w[n] = w[n] || function () { (w[n].q = w[n].q || []).push(arguments); };
52
+ w[n].l = +new Date();
53
+ var js = d.createElement(s), fjs = d.getElementsByTagName(s)[0];
54
+ js.async = 1;
55
+ js.src = u;
56
+ js.onload = function () {
57
+ var cfg = w[n].cfg;
58
+ w[n].client = w.KryptonAnalytics.init(cfg);
59
+ var q = w[n].q || [];
60
+ for (var i = 0; i < q.length; i++) {
61
+ var args = q[i];
62
+ if (args[0] === "track") w[n].client.track(args[1], args[2]);
63
+ }
64
+ };
65
+ fjs.parentNode.insertBefore(js, fjs);
66
+ })(window, document, "script", "https://cdn.jsdelivr.net/npm/@kryptonhq/analytics@latest/dist/browser.global.js", "krypton");
67
+
68
+ krypton.cfg = {
69
+ apiKey: "kapi_xxx",
70
+ endpoint: "https://ingest.yourdomain.com",
71
+ autoPageview: true,
72
+ heatmap: true
73
+ };
74
+
75
+ krypton("track", "landing_view");
76
+ </script>
77
+ ```
78
+
79
+ ## Publish to npm
80
+
81
+ This repository includes a GitHub Actions workflow at:
82
+
83
+ - `.github/workflows/publish-sdk.yml`
84
+
85
+ It triggers on:
86
+
87
+ - Manual run (`workflow_dispatch`)
88
+ - Tag push matching `sdk-v*` (example: `sdk-v0.1.1`)
89
+
90
+ Required GitHub secret:
91
+
92
+ - None, when using npm Trusted Publishing (recommended)
93
+
94
+ Publish flow:
95
+
96
+ 1. Configure npm Trusted Publishing for this package/repo in npm settings.
97
+ 2. Bump version in `package.json`.
98
+ 3. Commit and push.
99
+ 4. Create and push tag:
100
+ ```bash
101
+ git tag sdk-v0.1.1
102
+ git push origin sdk-v0.1.1
103
+ ```
@@ -0,0 +1 @@
1
+ "use strict";var KryptonAnalytics=(()=>{var Z=Object.defineProperty;var ke=Object.getOwnPropertyDescriptor;var Se=Object.getOwnPropertyNames;var be=Object.prototype.hasOwnProperty;var Ce=(e,t)=>{for(var r in t)Z(e,r,{get:t[r],enumerable:!0})},Ie=(e,t,r,i)=>{if(t&&typeof t=="object"||typeof t=="function")for(let n of Se(t))!be.call(e,n)&&n!==r&&Z(e,n,{get:()=>t[n],enumerable:!(i=ke(t,n))||i.enumerable});return e};var Te=e=>Ie(Z({},"__esModule",{value:!0}),e);var at={};Ce(at,{Krypton:()=>K,default:()=>nt,init:()=>ye});var g;(function(e){e[e.Document=0]="Document",e[e.DocumentType=1]="DocumentType",e[e.Element=2]="Element",e[e.Text=3]="Text",e[e.CDATA=4]="CDATA",e[e.Comment=5]="Comment"})(g||(g={}));function xe(e){return e.nodeType===e.ELEMENT_NODE}function Ee(e){var t=e?.host;return t?.shadowRoot===e}function ee(e){return Object.prototype.toString.call(e)==="[object ShadowRoot]"}function Le(e){return e.includes(" background-clip: text;")&&!e.includes(" -webkit-background-clip: text;")&&(e=e.replace(" background-clip: text;"," -webkit-background-clip: text; background-clip: text;")),e}function te(e){try{var t=e.rules||e.cssRules;return t?Le(Array.from(t).map(Ne).join("")):null}catch{return null}}function Ne(e){var t=e.cssText;if(_e(e))try{t=te(e.styleSheet)||t}catch{}return t}function _e(e){return"styleSheet"in e}var Re=(function(){function e(){this.idNodeMap=new Map,this.nodeMetaMap=new WeakMap}return e.prototype.getId=function(t){var r;if(!t)return-1;var i=(r=this.getMeta(t))===null||r===void 0?void 0:r.id;return i??-1},e.prototype.getNode=function(t){return this.idNodeMap.get(t)||null},e.prototype.getIds=function(){return Array.from(this.idNodeMap.keys())},e.prototype.getMeta=function(t){return this.nodeMetaMap.get(t)||null},e.prototype.removeNodeFromMap=function(t){var r=this,i=this.getId(t);this.idNodeMap.delete(i),t.childNodes&&t.childNodes.forEach(function(n){return r.removeNodeFromMap(n)})},e.prototype.has=function(t){return this.idNodeMap.has(t)},e.prototype.hasNode=function(t){return this.nodeMetaMap.has(t)},e.prototype.add=function(t,r){var i=r.id;this.idNodeMap.set(i,t),this.nodeMetaMap.set(t,r)},e.prototype.replace=function(t,r){var i=this.getNode(t);if(i){var n=this.nodeMetaMap.get(i);n&&this.nodeMetaMap.set(r,n)}this.idNodeMap.set(t,r)},e.prototype.reset=function(){this.idNodeMap=new Map,this.nodeMetaMap=new WeakMap},e})();function Oe(e){var t=e.maskInputOptions,r=e.tagName,i=e.type,n=e.value,a=e.maskInputFn,c=n||"";return(t[r.toLowerCase()]||t[i])&&(a?c=a(c):c="*".repeat(c.length)),c}var ue="__rrweb_original__";function Me(e){var t=e.getContext("2d");if(!t)return!0;for(var r=50,i=0;i<e.width;i+=r)for(var n=0;n<e.height;n+=r){var a=t.getImageData,c=ue in a?a[ue]:a,l=new Uint32Array(c.call(t,i,n,Math.min(r,e.width-i),Math.min(r,e.height-n)).data.buffer);if(l.some(function(o){return o!==0}))return!1}return!0}var De=1,Ae=new RegExp("[^a-z0-9-_:]"),le=-2;function Fe(){return De++}function Pe(e){if(e instanceof HTMLFormElement)return"form";var t=e.tagName.toLowerCase().trim();return Ae.test(t)?"div":t}function Ue(e){return e.cssRules?Array.from(e.cssRules).map(function(t){return t.cssText||""}).join(""):""}function He(e){var t="";return e.indexOf("//")>-1?t=e.split("/").slice(0,3).join("/"):t=e.split("/")[0],t=t.split("?")[0],t}var z,fe,We=/url\((?:(')([^']*)'|(")(.*?)"|([^)]*))\)/gm,Ge=/^(?!www\.|(?:http|ftp)s?:\/\/|[A-Za-z]:\\|\/\/|#).*/,ze=/^(data:)([^,]*),(.*)/i;function $(e,t){return(e||"").replace(We,function(r,i,n,a,c,l){var o=n||c||l,f=i||a||"";if(!o)return r;if(!Ge.test(o)||ze.test(o))return"url(".concat(f).concat(o).concat(f,")");if(o[0]==="/")return"url(".concat(f).concat(He(t)+o).concat(f,")");var u=t.split("/"),p=o.split("/");u.pop();for(var b=0,w=p;b<w.length;b++){var k=w[b];k!=="."&&(k===".."?u.pop():u.push(k))}return"url(".concat(f).concat(u.join("/")).concat(f,")")})}var je=/^[^ \t\n\r\u000c]+/,Ke=/^[, \t\n\r\u000c]+/;function Be(e,t){if(t.trim()==="")return t;var r=0;function i(f){var u,p=f.exec(t.substring(r));return p?(u=p[0],r+=u.length,u):""}for(var n=[];i(Ke),!(r>=t.length);){var a=i(je);if(a.slice(-1)===",")a=j(e,a.substring(0,a.length-1)),n.push(a);else{var c="";a=j(e,a);for(var l=!1;;){var o=t.charAt(r);if(o===""){n.push((a+c).trim());break}else if(l)o===")"&&(l=!1);else if(o===","){r+=1,n.push((a+c).trim());break}else o==="("&&(l=!0);c+=o,r+=1}}}return n.join(", ")}function j(e,t){if(!t||t.trim()==="")return t;var r=e.createElement("a");return r.href=t,r.href}function Qe(e){return!!(e.tagName==="svg"||e.ownerSVGElement)}function ie(){var e=document.createElement("a");return e.href="",e.href}function $e(e,t,r,i){return r==="src"||r==="href"&&i&&!(t==="use"&&i[0]==="#")||r==="xlink:href"&&i&&i[0]!=="#"||r==="background"&&i&&(t==="table"||t==="td"||t==="th")?j(e,i):r==="srcset"&&i?Be(e,i):r==="style"&&i?$(i,ie()):t==="object"&&r==="data"&&i?j(e,i):i}function qe(e,t,r){if(typeof t=="string"){if(e.classList.contains(t))return!0}else for(var i=e.classList.length;i--;){var n=e.classList[i];if(t.test(n))return!0}return r?e.matches(r):!1}function re(e,t,r){if(!e)return!1;if(e.nodeType!==e.ELEMENT_NODE)return r?re(e.parentNode,t,r):!1;for(var i=e.classList.length;i--;){var n=e.classList[i];if(t.test(n))return!0}return r?re(e.parentNode,t,r):!1}function Je(e,t,r){var i=e.nodeType===e.ELEMENT_NODE?e:e.parentElement;if(i===null)return!1;if(typeof t=="string"){if(i.classList.contains(t)||i.closest(".".concat(t)))return!0}else if(re(i,t,!0))return!0;return!!(r&&(i.matches(r)||i.closest(r)))}function Ye(e,t,r){var i=e.contentWindow;if(i){var n=!1,a;try{a=i.document.readyState}catch{return}if(a!=="complete"){var c=setTimeout(function(){n||(t(),n=!0)},r);e.addEventListener("load",function(){clearTimeout(c),n=!0,t()});return}var l="about:blank";if(i.location.href!==l||e.src===l||e.src==="")return setTimeout(t,0),e.addEventListener("load",t);e.addEventListener("load",t)}}function Ve(e,t,r){var i=!1,n;try{n=e.sheet}catch{return}if(!n){var a=setTimeout(function(){i||(t(),i=!0)},r);e.addEventListener("load",function(){clearTimeout(a),i=!0,t()})}}function Xe(e,t){var r=t.doc,i=t.mirror,n=t.blockClass,a=t.blockSelector,c=t.maskTextClass,l=t.maskTextSelector,o=t.inlineStylesheet,f=t.maskInputOptions,u=f===void 0?{}:f,p=t.maskTextFn,b=t.maskInputFn,w=t.dataURLOptions,k=w===void 0?{}:w,I=t.inlineImages,T=t.recordCanvas,x=t.keepIframeSrcFn,h=t.newlyAddedElement,s=h===void 0?!1:h,y=Ze(r,i);switch(e.nodeType){case e.DOCUMENT_NODE:return e.compatMode!=="CSS1Compat"?{type:g.Document,childNodes:[],compatMode:e.compatMode}:{type:g.Document,childNodes:[]};case e.DOCUMENT_TYPE_NODE:return{type:g.DocumentType,name:e.name,publicId:e.publicId,systemId:e.systemId,rootId:y};case e.ELEMENT_NODE:return tt(e,{doc:r,blockClass:n,blockSelector:a,inlineStylesheet:o,maskInputOptions:u,maskInputFn:b,dataURLOptions:k,inlineImages:I,recordCanvas:T,keepIframeSrcFn:x,newlyAddedElement:s,rootId:y});case e.TEXT_NODE:return et(e,{maskTextClass:c,maskTextSelector:l,maskTextFn:p,rootId:y});case e.CDATA_SECTION_NODE:return{type:g.CDATA,textContent:"",rootId:y};case e.COMMENT_NODE:return{type:g.Comment,textContent:e.textContent||"",rootId:y};default:return!1}}function Ze(e,t){if(t.hasNode(e)){var r=t.getId(e);return r===1?void 0:r}}function et(e,t){var r,i=t.maskTextClass,n=t.maskTextSelector,a=t.maskTextFn,c=t.rootId,l=e.parentNode&&e.parentNode.tagName,o=e.textContent,f=l==="STYLE"?!0:void 0,u=l==="SCRIPT"?!0:void 0;if(f&&o){try{e.nextSibling||e.previousSibling||!((r=e.parentNode.sheet)===null||r===void 0)&&r.cssRules&&(o=Ue(e.parentNode.sheet))}catch(p){console.warn("Cannot get CSS styles from text's parentNode. Error: ".concat(p),e)}o=$(o,ie())}return u&&(o="SCRIPT_PLACEHOLDER"),!f&&!u&&o&&Je(e,i,n)&&(o=a?a(o):o.replace(/[\S]/g,"*")),{type:g.Text,textContent:o||"",isStyle:f,rootId:c}}function tt(e,t){for(var r=t.doc,i=t.blockClass,n=t.blockSelector,a=t.inlineStylesheet,c=t.maskInputOptions,l=c===void 0?{}:c,o=t.maskInputFn,f=t.dataURLOptions,u=f===void 0?{}:f,p=t.inlineImages,b=t.recordCanvas,w=t.keepIframeSrcFn,k=t.newlyAddedElement,I=k===void 0?!1:k,T=t.rootId,x=qe(e,i,n),h=Pe(e),s={},y=e.attributes.length,M=0;M<y;M++){var E=e.attributes[M];s[E.name]=$e(r,h,E.name,E.value)}if(h==="link"&&a){var C=Array.from(r.styleSheets).find(function(N){return N.href===e.href}),m=null;C&&(m=te(C)),m&&(delete s.rel,delete s.href,s._cssText=$(m,C.href))}if(h==="style"&&e.sheet&&!(e.innerText||e.textContent||"").trim().length){var m=te(e.sheet);m&&(s._cssText=$(m,ie()))}if(h==="input"||h==="textarea"||h==="select"){var P=e.value,_=e.checked;s.type!=="radio"&&s.type!=="checkbox"&&s.type!=="submit"&&s.type!=="button"&&P?s.value=Oe({type:s.type,tagName:h,value:P,maskInputOptions:l,maskInputFn:o}):_&&(s.checked=_)}if(h==="option"&&(e.selected&&!l.select?s.selected=!0:delete s.selected),h==="canvas"&&b){if(e.__context==="2d")Me(e)||(s.rr_dataURL=e.toDataURL(u.type,u.quality));else if(!("__context"in e)){var L=e.toDataURL(u.type,u.quality),D=document.createElement("canvas");D.width=e.width,D.height=e.height;var A=D.toDataURL(u.type,u.quality);L!==A&&(s.rr_dataURL=L)}}if(h==="img"&&p){z||(z=r.createElement("canvas"),fe=z.getContext("2d"));var S=e,R=S.crossOrigin;S.crossOrigin="anonymous";var F=function(){try{z.width=S.naturalWidth,z.height=S.naturalHeight,fe.drawImage(S,0,0),s.rr_dataURL=z.toDataURL(u.type,u.quality)}catch(N){console.warn("Cannot inline img src=".concat(S.currentSrc,"! Error: ").concat(N))}R?s.crossOrigin=R:S.removeAttribute("crossorigin")};S.complete&&S.naturalWidth!==0?F():S.onload=F}if((h==="audio"||h==="video")&&(s.rr_mediaState=e.paused?"paused":"played",s.rr_mediaCurrentTime=e.currentTime),I||(e.scrollLeft&&(s.rr_scrollLeft=e.scrollLeft),e.scrollTop&&(s.rr_scrollTop=e.scrollTop)),x){var U=e.getBoundingClientRect(),H=U.width,O=U.height;s={class:s.class,rr_width:"".concat(H,"px"),rr_height:"".concat(O,"px")}}return h==="iframe"&&!w(s.src)&&(e.contentDocument||(s.rr_src=s.src),delete s.src),{type:g.Element,tagName:h,attributes:s,childNodes:[],isSVG:Qe(e)||void 0,needBlock:x,rootId:T}}function d(e){return e===void 0?"":e.toLowerCase()}function rt(e,t){if(t.comment&&e.type===g.Comment)return!0;if(e.type===g.Element){if(t.script&&(e.tagName==="script"||e.tagName==="link"&&e.attributes.rel==="preload"&&e.attributes.as==="script"||e.tagName==="link"&&e.attributes.rel==="prefetch"&&typeof e.attributes.href=="string"&&e.attributes.href.endsWith(".js")))return!0;if(t.headFavicon&&(e.tagName==="link"&&e.attributes.rel==="shortcut icon"||e.tagName==="meta"&&(d(e.attributes.name).match(/^msapplication-tile(image|color)$/)||d(e.attributes.name)==="application-name"||d(e.attributes.rel)==="icon"||d(e.attributes.rel)==="apple-touch-icon"||d(e.attributes.rel)==="shortcut icon")))return!0;if(e.tagName==="meta"){if(t.headMetaDescKeywords&&d(e.attributes.name).match(/^description|keywords$/))return!0;if(t.headMetaSocial&&(d(e.attributes.property).match(/^(og|twitter|fb):/)||d(e.attributes.name).match(/^(og|twitter):/)||d(e.attributes.name)==="pinterest"))return!0;if(t.headMetaRobots&&(d(e.attributes.name)==="robots"||d(e.attributes.name)==="googlebot"||d(e.attributes.name)==="bingbot"))return!0;if(t.headMetaHttpEquiv&&e.attributes["http-equiv"]!==void 0)return!0;if(t.headMetaAuthorship&&(d(e.attributes.name)==="author"||d(e.attributes.name)==="generator"||d(e.attributes.name)==="framework"||d(e.attributes.name)==="publisher"||d(e.attributes.name)==="progid"||d(e.attributes.property).match(/^article:/)||d(e.attributes.property).match(/^product:/)))return!0;if(t.headMetaVerification&&(d(e.attributes.name)==="google-site-verification"||d(e.attributes.name)==="yandex-verification"||d(e.attributes.name)==="csrf-token"||d(e.attributes.name)==="p:domain_verify"||d(e.attributes.name)==="verify-v1"||d(e.attributes.name)==="verification"||d(e.attributes.name)==="shopify-checkout-api-token"))return!0}}return!1}function Q(e,t){var r=t.doc,i=t.mirror,n=t.blockClass,a=t.blockSelector,c=t.maskTextClass,l=t.maskTextSelector,o=t.skipChild,f=o===void 0?!1:o,u=t.inlineStylesheet,p=u===void 0?!0:u,b=t.maskInputOptions,w=b===void 0?{}:b,k=t.maskTextFn,I=t.maskInputFn,T=t.slimDOMOptions,x=t.dataURLOptions,h=x===void 0?{}:x,s=t.inlineImages,y=s===void 0?!1:s,M=t.recordCanvas,E=M===void 0?!1:M,C=t.onSerialize,m=t.onIframeLoad,P=t.iframeLoadTimeout,_=P===void 0?5e3:P,L=t.onStylesheetLoad,D=t.stylesheetLoadTimeout,A=D===void 0?5e3:D,S=t.keepIframeSrcFn,R=S===void 0?function(){return!1}:S,F=t.newlyAddedElement,U=F===void 0?!1:F,H=t.preserveWhiteSpace,O=H===void 0?!0:H,N=Xe(e,{doc:r,mirror:i,blockClass:n,blockSelector:a,maskTextClass:c,maskTextSelector:l,inlineStylesheet:p,maskInputOptions:w,maskTextFn:k,maskInputFn:I,dataURLOptions:h,inlineImages:y,recordCanvas:E,keepIframeSrcFn:R,newlyAddedElement:U});if(!N)return console.warn(e,"not serialized"),null;var B;i.hasNode(e)?B=i.getId(e):rt(N,T)||!O&&N.type===g.Text&&!N.isStyle&&!N.textContent.replace(/^\s+|\s+$/gm,"").length?B=le:B=Fe();var v=Object.assign(N,{id:B});if(i.add(e,v),B===le)return null;C&&C(e);var J=!f;if(v.type===g.Element){J=J&&!v.needBlock,delete v.needBlock;var ne=e.shadowRoot;ne&&ee(ne)&&(v.isShadowHost=!0)}if((v.type===g.Document||v.type===g.Element)&&J){T.headWhitespace&&v.type===g.Element&&v.tagName==="head"&&(O=!1);for(var ae={doc:r,mirror:i,blockClass:n,blockSelector:a,maskTextClass:c,maskTextSelector:l,skipChild:f,inlineStylesheet:p,maskInputOptions:w,maskTextFn:k,maskInputFn:I,slimDOMOptions:T,dataURLOptions:h,inlineImages:y,recordCanvas:E,preserveWhiteSpace:O,onSerialize:C,onIframeLoad:m,iframeLoadTimeout:_,onStylesheetLoad:L,stylesheetLoadTimeout:A,keepIframeSrcFn:R},Y=0,oe=Array.from(e.childNodes);Y<oe.length;Y++){var V=oe[Y],W=Q(V,ae);W&&v.childNodes.push(W)}if(xe(e)&&e.shadowRoot)for(var X=0,se=Array.from(e.shadowRoot.childNodes);X<se.length;X++){var V=se[X],W=Q(V,ae);W&&(ee(e.shadowRoot)&&(W.isShadow=!0),v.childNodes.push(W))}}return e.parentNode&&Ee(e.parentNode)&&ee(e.parentNode)&&(v.isShadow=!0),v.type===g.Element&&v.tagName==="iframe"&&Ye(e,function(){var G=e.contentDocument;if(G&&m){var ce=Q(G,{doc:G,mirror:i,blockClass:n,blockSelector:a,maskTextClass:c,maskTextSelector:l,skipChild:!1,inlineStylesheet:p,maskInputOptions:w,maskTextFn:k,maskInputFn:I,slimDOMOptions:T,dataURLOptions:h,inlineImages:y,recordCanvas:E,preserveWhiteSpace:O,onSerialize:C,onIframeLoad:m,iframeLoadTimeout:_,onStylesheetLoad:L,stylesheetLoadTimeout:A,keepIframeSrcFn:R});ce&&m(e,ce)}},_),v.type===g.Element&&v.tagName==="link"&&v.attributes.rel==="stylesheet"&&Ve(e,function(){if(L){var G=Q(e,{doc:r,mirror:i,blockClass:n,blockSelector:a,maskTextClass:c,maskTextSelector:l,skipChild:!1,inlineStylesheet:p,maskInputOptions:w,maskTextFn:k,maskInputFn:I,slimDOMOptions:T,dataURLOptions:h,inlineImages:y,recordCanvas:E,preserveWhiteSpace:O,onSerialize:C,onIframeLoad:m,iframeLoadTimeout:_,onStylesheetLoad:L,stylesheetLoadTimeout:A,keepIframeSrcFn:R});G&&L(e,G)}},A),v}function de(e,t){var r=t||{},i=r.mirror,n=i===void 0?new Re:i,a=r.blockClass,c=a===void 0?"rr-block":a,l=r.blockSelector,o=l===void 0?null:l,f=r.maskTextClass,u=f===void 0?"rr-mask":f,p=r.maskTextSelector,b=p===void 0?null:p,w=r.inlineStylesheet,k=w===void 0?!0:w,I=r.inlineImages,T=I===void 0?!1:I,x=r.recordCanvas,h=x===void 0?!1:x,s=r.maskAllInputs,y=s===void 0?!1:s,M=r.maskTextFn,E=r.maskInputFn,C=r.slimDOM,m=C===void 0?!1:C,P=r.dataURLOptions,_=r.preserveWhiteSpace,L=r.onSerialize,D=r.onIframeLoad,A=r.iframeLoadTimeout,S=r.onStylesheetLoad,R=r.stylesheetLoadTimeout,F=r.keepIframeSrcFn,U=F===void 0?function(){return!1}:F,H=y===!0?{color:!0,date:!0,"datetime-local":!0,email:!0,month:!0,number:!0,range:!0,search:!0,tel:!0,text:!0,time:!0,url:!0,week:!0,textarea:!0,select:!0,password:!0}:y===!1?{password:!0}:y,O=m===!0||m==="all"?{script:!0,comment:!0,headFavicon:!0,headWhitespace:!0,headMetaDescKeywords:m==="all",headMetaSocial:!0,headMetaRobots:!0,headMetaHttpEquiv:!0,headMetaAuthorship:!0,headMetaVerification:!0}:m===!1?{}:m;return Q(e,{doc:e,mirror:n,blockClass:c,blockSelector:o,maskTextClass:u,maskTextSelector:b,skipChild:!1,inlineStylesheet:k,maskInputOptions:H,maskTextFn:M,maskInputFn:E,slimDOMOptions:O,dataURLOptions:P,inlineImages:T,recordCanvas:h,preserveWhiteSpace:_,onSerialize:L,onIframeLoad:D,iframeLoadTimeout:A,onStylesheetLoad:S,stylesheetLoadTimeout:R,keepIframeSrcFn:U,newlyAddedElement:!1})}var it=/([^\\]):hover/,st=new RegExp(it.source,"g");function q(){return typeof crypto<"u"&&crypto.randomUUID?crypto.randomUUID():"xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g,e=>{let t=Math.random()*16|0;return(e==="x"?t:t&3|8).toString(16)})}function he(){let e="_krypton_sid";if(typeof sessionStorage<"u"){let t=sessionStorage.getItem(e);return t||(t=q(),sessionStorage.setItem(e,t)),t}return q()}function pe(){let e="_krypton_did";if(typeof localStorage<"u"){let t=localStorage.getItem(e);return t||(t=q(),localStorage.setItem(e,t)),t}return q()}function me(){if(typeof window>"u")return{};let e=new URLSearchParams(window.location.search),t={};for(let r of["utm_source","utm_medium","utm_campaign","utm_term","utm_content"]){let i=e.get(r);i&&(t[r]=i)}return t}function ve(){if(typeof window>"u")return"unknown";let e=window.innerWidth;return e<768?"mobile":e<1024?"tablet":"desktop"}function ge(e){if(e.id)return`#${e.id}`;let t=[],r=e;for(;r&&r!==document.body;){let i=r.tagName.toLowerCase();if(r.id){t.unshift(`#${r.id}`);break}if(r.className&&typeof r.className=="string"){let n=r.className.trim().split(/\s+/).slice(0,2).join(".");n&&(i+=`.${n}`)}t.unshift(i),r=r.parentElement}return t.join(" > ")}var K=class{constructor(t){this.eventQueue=[];this.heatmapQueue=[];this.flushTimer=null;this.consentGiven=!1;this.initialized=!1;this.lastSnapshotUrl=null;this.serverConfig=null;this.configFetched=!1;this.config={autoPageview:!0,heatmap:!1,consentRequired:!1,flushInterval:5e3,batchSize:20,...t},this.distinctId=pe(),this.sessionId=he(),this.config.consentRequired||(this.consentGiven=!0),this.init()}init(){this.initialized||(this.initialized=!0,this.fetchConfig().then(()=>this.setupTracking()))}async fetchConfig(){if(typeof window>"u")return;let t=`_krypton_config_${this.config.apiKey}`,r=sessionStorage.getItem(t);if(r)try{let i=JSON.parse(r);if(i._ts&&Date.now()-i._ts<300*1e3){this.serverConfig=i,this.configFetched=!0;return}}catch{}try{let i=await fetch(`${this.config.endpoint}/api/v1/config?api_key=${encodeURIComponent(this.config.apiKey)}`);if(i.ok){let n=await i.json();this.serverConfig=n,this.configFetched=!0,sessionStorage.setItem(t,JSON.stringify({...n,_ts:Date.now()}))}}catch{}}setupTracking(){if(this.flushTimer=setInterval(()=>this.flush(),this.config.flushInterval),typeof window<"u"){window.addEventListener("beforeunload",()=>this.flush()),this.config.autoPageview&&this.consentGiven&&this.trackPageview();let t=this.config.heatmap&&(!this.configFetched||this.serverConfig?.heatmaps_enabled),r=history.pushState;history.pushState=(...i)=>{r.apply(history,i),this.config.autoPageview&&this.consentGiven&&setTimeout(()=>this.trackPageview(),0),t&&this.consentGiven&&setTimeout(()=>this.captureSnapshot(),0)},window.addEventListener("popstate",()=>{this.config.autoPageview&&this.consentGiven&&setTimeout(()=>this.trackPageview(),0),t&&this.consentGiven&&setTimeout(()=>this.captureSnapshot(),0)}),t&&this.consentGiven&&this.setupHeatmap()}}grantConsent(){this.consentGiven=!0,this.config.autoPageview&&this.trackPageview(),this.config.heatmap&&(!this.configFetched||this.serverConfig?.heatmaps_enabled)&&this.setupHeatmap()}revokeConsent(){this.consentGiven=!1,this.eventQueue=[],this.heatmapQueue=[]}identify(t){this.distinctId=t,typeof localStorage<"u"&&localStorage.setItem("_krypton_did",t)}trackPageview(t){this.consentGiven&&this.track("$pageview",{...t})}track(t,r){if(!this.consentGiven)return;let i=me(),n={project_id:this.config.apiKey,distinct_id:this.distinctId,event_name:t,timestamp:new Date().toISOString(),properties:r,page_url:typeof window<"u"?window.location.href:"",page_title:typeof document<"u"?document.title:"",referrer:typeof document<"u"?document.referrer:"",utm_source:i.utm_source||"",utm_medium:i.utm_medium||"",utm_campaign:i.utm_campaign||"",utm_term:i.utm_term||"",utm_content:i.utm_content||"",device_type:ve(),browser:this.getBrowser(),screen_width:typeof window<"u"?window.screen.width:0,screen_height:typeof window<"u"?window.screen.height:0,session_id:this.sessionId};this.eventQueue.push(n),this.eventQueue.length>=this.config.batchSize&&this.flush()}async flush(){await Promise.all([this.flushEvents(),this.flushHeatmapEvents()])}shutdown(){this.flushTimer&&(clearInterval(this.flushTimer),this.flushTimer=null),this.flush()}async flushEvents(){if(this.eventQueue.length===0)return;let t=this.eventQueue.splice(0,this.eventQueue.length),r={api_key:this.config.apiKey,events:t};try{await fetch(`${this.config.endpoint}/api/v1/ingest/batch`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(r),keepalive:!0})}catch{this.eventQueue.length<this.config.batchSize*5&&this.eventQueue.unshift(...t)}}async flushHeatmapEvents(){if(this.heatmapQueue.length===0)return;let t=this.heatmapQueue.splice(0,this.heatmapQueue.length),r={api_key:this.config.apiKey,events:t};try{await fetch(`${this.config.endpoint}/api/v1/heatmap`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(r),keepalive:!0})}catch{this.heatmapQueue.length<this.config.batchSize*5&&this.heatmapQueue.unshift(...t)}}captureSnapshot(){if(typeof window>"u"||typeof document>"u")return;let t=window.location.href;this.lastSnapshotUrl!==t&&(this.lastSnapshotUrl=t,setTimeout(()=>{try{let r=de(document,{maskAllInputs:!0,blockSelector:"[data-krypton-block]",inlineStylesheet:!0});if(!r)return;let i=JSON.stringify(r),n={api_key:this.config.apiKey,page_url:t,snapshot:i,viewport_width:window.innerWidth,viewport_height:window.innerHeight,page_width:document.documentElement.scrollWidth,page_height:document.documentElement.scrollHeight,timestamp:new Date().toISOString()};fetch(`${this.config.endpoint}/api/v1/snapshot`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(n),keepalive:!0}).catch(()=>{})}catch{}},1e3))}setupHeatmap(){if(typeof window>"u")return;this.captureSnapshot(),document.addEventListener("click",i=>{if(!this.consentGiven)return;let n=i.target;this.heatmapQueue.push({project_id:this.config.apiKey,distinct_id:this.distinctId,session_id:this.sessionId,timestamp:new Date().toISOString(),page_url:window.location.href,interaction_type:"click",x:i.clientX+window.scrollX,y:i.clientY+window.scrollY,viewport_width:window.innerWidth,viewport_height:window.innerHeight,page_width:document.documentElement.scrollWidth,page_height:document.documentElement.scrollHeight,selector:ge(n)})});let t=0,r=null;window.addEventListener("scroll",()=>{if(!this.consentGiven)return;let i=window.scrollY||document.documentElement.scrollTop,n=document.documentElement.scrollHeight-window.innerHeight,a=n>0?i/n*100:0;a>t&&(t=a),r&&clearTimeout(r),r=setTimeout(()=>{this.heatmapQueue.push({project_id:this.config.apiKey,distinct_id:this.distinctId,session_id:this.sessionId,timestamp:new Date().toISOString(),page_url:window.location.href,interaction_type:"scroll",scroll_depth:t,viewport_width:window.innerWidth,viewport_height:window.innerHeight,page_width:document.documentElement.scrollWidth,page_height:document.documentElement.scrollHeight})},500)})}getBrowser(){if(typeof navigator>"u")return"unknown";let t=navigator.userAgent;return t.includes("Firefox")?"Firefox":t.includes("Edg")?"Edge":t.includes("Chrome")?"Chrome":t.includes("Safari")?"Safari":"Other"}};function ye(e){return new K(e)}var we={Krypton:K,init:ye};typeof window<"u"&&(window.KryptonAnalytics=we);var nt=we;return Te(at);})();
@@ -0,0 +1,95 @@
1
+ interface KryptonConfig {
2
+ /** API key for the project */
3
+ apiKey: string;
4
+ /** Ingestion endpoint URL (e.g., "http://localhost:8090") */
5
+ endpoint: string;
6
+ /** Automatically track pageviews (default: true) */
7
+ autoPageview?: boolean;
8
+ /** Enable heatmap tracking (default: false) */
9
+ heatmap?: boolean;
10
+ /** Require consent before tracking (default: false) */
11
+ consentRequired?: boolean;
12
+ /** Flush interval in milliseconds (default: 5000) */
13
+ flushInterval?: number;
14
+ /** Max events per batch (default: 20) */
15
+ batchSize?: number;
16
+ }
17
+ interface EventPayload {
18
+ project_id: string;
19
+ distinct_id: string;
20
+ event_name: string;
21
+ timestamp: string;
22
+ properties?: Record<string, unknown>;
23
+ page_url?: string;
24
+ page_title?: string;
25
+ referrer?: string;
26
+ utm_source?: string;
27
+ utm_medium?: string;
28
+ utm_campaign?: string;
29
+ utm_term?: string;
30
+ utm_content?: string;
31
+ device_type?: string;
32
+ browser?: string;
33
+ browser_version?: string;
34
+ os?: string;
35
+ os_version?: string;
36
+ screen_width?: number;
37
+ screen_height?: number;
38
+ session_id?: string;
39
+ }
40
+ interface HeatmapEventPayload {
41
+ project_id: string;
42
+ distinct_id: string;
43
+ session_id?: string;
44
+ timestamp: string;
45
+ page_url: string;
46
+ interaction_type: "click" | "scroll" | "mousemove";
47
+ x?: number;
48
+ y?: number;
49
+ scroll_depth?: number;
50
+ viewport_width?: number;
51
+ viewport_height?: number;
52
+ page_width?: number;
53
+ page_height?: number;
54
+ selector?: string;
55
+ }
56
+
57
+ declare class Krypton {
58
+ private config;
59
+ private distinctId;
60
+ private sessionId;
61
+ private eventQueue;
62
+ private heatmapQueue;
63
+ private flushTimer;
64
+ private consentGiven;
65
+ private initialized;
66
+ private lastSnapshotUrl;
67
+ private serverConfig;
68
+ private configFetched;
69
+ constructor(config: KryptonConfig);
70
+ private init;
71
+ private fetchConfig;
72
+ private setupTracking;
73
+ /** Grant consent and begin tracking */
74
+ grantConsent(): void;
75
+ /** Revoke consent and stop tracking */
76
+ revokeConsent(): void;
77
+ /** Identify a user with a custom distinct ID */
78
+ identify(distinctId: string): void;
79
+ /** Track a pageview */
80
+ trackPageview(properties?: Record<string, unknown>): void;
81
+ /** Track a custom event */
82
+ track(eventName: string, properties?: Record<string, unknown>): void;
83
+ /** Flush all queued events to the server */
84
+ flush(): Promise<void>;
85
+ /** Shutdown the SDK */
86
+ shutdown(): void;
87
+ private flushEvents;
88
+ private flushHeatmapEvents;
89
+ private captureSnapshot;
90
+ private setupHeatmap;
91
+ private getBrowser;
92
+ }
93
+ declare function createKrypton(config: KryptonConfig): Krypton;
94
+
95
+ export { type EventPayload, type HeatmapEventPayload, Krypton, type KryptonConfig, createKrypton };
@@ -0,0 +1,95 @@
1
+ interface KryptonConfig {
2
+ /** API key for the project */
3
+ apiKey: string;
4
+ /** Ingestion endpoint URL (e.g., "http://localhost:8090") */
5
+ endpoint: string;
6
+ /** Automatically track pageviews (default: true) */
7
+ autoPageview?: boolean;
8
+ /** Enable heatmap tracking (default: false) */
9
+ heatmap?: boolean;
10
+ /** Require consent before tracking (default: false) */
11
+ consentRequired?: boolean;
12
+ /** Flush interval in milliseconds (default: 5000) */
13
+ flushInterval?: number;
14
+ /** Max events per batch (default: 20) */
15
+ batchSize?: number;
16
+ }
17
+ interface EventPayload {
18
+ project_id: string;
19
+ distinct_id: string;
20
+ event_name: string;
21
+ timestamp: string;
22
+ properties?: Record<string, unknown>;
23
+ page_url?: string;
24
+ page_title?: string;
25
+ referrer?: string;
26
+ utm_source?: string;
27
+ utm_medium?: string;
28
+ utm_campaign?: string;
29
+ utm_term?: string;
30
+ utm_content?: string;
31
+ device_type?: string;
32
+ browser?: string;
33
+ browser_version?: string;
34
+ os?: string;
35
+ os_version?: string;
36
+ screen_width?: number;
37
+ screen_height?: number;
38
+ session_id?: string;
39
+ }
40
+ interface HeatmapEventPayload {
41
+ project_id: string;
42
+ distinct_id: string;
43
+ session_id?: string;
44
+ timestamp: string;
45
+ page_url: string;
46
+ interaction_type: "click" | "scroll" | "mousemove";
47
+ x?: number;
48
+ y?: number;
49
+ scroll_depth?: number;
50
+ viewport_width?: number;
51
+ viewport_height?: number;
52
+ page_width?: number;
53
+ page_height?: number;
54
+ selector?: string;
55
+ }
56
+
57
+ declare class Krypton {
58
+ private config;
59
+ private distinctId;
60
+ private sessionId;
61
+ private eventQueue;
62
+ private heatmapQueue;
63
+ private flushTimer;
64
+ private consentGiven;
65
+ private initialized;
66
+ private lastSnapshotUrl;
67
+ private serverConfig;
68
+ private configFetched;
69
+ constructor(config: KryptonConfig);
70
+ private init;
71
+ private fetchConfig;
72
+ private setupTracking;
73
+ /** Grant consent and begin tracking */
74
+ grantConsent(): void;
75
+ /** Revoke consent and stop tracking */
76
+ revokeConsent(): void;
77
+ /** Identify a user with a custom distinct ID */
78
+ identify(distinctId: string): void;
79
+ /** Track a pageview */
80
+ trackPageview(properties?: Record<string, unknown>): void;
81
+ /** Track a custom event */
82
+ track(eventName: string, properties?: Record<string, unknown>): void;
83
+ /** Flush all queued events to the server */
84
+ flush(): Promise<void>;
85
+ /** Shutdown the SDK */
86
+ shutdown(): void;
87
+ private flushEvents;
88
+ private flushHeatmapEvents;
89
+ private captureSnapshot;
90
+ private setupHeatmap;
91
+ private getBrowser;
92
+ }
93
+ declare function createKrypton(config: KryptonConfig): Krypton;
94
+
95
+ export { type EventPayload, type HeatmapEventPayload, Krypton, type KryptonConfig, createKrypton };
package/dist/index.js ADDED
@@ -0,0 +1,406 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+
20
+ // src/index.ts
21
+ var index_exports = {};
22
+ __export(index_exports, {
23
+ Krypton: () => Krypton,
24
+ createKrypton: () => createKrypton
25
+ });
26
+ module.exports = __toCommonJS(index_exports);
27
+ var import_rrweb_snapshot = require("rrweb-snapshot");
28
+
29
+ // src/utils.ts
30
+ function generateId() {
31
+ if (typeof crypto !== "undefined" && crypto.randomUUID) {
32
+ return crypto.randomUUID();
33
+ }
34
+ return "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g, (c) => {
35
+ const r = Math.random() * 16 | 0;
36
+ const v = c === "x" ? r : r & 3 | 8;
37
+ return v.toString(16);
38
+ });
39
+ }
40
+ function getSessionId() {
41
+ const key = "_krypton_sid";
42
+ if (typeof sessionStorage !== "undefined") {
43
+ let sid = sessionStorage.getItem(key);
44
+ if (!sid) {
45
+ sid = generateId();
46
+ sessionStorage.setItem(key, sid);
47
+ }
48
+ return sid;
49
+ }
50
+ return generateId();
51
+ }
52
+ function getDistinctId() {
53
+ const key = "_krypton_did";
54
+ if (typeof localStorage !== "undefined") {
55
+ let did = localStorage.getItem(key);
56
+ if (!did) {
57
+ did = generateId();
58
+ localStorage.setItem(key, did);
59
+ }
60
+ return did;
61
+ }
62
+ return generateId();
63
+ }
64
+ function getUTMParams() {
65
+ if (typeof window === "undefined") return {};
66
+ const params = new URLSearchParams(window.location.search);
67
+ const utm = {};
68
+ for (const key of ["utm_source", "utm_medium", "utm_campaign", "utm_term", "utm_content"]) {
69
+ const val = params.get(key);
70
+ if (val) utm[key] = val;
71
+ }
72
+ return utm;
73
+ }
74
+ function getDeviceType() {
75
+ if (typeof window === "undefined") return "unknown";
76
+ const w = window.innerWidth;
77
+ if (w < 768) return "mobile";
78
+ if (w < 1024) return "tablet";
79
+ return "desktop";
80
+ }
81
+ function getSelector(el) {
82
+ if (el.id) return `#${el.id}`;
83
+ const parts = [];
84
+ let current = el;
85
+ while (current && current !== document.body) {
86
+ let selector = current.tagName.toLowerCase();
87
+ if (current.id) {
88
+ parts.unshift(`#${current.id}`);
89
+ break;
90
+ }
91
+ if (current.className && typeof current.className === "string") {
92
+ const classes = current.className.trim().split(/\s+/).slice(0, 2).join(".");
93
+ if (classes) selector += `.${classes}`;
94
+ }
95
+ parts.unshift(selector);
96
+ current = current.parentElement;
97
+ }
98
+ return parts.join(" > ");
99
+ }
100
+
101
+ // src/index.ts
102
+ var Krypton = class {
103
+ constructor(config) {
104
+ this.eventQueue = [];
105
+ this.heatmapQueue = [];
106
+ this.flushTimer = null;
107
+ this.consentGiven = false;
108
+ this.initialized = false;
109
+ this.lastSnapshotUrl = null;
110
+ this.serverConfig = null;
111
+ this.configFetched = false;
112
+ this.config = {
113
+ autoPageview: true,
114
+ heatmap: false,
115
+ consentRequired: false,
116
+ flushInterval: 5e3,
117
+ batchSize: 20,
118
+ ...config
119
+ };
120
+ this.distinctId = getDistinctId();
121
+ this.sessionId = getSessionId();
122
+ if (!this.config.consentRequired) {
123
+ this.consentGiven = true;
124
+ }
125
+ this.init();
126
+ }
127
+ init() {
128
+ if (this.initialized) return;
129
+ this.initialized = true;
130
+ this.fetchConfig().then(() => this.setupTracking());
131
+ }
132
+ async fetchConfig() {
133
+ if (typeof window === "undefined") return;
134
+ const cacheKey = `_krypton_config_${this.config.apiKey}`;
135
+ const cached = sessionStorage.getItem(cacheKey);
136
+ if (cached) {
137
+ try {
138
+ const parsed = JSON.parse(cached);
139
+ if (parsed._ts && Date.now() - parsed._ts < 5 * 60 * 1e3) {
140
+ this.serverConfig = parsed;
141
+ this.configFetched = true;
142
+ return;
143
+ }
144
+ } catch {
145
+ }
146
+ }
147
+ try {
148
+ const res = await fetch(
149
+ `${this.config.endpoint}/api/v1/config?api_key=${encodeURIComponent(this.config.apiKey)}`
150
+ );
151
+ if (res.ok) {
152
+ const data = await res.json();
153
+ this.serverConfig = data;
154
+ this.configFetched = true;
155
+ sessionStorage.setItem(cacheKey, JSON.stringify({ ...data, _ts: Date.now() }));
156
+ }
157
+ } catch {
158
+ }
159
+ }
160
+ setupTracking() {
161
+ this.flushTimer = setInterval(() => this.flush(), this.config.flushInterval);
162
+ if (typeof window !== "undefined") {
163
+ window.addEventListener("beforeunload", () => this.flush());
164
+ if (this.config.autoPageview && this.consentGiven) {
165
+ this.trackPageview();
166
+ }
167
+ const heatmapEnabled = this.config.heatmap && (!this.configFetched || this.serverConfig?.heatmaps_enabled);
168
+ const originalPushState = history.pushState;
169
+ history.pushState = (...args) => {
170
+ originalPushState.apply(history, args);
171
+ if (this.config.autoPageview && this.consentGiven) {
172
+ setTimeout(() => this.trackPageview(), 0);
173
+ }
174
+ if (heatmapEnabled && this.consentGiven) {
175
+ setTimeout(() => this.captureSnapshot(), 0);
176
+ }
177
+ };
178
+ window.addEventListener("popstate", () => {
179
+ if (this.config.autoPageview && this.consentGiven) {
180
+ setTimeout(() => this.trackPageview(), 0);
181
+ }
182
+ if (heatmapEnabled && this.consentGiven) {
183
+ setTimeout(() => this.captureSnapshot(), 0);
184
+ }
185
+ });
186
+ if (heatmapEnabled && this.consentGiven) {
187
+ this.setupHeatmap();
188
+ }
189
+ }
190
+ }
191
+ /** Grant consent and begin tracking */
192
+ grantConsent() {
193
+ this.consentGiven = true;
194
+ if (this.config.autoPageview) {
195
+ this.trackPageview();
196
+ }
197
+ const heatmapEnabled = this.config.heatmap && (!this.configFetched || this.serverConfig?.heatmaps_enabled);
198
+ if (heatmapEnabled) {
199
+ this.setupHeatmap();
200
+ }
201
+ }
202
+ /** Revoke consent and stop tracking */
203
+ revokeConsent() {
204
+ this.consentGiven = false;
205
+ this.eventQueue = [];
206
+ this.heatmapQueue = [];
207
+ }
208
+ /** Identify a user with a custom distinct ID */
209
+ identify(distinctId) {
210
+ this.distinctId = distinctId;
211
+ if (typeof localStorage !== "undefined") {
212
+ localStorage.setItem("_krypton_did", distinctId);
213
+ }
214
+ }
215
+ /** Track a pageview */
216
+ trackPageview(properties) {
217
+ if (!this.consentGiven) return;
218
+ this.track("$pageview", {
219
+ ...properties
220
+ });
221
+ }
222
+ /** Track a custom event */
223
+ track(eventName, properties) {
224
+ if (!this.consentGiven) return;
225
+ const utm = getUTMParams();
226
+ const event = {
227
+ project_id: this.config.apiKey,
228
+ distinct_id: this.distinctId,
229
+ event_name: eventName,
230
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
231
+ properties,
232
+ page_url: typeof window !== "undefined" ? window.location.href : "",
233
+ page_title: typeof document !== "undefined" ? document.title : "",
234
+ referrer: typeof document !== "undefined" ? document.referrer : "",
235
+ utm_source: utm.utm_source || "",
236
+ utm_medium: utm.utm_medium || "",
237
+ utm_campaign: utm.utm_campaign || "",
238
+ utm_term: utm.utm_term || "",
239
+ utm_content: utm.utm_content || "",
240
+ device_type: getDeviceType(),
241
+ browser: this.getBrowser(),
242
+ screen_width: typeof window !== "undefined" ? window.screen.width : 0,
243
+ screen_height: typeof window !== "undefined" ? window.screen.height : 0,
244
+ session_id: this.sessionId
245
+ };
246
+ this.eventQueue.push(event);
247
+ if (this.eventQueue.length >= this.config.batchSize) {
248
+ this.flush();
249
+ }
250
+ }
251
+ /** Flush all queued events to the server */
252
+ async flush() {
253
+ await Promise.all([this.flushEvents(), this.flushHeatmapEvents()]);
254
+ }
255
+ /** Shutdown the SDK */
256
+ shutdown() {
257
+ if (this.flushTimer) {
258
+ clearInterval(this.flushTimer);
259
+ this.flushTimer = null;
260
+ }
261
+ this.flush();
262
+ }
263
+ // ---- Private methods ----
264
+ async flushEvents() {
265
+ if (this.eventQueue.length === 0) return;
266
+ const events = this.eventQueue.splice(0, this.eventQueue.length);
267
+ const payload = {
268
+ api_key: this.config.apiKey,
269
+ events
270
+ };
271
+ try {
272
+ await fetch(`${this.config.endpoint}/api/v1/ingest/batch`, {
273
+ method: "POST",
274
+ headers: { "Content-Type": "application/json" },
275
+ body: JSON.stringify(payload),
276
+ keepalive: true
277
+ });
278
+ } catch {
279
+ if (this.eventQueue.length < this.config.batchSize * 5) {
280
+ this.eventQueue.unshift(...events);
281
+ }
282
+ }
283
+ }
284
+ async flushHeatmapEvents() {
285
+ if (this.heatmapQueue.length === 0) return;
286
+ const events = this.heatmapQueue.splice(0, this.heatmapQueue.length);
287
+ const payload = {
288
+ api_key: this.config.apiKey,
289
+ events
290
+ };
291
+ try {
292
+ await fetch(`${this.config.endpoint}/api/v1/heatmap`, {
293
+ method: "POST",
294
+ headers: { "Content-Type": "application/json" },
295
+ body: JSON.stringify(payload),
296
+ keepalive: true
297
+ });
298
+ } catch {
299
+ if (this.heatmapQueue.length < this.config.batchSize * 5) {
300
+ this.heatmapQueue.unshift(...events);
301
+ }
302
+ }
303
+ }
304
+ captureSnapshot() {
305
+ if (typeof window === "undefined" || typeof document === "undefined") return;
306
+ const currentUrl = window.location.href;
307
+ if (this.lastSnapshotUrl === currentUrl) return;
308
+ this.lastSnapshotUrl = currentUrl;
309
+ setTimeout(() => {
310
+ try {
311
+ const node = (0, import_rrweb_snapshot.snapshot)(document, {
312
+ maskAllInputs: true,
313
+ blockSelector: "[data-krypton-block]",
314
+ inlineStylesheet: true
315
+ });
316
+ if (!node) return;
317
+ const snapshotJson = JSON.stringify(node);
318
+ const payload = {
319
+ api_key: this.config.apiKey,
320
+ page_url: currentUrl,
321
+ snapshot: snapshotJson,
322
+ viewport_width: window.innerWidth,
323
+ viewport_height: window.innerHeight,
324
+ page_width: document.documentElement.scrollWidth,
325
+ page_height: document.documentElement.scrollHeight,
326
+ timestamp: (/* @__PURE__ */ new Date()).toISOString()
327
+ };
328
+ fetch(`${this.config.endpoint}/api/v1/snapshot`, {
329
+ method: "POST",
330
+ headers: { "Content-Type": "application/json" },
331
+ body: JSON.stringify(payload),
332
+ keepalive: true
333
+ }).catch(() => {
334
+ });
335
+ } catch {
336
+ }
337
+ }, 1e3);
338
+ }
339
+ setupHeatmap() {
340
+ if (typeof window === "undefined") return;
341
+ this.captureSnapshot();
342
+ document.addEventListener("click", (e) => {
343
+ if (!this.consentGiven) return;
344
+ const target = e.target;
345
+ this.heatmapQueue.push({
346
+ project_id: this.config.apiKey,
347
+ distinct_id: this.distinctId,
348
+ session_id: this.sessionId,
349
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
350
+ page_url: window.location.href,
351
+ interaction_type: "click",
352
+ x: e.clientX + window.scrollX,
353
+ y: e.clientY + window.scrollY,
354
+ viewport_width: window.innerWidth,
355
+ viewport_height: window.innerHeight,
356
+ page_width: document.documentElement.scrollWidth,
357
+ page_height: document.documentElement.scrollHeight,
358
+ selector: getSelector(target)
359
+ });
360
+ });
361
+ let maxScrollDepth = 0;
362
+ let scrollTimeout = null;
363
+ window.addEventListener("scroll", () => {
364
+ if (!this.consentGiven) return;
365
+ const scrollTop = window.scrollY || document.documentElement.scrollTop;
366
+ const docHeight = document.documentElement.scrollHeight - window.innerHeight;
367
+ const depth = docHeight > 0 ? scrollTop / docHeight * 100 : 0;
368
+ if (depth > maxScrollDepth) {
369
+ maxScrollDepth = depth;
370
+ }
371
+ if (scrollTimeout) clearTimeout(scrollTimeout);
372
+ scrollTimeout = setTimeout(() => {
373
+ this.heatmapQueue.push({
374
+ project_id: this.config.apiKey,
375
+ distinct_id: this.distinctId,
376
+ session_id: this.sessionId,
377
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
378
+ page_url: window.location.href,
379
+ interaction_type: "scroll",
380
+ scroll_depth: maxScrollDepth,
381
+ viewport_width: window.innerWidth,
382
+ viewport_height: window.innerHeight,
383
+ page_width: document.documentElement.scrollWidth,
384
+ page_height: document.documentElement.scrollHeight
385
+ });
386
+ }, 500);
387
+ });
388
+ }
389
+ getBrowser() {
390
+ if (typeof navigator === "undefined") return "unknown";
391
+ const ua = navigator.userAgent;
392
+ if (ua.includes("Firefox")) return "Firefox";
393
+ if (ua.includes("Edg")) return "Edge";
394
+ if (ua.includes("Chrome")) return "Chrome";
395
+ if (ua.includes("Safari")) return "Safari";
396
+ return "Other";
397
+ }
398
+ };
399
+ function createKrypton(config) {
400
+ return new Krypton(config);
401
+ }
402
+ // Annotate the CommonJS export names for ESM import in node:
403
+ 0 && (module.exports = {
404
+ Krypton,
405
+ createKrypton
406
+ });
package/dist/index.mjs ADDED
@@ -0,0 +1,380 @@
1
+ // src/index.ts
2
+ import { snapshot as rrwebSnapshot } from "rrweb-snapshot";
3
+
4
+ // src/utils.ts
5
+ function generateId() {
6
+ if (typeof crypto !== "undefined" && crypto.randomUUID) {
7
+ return crypto.randomUUID();
8
+ }
9
+ return "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g, (c) => {
10
+ const r = Math.random() * 16 | 0;
11
+ const v = c === "x" ? r : r & 3 | 8;
12
+ return v.toString(16);
13
+ });
14
+ }
15
+ function getSessionId() {
16
+ const key = "_krypton_sid";
17
+ if (typeof sessionStorage !== "undefined") {
18
+ let sid = sessionStorage.getItem(key);
19
+ if (!sid) {
20
+ sid = generateId();
21
+ sessionStorage.setItem(key, sid);
22
+ }
23
+ return sid;
24
+ }
25
+ return generateId();
26
+ }
27
+ function getDistinctId() {
28
+ const key = "_krypton_did";
29
+ if (typeof localStorage !== "undefined") {
30
+ let did = localStorage.getItem(key);
31
+ if (!did) {
32
+ did = generateId();
33
+ localStorage.setItem(key, did);
34
+ }
35
+ return did;
36
+ }
37
+ return generateId();
38
+ }
39
+ function getUTMParams() {
40
+ if (typeof window === "undefined") return {};
41
+ const params = new URLSearchParams(window.location.search);
42
+ const utm = {};
43
+ for (const key of ["utm_source", "utm_medium", "utm_campaign", "utm_term", "utm_content"]) {
44
+ const val = params.get(key);
45
+ if (val) utm[key] = val;
46
+ }
47
+ return utm;
48
+ }
49
+ function getDeviceType() {
50
+ if (typeof window === "undefined") return "unknown";
51
+ const w = window.innerWidth;
52
+ if (w < 768) return "mobile";
53
+ if (w < 1024) return "tablet";
54
+ return "desktop";
55
+ }
56
+ function getSelector(el) {
57
+ if (el.id) return `#${el.id}`;
58
+ const parts = [];
59
+ let current = el;
60
+ while (current && current !== document.body) {
61
+ let selector = current.tagName.toLowerCase();
62
+ if (current.id) {
63
+ parts.unshift(`#${current.id}`);
64
+ break;
65
+ }
66
+ if (current.className && typeof current.className === "string") {
67
+ const classes = current.className.trim().split(/\s+/).slice(0, 2).join(".");
68
+ if (classes) selector += `.${classes}`;
69
+ }
70
+ parts.unshift(selector);
71
+ current = current.parentElement;
72
+ }
73
+ return parts.join(" > ");
74
+ }
75
+
76
+ // src/index.ts
77
+ var Krypton = class {
78
+ constructor(config) {
79
+ this.eventQueue = [];
80
+ this.heatmapQueue = [];
81
+ this.flushTimer = null;
82
+ this.consentGiven = false;
83
+ this.initialized = false;
84
+ this.lastSnapshotUrl = null;
85
+ this.serverConfig = null;
86
+ this.configFetched = false;
87
+ this.config = {
88
+ autoPageview: true,
89
+ heatmap: false,
90
+ consentRequired: false,
91
+ flushInterval: 5e3,
92
+ batchSize: 20,
93
+ ...config
94
+ };
95
+ this.distinctId = getDistinctId();
96
+ this.sessionId = getSessionId();
97
+ if (!this.config.consentRequired) {
98
+ this.consentGiven = true;
99
+ }
100
+ this.init();
101
+ }
102
+ init() {
103
+ if (this.initialized) return;
104
+ this.initialized = true;
105
+ this.fetchConfig().then(() => this.setupTracking());
106
+ }
107
+ async fetchConfig() {
108
+ if (typeof window === "undefined") return;
109
+ const cacheKey = `_krypton_config_${this.config.apiKey}`;
110
+ const cached = sessionStorage.getItem(cacheKey);
111
+ if (cached) {
112
+ try {
113
+ const parsed = JSON.parse(cached);
114
+ if (parsed._ts && Date.now() - parsed._ts < 5 * 60 * 1e3) {
115
+ this.serverConfig = parsed;
116
+ this.configFetched = true;
117
+ return;
118
+ }
119
+ } catch {
120
+ }
121
+ }
122
+ try {
123
+ const res = await fetch(
124
+ `${this.config.endpoint}/api/v1/config?api_key=${encodeURIComponent(this.config.apiKey)}`
125
+ );
126
+ if (res.ok) {
127
+ const data = await res.json();
128
+ this.serverConfig = data;
129
+ this.configFetched = true;
130
+ sessionStorage.setItem(cacheKey, JSON.stringify({ ...data, _ts: Date.now() }));
131
+ }
132
+ } catch {
133
+ }
134
+ }
135
+ setupTracking() {
136
+ this.flushTimer = setInterval(() => this.flush(), this.config.flushInterval);
137
+ if (typeof window !== "undefined") {
138
+ window.addEventListener("beforeunload", () => this.flush());
139
+ if (this.config.autoPageview && this.consentGiven) {
140
+ this.trackPageview();
141
+ }
142
+ const heatmapEnabled = this.config.heatmap && (!this.configFetched || this.serverConfig?.heatmaps_enabled);
143
+ const originalPushState = history.pushState;
144
+ history.pushState = (...args) => {
145
+ originalPushState.apply(history, args);
146
+ if (this.config.autoPageview && this.consentGiven) {
147
+ setTimeout(() => this.trackPageview(), 0);
148
+ }
149
+ if (heatmapEnabled && this.consentGiven) {
150
+ setTimeout(() => this.captureSnapshot(), 0);
151
+ }
152
+ };
153
+ window.addEventListener("popstate", () => {
154
+ if (this.config.autoPageview && this.consentGiven) {
155
+ setTimeout(() => this.trackPageview(), 0);
156
+ }
157
+ if (heatmapEnabled && this.consentGiven) {
158
+ setTimeout(() => this.captureSnapshot(), 0);
159
+ }
160
+ });
161
+ if (heatmapEnabled && this.consentGiven) {
162
+ this.setupHeatmap();
163
+ }
164
+ }
165
+ }
166
+ /** Grant consent and begin tracking */
167
+ grantConsent() {
168
+ this.consentGiven = true;
169
+ if (this.config.autoPageview) {
170
+ this.trackPageview();
171
+ }
172
+ const heatmapEnabled = this.config.heatmap && (!this.configFetched || this.serverConfig?.heatmaps_enabled);
173
+ if (heatmapEnabled) {
174
+ this.setupHeatmap();
175
+ }
176
+ }
177
+ /** Revoke consent and stop tracking */
178
+ revokeConsent() {
179
+ this.consentGiven = false;
180
+ this.eventQueue = [];
181
+ this.heatmapQueue = [];
182
+ }
183
+ /** Identify a user with a custom distinct ID */
184
+ identify(distinctId) {
185
+ this.distinctId = distinctId;
186
+ if (typeof localStorage !== "undefined") {
187
+ localStorage.setItem("_krypton_did", distinctId);
188
+ }
189
+ }
190
+ /** Track a pageview */
191
+ trackPageview(properties) {
192
+ if (!this.consentGiven) return;
193
+ this.track("$pageview", {
194
+ ...properties
195
+ });
196
+ }
197
+ /** Track a custom event */
198
+ track(eventName, properties) {
199
+ if (!this.consentGiven) return;
200
+ const utm = getUTMParams();
201
+ const event = {
202
+ project_id: this.config.apiKey,
203
+ distinct_id: this.distinctId,
204
+ event_name: eventName,
205
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
206
+ properties,
207
+ page_url: typeof window !== "undefined" ? window.location.href : "",
208
+ page_title: typeof document !== "undefined" ? document.title : "",
209
+ referrer: typeof document !== "undefined" ? document.referrer : "",
210
+ utm_source: utm.utm_source || "",
211
+ utm_medium: utm.utm_medium || "",
212
+ utm_campaign: utm.utm_campaign || "",
213
+ utm_term: utm.utm_term || "",
214
+ utm_content: utm.utm_content || "",
215
+ device_type: getDeviceType(),
216
+ browser: this.getBrowser(),
217
+ screen_width: typeof window !== "undefined" ? window.screen.width : 0,
218
+ screen_height: typeof window !== "undefined" ? window.screen.height : 0,
219
+ session_id: this.sessionId
220
+ };
221
+ this.eventQueue.push(event);
222
+ if (this.eventQueue.length >= this.config.batchSize) {
223
+ this.flush();
224
+ }
225
+ }
226
+ /** Flush all queued events to the server */
227
+ async flush() {
228
+ await Promise.all([this.flushEvents(), this.flushHeatmapEvents()]);
229
+ }
230
+ /** Shutdown the SDK */
231
+ shutdown() {
232
+ if (this.flushTimer) {
233
+ clearInterval(this.flushTimer);
234
+ this.flushTimer = null;
235
+ }
236
+ this.flush();
237
+ }
238
+ // ---- Private methods ----
239
+ async flushEvents() {
240
+ if (this.eventQueue.length === 0) return;
241
+ const events = this.eventQueue.splice(0, this.eventQueue.length);
242
+ const payload = {
243
+ api_key: this.config.apiKey,
244
+ events
245
+ };
246
+ try {
247
+ await fetch(`${this.config.endpoint}/api/v1/ingest/batch`, {
248
+ method: "POST",
249
+ headers: { "Content-Type": "application/json" },
250
+ body: JSON.stringify(payload),
251
+ keepalive: true
252
+ });
253
+ } catch {
254
+ if (this.eventQueue.length < this.config.batchSize * 5) {
255
+ this.eventQueue.unshift(...events);
256
+ }
257
+ }
258
+ }
259
+ async flushHeatmapEvents() {
260
+ if (this.heatmapQueue.length === 0) return;
261
+ const events = this.heatmapQueue.splice(0, this.heatmapQueue.length);
262
+ const payload = {
263
+ api_key: this.config.apiKey,
264
+ events
265
+ };
266
+ try {
267
+ await fetch(`${this.config.endpoint}/api/v1/heatmap`, {
268
+ method: "POST",
269
+ headers: { "Content-Type": "application/json" },
270
+ body: JSON.stringify(payload),
271
+ keepalive: true
272
+ });
273
+ } catch {
274
+ if (this.heatmapQueue.length < this.config.batchSize * 5) {
275
+ this.heatmapQueue.unshift(...events);
276
+ }
277
+ }
278
+ }
279
+ captureSnapshot() {
280
+ if (typeof window === "undefined" || typeof document === "undefined") return;
281
+ const currentUrl = window.location.href;
282
+ if (this.lastSnapshotUrl === currentUrl) return;
283
+ this.lastSnapshotUrl = currentUrl;
284
+ setTimeout(() => {
285
+ try {
286
+ const node = rrwebSnapshot(document, {
287
+ maskAllInputs: true,
288
+ blockSelector: "[data-krypton-block]",
289
+ inlineStylesheet: true
290
+ });
291
+ if (!node) return;
292
+ const snapshotJson = JSON.stringify(node);
293
+ const payload = {
294
+ api_key: this.config.apiKey,
295
+ page_url: currentUrl,
296
+ snapshot: snapshotJson,
297
+ viewport_width: window.innerWidth,
298
+ viewport_height: window.innerHeight,
299
+ page_width: document.documentElement.scrollWidth,
300
+ page_height: document.documentElement.scrollHeight,
301
+ timestamp: (/* @__PURE__ */ new Date()).toISOString()
302
+ };
303
+ fetch(`${this.config.endpoint}/api/v1/snapshot`, {
304
+ method: "POST",
305
+ headers: { "Content-Type": "application/json" },
306
+ body: JSON.stringify(payload),
307
+ keepalive: true
308
+ }).catch(() => {
309
+ });
310
+ } catch {
311
+ }
312
+ }, 1e3);
313
+ }
314
+ setupHeatmap() {
315
+ if (typeof window === "undefined") return;
316
+ this.captureSnapshot();
317
+ document.addEventListener("click", (e) => {
318
+ if (!this.consentGiven) return;
319
+ const target = e.target;
320
+ this.heatmapQueue.push({
321
+ project_id: this.config.apiKey,
322
+ distinct_id: this.distinctId,
323
+ session_id: this.sessionId,
324
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
325
+ page_url: window.location.href,
326
+ interaction_type: "click",
327
+ x: e.clientX + window.scrollX,
328
+ y: e.clientY + window.scrollY,
329
+ viewport_width: window.innerWidth,
330
+ viewport_height: window.innerHeight,
331
+ page_width: document.documentElement.scrollWidth,
332
+ page_height: document.documentElement.scrollHeight,
333
+ selector: getSelector(target)
334
+ });
335
+ });
336
+ let maxScrollDepth = 0;
337
+ let scrollTimeout = null;
338
+ window.addEventListener("scroll", () => {
339
+ if (!this.consentGiven) return;
340
+ const scrollTop = window.scrollY || document.documentElement.scrollTop;
341
+ const docHeight = document.documentElement.scrollHeight - window.innerHeight;
342
+ const depth = docHeight > 0 ? scrollTop / docHeight * 100 : 0;
343
+ if (depth > maxScrollDepth) {
344
+ maxScrollDepth = depth;
345
+ }
346
+ if (scrollTimeout) clearTimeout(scrollTimeout);
347
+ scrollTimeout = setTimeout(() => {
348
+ this.heatmapQueue.push({
349
+ project_id: this.config.apiKey,
350
+ distinct_id: this.distinctId,
351
+ session_id: this.sessionId,
352
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
353
+ page_url: window.location.href,
354
+ interaction_type: "scroll",
355
+ scroll_depth: maxScrollDepth,
356
+ viewport_width: window.innerWidth,
357
+ viewport_height: window.innerHeight,
358
+ page_width: document.documentElement.scrollWidth,
359
+ page_height: document.documentElement.scrollHeight
360
+ });
361
+ }, 500);
362
+ });
363
+ }
364
+ getBrowser() {
365
+ if (typeof navigator === "undefined") return "unknown";
366
+ const ua = navigator.userAgent;
367
+ if (ua.includes("Firefox")) return "Firefox";
368
+ if (ua.includes("Edg")) return "Edge";
369
+ if (ua.includes("Chrome")) return "Chrome";
370
+ if (ua.includes("Safari")) return "Safari";
371
+ return "Other";
372
+ }
373
+ };
374
+ function createKrypton(config) {
375
+ return new Krypton(config);
376
+ }
377
+ export {
378
+ Krypton,
379
+ createKrypton
380
+ };
package/package.json ADDED
@@ -0,0 +1,34 @@
1
+ {
2
+ "name": "@kryptonhq/analytics",
3
+ "version": "0.1.0",
4
+ "description": "Krypton Analytics JavaScript SDK — privacy-first analytics tracking",
5
+ "main": "dist/index.js",
6
+ "module": "dist/index.mjs",
7
+ "types": "dist/index.d.ts",
8
+ "unpkg": "dist/browser.global.js",
9
+ "jsdelivr": "dist/browser.global.js",
10
+ "exports": {
11
+ ".": {
12
+ "types": "./dist/index.d.ts",
13
+ "import": "./dist/index.mjs",
14
+ "require": "./dist/index.js"
15
+ }
16
+ },
17
+ "files": [
18
+ "dist"
19
+ ],
20
+ "scripts": {
21
+ "build": "npm run build:lib && npm run build:cdn",
22
+ "build:lib": "tsup src/index.ts --format cjs,esm --dts --out-dir dist --clean",
23
+ "build:cdn": "tsup src/browser.ts --format iife --global-name KryptonAnalytics --minify --out-dir dist",
24
+ "dev": "tsup src/index.ts --format cjs,esm --dts --out-dir dist --watch"
25
+ },
26
+ "devDependencies": {
27
+ "tsup": "^8.0.0",
28
+ "typescript": "^5.4.0"
29
+ },
30
+ "license": "MIT",
31
+ "dependencies": {
32
+ "rrweb-snapshot": "^2.0.0-alpha.4"
33
+ }
34
+ }