@stacksjs/analytics 0.74.31 → 0.74.33
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/dist/drivers/fathom.d.ts +19 -1
- package/dist/drivers/fathom.js +2 -1
- package/dist/drivers/google-analytics.d.ts +17 -0
- package/dist/drivers/google-analytics.js +2 -0
- package/dist/drivers/index.d.ts +3 -0
- package/dist/drivers/index.js +1 -1
- package/dist/drivers/plausible.d.ts +23 -0
- package/dist/drivers/plausible.js +2 -0
- package/dist/drivers/self-hosted.d.ts +8 -20
- package/dist/drivers/self-hosted.js +7 -41
- package/dist/drivers/shared.d.ts +32 -0
- package/dist/drivers/shared.js +2 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -1
- package/dist/registry.d.ts +21 -0
- package/dist/registry.js +1 -0
- package/package.json +3 -2
package/dist/drivers/fathom.d.ts
CHANGED
|
@@ -1 +1,19 @@
|
|
|
1
|
-
|
|
1
|
+
import type { AnalyticsHeadTag } from './shared';
|
|
2
|
+
/**
|
|
3
|
+
* Build the Fathom head tags.
|
|
4
|
+
*
|
|
5
|
+
* Values are raw; escaping happens when the tags are rendered.
|
|
6
|
+
*/
|
|
7
|
+
export declare function getFathomAnalyticsHead(config: FathomConfig): AnalyticsHeadTag[];
|
|
8
|
+
/**
|
|
9
|
+
* Generate the Fathom tracking script tag.
|
|
10
|
+
*/
|
|
11
|
+
export declare function generateFathomScript(config: FathomConfig): string;
|
|
12
|
+
/** Fathom's hosted script, used when `scriptUrl` is not set. */
|
|
13
|
+
export declare const FATHOM_DEFAULT_SCRIPT_URL: 'https://cdn.usefathom.com/script.js';
|
|
14
|
+
export declare interface FathomConfig {
|
|
15
|
+
siteId: string
|
|
16
|
+
scriptUrl?: string
|
|
17
|
+
honorDnt?: boolean
|
|
18
|
+
spa?: boolean
|
|
19
|
+
}
|
package/dist/drivers/fathom.js
CHANGED
|
@@ -1 +1,2 @@
|
|
|
1
|
-
export const
|
|
1
|
+
import{renderHeadTags}from"./shared";export const FATHOM_DEFAULT_SCRIPT_URL="https://cdn.usefathom.com/script.js";export function getFathomAnalyticsHead(config){if(!config.siteId)return[];const attrs={src:config.scriptUrl||FATHOM_DEFAULT_SCRIPT_URL,"data-site":config.siteId,defer:""};if(config.honorDnt)attrs["data-honor-dnt"]="true";if(config.spa)attrs["data-spa"]="auto";return[["script",attrs]]}export function generateFathomScript(config){const tags=getFathomAnalyticsHead(config);if(!tags.length)return"";return`<!-- Stacks Fathom Analytics -->
|
|
2
|
+
${renderHeadTags(tags)}`}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { AnalyticsHeadTag } from './shared';
|
|
2
|
+
/**
|
|
3
|
+
* Build the Google Analytics head tags.
|
|
4
|
+
*
|
|
5
|
+
* Values are raw; escaping happens when the tags are rendered.
|
|
6
|
+
*/
|
|
7
|
+
export declare function getGoogleAnalyticsHead(config: GoogleAnalyticsConfig): AnalyticsHeadTag[];
|
|
8
|
+
/**
|
|
9
|
+
* Generate the Google Analytics tracking script tags.
|
|
10
|
+
*/
|
|
11
|
+
export declare function generateGoogleAnalyticsScript(config: GoogleAnalyticsConfig): string;
|
|
12
|
+
/** The gtag.js loader, used to build the tag `src`. */
|
|
13
|
+
export declare const GOOGLE_ANALYTICS_SCRIPT_URL: 'https://www.googletagmanager.com/gtag/js';
|
|
14
|
+
export declare interface GoogleAnalyticsConfig {
|
|
15
|
+
trackingId: string
|
|
16
|
+
debug?: boolean
|
|
17
|
+
}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import{jsString,renderHeadTags}from"./shared";export const GOOGLE_ANALYTICS_SCRIPT_URL="https://www.googletagmanager.com/gtag/js";export function getGoogleAnalyticsHead(config){if(!config.trackingId)return[];const id=jsString(config.trackingId),options=config.debug?",{debug_mode:true}":"";return[["script",{src:`${GOOGLE_ANALYTICS_SCRIPT_URL}?id=${encodeURIComponent(config.trackingId)}`,async:""}],["script",{innerHTML:`window.dataLayer=window.dataLayer||[];function gtag(){dataLayer.push(arguments);}gtag('js',new Date());gtag('config',${id}${options});`}]]}export function generateGoogleAnalyticsScript(config){const tags=getGoogleAnalyticsHead(config);if(!tags.length)return"";return`<!-- Stacks Google Analytics -->
|
|
2
|
+
${renderHeadTags(tags)}`}
|
package/dist/drivers/index.d.ts
CHANGED
package/dist/drivers/index.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export*from"./fathom";export*from"./self-hosted";
|
|
1
|
+
export*from"./fathom";export*from"./google-analytics";export*from"./plausible";export*from"./self-hosted";export*from"./shared";
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import type { AnalyticsHeadTag } from './shared';
|
|
2
|
+
/**
|
|
3
|
+
* Build the URL of the hosted script variant matching the config.
|
|
4
|
+
*/
|
|
5
|
+
export declare function plausibleScriptUrl(config: PlausibleConfig): string;
|
|
6
|
+
/**
|
|
7
|
+
* Build the Plausible head tags.
|
|
8
|
+
*
|
|
9
|
+
* Values are raw; escaping happens when the tags are rendered.
|
|
10
|
+
*/
|
|
11
|
+
export declare function getPlausibleAnalyticsHead(config: PlausibleConfig): AnalyticsHeadTag[];
|
|
12
|
+
/**
|
|
13
|
+
* Generate the Plausible tracking script tag.
|
|
14
|
+
*/
|
|
15
|
+
export declare function generatePlausibleScript(config: PlausibleConfig): string;
|
|
16
|
+
/** Plausible's hosted script host, used when `scriptUrl` is not set. */
|
|
17
|
+
export declare const PLAUSIBLE_DEFAULT_HOST: 'https://plausible.io';
|
|
18
|
+
export declare interface PlausibleConfig {
|
|
19
|
+
domain: string
|
|
20
|
+
scriptUrl?: string
|
|
21
|
+
trackLocalhost?: boolean
|
|
22
|
+
hashMode?: boolean
|
|
23
|
+
}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import{renderHeadTags}from"./shared";export const PLAUSIBLE_DEFAULT_HOST="https://plausible.io";export function plausibleScriptUrl(config){if(config.scriptUrl)return config.scriptUrl;const extensions=[config.hashMode?"hash":"",config.trackLocalhost?"local":""].filter(Boolean);return`${PLAUSIBLE_DEFAULT_HOST}/js/script${extensions.map((e)=>`.${e}`).join("")}.js`}export function getPlausibleAnalyticsHead(config){if(!config.domain)return[];return[["script",{src:plausibleScriptUrl(config),"data-domain":config.domain,defer:""}]]}export function generatePlausibleScript(config){const tags=getPlausibleAnalyticsHead(config);if(!tags.length)return"";return`<!-- Stacks Plausible Analytics -->
|
|
2
|
+
${renderHeadTags(tags)}`}
|
|
@@ -1,27 +1,15 @@
|
|
|
1
|
+
import type { AnalyticsHeadTag } from './shared';
|
|
1
2
|
/**
|
|
2
|
-
*
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
* Generate the head tag for including self-hosted analytics
|
|
7
|
-
* Compatible with Stacks docs config.ts head array format
|
|
3
|
+
* Build the self-hosted head tags.
|
|
4
|
+
*
|
|
5
|
+
* Compatible with the Stacks/BunPress docs `head` array format. Values are raw;
|
|
6
|
+
* escaping happens when the tags are rendered.
|
|
8
7
|
*/
|
|
9
|
-
export declare function getSelfHostedAnalyticsHead(config: SelfHostedConfig): [
|
|
8
|
+
export declare function getSelfHostedAnalyticsHead(config: SelfHostedConfig): AnalyticsHeadTag[];
|
|
10
9
|
/**
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
* This driver generates a minimal, privacy-focused tracking script
|
|
14
|
-
* that sends analytics data to your own API endpoint (powered by dynamodb-tooling analytics).
|
|
15
|
-
*
|
|
16
|
-
* Features:
|
|
17
|
-
* - No cookies required
|
|
18
|
-
* - Privacy-focused (hashed visitor IDs)
|
|
19
|
-
* - Do Not Track support
|
|
20
|
-
* - Page view tracking
|
|
21
|
-
* - Custom event tracking
|
|
22
|
-
* - Outbound link tracking (optional)
|
|
23
|
-
* - Hash-based routing support (optional)
|
|
10
|
+
* Generate the self-hosted analytics tracking script
|
|
24
11
|
*/
|
|
12
|
+
export declare function generateSelfHostedScript(config: SelfHostedConfig): string;
|
|
25
13
|
export declare interface SelfHostedConfig {
|
|
26
14
|
siteId: string
|
|
27
15
|
apiEndpoint: string
|
|
@@ -1,51 +1,17 @@
|
|
|
1
|
-
export function
|
|
2
|
-
|
|
3
|
-
var a=e.target.closest('a');
|
|
4
|
-
if(a&&a.hostname!==location.hostname){
|
|
5
|
-
t('outbound',{url:a.href});
|
|
6
|
-
}
|
|
7
|
-
});`:"";return`<!-- Stacks Self-Hosted Analytics -->
|
|
8
|
-
<script data-site="${siteId}" data-api="${apiEndpoint}" defer>
|
|
9
|
-
(function(){
|
|
10
|
-
'use strict';
|
|
11
|
-
var d=document,w=window,n=navigator,s=d.currentScript;
|
|
12
|
-
var site=s.dataset.site,api=s.dataset.api;
|
|
13
|
-
${honorDnt}
|
|
14
|
-
var q=[],sid=Math.random().toString(36).slice(2);
|
|
15
|
-
function t(e,p){
|
|
16
|
-
var x=new XMLHttpRequest();
|
|
17
|
-
x.open('POST',api+'/collect',true);
|
|
18
|
-
x.setRequestHeader('Content-Type','application/json');
|
|
19
|
-
// Strip the URL's query string before sending. location.href includes
|
|
20
|
-
// tokens / session IDs / search terms that shouldn't reach the
|
|
21
|
-
// analytics backend. Same goes for document.referrer when it points
|
|
22
|
-
// back to our own domain (path is fine, query is not).
|
|
23
|
-
var safeUrl=location.origin+location.pathname+location.hash;
|
|
24
|
-
var safeRef=d.referrer?d.referrer.split('?')[0]:'';
|
|
25
|
-
x.send(JSON.stringify({
|
|
26
|
-
s:site,sid:sid,e:e,p:p||{},
|
|
27
|
-
u:safeUrl,r:safeRef,t:d.title,
|
|
28
|
-
sw:screen.width,sh:screen.height
|
|
29
|
-
}));
|
|
30
|
-
}
|
|
31
|
-
function pv(){t('pageview');}
|
|
32
|
-
${hashTracking}
|
|
33
|
-
${outboundTracking}
|
|
34
|
-
if(d.readyState==='complete')pv();
|
|
35
|
-
else w.addEventListener('load',pv);
|
|
36
|
-
w.stacksAnalytics={track:function(n,v){t('event',{name:n,value:v});}};
|
|
37
|
-
})();
|
|
38
|
-
</script>`}export function getSelfHostedAnalyticsHead(config){if(!config.siteId||!config.apiEndpoint)return[];return[["script",{"data-site":config.siteId,"data-api":config.apiEndpoint,defer:"",innerHTML:generateInlineScript(config)}]]}function generateInlineScript(config){const honorDnt=config.honorDnt?'if(n.doNotTrack==="1")return;':"",hashTracking=config.trackHashChanges?"w.addEventListener('hashchange',pv);":"",outboundTracking=config.trackOutboundLinks?"d.addEventListener('click',function(e){var a=e.target.closest('a');if(a&&a.hostname!==location.hostname){t('outbound',{url:a.href});}});":"";return`(function(){
|
|
1
|
+
import{renderHeadTags}from"./shared";export function getSelfHostedAnalyticsHead(config){if(!config.siteId||!config.apiEndpoint)return[];return[["script",{"data-site":config.siteId,"data-api":config.apiEndpoint,defer:"",innerHTML:generateInlineScript(config)}]]}export function generateSelfHostedScript(config){const tags=getSelfHostedAnalyticsHead(config);if(!tags.length)return"";return`<!-- Stacks Self-Hosted Analytics -->
|
|
2
|
+
${renderHeadTags(tags)}`}function generateInlineScript(config){const honorDnt=config.honorDnt?'if(n.doNotTrack==="1")return;':"",hashTracking=config.trackHashChanges?"w.addEventListener('hashchange',pv);":"",outboundTracking=config.trackOutboundLinks?"d.addEventListener('click',function(e){var a=e.target.closest('a');if(a&&a.hostname!==location.hostname){t('outbound',{url:a.href});}});":"";return`(function(){
|
|
39
3
|
'use strict';
|
|
40
4
|
var d=document,w=window,n=navigator,s=d.currentScript;
|
|
41
5
|
var site=s.dataset.site,api=s.dataset.api;
|
|
42
6
|
${honorDnt}
|
|
43
|
-
var
|
|
7
|
+
var sid=Math.random().toString(36).slice(2);
|
|
44
8
|
function t(e,p){
|
|
45
9
|
var x=new XMLHttpRequest();
|
|
46
10
|
x.open('POST',api+'/collect',true);
|
|
47
11
|
x.setRequestHeader('Content-Type','application/json');
|
|
48
|
-
var safeUrl=location.origin+location.pathname+location.hash;
|
|
12
|
+
var safeUrl=location.origin+location.pathname+location.hash;
|
|
13
|
+
var safeRef=d.referrer?d.referrer.split('?')[0]:'';
|
|
14
|
+
x.send(JSON.stringify({s:site,sid:sid,e:e,p:p||{},u:safeUrl,r:safeRef,t:d.title,sw:screen.width,sh:screen.height}));
|
|
49
15
|
}
|
|
50
16
|
function pv(){t('pageview');}
|
|
51
17
|
${hashTracking}
|
|
@@ -53,4 +19,4 @@ ${outboundTracking}
|
|
|
53
19
|
if(d.readyState==='complete')pv();
|
|
54
20
|
else w.addEventListener('load',pv);
|
|
55
21
|
w.stacksAnalytics={track:function(n,v){t('event',{name:n,value:v});}};
|
|
56
|
-
})();`}
|
|
22
|
+
})();`}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Escape a value for safe insertion into an HTML attribute.
|
|
3
|
+
*/
|
|
4
|
+
export declare function escapeAttr(str: string): string;
|
|
5
|
+
/**
|
|
6
|
+
* Serialize a value as a JavaScript string literal for inline script bodies.
|
|
7
|
+
*
|
|
8
|
+
* `JSON.stringify` handles quoting and control characters; the `<` and `>`
|
|
9
|
+
* replacements stop a value containing `</script>` from closing the tag early.
|
|
10
|
+
*/
|
|
11
|
+
export declare function jsString(value: string): string;
|
|
12
|
+
/**
|
|
13
|
+
* Render an `[tag, attributes]` pair as HTML.
|
|
14
|
+
*
|
|
15
|
+
* Head tags carry raw values; escaping happens here, so a driver never escapes
|
|
16
|
+
* twice. An `innerHTML` attribute becomes the tag body instead of an attribute,
|
|
17
|
+
* and an empty value renders as a bare attribute (`defer`, `async`).
|
|
18
|
+
*/
|
|
19
|
+
export declare function renderHeadTag([tag, attrs]: AnalyticsHeadTag): string;
|
|
20
|
+
/**
|
|
21
|
+
* Render a driver's head tags as an HTML block.
|
|
22
|
+
*/
|
|
23
|
+
export declare function renderHeadTags(tags: AnalyticsHeadTag[]): string;
|
|
24
|
+
/**
|
|
25
|
+
* Shared helpers for the analytics drivers.
|
|
26
|
+
*
|
|
27
|
+
* Every driver emits a `<script>` tag, either as raw HTML or as the
|
|
28
|
+
* `[tag, attributes]` pair the Stacks/BunPress docs `head` array expects, so the
|
|
29
|
+
* tag shape and the escaping live here rather than once per driver.
|
|
30
|
+
*/
|
|
31
|
+
/** A single head entry: the tag name plus its attributes. */
|
|
32
|
+
export type AnalyticsHeadTag = [string, Record<string, string>];
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
export function escapeAttr(str){return str.replace(/&/g,"&").replace(/"/g,""").replace(/'/g,"'").replace(/</g,"<").replace(/>/g,">")}export function jsString(value){return JSON.stringify(value).replace(/</g,"\\u003C").replace(/>/g,"\\u003E")}export function renderHeadTag([tag,attrs]){const body=attrs.innerHTML??"",rendered=Object.entries(attrs).filter(([key])=>key!=="innerHTML").map(([key,value])=>value===""?` ${key}`:` ${key}="${escapeAttr(value)}"`).join("");return`<${tag}${rendered}>${body}</${tag}>`}export function renderHeadTags(tags){return tags.map(renderHeadTag).join(`
|
|
2
|
+
`)}
|
package/dist/index.d.ts
CHANGED
package/dist/index.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export{classifyAgent,isPageviewRequest,recordPageview,referrerHost}from"./capture";export*from"./drivers";
|
|
1
|
+
export{classifyAgent,isPageviewRequest,recordPageview,referrerHost}from"./capture";export*from"./drivers";export*from"./registry";
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import type { AnalyticsConfig, AnalyticsOptions } from '@stacksjs/types';
|
|
2
|
+
import type { AnalyticsHeadTag } from './drivers/shared';
|
|
3
|
+
/**
|
|
4
|
+
* Is this a driver name Stacks implements?
|
|
5
|
+
*/
|
|
6
|
+
export declare function isAnalyticsDriver(value: unknown): value is AnalyticsDriverName;
|
|
7
|
+
/**
|
|
8
|
+
* Resolve the configured driver's head tags.
|
|
9
|
+
*
|
|
10
|
+
* Returns an empty array when no driver is configured at all. Throws when a
|
|
11
|
+
* driver is named that Stacks does not implement, or when the named driver is
|
|
12
|
+
* missing the config it needs.
|
|
13
|
+
*/
|
|
14
|
+
export declare function getAnalyticsHead(config: AnalyticsConfig): AnalyticsHeadTag[];
|
|
15
|
+
/**
|
|
16
|
+
* Render the configured driver's tracking script as HTML.
|
|
17
|
+
*/
|
|
18
|
+
export declare function generateAnalyticsScript(config: AnalyticsConfig): string;
|
|
19
|
+
/** Every driver name that has an implementation behind it. */
|
|
20
|
+
export declare const ANALYTICS_DRIVERS: readonly AnalyticsDriverName[];
|
|
21
|
+
export type AnalyticsDriverName = AnalyticsOptions['driver'];
|
package/dist/registry.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{getFathomAnalyticsHead}from"./drivers/fathom";import{getGoogleAnalyticsHead}from"./drivers/google-analytics";import{getPlausibleAnalyticsHead}from"./drivers/plausible";import{getSelfHostedAnalyticsHead}from"./drivers/self-hosted";import{renderHeadTags}from"./drivers/shared";export const ANALYTICS_DRIVERS=["google-analytics","fathom","plausible","self-hosted"];export function isAnalyticsDriver(value){return typeof value==="string"&&ANALYTICS_DRIVERS.includes(value)}function misconfigured(driver,key,field){return Error(`Analytics driver "${driver}" is selected but not configured: set \`drivers.${key}.${field}\` in config/analytics.ts, or change \`driver\`.`)}export function getAnalyticsHead(config){const driver=config.driver;if(!driver)return[];if(!isAnalyticsDriver(driver))throw Error(`Unknown analytics driver "${String(driver)}". Available drivers: ${ANALYTICS_DRIVERS.join(", ")}.`);switch(driver){case"google-analytics":{const options=config.drivers?.googleAnalytics;if(!options?.trackingId)throw misconfigured(driver,"googleAnalytics","trackingId");return getGoogleAnalyticsHead(options)}case"fathom":{const options=config.drivers?.fathom;if(!options?.siteId)throw misconfigured(driver,"fathom","siteId");return getFathomAnalyticsHead(options)}case"plausible":{const options=config.drivers?.plausible;if(!options?.domain)throw misconfigured(driver,"plausible","domain");return getPlausibleAnalyticsHead(options)}case"self-hosted":{const options=config.drivers?.selfHosted;if(!options?.siteId)throw misconfigured(driver,"selfHosted","siteId");if(!options.apiEndpoint)throw misconfigured(driver,"selfHosted","apiEndpoint");return getSelfHostedAnalyticsHead(options)}default:throw Error(`Analytics driver "${String(driver)}" has no implementation.`)}}export function generateAnalyticsScript(config){const tags=getAnalyticsHead(config);if(!tags.length)return"";return renderHeadTags(tags)}
|
package/package.json
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"name": "@stacksjs/analytics",
|
|
3
3
|
"type": "module",
|
|
4
4
|
"sideEffects": false,
|
|
5
|
-
"version": "0.74.
|
|
5
|
+
"version": "0.74.33",
|
|
6
6
|
"description": "Stacks Analytics. Privacy-friendly.",
|
|
7
7
|
"author": "Chris Breuer",
|
|
8
8
|
"contributors": [
|
|
@@ -51,7 +51,8 @@
|
|
|
51
51
|
"prepublishOnly": "bun run build"
|
|
52
52
|
},
|
|
53
53
|
"dependencies": {
|
|
54
|
-
"@stacksjs/database": "0.74.
|
|
54
|
+
"@stacksjs/database": "0.74.33",
|
|
55
|
+
"@stacksjs/types": "0.74.33"
|
|
55
56
|
},
|
|
56
57
|
"devDependencies": {
|
|
57
58
|
"better-dx": "^0.2.24"
|