@borisch/snitch 1.0.1 → 1.0.2
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 +288 -28
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -11,12 +11,23 @@ npm install @borisch/snitch
|
|
|
11
11
|
## Quick Start
|
|
12
12
|
|
|
13
13
|
```ts
|
|
14
|
-
import {
|
|
14
|
+
import {
|
|
15
|
+
snitch,
|
|
16
|
+
sessionPlugin,
|
|
17
|
+
launchPlugin,
|
|
18
|
+
scrollPlugin,
|
|
19
|
+
locationPlugin,
|
|
20
|
+
beaconTransportPlugin,
|
|
21
|
+
debugLoggerPlugin,
|
|
22
|
+
} from '@borisch/snitch'
|
|
15
23
|
|
|
16
24
|
const captureEvent = snitch(
|
|
17
25
|
sessionPlugin(),
|
|
18
|
-
|
|
19
|
-
|
|
26
|
+
launchPlugin(),
|
|
27
|
+
scrollPlugin(),
|
|
28
|
+
locationPlugin({ captureLocationChange: true }),
|
|
29
|
+
beaconTransportPlugin({ hostname: 'analytics.example.com' }),
|
|
30
|
+
debugLoggerPlugin(),
|
|
20
31
|
)
|
|
21
32
|
|
|
22
33
|
// Manually capture events
|
|
@@ -25,43 +36,283 @@ captureEvent('button_click', { buttonId: 'signup' })
|
|
|
25
36
|
|
|
26
37
|
The `snitch()` function accepts any number of plugins and returns a `captureEvent` function. Plugins can:
|
|
27
38
|
|
|
28
|
-
- **Provide event parameters**
|
|
39
|
+
- **Provide event parameters** — automatically attached to every event
|
|
29
40
|
- **Emit events** on their own (e.g. scroll milestones, page views)
|
|
30
|
-
- **Transport events** to a backend
|
|
41
|
+
- **Transport events** to a backend
|
|
31
42
|
- **Intercept events** before they are sent
|
|
43
|
+
- **Expose mixins** — additional methods attached to the `captureEvent` function
|
|
32
44
|
|
|
33
45
|
## Plugins
|
|
34
46
|
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
|
47
|
+
### `sessionPlugin()`
|
|
48
|
+
|
|
49
|
+
Manages user sessions using `localStorage`. A new session starts when:
|
|
50
|
+
|
|
51
|
+
- No previous session exists
|
|
52
|
+
- The previous session has been inactive for 30+ minutes
|
|
53
|
+
- UTM parameters are present in the URL
|
|
54
|
+
|
|
55
|
+
If a session expires between events, a new session is started automatically before the next event is sent.
|
|
56
|
+
|
|
57
|
+
Emits: `sessionStart`
|
|
58
|
+
|
|
59
|
+
Attaches to every event:
|
|
60
|
+
| Param | Description |
|
|
61
|
+
|-------|-------------|
|
|
62
|
+
| `sid` | Unique session ID |
|
|
63
|
+
| `scnt` | Total session count for this device |
|
|
64
|
+
| `set` | Milliseconds since session started |
|
|
65
|
+
| `sutm` | Compact UTM parameters from the URL that started the session |
|
|
66
|
+
|
|
67
|
+
---
|
|
68
|
+
|
|
69
|
+
### `launchPlugin()`
|
|
70
|
+
|
|
71
|
+
Captures a `launch` event when the tracker initializes. Records whether the page runs inside an iframe.
|
|
72
|
+
|
|
73
|
+
Emits: `launch` with `{ ifr: "true" | "false" }`
|
|
74
|
+
|
|
75
|
+
Attaches to every event:
|
|
76
|
+
| Param | Description |
|
|
77
|
+
|-------|-------------|
|
|
78
|
+
| `lid` | Unique launch ID (generated per `snitch()` call) |
|
|
79
|
+
| `ref` | `document.referrer` at initialization time |
|
|
80
|
+
|
|
81
|
+
---
|
|
82
|
+
|
|
83
|
+
### `scrollPlugin()`
|
|
84
|
+
|
|
85
|
+
Tracks scroll depth. Emits events when the user scrolls past depth milestones (25%, 50%, 75%, 100%). The scroll depth cache resets whenever a `locationChange` or `screenChange` event occurs, so milestones are tracked per-page.
|
|
86
|
+
|
|
87
|
+
Emits: `scroll` with `{ depthPercent: number }`
|
|
88
|
+
|
|
89
|
+
---
|
|
90
|
+
|
|
91
|
+
### `locationPlugin(options)`
|
|
92
|
+
|
|
93
|
+
Tracks the current page URL and optionally emits events on URL changes (SPA navigation, `pushState`, etc.).
|
|
94
|
+
|
|
95
|
+
**Options:**
|
|
96
|
+
| Option | Type | Description |
|
|
97
|
+
|--------|------|-------------|
|
|
98
|
+
| `captureLocationChange` | `boolean` | Whether to listen for URL changes and emit events |
|
|
99
|
+
| `getLocation` | `() => string` | Custom location getter (defaults to `window.location.href`) |
|
|
100
|
+
|
|
101
|
+
Emits (when `captureLocationChange` is `true`): `locationChange` with `{ phref: string }` (previous URL)
|
|
102
|
+
|
|
103
|
+
Attaches to every event:
|
|
104
|
+
| Param | Description |
|
|
105
|
+
|-------|-------------|
|
|
106
|
+
| `href` | Current page URL (truncated to 500 characters) |
|
|
107
|
+
|
|
108
|
+
---
|
|
109
|
+
|
|
110
|
+
### `engagementPlugin(options?)`
|
|
111
|
+
|
|
112
|
+
Periodically emits engagement events while the page is visible. Events are suppressed when the tab is hidden (`document.hidden === true`).
|
|
113
|
+
|
|
114
|
+
**Options:**
|
|
115
|
+
| Option | Type | Default | Description |
|
|
116
|
+
|--------|------|---------|-------------|
|
|
117
|
+
| `engagementTrackingIntervalMsec` | `number` | `10000` | Interval in milliseconds between engagement pings |
|
|
118
|
+
|
|
119
|
+
Emits: `engage` (at configured interval, only when tab is visible)
|
|
120
|
+
|
|
121
|
+
---
|
|
122
|
+
|
|
123
|
+
### `screenPlugin(initialScreen)`
|
|
124
|
+
|
|
125
|
+
Tracks screen/page views within an app. Maintains current and previous screen state.
|
|
126
|
+
|
|
127
|
+
**Options:**
|
|
128
|
+
| Option | Type | Description |
|
|
129
|
+
|--------|------|-------------|
|
|
130
|
+
| `screenType` | `string` | Type/category of the initial screen |
|
|
131
|
+
| `screenId` | `string?` | Optional screen identifier |
|
|
132
|
+
|
|
133
|
+
To change screens, call `captureEvent('screenChange', { screenType: 'catalog', screenId: 'page2' })`. The plugin automatically injects previous screen params and removes the raw `screenType`/`screenId` from the event payload.
|
|
134
|
+
|
|
135
|
+
Attaches to every event:
|
|
136
|
+
| Param | Description |
|
|
137
|
+
|-------|-------------|
|
|
138
|
+
| `sct` | Current screen type |
|
|
139
|
+
| `scid` | Current screen ID (or `""`) |
|
|
140
|
+
|
|
141
|
+
Attaches to `screenChange` events:
|
|
142
|
+
| Param | Description |
|
|
143
|
+
|-------|-------------|
|
|
144
|
+
| `psct` | Previous screen type |
|
|
145
|
+
| `pscid` | Previous screen ID (or `""`) |
|
|
146
|
+
|
|
147
|
+
---
|
|
148
|
+
|
|
149
|
+
### `exceptionsPlugin()`
|
|
150
|
+
|
|
151
|
+
Captures unhandled errors and promise rejections globally.
|
|
152
|
+
|
|
153
|
+
Emits:
|
|
154
|
+
|
|
155
|
+
- `uncaughtError` with `{ message, filename, lineno, colno, error }`
|
|
156
|
+
- `unhandledRejection` with `{ reason }`
|
|
157
|
+
|
|
158
|
+
---
|
|
159
|
+
|
|
160
|
+
### `webVitalsPlugin()`
|
|
161
|
+
|
|
162
|
+
Reports [Core Web Vitals](https://web.dev/vitals/) using the `web-vitals` library. Tracks CLS, FID, LCP, TTFB, and FCP.
|
|
163
|
+
|
|
164
|
+
Emits: `webVital` with `{ name, value, delta, metricId }`
|
|
165
|
+
|
|
166
|
+
---
|
|
167
|
+
|
|
168
|
+
### `flagPlugin(options)`
|
|
169
|
+
|
|
170
|
+
Feature flag evaluation plugin. Adds `getFlag()` and `getFlags()` methods to the `captureEvent` function via mixins.
|
|
171
|
+
|
|
172
|
+
**Options:**
|
|
173
|
+
| Option | Type | Description |
|
|
174
|
+
|--------|------|-------------|
|
|
175
|
+
| `flagApiEndpoint` | `string` | URL of the flag evaluation API |
|
|
176
|
+
| `userIdResolver` | `() => string \| null \| undefined` | Optional custom user ID resolver |
|
|
177
|
+
|
|
178
|
+
User ID is resolved in order: custom resolver → VK user ID from URL → Top Mail.ru counter cookie → auto-generated anonymous ID (persisted in `localStorage`).
|
|
179
|
+
|
|
180
|
+
**Usage:**
|
|
181
|
+
|
|
182
|
+
```ts
|
|
183
|
+
const captureEvent = snitch(flagPlugin({ flagApiEndpoint: 'https://flags.example.com/api' })) as any
|
|
184
|
+
|
|
185
|
+
const flag = await captureEvent.getFlag('new-feature')
|
|
186
|
+
// { flagKey: 'new-feature', match: true, variant: 'control', attachment: '...' }
|
|
187
|
+
|
|
188
|
+
const flags = await captureEvent.getFlags(['feature-a', 'feature-b'])
|
|
189
|
+
```
|
|
190
|
+
|
|
191
|
+
Emits:
|
|
192
|
+
|
|
193
|
+
- `flagEvaluationComplete` with full evaluation response
|
|
194
|
+
- `flagEvaluationFailed` with `{ flagKey, errorMessage }`
|
|
195
|
+
|
|
196
|
+
---
|
|
197
|
+
|
|
198
|
+
### `useragentPlugin()`
|
|
199
|
+
|
|
200
|
+
Attaches the browser user agent string to every event.
|
|
201
|
+
|
|
202
|
+
Attaches to every event:
|
|
203
|
+
| Param | Description |
|
|
204
|
+
|-------|-------------|
|
|
205
|
+
| `ua` | `navigator.userAgent` |
|
|
206
|
+
|
|
207
|
+
---
|
|
208
|
+
|
|
209
|
+
### `debugLoggerPlugin()`
|
|
210
|
+
|
|
211
|
+
Development helper. Logs every event to the browser console with timestamps and time deltas between events. When the event has a non-empty payload, it is also rendered via `console.table()`.
|
|
212
|
+
|
|
213
|
+
**Silent by default.** To enable, set a `localStorage` flag:
|
|
214
|
+
|
|
215
|
+
```js
|
|
216
|
+
localStorage.setItem('snitch:debug', 'true')
|
|
217
|
+
```
|
|
218
|
+
|
|
219
|
+
The flag is read once when `debugLoggerPlugin()` is called. To disable, remove the flag and reload:
|
|
220
|
+
|
|
221
|
+
```js
|
|
222
|
+
localStorage.removeItem('snitch:debug')
|
|
223
|
+
```
|
|
224
|
+
|
|
225
|
+
---
|
|
49
226
|
|
|
50
227
|
## Transports
|
|
51
228
|
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
|
229
|
+
### `beaconTransportPlugin(options?)`
|
|
230
|
+
|
|
231
|
+
Sends events via [`navigator.sendBeacon()`](https://developer.mozilla.org/en-US/docs/Web/API/Navigator/sendBeacon). All event data is encoded as URL query parameters — designed for CDN log-based analytics where request URLs are parsed from access logs.
|
|
232
|
+
|
|
233
|
+
**Options:**
|
|
234
|
+
| Option | Type | Default | Description |
|
|
235
|
+
|--------|------|---------|-------------|
|
|
236
|
+
| `hostname` | `string` | `window.location.hostname` | Target hostname |
|
|
237
|
+
| `path` | `string` | `/_snitch` | URL path |
|
|
238
|
+
|
|
239
|
+
Requests are sent to: `{protocol}//{hostname}{path}?event={name}&...params`
|
|
240
|
+
|
|
241
|
+
---
|
|
242
|
+
|
|
243
|
+
### `s2sTransportPlugin(options)`
|
|
244
|
+
|
|
245
|
+
Sends events via `fetch()` GET requests over HTTPS. Fire-and-forget (errors are silently caught). Designed for server-side environments (Node 18+, Cloudflare Workers) or any environment with `fetch`.
|
|
246
|
+
|
|
247
|
+
**Options:**
|
|
248
|
+
| Option | Type | Default | Description |
|
|
249
|
+
|--------|------|---------|-------------|
|
|
250
|
+
| `hostname` | `string` | _required_ | Target hostname |
|
|
251
|
+
| `path` | `string` | `/_snitch` | URL path |
|
|
252
|
+
| `s2sToken` | `string` | — | Optional auth token (sent as a query parameter) |
|
|
253
|
+
|
|
254
|
+
Requests are sent to: `https://{hostname}{path}?event={name}&...params[&s2sToken=...]`
|
|
255
|
+
|
|
256
|
+
---
|
|
257
|
+
|
|
258
|
+
### `topmailruTransportPlugin(counterId, userIdResolver?)`
|
|
259
|
+
|
|
260
|
+
Sends events to [Top Mail.ru](https://top.mail.ru/) analytics counter by pushing to the `window._tmr` queue.
|
|
261
|
+
|
|
262
|
+
**Parameters:**
|
|
263
|
+
| Param | Type | Description |
|
|
264
|
+
|-------|------|-------------|
|
|
265
|
+
| `counterId` | `string` | Top Mail.ru counter ID (required) |
|
|
266
|
+
| `userIdResolver` | `() => string \| null \| undefined` | Optional custom user ID resolver |
|
|
267
|
+
|
|
268
|
+
User ID resolution order: custom resolver → TMR counter cookie → auto-generated anonymous ID.
|
|
269
|
+
|
|
270
|
+
---
|
|
271
|
+
|
|
272
|
+
### `vkBridgeTransportPlugin()`
|
|
273
|
+
|
|
274
|
+
Sends events via [VK Bridge](https://dev.vk.com/mini-apps/bridge) for VK Mini Apps. Extracts `vk_user_id` from the URL. Each event triggers two VK Bridge calls: `VKWebAppTrackEvent` and `VKWebAppSendCustomEvent`. All param values are coerced to strings to work around iOS VK Bridge limitations.
|
|
275
|
+
|
|
276
|
+
---
|
|
277
|
+
|
|
278
|
+
## Platform-Specific Plugins
|
|
279
|
+
|
|
280
|
+
### `vkmaLaunchPlugin()`
|
|
281
|
+
|
|
282
|
+
A VK Mini Apps variant of `launchPlugin`. Parses VK Mini App launch parameters from the URL (`vk_user_id`, `vk_app_id`, `vk_platform`, `vk_ref`, etc.).
|
|
283
|
+
|
|
284
|
+
Emits: `launch` (with iframe flag + VKMA params), `mt_internal_launch`
|
|
285
|
+
|
|
286
|
+
Attaches to every event:
|
|
287
|
+
| Param | Description |
|
|
288
|
+
|-------|-------------|
|
|
289
|
+
| `lid` | Unique launch ID |
|
|
290
|
+
| `ref` | `document.referrer` |
|
|
291
|
+
| `mauid` | VK user ID |
|
|
292
|
+
| `maaid` | VK app ID |
|
|
293
|
+
| `malang` | VK language |
|
|
294
|
+
| `mac` | VK access token settings |
|
|
295
|
+
| `map` | VK platform |
|
|
296
|
+
| `maref` | VK ref |
|
|
297
|
+
|
|
298
|
+
---
|
|
58
299
|
|
|
59
300
|
## Types
|
|
60
301
|
|
|
61
302
|
All public types are exported for TypeScript consumers:
|
|
62
303
|
|
|
63
304
|
```ts
|
|
64
|
-
import type {
|
|
305
|
+
import type {
|
|
306
|
+
Plugin,
|
|
307
|
+
EventTransport,
|
|
308
|
+
EventSource,
|
|
309
|
+
EventPayloadParamsProvider,
|
|
310
|
+
InitializationHandler,
|
|
311
|
+
BeforeCaptureEventHandler,
|
|
312
|
+
MixinProvider,
|
|
313
|
+
TrackerEventPayload,
|
|
314
|
+
EventHandler,
|
|
315
|
+
} from '@borisch/snitch'
|
|
65
316
|
```
|
|
66
317
|
|
|
67
318
|
## Writing a Custom Plugin
|
|
@@ -73,12 +324,21 @@ import type { Plugin } from '@borisch/snitch'
|
|
|
73
324
|
|
|
74
325
|
function myPlugin(): Plugin {
|
|
75
326
|
return {
|
|
327
|
+
// Attach params to every event
|
|
76
328
|
getEventPayloadParams() {
|
|
77
329
|
return { customParam: 'value' }
|
|
78
330
|
},
|
|
331
|
+
// React to events before transport
|
|
332
|
+
beforeCaptureEvent(eventName, eventParams) {
|
|
333
|
+
// filter, modify, log, etc.
|
|
334
|
+
},
|
|
335
|
+
// Transport events
|
|
79
336
|
sendEvent(eventName, eventParams) {
|
|
80
|
-
|
|
81
|
-
|
|
337
|
+
fetch('/analytics', {
|
|
338
|
+
method: 'POST',
|
|
339
|
+
body: JSON.stringify({ eventName, ...eventParams }),
|
|
340
|
+
})
|
|
341
|
+
},
|
|
82
342
|
}
|
|
83
343
|
}
|
|
84
344
|
```
|