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.
Files changed (3) hide show
  1. package/README.md +56 -27
  2. package/dist/index.js +11 -6
  3. 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
- ## Usage
12
+ ## Setup Guides
13
13
 
14
- ### 1. Unblockable (Server-Side) **Recommended**
15
- This is immune to ad-blockers and requires zero client-side JavaScript. Best for Next.js, Nuxt, or any server-side framework.
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 (Next.js Example)
19
- import { NextResponse } from 'next/server';
52
+ // middleware.ts
20
53
  import { trackServerEvent } from 'stat18ion';
21
54
 
22
- export async function middleware(req) {
23
- // Track the event without blocking the visitor
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
- ```javascript
39
- import { init } from 'stat18ion';
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
- init({
42
- siteId: 'YOUR_SITE_ID',
43
- debug: false,
44
- trackLocal: false
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
- - 🚀 **Lightweight**: Zero dependencies, tiny bundle.
50
- - 🕵️ **Privacy Friendly**: No IP storage, no cookies.
51
- - **Auto Tracking**: Automatically tracks route changes in SPAs (Next.js, React Router).
52
- - 🛡️ **Dev Safety**: Automatically ignores `localhost` traffic unless `debug: true` is set.
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
- return (staticExtensions.some(ext => path.toLowerCase().endsWith(ext)) ||
38
- path.includes('/_next/static/') ||
39
- path.includes('/api/'));
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 500ms
52
- if (path === lastTrackedPath && now - lastTrackedTime < 500) {
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))
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "stat18ion",
3
- "version": "0.1.6",
3
+ "version": "0.1.9",
4
4
  "description": "Privacy-first, lightweight analytics tracker for the modern web.",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",