@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 +103 -0
- package/dist/browser.global.js +1 -0
- package/dist/index.d.mts +95 -0
- package/dist/index.d.ts +95 -0
- package/dist/index.js +406 -0
- package/dist/index.mjs +380 -0
- package/package.json +34 -0
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);})();
|
package/dist/index.d.mts
ADDED
|
@@ -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.d.ts
ADDED
|
@@ -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
|
+
}
|