site-vigil-visitors 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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Tithi
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,219 @@
1
+ # `site-vigil-visitors`
2
+
3
+ Lightweight browser visit tracking with an optional React wrapper.
4
+
5
+ ## Features
6
+
7
+ - small browser-side tracker
8
+ - configurable `siteId` and `endpoint`
9
+ - uses `navigator.sendBeacon()` when available
10
+ - optional `beforeunload` leave event
11
+ - framework-agnostic core API
12
+ - React wrapper available via `site-vigil-visitors/react`
13
+
14
+ ## Installation
15
+
16
+ ```bash
17
+ npm install site-vigil-visitors
18
+ ```
19
+
20
+ For React projects:
21
+
22
+ ```bash
23
+ npm install react site-vigil-visitors
24
+ ```
25
+
26
+ ## Quick start
27
+
28
+ ### React
29
+
30
+ ```tsx
31
+ import { Tracking } from 'site-vigil-visitors/react'
32
+
33
+ export function App() {
34
+ return (
35
+ <Tracking
36
+ siteId="my-site"
37
+ endpoint="https://visit-tracker.tithij.workers.dev"
38
+ />
39
+ )
40
+ }
41
+ ```
42
+
43
+ ### Plain JavaScript / TypeScript
44
+
45
+ ```ts
46
+ import { createTracker } from 'site-vigil-visitors'
47
+
48
+ const tracker = createTracker({
49
+ siteId: 'my-site',
50
+ endpoint: 'https://visit-tracker.tithij.workers.dev',
51
+ })
52
+
53
+ tracker.start()
54
+ ```
55
+
56
+ ## Core usage
57
+
58
+ ```ts
59
+ import { createTracker } from 'site-vigil-visitors'
60
+
61
+ const tracker = createTracker({
62
+ siteId: 'my-site',
63
+ endpoint: 'https://visit-tracker.tithij.workers.dev',
64
+ })
65
+
66
+ tracker.start()
67
+ tracker.trackEvent('cta_click', { label: 'Register' })
68
+ ```
69
+
70
+ ## React usage
71
+
72
+ ```tsx
73
+ import { Tracking } from 'site-vigil-visitors/react'
74
+
75
+ export function App() {
76
+ return (
77
+ <>
78
+ <Tracking
79
+ siteId="my-site"
80
+ endpoint="https://visit-tracker.tithij.workers.dev"
81
+ />
82
+ <main>Hello world</main>
83
+ </>
84
+ )
85
+ }
86
+ ```
87
+
88
+ ## Small demo app
89
+
90
+ Here is a minimal Vite + React example using the package:
91
+
92
+ ```tsx
93
+ import { Tracking } from 'site-vigil-visitors/react'
94
+
95
+ export default function App() {
96
+ return (
97
+ <div>
98
+ <Tracking
99
+ siteId="demo-site"
100
+ endpoint="https://visit-tracker.tithij.workers.dev"
101
+ />
102
+
103
+ <h1>Visit Tracker Demo</h1>
104
+ <button
105
+ onClick={() => {
106
+ console.log('CTA clicked')
107
+ }}
108
+ >
109
+ Click me
110
+ </button>
111
+ </div>
112
+ )
113
+ }
114
+ ```
115
+
116
+ If you want manual event tracking in the same app:
117
+
118
+ ```ts
119
+ import { createTracker } from 'site-vigil-visitors'
120
+
121
+ const tracker = createTracker({
122
+ siteId: 'demo-site',
123
+ endpoint: 'https://visit-tracker.tithij.workers.dev',
124
+ })
125
+
126
+ tracker.trackEvent('button_click', { button: 'hero-cta' })
127
+ ```
128
+
129
+ ## Next.js example
130
+
131
+ Use the React wrapper inside a client component:
132
+
133
+ ```tsx
134
+ 'use client'
135
+
136
+ import { Tracking } from 'site-vigil-visitors/react'
137
+
138
+ export default function TrackingClient() {
139
+ return (
140
+ <Tracking
141
+ siteId="next-app"
142
+ endpoint="https://visit-tracker.tithij.workers.dev"
143
+ />
144
+ )
145
+ }
146
+ ```
147
+
148
+ Then include it in your app layout or page.
149
+
150
+ ## Plain JavaScript example
151
+
152
+ ```html
153
+ <script type="module">
154
+ import { createTracker } from 'site-vigil-visitors'
155
+
156
+ const tracker = createTracker({
157
+ siteId: 'marketing-site',
158
+ endpoint: 'https://visit-tracker.tithij.workers.dev',
159
+ })
160
+
161
+ tracker.start()
162
+
163
+ document.getElementById('cta')?.addEventListener('click', () => {
164
+ tracker.trackEvent('cta_click', { location: 'hero' })
165
+ })
166
+ </script>
167
+ ```
168
+
169
+ ## API
170
+
171
+ ### `createTracker(config)`
172
+
173
+ Config options:
174
+
175
+ - `siteId: string` — unique identifier for the website/app
176
+ - `endpoint: string` — tracking endpoint URL
177
+ - `sessionKey?: string` — session storage key, defaults to `tithij_session`
178
+ - `includeLeaveEvent?: boolean` — whether to send a `leave` event on unload, defaults to `true`
179
+
180
+ Returned methods:
181
+
182
+ - `start()` — sends the initial `visit` event and binds unload tracking
183
+ - `destroy()` — removes unload tracking
184
+ - `trackPageView()` — manually sends a `visit` event
185
+ - `trackEvent(action, metadata?)` — sends a custom event
186
+
187
+ ## Local development
188
+
189
+ From the package folder:
190
+
191
+ ```bash
192
+ npm install
193
+ npm run build
194
+ npm test
195
+ ```
196
+
197
+ To preview what will be published:
198
+
199
+ ```bash
200
+ npm run pack:check
201
+ ```
202
+
203
+ ## Publishing
204
+
205
+ 1. Update the version in `package.json`
206
+ 2. Verify the package:
207
+ ```bash
208
+ npm test
209
+ npm run pack:check
210
+ ```
211
+ 3. Publish to npm:
212
+ ```bash
213
+ npm publish --access public
214
+ ```
215
+
216
+ ## Notes
217
+
218
+ - This package is intended for browser environments.
219
+ - In React development mode with `StrictMode`, effects may run twice.
package/dist/core.d.ts ADDED
@@ -0,0 +1,15 @@
1
+ export interface TrackerConfig {
2
+ siteId: string;
3
+ endpoint: string;
4
+ sessionKey?: string;
5
+ includeLeaveEvent?: boolean;
6
+ }
7
+ export type TrackMetadata = Record<string, unknown>;
8
+ export interface Tracker {
9
+ start: () => void;
10
+ destroy: () => void;
11
+ trackPageView: () => void;
12
+ trackEvent: (action: string, metadata?: TrackMetadata) => void;
13
+ }
14
+ export declare function createTracker({ siteId, endpoint, sessionKey, includeLeaveEvent, }: TrackerConfig): Tracker;
15
+ //# sourceMappingURL=core.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"core.d.ts","sourceRoot":"","sources":["../src/core.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,aAAa;IAC5B,MAAM,EAAE,MAAM,CAAA;IACd,QAAQ,EAAE,MAAM,CAAA;IAChB,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,iBAAiB,CAAC,EAAE,OAAO,CAAA;CAC5B;AAED,MAAM,MAAM,aAAa,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;AAEnD,MAAM,WAAW,OAAO;IACtB,KAAK,EAAE,MAAM,IAAI,CAAA;IACjB,OAAO,EAAE,MAAM,IAAI,CAAA;IACnB,aAAa,EAAE,MAAM,IAAI,CAAA;IACzB,UAAU,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,aAAa,KAAK,IAAI,CAAA;CAC/D;AAYD,wBAAgB,aAAa,CAAC,EAC5B,MAAM,EACN,QAAQ,EACR,UAA6B,EAC7B,iBAAwB,GACzB,EAAE,aAAa,GAAG,OAAO,CAoEzB"}
package/dist/core.js ADDED
@@ -0,0 +1,68 @@
1
+ function createSessionId() {
2
+ const randomUUID = globalThis.crypto?.randomUUID?.bind(globalThis.crypto);
3
+ if (randomUUID) {
4
+ return randomUUID();
5
+ }
6
+ return `session-${Math.random().toString(36).slice(2)}-${Date.now()}`;
7
+ }
8
+ export function createTracker({ siteId, endpoint, sessionKey = 'tithij_session', includeLeaveEvent = true, }) {
9
+ const isBrowser = typeof window !== 'undefined' &&
10
+ typeof document !== 'undefined' &&
11
+ typeof navigator !== 'undefined';
12
+ const getSessionId = () => {
13
+ if (!isBrowser)
14
+ return 'server';
15
+ const existing = window.sessionStorage.getItem(sessionKey);
16
+ if (existing)
17
+ return existing;
18
+ const id = createSessionId();
19
+ window.sessionStorage.setItem(sessionKey, id);
20
+ return id;
21
+ };
22
+ const send = (action, metadata = {}) => {
23
+ if (!isBrowser)
24
+ return;
25
+ const payload = {
26
+ siteId,
27
+ timestamp: new Date().toISOString(),
28
+ page: window.location.pathname,
29
+ referrer: document.referrer,
30
+ action,
31
+ sessionId: getSessionId(),
32
+ ...metadata,
33
+ };
34
+ const body = JSON.stringify(payload);
35
+ if (typeof navigator.sendBeacon === 'function') {
36
+ const blob = new Blob([body], { type: 'application/json' });
37
+ navigator.sendBeacon(endpoint, blob);
38
+ return;
39
+ }
40
+ void fetch(endpoint, {
41
+ method: 'POST',
42
+ headers: { 'Content-Type': 'application/json' },
43
+ body,
44
+ keepalive: true,
45
+ }).catch(() => { });
46
+ };
47
+ const handleBeforeUnload = () => send('leave');
48
+ return {
49
+ start() {
50
+ send('visit');
51
+ if (includeLeaveEvent && isBrowser) {
52
+ window.addEventListener('beforeunload', handleBeforeUnload);
53
+ }
54
+ },
55
+ destroy() {
56
+ if (includeLeaveEvent && isBrowser) {
57
+ window.removeEventListener('beforeunload', handleBeforeUnload);
58
+ }
59
+ },
60
+ trackPageView() {
61
+ send('visit');
62
+ },
63
+ trackEvent(action, metadata = {}) {
64
+ send(action, metadata);
65
+ },
66
+ };
67
+ }
68
+ //# sourceMappingURL=core.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"core.js","sourceRoot":"","sources":["../src/core.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,3 @@
1
+ export { createTracker } from './core.js';
2
+ export type { Tracker, TrackerConfig, TrackMetadata } from './core.js';
3
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,WAAW,CAAA;AACzC,YAAY,EAAE,OAAO,EAAE,aAAa,EAAE,aAAa,EAAE,MAAM,WAAW,CAAA"}
package/dist/index.js ADDED
@@ -0,0 +1,2 @@
1
+ export { createTracker } from './core.js';
2
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,WAAW,CAAA"}
@@ -0,0 +1,5 @@
1
+ import { type TrackerConfig } from './core.js';
2
+ export interface TrackingProps extends TrackerConfig {
3
+ }
4
+ export declare function Tracking({ siteId, endpoint, sessionKey, includeLeaveEvent, }: TrackingProps): null;
5
+ //# sourceMappingURL=react.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"react.d.ts","sourceRoot":"","sources":["../src/react.tsx"],"names":[],"mappings":"AACA,OAAO,EAAiB,KAAK,aAAa,EAAE,MAAM,WAAW,CAAA;AAE7D,MAAM,WAAW,aAAc,SAAQ,aAAa;CAAG;AAEvD,wBAAgB,QAAQ,CAAC,EACvB,MAAM,EACN,QAAQ,EACR,UAAU,EACV,iBAAiB,GAClB,EAAE,aAAa,QAiBf"}
package/dist/react.js ADDED
@@ -0,0 +1,18 @@
1
+ import { useEffect } from 'react';
2
+ import { createTracker } from './core.js';
3
+ export function Tracking({ siteId, endpoint, sessionKey, includeLeaveEvent, }) {
4
+ useEffect(() => {
5
+ const tracker = createTracker({
6
+ siteId,
7
+ endpoint,
8
+ sessionKey,
9
+ includeLeaveEvent,
10
+ });
11
+ tracker.start();
12
+ return () => {
13
+ tracker.destroy();
14
+ };
15
+ }, [siteId, endpoint, sessionKey, includeLeaveEvent]);
16
+ return null;
17
+ }
18
+ //# sourceMappingURL=react.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"react.js","sourceRoot":"","sources":["../src/react.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,OAAO,CAAA;AACjC,OAAO,EAAE,aAAa,EAAsB,MAAM,WAAW,CAAA;AAI7D,MAAM,UAAU,QAAQ,CAAC,EACvB,MAAM,EACN,QAAQ,EACR,UAAU,EACV,iBAAiB,GACH;IACd,SAAS,CAAC,GAAG,EAAE;QACb,MAAM,OAAO,GAAG,aAAa,CAAC;YAC5B,MAAM;YACN,QAAQ;YACR,UAAU;YACV,iBAAiB;SAClB,CAAC,CAAA;QAEF,OAAO,CAAC,KAAK,EAAE,CAAA;QAEf,OAAO,GAAG,EAAE;YACV,OAAO,CAAC,OAAO,EAAE,CAAA;QACnB,CAAC,CAAA;IACH,CAAC,EAAE,CAAC,MAAM,EAAE,QAAQ,EAAE,UAAU,EAAE,iBAAiB,CAAC,CAAC,CAAA;IAErD,OAAO,IAAI,CAAA;AACb,CAAC"}
package/package.json ADDED
@@ -0,0 +1,58 @@
1
+ {
2
+ "name": "site-vigil-visitors",
3
+ "version": "0.1.0",
4
+ "description": "Lightweight browser visit tracking with optional React integration.",
5
+ "license": "MIT",
6
+ "type": "module",
7
+ "sideEffects": false,
8
+ "main": "./dist/index.js",
9
+ "module": "./dist/index.js",
10
+ "types": "./dist/index.d.ts",
11
+ "exports": {
12
+ ".": {
13
+ "types": "./dist/index.d.ts",
14
+ "import": "./dist/index.js"
15
+ },
16
+ "./react": {
17
+ "types": "./dist/react.d.ts",
18
+ "import": "./dist/react.js"
19
+ }
20
+ },
21
+ "files": [
22
+ "dist",
23
+ "README.md",
24
+ "LICENSE"
25
+ ],
26
+ "keywords": [
27
+ "analytics",
28
+ "tracking",
29
+ "react",
30
+ "browser",
31
+ "visit-tracker"
32
+ ],
33
+ "scripts": {
34
+ "build": "tsc -p tsconfig.json",
35
+ "clean": "node -e \"import('node:fs/promises').then(fs => fs.rm('dist', { recursive: true, force: true }))\"",
36
+ "test": "npm run build && node --test test/**/*.test.js",
37
+ "pack:check": "npm pack --dry-run",
38
+ "prepublishOnly": "npm run test && npm run pack:check"
39
+ },
40
+ "peerDependencies": {
41
+ "react": ">=18"
42
+ },
43
+ "peerDependenciesMeta": {
44
+ "react": {
45
+ "optional": true
46
+ }
47
+ },
48
+ "devDependencies": {
49
+ "@types/react": "^19.2.14",
50
+ "typescript": "^5.9.3"
51
+ },
52
+ "publishConfig": {
53
+ "access": "public"
54
+ },
55
+ "engines": {
56
+ "node": ">=18"
57
+ }
58
+ }