@borisch/snitch 1.0.1 → 1.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/README.md +383 -28
- package/dist/index.d.ts +2 -0
- package/dist/index.js +15 -11
- package/dist/index.js.map +1 -1
- package/dist/packages/snitch-plugin-debug-logger/debug-logger.d.ts +1 -1
- package/dist/packages/snitch-plugin-debug-logger/debug-logger.js +12 -4
- package/dist/packages/snitch-plugin-debug-logger/debug-logger.js.map +1 -1
- package/dist/packages/snitch-plugin-device/device.d.ts +2 -0
- package/dist/packages/snitch-plugin-device/device.js +30 -0
- package/dist/packages/snitch-plugin-device/device.js.map +1 -0
- package/dist/packages/snitch-plugin-device/index.d.ts +2 -0
- package/dist/packages/snitch-plugin-device/index.js +8 -0
- package/dist/packages/snitch-plugin-device/index.js.map +1 -0
- package/dist/packages/snitch-plugin-user/index.d.ts +2 -0
- package/dist/packages/snitch-plugin-user/index.js +8 -0
- package/dist/packages/snitch-plugin-user/index.js.map +1 -0
- package/dist/packages/snitch-plugin-user/user.d.ts +2 -0
- package/dist/packages/snitch-plugin-user/user.js +35 -0
- package/dist/packages/snitch-plugin-user/user.js.map +1 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -11,12 +11,27 @@ npm install @borisch/snitch
|
|
|
11
11
|
## Quick Start
|
|
12
12
|
|
|
13
13
|
```ts
|
|
14
|
-
import {
|
|
14
|
+
import {
|
|
15
|
+
snitch,
|
|
16
|
+
devicePlugin,
|
|
17
|
+
userPlugin,
|
|
18
|
+
sessionPlugin,
|
|
19
|
+
launchPlugin,
|
|
20
|
+
scrollPlugin,
|
|
21
|
+
locationPlugin,
|
|
22
|
+
beaconTransportPlugin,
|
|
23
|
+
debugLoggerPlugin,
|
|
24
|
+
} from '@borisch/snitch'
|
|
15
25
|
|
|
16
26
|
const captureEvent = snitch(
|
|
27
|
+
devicePlugin(),
|
|
28
|
+
userPlugin(),
|
|
17
29
|
sessionPlugin(),
|
|
18
|
-
|
|
19
|
-
|
|
30
|
+
launchPlugin(),
|
|
31
|
+
scrollPlugin(),
|
|
32
|
+
locationPlugin({ captureLocationChange: true }),
|
|
33
|
+
beaconTransportPlugin({ hostname: 'analytics.example.com' }),
|
|
34
|
+
debugLoggerPlugin(),
|
|
20
35
|
)
|
|
21
36
|
|
|
22
37
|
// Manually capture events
|
|
@@ -25,43 +40,374 @@ captureEvent('button_click', { buttonId: 'signup' })
|
|
|
25
40
|
|
|
26
41
|
The `snitch()` function accepts any number of plugins and returns a `captureEvent` function. Plugins can:
|
|
27
42
|
|
|
28
|
-
- **Provide event parameters**
|
|
43
|
+
- **Provide event parameters** — automatically attached to every event
|
|
29
44
|
- **Emit events** on their own (e.g. scroll milestones, page views)
|
|
30
|
-
- **Transport events** to a backend
|
|
45
|
+
- **Transport events** to a backend
|
|
31
46
|
- **Intercept events** before they are sent
|
|
47
|
+
- **Expose mixins** — additional methods attached to the `captureEvent` function
|
|
32
48
|
|
|
33
49
|
## Plugins
|
|
34
50
|
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
|
51
|
+
### `sessionPlugin()`
|
|
52
|
+
|
|
53
|
+
Manages user sessions using `localStorage`. A new session starts when:
|
|
54
|
+
|
|
55
|
+
- No previous session exists
|
|
56
|
+
- The previous session has been inactive for 30+ minutes
|
|
57
|
+
- UTM parameters are present in the URL
|
|
58
|
+
|
|
59
|
+
If a session expires between events, a new session is started automatically before the next event is sent.
|
|
60
|
+
|
|
61
|
+
Emits: `sessionStart`
|
|
62
|
+
|
|
63
|
+
Attaches to every event:
|
|
64
|
+
| Param | Description |
|
|
65
|
+
|-------|-------------|
|
|
66
|
+
| `sid` | Unique session ID |
|
|
67
|
+
| `scnt` | Total session count for this device |
|
|
68
|
+
| `set` | Milliseconds since session started |
|
|
69
|
+
| `sutm` | Compact UTM parameters from the URL that started the session |
|
|
70
|
+
|
|
71
|
+
---
|
|
72
|
+
|
|
73
|
+
### `launchPlugin()`
|
|
74
|
+
|
|
75
|
+
Captures a `launch` event when the tracker initializes. Records whether the page runs inside an iframe.
|
|
76
|
+
|
|
77
|
+
Emits: `launch` with `{ ifr: "true" | "false" }`
|
|
78
|
+
|
|
79
|
+
Attaches to every event:
|
|
80
|
+
| Param | Description |
|
|
81
|
+
|-------|-------------|
|
|
82
|
+
| `lid` | Unique launch ID (generated per `snitch()` call) |
|
|
83
|
+
| `ref` | `document.referrer` at initialization time |
|
|
84
|
+
|
|
85
|
+
---
|
|
86
|
+
|
|
87
|
+
### `scrollPlugin()`
|
|
88
|
+
|
|
89
|
+
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.
|
|
90
|
+
|
|
91
|
+
Emits: `scroll` with `{ depthPercent: number }`
|
|
92
|
+
|
|
93
|
+
---
|
|
94
|
+
|
|
95
|
+
### `locationPlugin(options)`
|
|
96
|
+
|
|
97
|
+
Tracks the current page URL and optionally emits events on URL changes (SPA navigation, `pushState`, etc.).
|
|
98
|
+
|
|
99
|
+
**Options:**
|
|
100
|
+
| Option | Type | Description |
|
|
101
|
+
|--------|------|-------------|
|
|
102
|
+
| `captureLocationChange` | `boolean` | Whether to listen for URL changes and emit events |
|
|
103
|
+
| `getLocation` | `() => string` | Custom location getter (defaults to `window.location.href`) |
|
|
104
|
+
|
|
105
|
+
Emits (when `captureLocationChange` is `true`): `locationChange` with `{ phref: string }` (previous URL)
|
|
106
|
+
|
|
107
|
+
Attaches to every event:
|
|
108
|
+
| Param | Description |
|
|
109
|
+
|-------|-------------|
|
|
110
|
+
| `href` | Current page URL (truncated to 500 characters) |
|
|
111
|
+
|
|
112
|
+
---
|
|
113
|
+
|
|
114
|
+
### `engagementPlugin(options?)`
|
|
115
|
+
|
|
116
|
+
Periodically emits engagement events while the page is visible. Events are suppressed when the tab is hidden (`document.hidden === true`).
|
|
117
|
+
|
|
118
|
+
**Options:**
|
|
119
|
+
| Option | Type | Default | Description |
|
|
120
|
+
|--------|------|---------|-------------|
|
|
121
|
+
| `engagementTrackingIntervalMsec` | `number` | `10000` | Interval in milliseconds between engagement pings |
|
|
122
|
+
|
|
123
|
+
Emits: `engage` (at configured interval, only when tab is visible)
|
|
124
|
+
|
|
125
|
+
---
|
|
126
|
+
|
|
127
|
+
### `screenPlugin(initialScreen)`
|
|
128
|
+
|
|
129
|
+
Tracks screen/page views within an app. Maintains current and previous screen state.
|
|
130
|
+
|
|
131
|
+
**Options:**
|
|
132
|
+
| Option | Type | Description |
|
|
133
|
+
|--------|------|-------------|
|
|
134
|
+
| `screenType` | `string` | Type/category of the initial screen |
|
|
135
|
+
| `screenId` | `string?` | Optional screen identifier |
|
|
136
|
+
|
|
137
|
+
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.
|
|
138
|
+
|
|
139
|
+
Attaches to every event:
|
|
140
|
+
| Param | Description |
|
|
141
|
+
|-------|-------------|
|
|
142
|
+
| `sct` | Current screen type |
|
|
143
|
+
| `scid` | Current screen ID (or `""`) |
|
|
144
|
+
|
|
145
|
+
Attaches to `screenChange` events:
|
|
146
|
+
| Param | Description |
|
|
147
|
+
|-------|-------------|
|
|
148
|
+
| `psct` | Previous screen type |
|
|
149
|
+
| `pscid` | Previous screen ID (or `""`) |
|
|
150
|
+
|
|
151
|
+
---
|
|
152
|
+
|
|
153
|
+
### `exceptionsPlugin()`
|
|
154
|
+
|
|
155
|
+
Captures unhandled errors and promise rejections globally.
|
|
156
|
+
|
|
157
|
+
Emits:
|
|
158
|
+
|
|
159
|
+
- `uncaughtError` with `{ message, filename, lineno, colno, error }`
|
|
160
|
+
- `unhandledRejection` with `{ reason }`
|
|
161
|
+
|
|
162
|
+
---
|
|
163
|
+
|
|
164
|
+
### `webVitalsPlugin()`
|
|
165
|
+
|
|
166
|
+
Reports [Core Web Vitals](https://web.dev/vitals/) using the `web-vitals` library. Tracks CLS, FID, LCP, TTFB, and FCP.
|
|
167
|
+
|
|
168
|
+
Emits: `webVital` with `{ name, value, delta, metricId }`
|
|
169
|
+
|
|
170
|
+
---
|
|
171
|
+
|
|
172
|
+
### `flagPlugin(options)`
|
|
173
|
+
|
|
174
|
+
Feature flag evaluation plugin. Adds `getFlag()` and `getFlags()` methods to the `captureEvent` function via mixins.
|
|
175
|
+
|
|
176
|
+
**Options:**
|
|
177
|
+
| Option | Type | Description |
|
|
178
|
+
|--------|------|-------------|
|
|
179
|
+
| `flagApiEndpoint` | `string` | URL of the flag evaluation API |
|
|
180
|
+
| `userIdResolver` | `() => string \| null \| undefined` | Optional custom user ID resolver |
|
|
181
|
+
|
|
182
|
+
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`).
|
|
183
|
+
|
|
184
|
+
**Usage:**
|
|
185
|
+
|
|
186
|
+
```ts
|
|
187
|
+
const captureEvent = snitch(flagPlugin({ flagApiEndpoint: 'https://flags.example.com/api' })) as any
|
|
188
|
+
|
|
189
|
+
const flag = await captureEvent.getFlag('new-feature')
|
|
190
|
+
// { flagKey: 'new-feature', match: true, variant: 'control', attachment: '...' }
|
|
191
|
+
|
|
192
|
+
const flags = await captureEvent.getFlags(['feature-a', 'feature-b'])
|
|
193
|
+
```
|
|
194
|
+
|
|
195
|
+
Emits:
|
|
196
|
+
|
|
197
|
+
- `flagEvaluationComplete` with full evaluation response
|
|
198
|
+
- `flagEvaluationFailed` with `{ flagKey, errorMessage }`
|
|
199
|
+
|
|
200
|
+
---
|
|
201
|
+
|
|
202
|
+
### `useragentPlugin()`
|
|
203
|
+
|
|
204
|
+
Attaches the browser user agent string to every event.
|
|
205
|
+
|
|
206
|
+
Attaches to every event:
|
|
207
|
+
| Param | Description |
|
|
208
|
+
|-------|-------------|
|
|
209
|
+
| `ua` | `navigator.userAgent` |
|
|
210
|
+
|
|
211
|
+
---
|
|
212
|
+
|
|
213
|
+
### `devicePlugin()`
|
|
214
|
+
|
|
215
|
+
Generates a persistent device (browser) identifier stored in `localStorage` under the key `snitch:did`. The ID is created once and reused forever across all sessions — it survives page reloads, tab closes, and new sessions. It only resets if the user clears their browser storage.
|
|
216
|
+
|
|
217
|
+
If `localStorage` is unavailable, a new ID is generated per `snitch()` call (in-memory only).
|
|
218
|
+
|
|
219
|
+
Attaches to every event:
|
|
220
|
+
| Param | Description |
|
|
221
|
+
|-------|-------------|
|
|
222
|
+
| `did` | Persistent device ID |
|
|
223
|
+
|
|
224
|
+
---
|
|
225
|
+
|
|
226
|
+
### `userPlugin(userId?)`
|
|
227
|
+
|
|
228
|
+
Tracks the current user. Exposes `.setUserId(id)` and `.clearUserId()` methods on the `captureEvent` function via mixins. The user ID is stored in-memory only — no `localStorage`, no emitted events. This makes it safe to use in both browser and server-side environments.
|
|
229
|
+
|
|
230
|
+
When no user ID is set, `uid` is omitted from events entirely.
|
|
231
|
+
|
|
232
|
+
If the user ID is known at initialization time, it can be passed directly:
|
|
233
|
+
|
|
234
|
+
```ts
|
|
235
|
+
const captureEvent = snitch(
|
|
236
|
+
userPlugin('user-123'),
|
|
237
|
+
// ...
|
|
238
|
+
) as any
|
|
239
|
+
```
|
|
240
|
+
|
|
241
|
+
Otherwise, set it later:
|
|
242
|
+
|
|
243
|
+
```ts
|
|
244
|
+
captureEvent.setUserId('user-123')
|
|
245
|
+
```
|
|
246
|
+
|
|
247
|
+
**Methods (mixins):**
|
|
248
|
+
| Method | Description |
|
|
249
|
+
|--------|-------------|
|
|
250
|
+
| `setUserId(id: string)` | Set the user ID. All subsequent events will include `uid`. |
|
|
251
|
+
| `clearUserId()` | Clear the user ID. `uid` is no longer attached to events. |
|
|
252
|
+
| `withUserId(id: string, eventName: string, eventPayload?)` | Temporarily set the user ID, send a single event, then restore the previous user ID. Designed for server-side use where a single snitch instance handles multiple users. |
|
|
253
|
+
|
|
254
|
+
Attaches to every event (while user ID is set):
|
|
255
|
+
| Param | Description |
|
|
256
|
+
|-------|-------------|
|
|
257
|
+
| `uid` | Current user ID |
|
|
258
|
+
|
|
259
|
+
**Usage:**
|
|
260
|
+
|
|
261
|
+
```ts
|
|
262
|
+
const captureEvent = snitch(
|
|
263
|
+
devicePlugin(),
|
|
264
|
+
userPlugin(),
|
|
265
|
+
sessionPlugin(),
|
|
266
|
+
beaconTransportPlugin({ hostname: '...' }),
|
|
267
|
+
) as any
|
|
268
|
+
|
|
269
|
+
// User logs in
|
|
270
|
+
captureEvent.setUserId('user-123')
|
|
271
|
+
|
|
272
|
+
captureEvent('add_to_cart', { productId: 'abc' })
|
|
273
|
+
// => { event: 'add_to_cart', productId: 'abc', uid: 'user-123', did: '...', sid: '...' }
|
|
274
|
+
|
|
275
|
+
// User logs out
|
|
276
|
+
captureEvent.clearUserId()
|
|
277
|
+
// uid is no longer attached to events
|
|
278
|
+
|
|
279
|
+
// Server-side (s2s-transport) — pass uid at init, no localStorage needed
|
|
280
|
+
const track = snitch(
|
|
281
|
+
userPlugin(req.userId),
|
|
282
|
+
s2sTransportPlugin({ hostname: 'analytics.example.com' }),
|
|
283
|
+
)
|
|
284
|
+
track('subscriptionRenewalPaymentFailed')
|
|
285
|
+
// => { event: 'subscriptionRenewalPaymentFailed', uid: 'user-123' }
|
|
286
|
+
```
|
|
287
|
+
|
|
288
|
+
**Server-side with `.withUserId()`:**
|
|
289
|
+
|
|
290
|
+
When a single snitch instance handles requests from multiple users (e.g., in an Express handler), use `.withUserId()` to atomically send an event with a specific user ID without affecting other requests. The captureEvent pipeline is synchronous, so the temporary uid swap is safe — no interleaving is possible.
|
|
291
|
+
|
|
292
|
+
```ts
|
|
293
|
+
const track = snitch(userPlugin(), s2sTransportPlugin({ hostname: 'analytics.example.com' })) as any
|
|
294
|
+
|
|
295
|
+
app.post('/api/checkout', (req, res) => {
|
|
296
|
+
// Sends this one event with uid='user-42', then restores previous state
|
|
297
|
+
track.withUserId(req.userId, 'checkout_completed', { orderId: req.body.orderId })
|
|
298
|
+
res.json({ ok: true })
|
|
299
|
+
})
|
|
300
|
+
```
|
|
301
|
+
|
|
302
|
+
---
|
|
303
|
+
|
|
304
|
+
### `debugLoggerPlugin()`
|
|
305
|
+
|
|
306
|
+
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()`.
|
|
307
|
+
|
|
308
|
+
**Silent by default.** To enable, set a `localStorage` flag:
|
|
309
|
+
|
|
310
|
+
```js
|
|
311
|
+
localStorage.setItem('snitch:debug', 'true')
|
|
312
|
+
```
|
|
313
|
+
|
|
314
|
+
The flag is read once when `debugLoggerPlugin()` is called. To disable, remove the flag and reload:
|
|
315
|
+
|
|
316
|
+
```js
|
|
317
|
+
localStorage.removeItem('snitch:debug')
|
|
318
|
+
```
|
|
319
|
+
|
|
320
|
+
---
|
|
49
321
|
|
|
50
322
|
## Transports
|
|
51
323
|
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
|
324
|
+
### `beaconTransportPlugin(options?)`
|
|
325
|
+
|
|
326
|
+
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.
|
|
327
|
+
|
|
328
|
+
**Options:**
|
|
329
|
+
| Option | Type | Default | Description |
|
|
330
|
+
|--------|------|---------|-------------|
|
|
331
|
+
| `hostname` | `string` | `window.location.hostname` | Target hostname |
|
|
332
|
+
| `path` | `string` | `/_snitch` | URL path |
|
|
333
|
+
|
|
334
|
+
Requests are sent to: `{protocol}//{hostname}{path}?event={name}&...params`
|
|
335
|
+
|
|
336
|
+
---
|
|
337
|
+
|
|
338
|
+
### `s2sTransportPlugin(options)`
|
|
339
|
+
|
|
340
|
+
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`.
|
|
341
|
+
|
|
342
|
+
**Options:**
|
|
343
|
+
| Option | Type | Default | Description |
|
|
344
|
+
|--------|------|---------|-------------|
|
|
345
|
+
| `hostname` | `string` | _required_ | Target hostname |
|
|
346
|
+
| `path` | `string` | `/_snitch` | URL path |
|
|
347
|
+
| `s2sToken` | `string` | — | Optional auth token (sent as a query parameter) |
|
|
348
|
+
|
|
349
|
+
Requests are sent to: `https://{hostname}{path}?event={name}&...params[&s2sToken=...]`
|
|
350
|
+
|
|
351
|
+
---
|
|
352
|
+
|
|
353
|
+
### `topmailruTransportPlugin(counterId, userIdResolver?)`
|
|
354
|
+
|
|
355
|
+
Sends events to [Top Mail.ru](https://top.mail.ru/) analytics counter by pushing to the `window._tmr` queue.
|
|
356
|
+
|
|
357
|
+
**Parameters:**
|
|
358
|
+
| Param | Type | Description |
|
|
359
|
+
|-------|------|-------------|
|
|
360
|
+
| `counterId` | `string` | Top Mail.ru counter ID (required) |
|
|
361
|
+
| `userIdResolver` | `() => string \| null \| undefined` | Optional custom user ID resolver |
|
|
362
|
+
|
|
363
|
+
User ID resolution order: custom resolver → TMR counter cookie → auto-generated anonymous ID.
|
|
364
|
+
|
|
365
|
+
---
|
|
366
|
+
|
|
367
|
+
### `vkBridgeTransportPlugin()`
|
|
368
|
+
|
|
369
|
+
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.
|
|
370
|
+
|
|
371
|
+
---
|
|
372
|
+
|
|
373
|
+
## Platform-Specific Plugins
|
|
374
|
+
|
|
375
|
+
### `vkmaLaunchPlugin()`
|
|
376
|
+
|
|
377
|
+
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.).
|
|
378
|
+
|
|
379
|
+
Emits: `launch` (with iframe flag + VKMA params), `mt_internal_launch`
|
|
380
|
+
|
|
381
|
+
Attaches to every event:
|
|
382
|
+
| Param | Description |
|
|
383
|
+
|-------|-------------|
|
|
384
|
+
| `lid` | Unique launch ID |
|
|
385
|
+
| `ref` | `document.referrer` |
|
|
386
|
+
| `mauid` | VK user ID |
|
|
387
|
+
| `maaid` | VK app ID |
|
|
388
|
+
| `malang` | VK language |
|
|
389
|
+
| `mac` | VK access token settings |
|
|
390
|
+
| `map` | VK platform |
|
|
391
|
+
| `maref` | VK ref |
|
|
392
|
+
|
|
393
|
+
---
|
|
58
394
|
|
|
59
395
|
## Types
|
|
60
396
|
|
|
61
397
|
All public types are exported for TypeScript consumers:
|
|
62
398
|
|
|
63
399
|
```ts
|
|
64
|
-
import type {
|
|
400
|
+
import type {
|
|
401
|
+
Plugin,
|
|
402
|
+
EventTransport,
|
|
403
|
+
EventSource,
|
|
404
|
+
EventPayloadParamsProvider,
|
|
405
|
+
InitializationHandler,
|
|
406
|
+
BeforeCaptureEventHandler,
|
|
407
|
+
MixinProvider,
|
|
408
|
+
TrackerEventPayload,
|
|
409
|
+
EventHandler,
|
|
410
|
+
} from '@borisch/snitch'
|
|
65
411
|
```
|
|
66
412
|
|
|
67
413
|
## Writing a Custom Plugin
|
|
@@ -73,12 +419,21 @@ import type { Plugin } from '@borisch/snitch'
|
|
|
73
419
|
|
|
74
420
|
function myPlugin(): Plugin {
|
|
75
421
|
return {
|
|
422
|
+
// Attach params to every event
|
|
76
423
|
getEventPayloadParams() {
|
|
77
424
|
return { customParam: 'value' }
|
|
78
425
|
},
|
|
426
|
+
// React to events before transport
|
|
427
|
+
beforeCaptureEvent(eventName, eventParams) {
|
|
428
|
+
// filter, modify, log, etc.
|
|
429
|
+
},
|
|
430
|
+
// Transport events
|
|
79
431
|
sendEvent(eventName, eventParams) {
|
|
80
|
-
|
|
81
|
-
|
|
432
|
+
fetch('/analytics', {
|
|
433
|
+
method: 'POST',
|
|
434
|
+
body: JSON.stringify({ eventName, ...eventParams }),
|
|
435
|
+
})
|
|
436
|
+
},
|
|
82
437
|
}
|
|
83
438
|
}
|
|
84
439
|
```
|
package/dist/index.d.ts
CHANGED
|
@@ -13,6 +13,8 @@ export { default as webVitalsPlugin } from './packages/snitch-plugin-web-vitals/
|
|
|
13
13
|
export { default as flagPlugin } from './packages/snitch-plugin-flag/index';
|
|
14
14
|
export { default as debugLoggerPlugin } from './packages/snitch-plugin-debug-logger/index';
|
|
15
15
|
export { default as useragentPlugin } from './packages/snitch-plugin-useragent/index';
|
|
16
|
+
export { default as devicePlugin } from './packages/snitch-plugin-device/index';
|
|
17
|
+
export { default as userPlugin } from './packages/snitch-plugin-user/index';
|
|
16
18
|
export { default as beaconTransportPlugin } from './packages/snitch-plugin-beacon-transport/index';
|
|
17
19
|
export { default as s2sTransportPlugin } from './packages/snitch-plugin-s2s-transport/index';
|
|
18
20
|
export { default as topmailruTransportPlugin } from './packages/snitch-plugin-topmailru-transport/index';
|
package/dist/index.js
CHANGED
|
@@ -3,7 +3,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
3
3
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
4
|
};
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
-
exports.vkmaLaunchPlugin = exports.vkBridgeTransportPlugin = exports.topmailruTransportPlugin = exports.s2sTransportPlugin = exports.beaconTransportPlugin = exports.useragentPlugin = exports.debugLoggerPlugin = exports.flagPlugin = exports.webVitalsPlugin = exports.exceptionsPlugin = exports.screenPlugin = exports.engagementPlugin = exports.launchPlugin = exports.sessionPlugin = exports.locationPlugin = exports.scrollPlugin = exports.default = exports.snitch = void 0;
|
|
6
|
+
exports.vkmaLaunchPlugin = exports.vkBridgeTransportPlugin = exports.topmailruTransportPlugin = exports.s2sTransportPlugin = exports.beaconTransportPlugin = exports.userPlugin = exports.devicePlugin = exports.useragentPlugin = exports.debugLoggerPlugin = exports.flagPlugin = exports.webVitalsPlugin = exports.exceptionsPlugin = exports.screenPlugin = exports.engagementPlugin = exports.launchPlugin = exports.sessionPlugin = exports.locationPlugin = exports.scrollPlugin = exports.default = exports.snitch = void 0;
|
|
7
7
|
// Core
|
|
8
8
|
var index_1 = require("./packages/snitch/index");
|
|
9
9
|
Object.defineProperty(exports, "snitch", { enumerable: true, get: function () { return __importDefault(index_1).default; } });
|
|
@@ -32,16 +32,20 @@ var index_12 = require("./packages/snitch-plugin-debug-logger/index");
|
|
|
32
32
|
Object.defineProperty(exports, "debugLoggerPlugin", { enumerable: true, get: function () { return __importDefault(index_12).default; } });
|
|
33
33
|
var index_13 = require("./packages/snitch-plugin-useragent/index");
|
|
34
34
|
Object.defineProperty(exports, "useragentPlugin", { enumerable: true, get: function () { return __importDefault(index_13).default; } });
|
|
35
|
+
var index_14 = require("./packages/snitch-plugin-device/index");
|
|
36
|
+
Object.defineProperty(exports, "devicePlugin", { enumerable: true, get: function () { return __importDefault(index_14).default; } });
|
|
37
|
+
var index_15 = require("./packages/snitch-plugin-user/index");
|
|
38
|
+
Object.defineProperty(exports, "userPlugin", { enumerable: true, get: function () { return __importDefault(index_15).default; } });
|
|
35
39
|
// Transports
|
|
36
|
-
var
|
|
37
|
-
Object.defineProperty(exports, "beaconTransportPlugin", { enumerable: true, get: function () { return __importDefault(
|
|
38
|
-
var
|
|
39
|
-
Object.defineProperty(exports, "s2sTransportPlugin", { enumerable: true, get: function () { return __importDefault(
|
|
40
|
-
var
|
|
41
|
-
Object.defineProperty(exports, "topmailruTransportPlugin", { enumerable: true, get: function () { return __importDefault(
|
|
42
|
-
var
|
|
43
|
-
Object.defineProperty(exports, "vkBridgeTransportPlugin", { enumerable: true, get: function () { return __importDefault(
|
|
40
|
+
var index_16 = require("./packages/snitch-plugin-beacon-transport/index");
|
|
41
|
+
Object.defineProperty(exports, "beaconTransportPlugin", { enumerable: true, get: function () { return __importDefault(index_16).default; } });
|
|
42
|
+
var index_17 = require("./packages/snitch-plugin-s2s-transport/index");
|
|
43
|
+
Object.defineProperty(exports, "s2sTransportPlugin", { enumerable: true, get: function () { return __importDefault(index_17).default; } });
|
|
44
|
+
var index_18 = require("./packages/snitch-plugin-topmailru-transport/index");
|
|
45
|
+
Object.defineProperty(exports, "topmailruTransportPlugin", { enumerable: true, get: function () { return __importDefault(index_18).default; } });
|
|
46
|
+
var index_19 = require("./packages/snitch-plugin-vkbridge-transport/index");
|
|
47
|
+
Object.defineProperty(exports, "vkBridgeTransportPlugin", { enumerable: true, get: function () { return __importDefault(index_19).default; } });
|
|
44
48
|
// Platform-specific
|
|
45
|
-
var
|
|
46
|
-
Object.defineProperty(exports, "vkmaLaunchPlugin", { enumerable: true, get: function () { return __importDefault(
|
|
49
|
+
var index_20 = require("./packages/snitch-plugin-vkma-launch/index");
|
|
50
|
+
Object.defineProperty(exports, "vkmaLaunchPlugin", { enumerable: true, get: function () { return __importDefault(index_20).default; } });
|
|
47
51
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../index.ts"],"names":[],"mappings":";;;;;;AAAA,OAAO;AACP,iDAA2D;AAAlD,gHAAA,OAAO,OAAU;AAC1B,iDAAiD;AAAxC,iHAAA,OAAO,OAAA;AAmBhB,UAAU;AACV,+DAA+E;AAAtE,sHAAA,OAAO,OAAgB;AAChC,iEAAmF;AAA1E,wHAAA,OAAO,OAAkB;AAClC,gEAAiF;AAAxE,uHAAA,OAAO,OAAiB;AACjC,+DAA+E;AAAtE,sHAAA,OAAO,OAAgB;AAChC,mEAAuF;AAA9E,0HAAA,OAAO,OAAoB;AACpC,gEAAgF;AAAvE,sHAAA,OAAO,OAAgB;AAChC,mEAAuF;AAA9E,0HAAA,OAAO,OAAoB;AACpC,oEAAsF;AAA7E,0HAAA,OAAO,OAAmB;AACnC,8DAA2E;AAAlE,qHAAA,OAAO,OAAc;AAC9B,sEAA0F;AAAjF,4HAAA,OAAO,OAAqB;AACrC,mEAAqF;AAA5E,0HAAA,OAAO,OAAmB;
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../index.ts"],"names":[],"mappings":";;;;;;AAAA,OAAO;AACP,iDAA2D;AAAlD,gHAAA,OAAO,OAAU;AAC1B,iDAAiD;AAAxC,iHAAA,OAAO,OAAA;AAmBhB,UAAU;AACV,+DAA+E;AAAtE,sHAAA,OAAO,OAAgB;AAChC,iEAAmF;AAA1E,wHAAA,OAAO,OAAkB;AAClC,gEAAiF;AAAxE,uHAAA,OAAO,OAAiB;AACjC,+DAA+E;AAAtE,sHAAA,OAAO,OAAgB;AAChC,mEAAuF;AAA9E,0HAAA,OAAO,OAAoB;AACpC,gEAAgF;AAAvE,sHAAA,OAAO,OAAgB;AAChC,mEAAuF;AAA9E,0HAAA,OAAO,OAAoB;AACpC,oEAAsF;AAA7E,0HAAA,OAAO,OAAmB;AACnC,8DAA2E;AAAlE,qHAAA,OAAO,OAAc;AAC9B,sEAA0F;AAAjF,4HAAA,OAAO,OAAqB;AACrC,mEAAqF;AAA5E,0HAAA,OAAO,OAAmB;AACnC,gEAA+E;AAAtE,uHAAA,OAAO,OAAgB;AAChC,8DAA2E;AAAlE,qHAAA,OAAO,OAAc;AAE9B,aAAa;AACb,0EAAkG;AAAzF,gIAAA,OAAO,OAAyB;AACzC,uEAA4F;AAAnF,6HAAA,OAAO,OAAsB;AACtC,6EAAwG;AAA/F,mIAAA,OAAO,OAA4B;AAC5C,4EAAsG;AAA7F,kIAAA,OAAO,OAA2B;AAE3C,oBAAoB;AACpB,qEAAwF;AAA/E,2HAAA,OAAO,OAAoB"}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
import { BeforeCaptureEventHandler, InitializationHandler } from '../common/plugin-interfaces';
|
|
2
|
-
export default function
|
|
2
|
+
export default function debugLoggerPlugin(): InitializationHandler & BeforeCaptureEventHandler;
|
|
@@ -1,9 +1,17 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.default =
|
|
4
|
-
|
|
3
|
+
exports.default = debugLoggerPlugin;
|
|
4
|
+
const STORAGE_KEY = 'snitch:debug';
|
|
5
|
+
function debugLoggerPlugin() {
|
|
6
|
+
let enabled = false;
|
|
7
|
+
try {
|
|
8
|
+
enabled = localStorage.getItem(STORAGE_KEY) === 'true';
|
|
9
|
+
}
|
|
10
|
+
catch (_a) { }
|
|
5
11
|
let lastLogTS = null;
|
|
6
12
|
function logLine(message) {
|
|
13
|
+
if (!enabled)
|
|
14
|
+
return;
|
|
7
15
|
const now = new Date();
|
|
8
16
|
console.log(`%c[${now.getHours()}:${now.getMinutes()}:${now.getSeconds()}]${lastLogTS ? ` (+${Math.floor((now.getTime() - lastLogTS) / 1e3)}s)` : ''} %cSnitch: %c${message}`, 'color: gray', 'color: black', 'font-weight: bold');
|
|
9
17
|
lastLogTS = now.getTime();
|
|
@@ -14,9 +22,9 @@ function engagementPlugin() {
|
|
|
14
22
|
},
|
|
15
23
|
beforeCaptureEvent(eventName, eventPayload) {
|
|
16
24
|
logLine(`captured event '${eventName}'`);
|
|
17
|
-
if (eventPayload && Object.keys(eventPayload).length !== 0)
|
|
25
|
+
if (enabled && eventPayload && Object.keys(eventPayload).length !== 0)
|
|
18
26
|
console.table(eventPayload);
|
|
19
|
-
}
|
|
27
|
+
},
|
|
20
28
|
};
|
|
21
29
|
}
|
|
22
30
|
//# sourceMappingURL=debug-logger.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"debug-logger.js","sourceRoot":"","sources":["../../../packages/snitch-plugin-debug-logger/debug-logger.ts"],"names":[],"mappings":";;
|
|
1
|
+
{"version":3,"file":"debug-logger.js","sourceRoot":"","sources":["../../../packages/snitch-plugin-debug-logger/debug-logger.ts"],"names":[],"mappings":";;AAKA,oCA+BC;AAjCD,MAAM,WAAW,GAAG,cAAc,CAAA;AAElC,SAAwB,iBAAiB;IACvC,IAAI,OAAO,GAAG,KAAK,CAAA;IACnB,IAAI,CAAC;QACH,OAAO,GAAG,YAAY,CAAC,OAAO,CAAC,WAAW,CAAC,KAAK,MAAM,CAAA;IACxD,CAAC;IAAC,WAAM,CAAC,CAAA,CAAC;IAEV,IAAI,SAAS,GAAkB,IAAI,CAAA;IACnC,SAAS,OAAO,CAAC,OAAe;QAC9B,IAAI,CAAC,OAAO;YAAE,OAAM;QACpB,MAAM,GAAG,GAAG,IAAI,IAAI,EAAE,CAAA;QACtB,OAAO,CAAC,GAAG,CACT,MAAM,GAAG,CAAC,QAAQ,EAAE,IAAI,GAAG,CAAC,UAAU,EAAE,IAAI,GAAG,CAAC,UAAU,EAAE,IAC1D,SAAS,CAAC,CAAC,CAAC,MAAM,IAAI,CAAC,KAAK,CAAC,CAAC,GAAG,CAAC,OAAO,EAAE,GAAI,SAAoB,CAAC,GAAG,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,EACpF,gBAAgB,OAAO,EAAE,EACzB,aAAa,EACb,cAAc,EACd,mBAAmB,CACpB,CAAA;QACD,SAAS,GAAG,GAAG,CAAC,OAAO,EAAE,CAAA;IAC3B,CAAC;IACD,OAAO;QACL,MAAM;YACJ,OAAO,CAAC,yBAAyB,CAAC,CAAA;QACpC,CAAC;QAED,kBAAkB,CAAC,SAAiB,EAAE,YAAiC;YACrE,OAAO,CAAC,mBAAmB,SAAS,GAAG,CAAC,CAAA;YACxC,IAAI,OAAO,IAAI,YAAY,IAAI,MAAM,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC,MAAM,KAAK,CAAC;gBACnE,OAAO,CAAC,KAAK,CAAC,YAAY,CAAC,CAAA;QAC/B,CAAC;KACF,CAAA;AACH,CAAC"}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.default = devicePlugin;
|
|
7
|
+
const create_unique_id_1 = __importDefault(require("../common/create-unique-id"));
|
|
8
|
+
const STORAGE_KEY = 'snitch:did';
|
|
9
|
+
function devicePlugin() {
|
|
10
|
+
let deviceId;
|
|
11
|
+
try {
|
|
12
|
+
const stored = localStorage.getItem(STORAGE_KEY);
|
|
13
|
+
if (stored) {
|
|
14
|
+
deviceId = stored;
|
|
15
|
+
}
|
|
16
|
+
else {
|
|
17
|
+
deviceId = (0, create_unique_id_1.default)();
|
|
18
|
+
localStorage.setItem(STORAGE_KEY, deviceId);
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
catch (_a) {
|
|
22
|
+
deviceId = (0, create_unique_id_1.default)();
|
|
23
|
+
}
|
|
24
|
+
return {
|
|
25
|
+
getEventPayloadParams() {
|
|
26
|
+
return { did: deviceId };
|
|
27
|
+
},
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
//# sourceMappingURL=device.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"device.js","sourceRoot":"","sources":["../../../packages/snitch-plugin-device/device.ts"],"names":[],"mappings":";;;;;AAKA,+BAoBC;AAzBD,kFAAuD;AAGvD,MAAM,WAAW,GAAG,YAAY,CAAA;AAEhC,SAAwB,YAAY;IAClC,IAAI,QAAgB,CAAA;IAEpB,IAAI,CAAC;QACH,MAAM,MAAM,GAAG,YAAY,CAAC,OAAO,CAAC,WAAW,CAAC,CAAA;QAChD,IAAI,MAAM,EAAE,CAAC;YACX,QAAQ,GAAG,MAAM,CAAA;QACnB,CAAC;aAAM,CAAC;YACN,QAAQ,GAAG,IAAA,0BAAc,GAAE,CAAA;YAC3B,YAAY,CAAC,OAAO,CAAC,WAAW,EAAE,QAAQ,CAAC,CAAA;QAC7C,CAAC;IACH,CAAC;IAAC,WAAM,CAAC;QACP,QAAQ,GAAG,IAAA,0BAAc,GAAE,CAAA;IAC7B,CAAC;IAED,OAAO;QACL,qBAAqB;YACnB,OAAO,EAAE,GAAG,EAAE,QAAQ,EAAE,CAAA;QAC1B,CAAC;KACF,CAAA;AACH,CAAC"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
const device_1 = __importDefault(require("./device"));
|
|
7
|
+
exports.default = device_1.default;
|
|
8
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../packages/snitch-plugin-device/index.ts"],"names":[],"mappings":";;;;;AAAA,sDAAmC;AACnC,kBAAe,gBAAY,CAAA"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
const user_1 = __importDefault(require("./user"));
|
|
7
|
+
exports.default = user_1.default;
|
|
8
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../packages/snitch-plugin-user/index.ts"],"names":[],"mappings":";;;;;AAAA,kDAA+B;AAC/B,kBAAe,cAAU,CAAA"}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.default = userPlugin;
|
|
4
|
+
function userPlugin(userId) {
|
|
5
|
+
let uid = userId !== null && userId !== void 0 ? userId : null;
|
|
6
|
+
let captureEvent;
|
|
7
|
+
return {
|
|
8
|
+
setEventHandler(eventHandler) {
|
|
9
|
+
captureEvent = eventHandler;
|
|
10
|
+
},
|
|
11
|
+
getEventPayloadParams() {
|
|
12
|
+
if (uid) {
|
|
13
|
+
return { uid };
|
|
14
|
+
}
|
|
15
|
+
return {};
|
|
16
|
+
},
|
|
17
|
+
getMixins() {
|
|
18
|
+
return {
|
|
19
|
+
setUserId(id) {
|
|
20
|
+
uid = id;
|
|
21
|
+
},
|
|
22
|
+
clearUserId() {
|
|
23
|
+
uid = null;
|
|
24
|
+
},
|
|
25
|
+
withUserId(id, eventName, eventPayload) {
|
|
26
|
+
const prevUid = uid;
|
|
27
|
+
uid = id;
|
|
28
|
+
captureEvent(eventName, eventPayload);
|
|
29
|
+
uid = prevUid;
|
|
30
|
+
},
|
|
31
|
+
};
|
|
32
|
+
},
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
//# sourceMappingURL=user.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"user.js","sourceRoot":"","sources":["../../../packages/snitch-plugin-user/user.ts"],"names":[],"mappings":";;AAGA,6BAmCC;AAnCD,SAAwB,UAAU,CAChC,MAAe;IAEf,IAAI,GAAG,GAAkB,MAAM,aAAN,MAAM,cAAN,MAAM,GAAI,IAAI,CAAA;IACvC,IAAI,YAA0B,CAAA;IAE9B,OAAO;QACL,eAAe,CAAC,YAA0B;YACxC,YAAY,GAAG,YAAY,CAAA;QAC7B,CAAC;QAED,qBAAqB;YACnB,IAAI,GAAG,EAAE,CAAC;gBACR,OAAO,EAAE,GAAG,EAAE,CAAA;YAChB,CAAC;YACD,OAAO,EAAE,CAAA;QACX,CAAC;QAED,SAAS;YACP,OAAO;gBACL,SAAS,CAAC,EAAU;oBAClB,GAAG,GAAG,EAAE,CAAA;gBACV,CAAC;gBACD,WAAW;oBACT,GAAG,GAAG,IAAI,CAAA;gBACZ,CAAC;gBACD,UAAU,CAAC,EAAU,EAAE,SAAiB,EAAE,YAAkC;oBAC1E,MAAM,OAAO,GAAG,GAAG,CAAA;oBACnB,GAAG,GAAG,EAAE,CAAA;oBACR,YAAY,CAAC,SAAS,EAAE,YAAY,CAAC,CAAA;oBACrC,GAAG,GAAG,OAAO,CAAA;gBACf,CAAC;aACF,CAAA;QACH,CAAC;KACF,CAAA;AACH,CAAC"}
|