@mixdive/browser 0.1.0 → 0.1.1

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 CHANGED
@@ -1,18 +1,175 @@
1
1
  # @mixdive/browser
2
2
 
3
- The [Mixdive](https://mixdive.com) browser SDK — one script tag for any
4
- website, or an npm module for bundled apps. Visitors, sessions, page views
5
- and session durations are sent automatically; your own events take one call.
3
+ The browser SDK for **[Mixdive](https://mixdive.com/?utm_source=npm&utm_medium=referral&utm_campaign=browser_readme&utm_content=intro)**,
4
+ self-hosted product analytics. One script tag on any website, or an npm import
5
+ in a bundled app. Page views, visitors, sessions and session durations count
6
+ themselves. Your own events take one call.
6
7
 
7
- ```html
8
- <script defer src="https://analytics.example.com/js/mixdive.js" data-key="mx_…"></script>
8
+ [![The Mixdive console: the events list, with every event a product sends, its counts and its unique users](https://mixdive.com/assets/video/hero-console-poster.webp)](https://mixdive.com/?utm_source=npm&utm_medium=referral&utm_campaign=browser_readme&utm_content=hero)
9
+
10
+ The console your events land in, running on your own server.
11
+ [Open the live demo](https://demo1.mixdive.com/): a full Mixdive filled with a
12
+ fictional shop's data, no sign-up.
13
+
14
+ [Website](https://mixdive.com/?utm_source=npm&utm_medium=referral&utm_campaign=browser_readme&utm_content=links)
15
+ ·
16
+ [Documentation](https://docs.mixdive.com/?utm_source=npm&utm_medium=referral&utm_campaign=browser_readme&utm_content=links)
17
+ ·
18
+ [Live demo](https://demo1.mixdive.com/)
19
+ ·
20
+ [Web tag guide](https://docs.mixdive.com/integrations/web-tag/?utm_source=npm&utm_medium=referral&utm_campaign=browser_readme&utm_content=links)
21
+ ·
22
+ [GitHub](https://github.com/mixdive/mixdive-js)
23
+
24
+ ## Why send your events here
25
+
26
+ **Your visitors' behaviour stays with you.** Mixdive runs on a server you own
27
+ and writes to a database you hold the credentials for. Nobody else keeps a copy
28
+ of who your users are or what they did. Teams in regulated industries, EU public
29
+ sector work, and anyone whose data simply cannot go to a third party tend to
30
+ arrive for exactly this reason.
31
+
32
+ **Nothing to define first.** Send `checkout_completed` and its report screen
33
+ exists. Properties register themselves the first time they show up, so a new
34
+ field becomes a breakdown you can look at instead of a ticket for somebody else.
35
+
36
+ **You don't need a data team.** The screens answer what a product person
37
+ actually asks: which features get used, by whom, how often, and what people did
38
+ before they stopped.
39
+
40
+ ## Install
41
+
42
+ ```sh
43
+ npm i @mixdive/browser
9
44
  ```
10
45
 
11
46
  ```ts
12
47
  import { mixdive } from '@mixdive/browser'
48
+
13
49
  mixdive.init({ key: 'mx_…', server: 'https://analytics.example.com' })
14
- mixdive.track('checkout_completed', { plan: 'team' })
50
+
51
+ mixdive.track('checkout_completed', { plan: 'team', seats: 4 })
52
+ mixdive.identify(user.id)
53
+ ```
54
+
55
+ The key comes from a **Web** app under **Settings → Apps** in your Mixdive
56
+ console. It is visible in page source, which is fine: it can write events and
57
+ read nothing.
58
+
59
+ React app? [`@mixdive/react`](https://www.npmjs.com/package/@mixdive/react) adds
60
+ a provider, a hook and router-driven page views on top of this package.
61
+
62
+ ## Or one script tag, no build step
63
+
64
+ ```html
65
+ <script defer src="https://analytics.example.com/js/mixdive.js" data-key="mx_…"></script>
66
+ ```
67
+
68
+ That is the whole integration, and your Mixdive server serves the file. To call
69
+ `mixdive(…)` from page code before the script has loaded, add the one-line stub:
70
+
71
+ ```html
72
+ <script>window.mixdive=window.mixdive||function(){(mixdive.q=mixdive.q||[]).push(arguments)};</script>
73
+ <script defer src="https://analytics.example.com/js/mixdive.js" data-key="mx_…"></script>
74
+ <script>
75
+ mixdive('track', 'checkout_completed', { plan: 'team' });
76
+ mixdive('identify', currentUser.id);
77
+ </script>
78
+ ```
79
+
80
+ The same file ships in this package as `dist/mixdive.js` (7 KB), so you can copy
81
+ it from `node_modules` into your own static assets or load it from a CDN mirror:
82
+ `https://cdn.jsdelivr.net/npm/@mixdive/browser@0.1/dist/mixdive.js`. Attributes:
83
+ `data-key`, `data-server`, `data-auto-page-views`, `data-app-version`,
84
+ `data-debug`. Full reference:
85
+ [Web tag](https://docs.mixdive.com/integrations/web-tag/?utm_source=npm&utm_medium=referral&utm_campaign=browser_readme&utm_content=tag).
86
+
87
+ ## What arrives without you writing anything
88
+
89
+ - **Page views** on load and on every history change, single-page apps
90
+ included, with the path, the title and the previous path as referrer. Query
91
+ strings are never sent.
92
+ - **Visitors and sessions**: a device id minted on the first visit, never
93
+ derived from the browser, and a session that renews after 30 minutes idle and
94
+ rotates when a landing URL carries a different `utm_source`.
95
+ - **Session duration**, sent when the page is hidden or unloaded.
96
+ - **Acquisition and context** on every item: landing referrer, `utm_source`,
97
+ `utm_medium`, `utm_campaign`, screen size, browser language.
98
+
99
+ Never sent: full URLs, query strings, the visitor's IP, or browser timestamps.
100
+ In an automated browser nothing is sent at all, so test runners stay out of your
101
+ numbers. [Visitors and
102
+ sessions](https://docs.mixdive.com/concepts/visitors-sessions/?utm_source=npm&utm_medium=referral&utm_campaign=browser_readme&utm_content=concepts)
103
+ has the rules.
104
+
105
+ ## API
106
+
107
+ ```ts
108
+ mixdive.init({ key, server, autoPageViews?, appVersion?, debug? })
109
+ mixdive.track(eventKey, properties?, { count?, sum?, duration?, id? }?)
110
+ mixdive.pageView(url?)
111
+ mixdive.identify(userId)
112
+ mixdive.setUser(userId, { name?, username?, email?, organization?, phone?, picture?, gender?, birthYear?, custom? }?)
113
+ mixdive.login(method?)
114
+ mixdive.signUp(method?)
115
+ mixdive.reset()
116
+ mixdive.version
15
117
  ```
16
118
 
17
- Full guide, API and privacy notes: [github.com/mixdive/mixdive-js](https://github.com/mixdive/mixdive-js#readme).
18
- The tag file ships in this package as `dist/mixdive.js`. Apache-2.0.
119
+ Every call returns immediately and never throws into your page. With no key, no
120
+ server, or an automated browser, every call is a silent no-op. The first `init`
121
+ wins, and calls made before it are buffered and delivered once it runs.
122
+
123
+ `track` takes three measures beside the properties: `count` for one occurrence
124
+ that stands for several, `sum` for money or points, `duration` in seconds. The
125
+ report totals and averages them for you.
126
+
127
+ `identify(userId)` must be **your** user id, the same one your backend sends.
128
+ The device's earlier anonymous history is then attributed to that user, so
129
+ pre-signup browsing becomes part of their story. `reset()` forgets device,
130
+ session and user, which is what you want on sign-out and when consent is
131
+ withdrawn.
132
+
133
+ ## Consent
134
+
135
+ The device id is a persistent identifier, so treat the tag like any other
136
+ analytics tag: load it once consent is granted, and call `reset()` when it is
137
+ withdrawn. No cookies are set. Three localStorage keys are used, `mx_did`,
138
+ `mx_ses` and `mx_uid`, which you can name in your storage declarations.
139
+
140
+ ## Running Mixdive
141
+
142
+ ```sh
143
+ curl -O https://docs.mixdive.com/compose.yaml
144
+ docker compose up -d
145
+ ```
146
+
147
+ Then open `http://localhost:8080`, where a one-time wizard asks for a project
148
+ name, the address your apps will use, and an owner account.
149
+ [Quick start](https://docs.mixdive.com/start/quick-start/?utm_source=npm&utm_medium=referral&utm_campaign=browser_readme&utm_content=quickstart)
150
+ ·
151
+ [Install](https://docs.mixdive.com/operate/install/?utm_source=npm&utm_medium=referral&utm_campaign=browser_readme&utm_content=install)
152
+
153
+ Mixdive is in beta with a small group of teams whose requirements shape what
154
+ gets built. Running a real product whose data cannot go to a third party? A few
155
+ lines to beta@mixdive.com is enough, and a person answers.
156
+
157
+ ## More SDKs
158
+
159
+ A backend belongs in the picture too: profile enrichment and server-side events
160
+ go through [`mixdive-go`](https://github.com/mixdive/mixdive-go). There is also
161
+ a [Google Tag
162
+ Manager](https://docs.mixdive.com/integrations/google-tag-manager/?utm_source=npm&utm_medium=referral&utm_campaign=browser_readme&utm_content=integrations)
163
+ template and an [HTTP
164
+ API](https://docs.mixdive.com/integrations/http-api/?utm_source=npm&utm_medium=referral&utm_campaign=browser_readme&utm_content=integrations)
165
+ for anything that can POST JSON.
166
+
167
+ More SDKs are on the way. Missing one for your platform? Tell us at
168
+ [hello@mixdive.com](mailto:hello@mixdive.com). Requests set the order.
169
+
170
+ ## License
171
+
172
+ This SDK is Apache-2.0. The Mixdive server itself is commercial software you run
173
+ on your own machines, and [that
174
+ distinction](https://docs.mixdive.com/start/open-source/?utm_source=npm&utm_medium=referral&utm_campaign=browser_readme&utm_content=license)
175
+ is worth two minutes if it matters to you.
package/dist/index.cjs CHANGED
@@ -145,7 +145,7 @@ function createTransport(debug) {
145
145
  }
146
146
 
147
147
  // src/client.ts
148
- var VERSION = "0.1.0" ;
148
+ var VERSION = "0.1.1" ;
149
149
  var STORAGE_KEYS = { device: "mx_did", session: "mx_ses", user: "mx_uid" };
150
150
  var MAX_BUFFER = 200;
151
151
  var MAX_COUNT = 1e6;
package/dist/index.js CHANGED
@@ -141,7 +141,7 @@ function createTransport(debug) {
141
141
  }
142
142
 
143
143
  // src/client.ts
144
- var VERSION = "0.1.0" ;
144
+ var VERSION = "0.1.1" ;
145
145
  var STORAGE_KEYS = { device: "mx_did", session: "mx_ses", user: "mx_uid" };
146
146
  var MAX_BUFFER = 200;
147
147
  var MAX_COUNT = 1e6;
package/dist/mixdive.js CHANGED
@@ -1,2 +1,2 @@
1
- /*! Mixdive web tag v0.1.0 · https://github.com/mixdive/mixdive-js · Apache-2.0 */
2
- "use strict";(()=>{var Z=Object.defineProperty;var R=Object.getOwnPropertySymbols;var K=Object.prototype.hasOwnProperty,ee=Object.prototype.propertyIsEnumerable;var T=(i,t,n)=>t in i?Z(i,t,{enumerable:!0,configurable:!0,writable:!0,value:n}):i[t]=n,E=(i,t)=>{for(var n in t||(t={}))K.call(t,n)&&T(i,n,t[n]);if(R)for(var n of R(t))ee.call(t,n)&&T(i,n,t[n]);return i};function I(){let i="";try{let t=new Uint8Array(16);crypto.getRandomValues(t);for(let n=0;n<16;n++)i+=(t[n]+256).toString(16).slice(1);return i}catch(t){}for(let t=0;t<32;t++)i+=Math.floor(Math.random()*16).toString(16);return i}function L(i,t){let n={ref:"",us:"",um:"",uc:""};try{if(t){let a=/^[a-z]+:\/\/([^/]+)/i.exec(t);a&&a[1].toLowerCase()!==i.host.toLowerCase()&&(n.ref=t.split("?")[0].slice(0,256))}let u=i.search.replace(/^\?/,"").split("&");for(let a of u){let f=a.indexOf("="),g=f<0?a:a.slice(0,f),m=f<0?"":a.slice(f+1),s="";try{s=decodeURIComponent(m.replace(/\+/g," ")).slice(0,128)}catch(d){s=m.slice(0,128)}g==="utm_source"&&(n.us=s),g==="utm_medium"&&(n.um=s),g==="utm_campaign"&&(n.uc=s)}}catch(u){}return n}function D(i,t,n,u){let a=!!t.us&&!!i&&i.us!==t.us,f=i;return(!f||n-f.last>18e5||a)&&(f={id:u(),start:n,last:n,ref:t.ref,us:t.us,um:t.um,uc:t.uc}),f.last=n,f}function N(i){if(!i)return null;try{let t=JSON.parse(i);if(t&&typeof t.id=="string"&&typeof t.start=="number"&&typeof t.last=="number")return{id:t.id,start:t.start,last:t.last,ref:t.ref||"",us:t.us||"",um:t.um||"",uc:t.uc||""}}catch(t){}return null}function U(i,t){let n={};return t.language&&(n.language=String(t.language).slice(0,16)),t.width&&t.height&&(n.resolution=`${t.width}x${t.height}`),t.density&&(n.density=t.density),i.ref&&(n.referrer=i.ref),i.us&&(n.utm_source=i.us),i.um&&(n.utm_medium=i.um),i.uc&&(n.utm_campaign=i.uc),n}function C(){let i=new Map;return{get(t){var n;try{let u=window.localStorage.getItem(t);if(u!==null)return u}catch(u){}return(n=i.get(t))!=null?n:null},set(t,n){i.set(t,n);try{window.localStorage.setItem(t,n)}catch(u){}},remove(t){i.delete(t);try{window.localStorage.removeItem(t)}catch(n){}}}}function H(i){return{send(t,n,u){if(!u){let a=typeof navigator!="undefined"?navigator:void 0;if(a&&typeof a.sendBeacon=="function")try{if(a.sendBeacon(t,new Blob([n],{type:"text/plain"})))return}catch(f){}}if(typeof fetch!="function"){i("no fetch available; dropped",t);return}try{let a={method:"POST",body:n,keepalive:!0};u&&(a.headers=E({"Content-Type":"application/json"},u));let f=fetch(t,a);f&&typeof f.then=="function"&&f.then(g=>{g.ok||i(`${g.status} from ${t}`)},g=>i("send failed",g))}catch(a){i("send failed",a)}}}}var j="0.1.0",h={device:"mx_did",session:"mx_ses",user:"mx_uid"},ne=200,ie=1e6,re=["name","username","email","organization","phone","picture","gender"];function X(){let i=C(),t=null,n=!1,u=[],a={ref:"",us:"",um:"",uc:""},f=null,g=null,m=null,s=null,d=(e,...r)=>{if(t&&t.debug)try{console.log("[mixdive]",e,...r)}catch(o){}},v=H(d),V=()=>typeof window!="undefined"&&typeof document!="undefined";function b(){if(g)return g;let e=i.get(h.device);return e||(e=I(),i.set(h.device,e)),g=e,e}function k(){return f!=null?f:N(i.get(h.session))}function A(e){f=e,i.set(h.session,JSON.stringify(e))}function B(e){let r=D(k(),a,e,I);return A(r),r}function P(e){let r={};try{navigator.language&&(r.language=navigator.language)}catch(o){}try{r.width=screen.width,r.height=screen.height}catch(o){}try{window.devicePixelRatio&&(r.density=window.devicePixelRatio)}catch(o){}return U(e,r)}function _(e,r,o=!1){let c=t,l=JSON.stringify(r);c.appVersion&&!o?v.send(c.server+e,l,{"X-Api-Key":c.key,"X-App-Version":c.appVersion}):v.send(c.server+e+"?api_key="+encodeURIComponent(c.key),l),d("sent",e,r)}function y(e){if(!n){if(!t){u.length>=ne&&u.shift(),u.push(e);return}try{e()}catch(r){d("call failed",r)}}}function w(e,r,o){if(typeof e!="string"||!e.trim()||e.indexOf("gtm.")===0){d("ignored event key",e);return}let c=B(Date.now()),l={event_key:e,device_id:b(),session_id:c.id,context:P(c)};if(m&&(l.user_id=m),r&&typeof r=="object"&&(l.properties=r),o&&typeof o=="object"){typeof o.id=="string"&&o.id&&(l.id=o.id);let{count:p,sum:x,duration:S}=o;typeof p=="number"&&Number.isInteger(p)&&p>1&&p<=ie?l.count=p:p!==void 0&&p!==1&&d("ignored count (whole number 2\u20261000000)",p),typeof x=="number"&&Number.isFinite(x)&&x!==0&&(l.sum=x),typeof S=="number"&&Number.isFinite(S)&&S>0&&(l.duration=S)}_("/ingest/event",l)}function M(e){let r=typeof e=="string"&&e?e:"";if(!r)try{r=location.pathname||"/"}catch(l){r="/"}if(r===s)return;let o={url:r};try{document.title&&(o.title=String(document.title).slice(0,256))}catch(l){}let c=s;if(!c)try{document.referrer&&(c=document.referrer.split("?")[0].slice(0,256))}catch(l){}c&&(o.referrer=c),s=r,w("page_view",o)}function G(e,r){let o={user_id:e,device_id:b()};if(r&&typeof r=="object"){for(let l of re){let p=r[l];typeof p=="string"&&p&&(o[l]=p)}let c=r.birthYear;typeof c=="number"&&Number.isInteger(c)&&c>0&&(o.birth_year=c),r.custom&&typeof r.custom=="object"&&(o.custom=r.custom)}return o}function O(e,r){if(typeof e!="string"||!e.trim()){d("ignored user id",e);return}m=e,i.set(h.user,e),_("/ingest/user",G(e,r))}function q(){try{let e=k();if(!e)return;let r=Date.now(),o=r-e.last>18e5?e.last:r,c=Math.max(0,Math.round((o-e.start)/1e3));_("/ingest/event",{event_key:"session_end",id:e.id+":end",device_id:b(),session_id:e.id,context:P(e),properties:{duration:c}},!0)}catch(e){d("session_end failed",e)}}function J(){let e=k();e&&(e.last=Date.now(),A(e))}function z(){document.addEventListener("visibilitychange",()=>{document.visibilityState==="hidden"?q():J()}),window.addEventListener("pagehide",q)}function Y(){let e=window.history,r=()=>{setTimeout(()=>y(()=>M()),0)};for(let o of["pushState","replaceState"]){let c=e[o];typeof c=="function"&&(e[o]=function(...l){let p=c.apply(this,l);return r(),p})}window.addEventListener("popstate",r)}function W(e){if(!V())return;if(t){d("init called again; the first call wins");return}let r=e&&typeof e.key=="string"?e.key.trim():"",o=e&&typeof e.server=="string"?e.server.trim().replace(/\/+$/,""):"";if(!r||!/^https?:\/\/[^/]+/i.test(o)){n=!0,u=[];try{console.warn("[mixdive] off: init needs an API key and the server origin (https://analytics.example.com)")}catch(l){}return}if(typeof navigator!="undefined"&&navigator.webdriver){n=!0,u=[];return}t={key:r,server:o,autoPageViews:e.autoPageViews!==!1,appVersion:typeof e.appVersion=="string"?e.appVersion.slice(0,64):"",debug:!!e.debug},a=L(location,document.referrer),m=i.get(h.user),z();let c=u;u=[];for(let l of c)y(l);t.autoPageViews&&(Y(),y(()=>M())),d("started",{server:o,autoPageViews:t.autoPageViews,version:j})}function Q(){i.remove(h.device),i.remove(h.session),i.remove(h.user),f=null,g=null,m=null}return{init:W,track:(e,r,o)=>y(()=>w(e,r,o)),pageView:e=>y(()=>M(e)),identify:e=>y(()=>O(e)),setUser:(e,r)=>y(()=>O(e,r)),login:e=>y(()=>w("login",e?{method:e}:void 0)),signUp:e=>y(()=>w("sign_up",e?{method:e}:void 0)),reset:Q,get version(){return j}}}var F=["init","track","pageView","identify","setUser","login","signUp","reset"];function $(i,t){let n=i.mixdive;if(n&&n.loaded)return null;let u=X(),a=(s,d)=>{typeof s=="string"&&F.indexOf(s)>=0&&u[s](...d)},f=s=>{try{let d=Array.prototype.slice.call(s);a(d[0],d.slice(1))}catch(d){}},g=function(s,...d){a(s,d)};for(let s of F)g[s]=(...d)=>a(s,d);Object.defineProperty(g,"version",{get:()=>u.version}),g.loaded=!0,g.q={push:f},i.mixdive=g;let m=n&&n.q&&typeof n.q.length=="number"?Array.prototype.slice.call(n.q):[];for(let s of m)f(s);if(t){let s=t.dataset,d=s.key||"",v=s.server||"";if(!v)try{v=new URL(t.src,i.location.href).origin}catch(V){}d&&u.init({key:d,server:v,autoPageViews:s.autoPageViews!=="false",appVersion:s.appVersion||void 0,debug:s.debug==="true"})}return u}$(window,document.currentScript);})();
1
+ /*! Mixdive web tag v0.1.1 · https://github.com/mixdive/mixdive-js · Apache-2.0 */
2
+ "use strict";(()=>{var Z=Object.defineProperty;var R=Object.getOwnPropertySymbols;var K=Object.prototype.hasOwnProperty,ee=Object.prototype.propertyIsEnumerable;var T=(i,t,n)=>t in i?Z(i,t,{enumerable:!0,configurable:!0,writable:!0,value:n}):i[t]=n,E=(i,t)=>{for(var n in t||(t={}))K.call(t,n)&&T(i,n,t[n]);if(R)for(var n of R(t))ee.call(t,n)&&T(i,n,t[n]);return i};function I(){let i="";try{let t=new Uint8Array(16);crypto.getRandomValues(t);for(let n=0;n<16;n++)i+=(t[n]+256).toString(16).slice(1);return i}catch(t){}for(let t=0;t<32;t++)i+=Math.floor(Math.random()*16).toString(16);return i}function L(i,t){let n={ref:"",us:"",um:"",uc:""};try{if(t){let a=/^[a-z]+:\/\/([^/]+)/i.exec(t);a&&a[1].toLowerCase()!==i.host.toLowerCase()&&(n.ref=t.split("?")[0].slice(0,256))}let u=i.search.replace(/^\?/,"").split("&");for(let a of u){let f=a.indexOf("="),g=f<0?a:a.slice(0,f),m=f<0?"":a.slice(f+1),s="";try{s=decodeURIComponent(m.replace(/\+/g," ")).slice(0,128)}catch(d){s=m.slice(0,128)}g==="utm_source"&&(n.us=s),g==="utm_medium"&&(n.um=s),g==="utm_campaign"&&(n.uc=s)}}catch(u){}return n}function D(i,t,n,u){let a=!!t.us&&!!i&&i.us!==t.us,f=i;return(!f||n-f.last>18e5||a)&&(f={id:u(),start:n,last:n,ref:t.ref,us:t.us,um:t.um,uc:t.uc}),f.last=n,f}function N(i){if(!i)return null;try{let t=JSON.parse(i);if(t&&typeof t.id=="string"&&typeof t.start=="number"&&typeof t.last=="number")return{id:t.id,start:t.start,last:t.last,ref:t.ref||"",us:t.us||"",um:t.um||"",uc:t.uc||""}}catch(t){}return null}function U(i,t){let n={};return t.language&&(n.language=String(t.language).slice(0,16)),t.width&&t.height&&(n.resolution=`${t.width}x${t.height}`),t.density&&(n.density=t.density),i.ref&&(n.referrer=i.ref),i.us&&(n.utm_source=i.us),i.um&&(n.utm_medium=i.um),i.uc&&(n.utm_campaign=i.uc),n}function C(){let i=new Map;return{get(t){var n;try{let u=window.localStorage.getItem(t);if(u!==null)return u}catch(u){}return(n=i.get(t))!=null?n:null},set(t,n){i.set(t,n);try{window.localStorage.setItem(t,n)}catch(u){}},remove(t){i.delete(t);try{window.localStorage.removeItem(t)}catch(n){}}}}function H(i){return{send(t,n,u){if(!u){let a=typeof navigator!="undefined"?navigator:void 0;if(a&&typeof a.sendBeacon=="function")try{if(a.sendBeacon(t,new Blob([n],{type:"text/plain"})))return}catch(f){}}if(typeof fetch!="function"){i("no fetch available; dropped",t);return}try{let a={method:"POST",body:n,keepalive:!0};u&&(a.headers=E({"Content-Type":"application/json"},u));let f=fetch(t,a);f&&typeof f.then=="function"&&f.then(g=>{g.ok||i(`${g.status} from ${t}`)},g=>i("send failed",g))}catch(a){i("send failed",a)}}}}var j="0.1.1",h={device:"mx_did",session:"mx_ses",user:"mx_uid"},ne=200,ie=1e6,re=["name","username","email","organization","phone","picture","gender"];function X(){let i=C(),t=null,n=!1,u=[],a={ref:"",us:"",um:"",uc:""},f=null,g=null,m=null,s=null,d=(e,...r)=>{if(t&&t.debug)try{console.log("[mixdive]",e,...r)}catch(o){}},v=H(d),V=()=>typeof window!="undefined"&&typeof document!="undefined";function b(){if(g)return g;let e=i.get(h.device);return e||(e=I(),i.set(h.device,e)),g=e,e}function k(){return f!=null?f:N(i.get(h.session))}function A(e){f=e,i.set(h.session,JSON.stringify(e))}function B(e){let r=D(k(),a,e,I);return A(r),r}function P(e){let r={};try{navigator.language&&(r.language=navigator.language)}catch(o){}try{r.width=screen.width,r.height=screen.height}catch(o){}try{window.devicePixelRatio&&(r.density=window.devicePixelRatio)}catch(o){}return U(e,r)}function _(e,r,o=!1){let c=t,l=JSON.stringify(r);c.appVersion&&!o?v.send(c.server+e,l,{"X-Api-Key":c.key,"X-App-Version":c.appVersion}):v.send(c.server+e+"?api_key="+encodeURIComponent(c.key),l),d("sent",e,r)}function y(e){if(!n){if(!t){u.length>=ne&&u.shift(),u.push(e);return}try{e()}catch(r){d("call failed",r)}}}function w(e,r,o){if(typeof e!="string"||!e.trim()||e.indexOf("gtm.")===0){d("ignored event key",e);return}let c=B(Date.now()),l={event_key:e,device_id:b(),session_id:c.id,context:P(c)};if(m&&(l.user_id=m),r&&typeof r=="object"&&(l.properties=r),o&&typeof o=="object"){typeof o.id=="string"&&o.id&&(l.id=o.id);let{count:p,sum:x,duration:S}=o;typeof p=="number"&&Number.isInteger(p)&&p>1&&p<=ie?l.count=p:p!==void 0&&p!==1&&d("ignored count (whole number 2\u20261000000)",p),typeof x=="number"&&Number.isFinite(x)&&x!==0&&(l.sum=x),typeof S=="number"&&Number.isFinite(S)&&S>0&&(l.duration=S)}_("/ingest/event",l)}function M(e){let r=typeof e=="string"&&e?e:"";if(!r)try{r=location.pathname||"/"}catch(l){r="/"}if(r===s)return;let o={url:r};try{document.title&&(o.title=String(document.title).slice(0,256))}catch(l){}let c=s;if(!c)try{document.referrer&&(c=document.referrer.split("?")[0].slice(0,256))}catch(l){}c&&(o.referrer=c),s=r,w("page_view",o)}function G(e,r){let o={user_id:e,device_id:b()};if(r&&typeof r=="object"){for(let l of re){let p=r[l];typeof p=="string"&&p&&(o[l]=p)}let c=r.birthYear;typeof c=="number"&&Number.isInteger(c)&&c>0&&(o.birth_year=c),r.custom&&typeof r.custom=="object"&&(o.custom=r.custom)}return o}function O(e,r){if(typeof e!="string"||!e.trim()){d("ignored user id",e);return}m=e,i.set(h.user,e),_("/ingest/user",G(e,r))}function q(){try{let e=k();if(!e)return;let r=Date.now(),o=r-e.last>18e5?e.last:r,c=Math.max(0,Math.round((o-e.start)/1e3));_("/ingest/event",{event_key:"session_end",id:e.id+":end",device_id:b(),session_id:e.id,context:P(e),properties:{duration:c}},!0)}catch(e){d("session_end failed",e)}}function J(){let e=k();e&&(e.last=Date.now(),A(e))}function z(){document.addEventListener("visibilitychange",()=>{document.visibilityState==="hidden"?q():J()}),window.addEventListener("pagehide",q)}function Y(){let e=window.history,r=()=>{setTimeout(()=>y(()=>M()),0)};for(let o of["pushState","replaceState"]){let c=e[o];typeof c=="function"&&(e[o]=function(...l){let p=c.apply(this,l);return r(),p})}window.addEventListener("popstate",r)}function W(e){if(!V())return;if(t){d("init called again; the first call wins");return}let r=e&&typeof e.key=="string"?e.key.trim():"",o=e&&typeof e.server=="string"?e.server.trim().replace(/\/+$/,""):"";if(!r||!/^https?:\/\/[^/]+/i.test(o)){n=!0,u=[];try{console.warn("[mixdive] off: init needs an API key and the server origin (https://analytics.example.com)")}catch(l){}return}if(typeof navigator!="undefined"&&navigator.webdriver){n=!0,u=[];return}t={key:r,server:o,autoPageViews:e.autoPageViews!==!1,appVersion:typeof e.appVersion=="string"?e.appVersion.slice(0,64):"",debug:!!e.debug},a=L(location,document.referrer),m=i.get(h.user),z();let c=u;u=[];for(let l of c)y(l);t.autoPageViews&&(Y(),y(()=>M())),d("started",{server:o,autoPageViews:t.autoPageViews,version:j})}function Q(){i.remove(h.device),i.remove(h.session),i.remove(h.user),f=null,g=null,m=null}return{init:W,track:(e,r,o)=>y(()=>w(e,r,o)),pageView:e=>y(()=>M(e)),identify:e=>y(()=>O(e)),setUser:(e,r)=>y(()=>O(e,r)),login:e=>y(()=>w("login",e?{method:e}:void 0)),signUp:e=>y(()=>w("sign_up",e?{method:e}:void 0)),reset:Q,get version(){return j}}}var F=["init","track","pageView","identify","setUser","login","signUp","reset"];function $(i,t){let n=i.mixdive;if(n&&n.loaded)return null;let u=X(),a=(s,d)=>{typeof s=="string"&&F.indexOf(s)>=0&&u[s](...d)},f=s=>{try{let d=Array.prototype.slice.call(s);a(d[0],d.slice(1))}catch(d){}},g=function(s,...d){a(s,d)};for(let s of F)g[s]=(...d)=>a(s,d);Object.defineProperty(g,"version",{get:()=>u.version}),g.loaded=!0,g.q={push:f},i.mixdive=g;let m=n&&n.q&&typeof n.q.length=="number"?Array.prototype.slice.call(n.q):[];for(let s of m)f(s);if(t){let s=t.dataset,d=s.key||"",v=s.server||"";if(!v)try{v=new URL(t.src,i.location.href).origin}catch(V){}d&&u.init({key:d,server:v,autoPageViews:s.autoPageViews!=="false",appVersion:s.appVersion||void 0,debug:s.debug==="true"})}return u}$(window,document.currentScript);})();
package/package.json CHANGED
@@ -1,9 +1,9 @@
1
1
  {
2
2
  "name": "@mixdive/browser",
3
- "version": "0.1.0",
4
- "description": "Mixdive browser SDK — the web tag and the npm client for self-hosted product analytics.",
3
+ "version": "0.1.1",
4
+ "description": "Self-hosted product analytics for the web. The Mixdive script tag and npm client: page views, visitors, sessions and your own events.",
5
5
  "license": "Apache-2.0",
6
- "homepage": "https://github.com/mixdive/mixdive-js#readme",
6
+ "homepage": "https://mixdive.com",
7
7
  "repository": {
8
8
  "type": "git",
9
9
  "url": "git+https://github.com/mixdive/mixdive-js.git",
@@ -16,7 +16,11 @@
16
16
  "product-analytics",
17
17
  "web-analytics",
18
18
  "self-hosted",
19
- "privacy"
19
+ "privacy",
20
+ "gdpr",
21
+ "cookieless",
22
+ "page-views",
23
+ "event-tracking"
20
24
  ],
21
25
  "type": "module",
22
26
  "sideEffects": [