@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 CHANGED
@@ -11,12 +11,27 @@ npm install @borisch/snitch
11
11
  ## Quick Start
12
12
 
13
13
  ```ts
14
- import { snitch, sessionPlugin, scrollPlugin, beaconTransportPlugin } from '@borisch/snitch'
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
- scrollPlugin({ threshold: 25 }),
19
- beaconTransportPlugin({ url: 'https://analytics.example.com/collect' })
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** (automatically attached to every event)
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 (Beacon API, fetch, VK Bridge, etc.)
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
- | Plugin | Factory | Description |
36
- | ------------------ | ------------------------ | ---------------------------------------------------------- |
37
- | Session | `sessionPlugin()` | Generates a unique session ID, attaches it to every event |
38
- | Launch | `launchPlugin()` | Captures a `launch` event on initialization |
39
- | Scroll | `scrollPlugin(opts)` | Tracks scroll depth milestones (default: 25/50/75/100%) |
40
- | Location | `locationPlugin()` | Attaches page URL and referrer to every event |
41
- | Engagement | `engagementPlugin(opts)` | Tracks time spent on page, emits engagement events |
42
- | Screens | `screenPlugin()` | Tracks screen/page view events |
43
- | Exceptions | `exceptionsPlugin()` | Captures unhandled errors and unhandled promise rejections |
44
- | Web Vitals | `webVitalsPlugin()` | Reports Core Web Vitals (LCP, FID, CLS, etc.) |
45
- | Flag | `flagPlugin(opts)` | Attaches a static key-value flag to every event |
46
- | User Agent | `useragentPlugin()` | Attaches `navigator.userAgent` to every event |
47
- | Debug Logger | `debugLoggerPlugin()` | Logs all events to the console (for development) |
48
- | VK Mini App Launch | `vkmaLaunchPlugin()` | Parses VK Mini App launch parameters |
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
- | Transport | Factory | Description |
53
- | ----------- | -------------------------------- | ------------------------------------------------------------------------------- |
54
- | Beacon | `beaconTransportPlugin(opts)` | Sends events as URL query params via `navigator.sendBeacon()` |
55
- | S2S | `s2sTransportPlugin(opts)` | Sends events as URL query params via `fetch()` GET (for server-side / Node 18+) |
56
- | Top Mail.ru | `topmailruTransportPlugin(opts)` | Sends events to Top Mail.ru counter |
57
- | VK Bridge | `vkBridgeTransportPlugin(opts)` | Sends events via VK Bridge |
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 { Plugin, EventTransport, TrackerEventPayload } from '@borisch/snitch'
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
- // custom transport logic
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 index_14 = require("./packages/snitch-plugin-beacon-transport/index");
37
- Object.defineProperty(exports, "beaconTransportPlugin", { enumerable: true, get: function () { return __importDefault(index_14).default; } });
38
- var index_15 = require("./packages/snitch-plugin-s2s-transport/index");
39
- Object.defineProperty(exports, "s2sTransportPlugin", { enumerable: true, get: function () { return __importDefault(index_15).default; } });
40
- var index_16 = require("./packages/snitch-plugin-topmailru-transport/index");
41
- Object.defineProperty(exports, "topmailruTransportPlugin", { enumerable: true, get: function () { return __importDefault(index_16).default; } });
42
- var index_17 = require("./packages/snitch-plugin-vkbridge-transport/index");
43
- Object.defineProperty(exports, "vkBridgeTransportPlugin", { enumerable: true, get: function () { return __importDefault(index_17).default; } });
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 index_18 = require("./packages/snitch-plugin-vkma-launch/index");
46
- Object.defineProperty(exports, "vkmaLaunchPlugin", { enumerable: true, get: function () { return __importDefault(index_18).default; } });
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;AAEnC,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
+ {"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 engagementPlugin(): InitializationHandler & BeforeCaptureEventHandler;
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 = engagementPlugin;
4
- function engagementPlugin() {
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":";;AAEA,mCAwBC;AAxBD,SAAwB,gBAAgB;IACtC,IAAI,SAAS,GAAkB,IAAI,CAAA;IACnC,SAAS,OAAO,CAAC,OAAe;QAC9B,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,YAAY,IAAI,MAAM,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC,MAAM,KAAK,CAAC;gBAAE,OAAO,CAAC,KAAK,CAAC,YAAY,CAAC,CAAA;QACzF,CAAC;KACF,CAAA;AACH,CAAC"}
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,2 @@
1
+ import { EventPayloadParamsProvider } from '../common/plugin-interfaces';
2
+ export default function devicePlugin(): EventPayloadParamsProvider;
@@ -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,2 @@
1
+ import devicePlugin from './device';
2
+ export default devicePlugin;
@@ -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,2 @@
1
+ import userPlugin from './user';
2
+ export default userPlugin;
@@ -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,2 @@
1
+ import { EventPayloadParamsProvider, EventSource, MixinProvider } from '../common/plugin-interfaces';
2
+ export default function userPlugin(userId?: string): EventPayloadParamsProvider & MixinProvider & EventSource;
@@ -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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@borisch/snitch",
3
- "version": "1.0.1",
3
+ "version": "1.1.0",
4
4
  "description": "Modular analytics tracking library with pluggable transports",
5
5
  "keywords": [
6
6
  "analytics",