stat18ion 0.1.6 → 0.1.9
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 +56 -27
- package/dist/index.js +11 -6
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -9,44 +9,73 @@ Privacy-first, lightweight (< 1KB), and cookie-free analytics.
|
|
|
9
9
|
npm install stat18ion
|
|
10
10
|
```
|
|
11
11
|
|
|
12
|
-
##
|
|
12
|
+
## Setup Guides
|
|
13
13
|
|
|
14
|
-
### 1.
|
|
15
|
-
|
|
14
|
+
### 1. Next.js App Router (Recommended)
|
|
15
|
+
Create a client-side provider component and drop it in your `layout.tsx`. This avoids SSR issues and makes setup "Plug n Play".
|
|
16
|
+
|
|
17
|
+
```tsx
|
|
18
|
+
// Stat18ionProvider.tsx
|
|
19
|
+
'use client'
|
|
20
|
+
|
|
21
|
+
import { useEffect } from 'react'
|
|
22
|
+
import { init } from 'stat18ion'
|
|
23
|
+
|
|
24
|
+
export function Stat18ionProvider() {
|
|
25
|
+
useEffect(() => {
|
|
26
|
+
init({ siteId: 'YOUR_SITE_ID' })
|
|
27
|
+
}, [])
|
|
28
|
+
return null
|
|
29
|
+
}
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
```tsx
|
|
33
|
+
// app/layout.tsx
|
|
34
|
+
import { Stat18ionProvider } from './Stat18ionProvider'
|
|
35
|
+
|
|
36
|
+
export default function RootLayout({ children }) {
|
|
37
|
+
return (
|
|
38
|
+
<html>
|
|
39
|
+
<body>
|
|
40
|
+
<Stat18ionProvider />
|
|
41
|
+
{children}
|
|
42
|
+
</body>
|
|
43
|
+
</html>
|
|
44
|
+
)
|
|
45
|
+
}
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
### 2. Unblockable (Middleware)
|
|
49
|
+
Zero client-side JS. Resistant to ad-blockers. Use a tight `matcher` to avoid tracking static assets or internal chunks.
|
|
16
50
|
|
|
17
51
|
```typescript
|
|
18
|
-
// middleware.ts
|
|
19
|
-
import { NextResponse } from 'next/server';
|
|
52
|
+
// middleware.ts
|
|
20
53
|
import { trackServerEvent } from 'stat18ion';
|
|
21
54
|
|
|
22
|
-
export
|
|
23
|
-
|
|
24
|
-
trackServerEvent({
|
|
55
|
+
export function middleware(req) {
|
|
56
|
+
trackServerEvent({
|
|
25
57
|
siteId: 'YOUR_SITE_ID',
|
|
26
58
|
path: req.nextUrl.pathname,
|
|
27
|
-
ua: req.headers.get('user-agent')
|
|
28
|
-
referrer: req.headers.get('referer') || '',
|
|
59
|
+
ua: req.headers.get('user-agent'),
|
|
29
60
|
});
|
|
30
|
-
|
|
31
|
-
return NextResponse.next();
|
|
32
61
|
}
|
|
33
|
-
```
|
|
34
|
-
|
|
35
|
-
### 2. Standard (Client-Side)
|
|
36
|
-
Initialize once in your application root.
|
|
37
62
|
|
|
38
|
-
|
|
39
|
-
|
|
63
|
+
export const config = {
|
|
64
|
+
// Filters out images, chunks, and system files at the edge level
|
|
65
|
+
matcher: ['/((?!api|_next/static|_next/image|favicon.ico|robots.txt|sitemap.xml|.*\\..*).*)'],
|
|
66
|
+
};
|
|
67
|
+
```
|
|
40
68
|
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
69
|
+
### 3. Basic Script (HTML)
|
|
70
|
+
```html
|
|
71
|
+
<script defer
|
|
72
|
+
src="https://unpkg.com/stat18ion@latest/dist/index.js"
|
|
73
|
+
data-site-id="YOUR_SITE_ID">
|
|
74
|
+
</script>
|
|
46
75
|
```
|
|
47
76
|
|
|
48
77
|
## Features
|
|
49
|
-
- 🚀 **
|
|
50
|
-
-
|
|
51
|
-
-
|
|
52
|
-
-
|
|
78
|
+
- 🚀 **Aggressive Filtering**: Automatically ignores `.js`, `.css`, `.webp`, `.avif`, and other media noise.
|
|
79
|
+
- ⚡ **SPA Deduplication**: Built-in 1000ms cooldown to prevent duplicate events during hydration.
|
|
80
|
+
- 🕵️ **Privacy Friendly**: No IP storage, no cookies, 10-second backend cooldown.
|
|
81
|
+
- ⚡ **Auto Tracking**: Automatically tracks route changes in SPAs.
|
package/dist/index.js
CHANGED
|
@@ -32,11 +32,16 @@ const sendEvent = (payload) => {
|
|
|
32
32
|
};
|
|
33
33
|
const isStaticAsset = (path) => {
|
|
34
34
|
const staticExtensions = [
|
|
35
|
-
'.js', '.css', '.png', '.jpg', '.jpeg', '.gif', '.svg', '.ico', '.woff', '.woff2', '.ttf', '.otf', '.json', '.map'
|
|
35
|
+
'.js', '.css', '.png', '.jpg', '.jpeg', '.gif', '.svg', '.ico', '.woff', '.woff2', '.ttf', '.otf', '.json', '.map', '.txt', '.xml', '.webp', '.avif', '.mp4', '.webm'
|
|
36
36
|
];
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
37
|
+
// Clean path (remove query params and hashes)
|
|
38
|
+
const cleanPath = path.split('?')[0].split('#')[0].toLowerCase();
|
|
39
|
+
return (staticExtensions.some(ext => cleanPath.endsWith(ext)) ||
|
|
40
|
+
cleanPath.includes('/_next/static/') ||
|
|
41
|
+
cleanPath.includes('/_next/image/') ||
|
|
42
|
+
cleanPath.includes('/api/') ||
|
|
43
|
+
cleanPath === '/favicon.ico' ||
|
|
44
|
+
cleanPath === '/robots.txt');
|
|
40
45
|
};
|
|
41
46
|
let lastTrackedPath = '';
|
|
42
47
|
let lastTrackedTime = 0;
|
|
@@ -48,8 +53,8 @@ const trackPageView = () => {
|
|
|
48
53
|
return;
|
|
49
54
|
const path = window.location.pathname;
|
|
50
55
|
const now = Date.now();
|
|
51
|
-
// Deduplicate: Don't track same path within
|
|
52
|
-
if (path === lastTrackedPath && now - lastTrackedTime <
|
|
56
|
+
// Deduplicate: Don't track same path within 1000ms
|
|
57
|
+
if (path === lastTrackedPath && now - lastTrackedTime < 1000) {
|
|
53
58
|
return;
|
|
54
59
|
}
|
|
55
60
|
if (isStaticAsset(path))
|