@arthurreira/analytics 0.5.0 → 0.6.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/README.md CHANGED
@@ -1,28 +1,175 @@
1
- af-analytics SDK
1
+ # @arthurreira/analytics
2
2
 
3
- Lightweight analytics SDK (browser client + server helpers) extracted from the af-analytics monorepo.
3
+ Lightweight analytics SDK for Next.js / React apps. Drop-in component + hook for tracking pageviews, clicks, scrolls, copies, errors, CTAs, and searches — with automatic session management.
4
4
 
5
- Quick start
6
-
7
- Install from npm:
5
+ ## Installation
8
6
 
7
+ ```bash
9
8
  pnpm add @arthurreira/analytics
9
+ # or
10
+ npm install @arthurreira/analytics
11
+ # or
12
+ yarn add @arthurreira/analytics
13
+ ```
14
+
15
+ **Peer dependencies:** `react ^18 || ^19`, `next ^14 || ^15 || ^16`
16
+
17
+ ---
18
+
19
+ ## Quick start
20
+
21
+ ### Option A — Drop-in `<Analytics />` component (recommended)
22
+
23
+ Add it once in your root layout. It automatically tracks pageviews, clicks, scroll depth milestones (25 / 50 / 75 / 100 %), text copies, and uncaught JS errors with zero extra code.
24
+
25
+ ```tsx
26
+ // app/layout.tsx (Next.js App Router)
27
+ import { Analytics } from '@arthurreira/analytics/client'
28
+
29
+ export default function RootLayout({ children }: { children: React.ReactNode }) {
30
+ return (
31
+ <html>
32
+ <body>
33
+ {children}
34
+ <Analytics
35
+ apiUrl={process.env.NEXT_PUBLIC_ANALYTICS_URL!}
36
+ apiKey={process.env.NEXT_PUBLIC_ANALYTICS_KEY!}
37
+ />
38
+ </body>
39
+ </html>
40
+ )
41
+ }
42
+ ```
43
+
44
+ ### Option B — `useAnalytics` hook (manual control)
45
+
46
+ Use the hook directly when you need to fire custom events from your own components.
47
+
48
+ ```tsx
49
+ 'use client'
50
+ import { useAnalytics } from '@arthurreira/analytics/client'
51
+
52
+ export function SearchBar() {
53
+ const { trackSearch, trackCTA } = useAnalytics(
54
+ process.env.NEXT_PUBLIC_ANALYTICS_URL!,
55
+ process.env.NEXT_PUBLIC_ANALYTICS_KEY!,
56
+ )
57
+
58
+ return (
59
+ <input
60
+ onKeyDown={(e) => {
61
+ if (e.key === 'Enter') trackSearch(e.currentTarget.value)
62
+ }}
63
+ />
64
+ )
65
+ }
66
+ ```
67
+
68
+ ---
69
+
70
+ ## API reference
71
+
72
+ ### `<Analytics apiUrl apiKey />`
73
+
74
+ | Prop | Type | Description |
75
+ | -------- | -------- | --------------------------------------------------------- |
76
+ | `apiUrl` | `string` | Base URL of your af-analytics backend (no trailing slash) |
77
+ | `apiKey` | `string` | Bearer token for your analytics project |
78
+
79
+ Automatically tracks: **pageview**, **click**, **scroll** (depth milestones), **copy**, **error**.
80
+
81
+ ---
82
+
83
+ ### `useAnalytics(apiUrl, apiKey)`
84
+
85
+ Returns an object with the following tracking functions. Events are queued internally until the session is ready, so it is safe to call them immediately on mount.
10
86
 
11
- Client usage (browser-only entry):
87
+ | Method | Signature | Description |
88
+ | --------------- | ----------------------------------------------- | -------------------------------------------------------- |
89
+ | `trackPageview` | `(path: string) => void` | Manual pageview (e.g. SPA route changes) |
90
+ | `trackClick` | `(e: MouseEvent, element: HTMLElement) => void` | Click with position + element metadata |
91
+ | `trackScroll` | `(depth: number) => void` | Scroll depth percentage (0–100) |
92
+ | `trackCopy` | `() => void` | Text copy event (captures selected text up to 200 chars) |
93
+ | `trackError` | `(error: Error) => void` | JS error with message + stack trace |
94
+ | `trackCTA` | `(ctaId: string, ctaVariant?: string) => void` | CTA button interaction |
95
+ | `trackSearch` | `(query: string) => void` | Search query |
12
96
 
13
- import { initClient } from '@arthurreira/analytics/client'
97
+ ---
14
98
 
15
- Server / build entry (exports server-safe helpers):
99
+ ### Server / build entry
16
100
 
17
- import analytics from '@arthurreira/analytics'
101
+ The default entry (`@arthurreira/analytics`) exports the raw API helpers — safe to import in server components or build scripts because it contains no browser globals.
18
102
 
19
- Building locally
103
+ ```ts
104
+ import {
105
+ createSession,
106
+ trackPageview,
107
+ trackClick,
108
+ trackScroll,
109
+ trackCopy,
110
+ trackError,
111
+ trackCTA,
112
+ trackSearch,
113
+ } from '@arthurreira/analytics'
114
+ ```
20
115
 
116
+ ---
117
+
118
+ ## Session management
119
+
120
+ Sessions are created automatically on first load and stored in `localStorage` (`af_session_id`). A session expires after **30 minutes of inactivity**. When the user leaves or hides the tab the SDK fires a `sendBeacon` to close the session gracefully.
121
+
122
+ Visitor identity is persisted across sessions via `af_analytics_visitor_id` (a random UUID stored in `localStorage`).
123
+
124
+ ---
125
+
126
+ ## Data collected
127
+
128
+ On session start the SDK sends:
129
+
130
+ - Visitor ID, language, timezone
131
+ - Screen / viewport dimensions
132
+ - Referrer URL, landing page
133
+ - UTM parameters (`utm_source`, `utm_medium`, `utm_campaign`, `utm_term`, `utm_content`)
134
+
135
+ On each event:
136
+
137
+ - `session_id`, `event_type`, `path`, `page_url`
138
+ - Event-specific fields (position, element, scroll depth, error stack, etc.)
139
+
140
+ ---
141
+
142
+ ## Environment variables
143
+
144
+ | Variable | Description |
145
+ | --------------------------- | ------------------------------------------------------- |
146
+ | `NEXT_PUBLIC_ANALYTICS_URL` | Backend base URL, e.g. `https://analytics.example.com/api` |
147
+ | `NEXT_PUBLIC_ANALYTICS_KEY` | Project API key (Bearer token) |
148
+
149
+ ---
150
+
151
+ ## Package exports
152
+
153
+ | Import path | Contents | Use in |
154
+ | -------------------------------- | ------------------------------------------- | ----------------- |
155
+ | `@arthurreira/analytics` | Raw API helpers (no browser globals) | Server / build |
156
+ | `@arthurreira/analytics/client` | `Analytics` component + `useAnalytics` hook | Client components |
157
+
158
+ ---
159
+
160
+ ## Building locally
161
+
162
+ ```bash
21
163
  pnpm install
22
164
  pnpm build
165
+ ```
166
+
167
+ ## Publishing
168
+
169
+ Tag a release (e.g. `v0.5.0`) after adding an `NPM_TOKEN` secret to the repository to trigger the CI publish workflow.
23
170
 
24
- Publishing
171
+ ---
25
172
 
26
- - Add an `NPM_TOKEN` secret to the repository and tag a release (e.g. `v0.1.0`) to trigger CI publish.
173
+ ## License
27
174
 
28
- License: MIT
175
+ MIT
package/dist/client.js CHANGED
@@ -14,6 +14,18 @@ var BASE_FIELDS = (sessionId, eventType, path) => ({
14
14
  path,
15
15
  page_url: typeof window !== "undefined" ? window.location.href : ""
16
16
  });
17
+ function getGpu() {
18
+ try {
19
+ const canvas = document.createElement("canvas");
20
+ const gl = canvas.getContext("webgl");
21
+ if (!gl) return null;
22
+ const ext = gl.getExtension("WEBGL_debug_renderer_info");
23
+ if (!ext) return null;
24
+ return gl.getParameter(ext.UNMASKED_RENDERER_WEBGL);
25
+ } catch {
26
+ return null;
27
+ }
28
+ }
17
29
  async function createSession(apiUrl, apiKey, visitorId) {
18
30
  const sessionData = { visitor_id: visitorId };
19
31
  if (typeof window !== "undefined") {
@@ -26,6 +38,12 @@ async function createSession(apiUrl, apiKey, visitorId) {
26
38
  sessionData.referrer = document.referrer || null;
27
39
  sessionData.landing_page = window.location.pathname || null;
28
40
  sessionData.timezone = Intl.DateTimeFormat().resolvedOptions().timeZone || null;
41
+ sessionData.cpu_threads = navigator.hardwareConcurrency ?? null;
42
+ sessionData.memory_gb = nav.deviceMemory ?? null;
43
+ sessionData.gpu = getGpu();
44
+ const conn = nav.connection ?? nav.mozConnection ?? nav.webkitConnection ?? null;
45
+ sessionData.network_type = conn?.effectiveType ?? null;
46
+ sessionData.connection_speed_mbps = conn?.downlink ?? null;
29
47
  const params = new URLSearchParams(window.location.search);
30
48
  sessionData.utm_source = params.get("utm_source");
31
49
  sessionData.utm_medium = params.get("utm_medium");
package/dist/index.js CHANGED
@@ -5,6 +5,18 @@ var BASE_FIELDS = (sessionId, eventType, path) => ({
5
5
  path,
6
6
  page_url: typeof window !== "undefined" ? window.location.href : ""
7
7
  });
8
+ function getGpu() {
9
+ try {
10
+ const canvas = document.createElement("canvas");
11
+ const gl = canvas.getContext("webgl");
12
+ if (!gl) return null;
13
+ const ext = gl.getExtension("WEBGL_debug_renderer_info");
14
+ if (!ext) return null;
15
+ return gl.getParameter(ext.UNMASKED_RENDERER_WEBGL);
16
+ } catch {
17
+ return null;
18
+ }
19
+ }
8
20
  async function createSession(apiUrl, apiKey, visitorId) {
9
21
  const sessionData = { visitor_id: visitorId };
10
22
  if (typeof window !== "undefined") {
@@ -17,6 +29,12 @@ async function createSession(apiUrl, apiKey, visitorId) {
17
29
  sessionData.referrer = document.referrer || null;
18
30
  sessionData.landing_page = window.location.pathname || null;
19
31
  sessionData.timezone = Intl.DateTimeFormat().resolvedOptions().timeZone || null;
32
+ sessionData.cpu_threads = navigator.hardwareConcurrency ?? null;
33
+ sessionData.memory_gb = nav.deviceMemory ?? null;
34
+ sessionData.gpu = getGpu();
35
+ const conn = nav.connection ?? nav.mozConnection ?? nav.webkitConnection ?? null;
36
+ sessionData.network_type = conn?.effectiveType ?? null;
37
+ sessionData.connection_speed_mbps = conn?.downlink ?? null;
20
38
  const params = new URLSearchParams(window.location.search);
21
39
  sessionData.utm_source = params.get("utm_source");
22
40
  sessionData.utm_medium = params.get("utm_medium");
package/package.json CHANGED
@@ -1,5 +1,5 @@
1
1
  { "name": "@arthurreira/analytics",
2
- "version": "0.5.0",
2
+ "version": "0.6.0",
3
3
  "type": "module",
4
4
  "scripts": {
5
5
  "build": "tsup",