@obsrviq/tracker 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +67 -0
- package/dist/index.d.ts +96 -0
- package/dist/index.js +1827 -0
- package/dist/index.js.map +1 -0
- package/dist/obsrviq.global.js +189 -0
- package/dist/obsrviq.global.js.map +1 -0
- package/package.json +41 -0
- package/src/console.ts +96 -0
- package/src/context.ts +44 -0
- package/src/errors.ts +78 -0
- package/src/forms.ts +189 -0
- package/src/global.ts +61 -0
- package/src/index.ts +436 -0
- package/src/interactions.ts +70 -0
- package/src/mask.ts +42 -0
- package/src/network.ts +439 -0
- package/src/pageviews.ts +67 -0
- package/src/recorder.ts +53 -0
- package/src/scroll.ts +65 -0
- package/src/serialize.ts +120 -0
- package/src/storage.ts +131 -0
- package/src/transport.ts +181 -0
- package/src/ui.ts +182 -0
- package/src/util.ts +96 -0
- package/src/vitals.ts +111 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Obsrviq
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
# @obsrviq/tracker
|
|
2
|
+
|
|
3
|
+
The Obsrviq capture SDK. A tiny, privacy-first script that records DOM (rrweb),
|
|
4
|
+
console, network, errors, and web vitals, then ships them in masked, gzipped
|
|
5
|
+
batches. No tagging required.
|
|
6
|
+
|
|
7
|
+
## Install
|
|
8
|
+
|
|
9
|
+
**Script tag (no build):**
|
|
10
|
+
```html
|
|
11
|
+
<script
|
|
12
|
+
src="https://cdn.yourapp.com/obsrviq.global.js"
|
|
13
|
+
data-obsrviq-key="pk_live_xxx"
|
|
14
|
+
data-obsrviq-endpoint="https://in.yourapp.com"></script>
|
|
15
|
+
```
|
|
16
|
+
The built file is `dist/obsrviq.global.js` (host it on your CDN).
|
|
17
|
+
|
|
18
|
+
**npm:**
|
|
19
|
+
```bash
|
|
20
|
+
npm install @obsrviq/tracker
|
|
21
|
+
```
|
|
22
|
+
```ts
|
|
23
|
+
import { Obsrviq } from '@obsrviq/tracker';
|
|
24
|
+
Obsrviq.init({ siteKey: 'pk_live_xxx', endpoint: 'https://in.yourapp.com' });
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
## API
|
|
28
|
+
|
|
29
|
+
```ts
|
|
30
|
+
Obsrviq.init(config); // see config below
|
|
31
|
+
Obsrviq.identify(userId, traits?); // raw userId (searchable) + SHA-256 hash; retro-stamps
|
|
32
|
+
Obsrviq.setMetadata({ plan: 'pro' }); // merge searchable session properties anytime
|
|
33
|
+
Obsrviq.reset(); // forget identity on logout → fresh anonymous session
|
|
34
|
+
Obsrviq.track('event', { conversion: true });
|
|
35
|
+
Obsrviq.setConsent(true | false); // gate recording (PRIV-2)
|
|
36
|
+
Obsrviq.stop(); // hard stop + flush
|
|
37
|
+
Obsrviq.getDiagnostics(); // { recording, sessionId, flushes, bytesSent, … }
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
## Config
|
|
41
|
+
|
|
42
|
+
| Option | Default | Notes |
|
|
43
|
+
|---|---|---|
|
|
44
|
+
| `siteKey` | — | **Required.** The `pk_` ingest key. |
|
|
45
|
+
| `endpoint` | `https://in.lumera.app` | Your ingest URL. |
|
|
46
|
+
| `maskAllInputs` | `true` | Never record input values. |
|
|
47
|
+
| `maskTextSelector` | — | Mask text inside matching nodes. |
|
|
48
|
+
| `blockSelector` | `.obsrviq-block` | Never record these nodes. |
|
|
49
|
+
| `captureNetworkBodies` | `false` | Opt-in, masked, size-capped. |
|
|
50
|
+
| `redactHeaders` | `authorization, cookie, set-cookie` | Redacted in captured headers. |
|
|
51
|
+
| `requireConsent` | `false` | If true, nothing records until `setConsent(true)`. |
|
|
52
|
+
| `sampleRate` | `1.0` | 0..1 fraction of sessions to record. |
|
|
53
|
+
| `flushIntervalMs` | `5000` | Batch flush cadence. |
|
|
54
|
+
| `beforeSend` | — | Last-chance redaction; return `null` to drop. |
|
|
55
|
+
|
|
56
|
+
## Behavior
|
|
57
|
+
|
|
58
|
+
- **Consent & privacy:** with a consent gate and consent withheld, *zero* network
|
|
59
|
+
calls leave the page (honors Global Privacy Control / DNT).
|
|
60
|
+
- **Transport:** gzipped via fflate, sent with `sendBeacon` (or `fetch` keepalive)
|
|
61
|
+
on a timer, on `visibilitychange=hidden`, and on `pagehide`.
|
|
62
|
+
- **Capture:** rrweb for DOM (periodic full snapshots for progressive replay);
|
|
63
|
+
custom interceptors for `fetch` / `XHR` / `PerformanceObserver` / `sendBeacon`;
|
|
64
|
+
console hooks; `window.onerror` + `onunhandledrejection`; web vitals; rage/dead
|
|
65
|
+
click detection.
|
|
66
|
+
|
|
67
|
+
Full guide: [docs/EMBEDDING.md](../../docs/EMBEDDING.md).
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
import { ObsrviqInitConfig } from '@obsrviq/types';
|
|
2
|
+
export { ObsrviqInitConfig } from '@obsrviq/types';
|
|
3
|
+
|
|
4
|
+
interface Diagnostics {
|
|
5
|
+
recording: boolean;
|
|
6
|
+
sessionId: string | null;
|
|
7
|
+
flushes: number;
|
|
8
|
+
bytesSent: number;
|
|
9
|
+
eventsSent: number;
|
|
10
|
+
eventsQueued: number;
|
|
11
|
+
}
|
|
12
|
+
declare class ObsrviqClient {
|
|
13
|
+
private config;
|
|
14
|
+
private clock;
|
|
15
|
+
private transport;
|
|
16
|
+
private teardowns;
|
|
17
|
+
private meta;
|
|
18
|
+
private recording;
|
|
19
|
+
private consent;
|
|
20
|
+
private sampledIn;
|
|
21
|
+
private lastActivity;
|
|
22
|
+
/** The userId this session is attributed to (for the shared-device split). */
|
|
23
|
+
private currentUserId;
|
|
24
|
+
/** Next batch seq — mirrored from the transport so we can persist continuation. */
|
|
25
|
+
private nextSeq;
|
|
26
|
+
/** Open task spans keyed by task name → the span id + start time, so endTask()
|
|
27
|
+
* can pair with its startTask(). Re-starting the same name supersedes the
|
|
28
|
+
* earlier open span (the player pairs by spanId, so this only affects which
|
|
29
|
+
* start an unqualified end() resolves to). */
|
|
30
|
+
private openTasks;
|
|
31
|
+
private consentUnmount;
|
|
32
|
+
private recDotUnmount;
|
|
33
|
+
private diag;
|
|
34
|
+
/** The current session id (null until init). */
|
|
35
|
+
get sessionId(): string | null;
|
|
36
|
+
init(config: ObsrviqInitConfig): void;
|
|
37
|
+
/** (Re)create the session id, clock, meta, and transport. Used by init() and
|
|
38
|
+
* reset(). Does not start recording — callers decide via maybeStart().
|
|
39
|
+
* @param seedInitUser seed identity from the init-time `cfg.userId`. True for
|
|
40
|
+
* the initial init() session; FALSE for reset() (logout) — otherwise the fresh
|
|
41
|
+
* post-logout session would be re-attributed to the user who just logged out. */
|
|
42
|
+
private startSession;
|
|
43
|
+
/** Persist the session-continuation record (no-op unless continueSession). Only
|
|
44
|
+
* ever writes for a RECORDING session — a sampled-out or pre-consent session must
|
|
45
|
+
* not leave a record that a later page would resume (and force back into
|
|
46
|
+
* recording), and identify() can fire before recording starts. */
|
|
47
|
+
private persistSession;
|
|
48
|
+
private maybeStart;
|
|
49
|
+
/** Associate the session with an application user. Stores the RAW userId (for
|
|
50
|
+
* search + display) AND a SHA-256 identityHash, merges any traits into metadata,
|
|
51
|
+
* and force-flushes so the live session is retro-stamped promptly. Safe to call
|
|
52
|
+
* before recording starts (values ride the first batch) and after login. */
|
|
53
|
+
identify(userId: string, traits?: Record<string, unknown>): void;
|
|
54
|
+
/** Merge custom properties onto the session at any time (searchable server-side). */
|
|
55
|
+
setMetadata(props: Record<string, unknown>): void;
|
|
56
|
+
/** Forget the current identity (e.g. on logout) and rotate to a fresh anonymous
|
|
57
|
+
* session, so post-logout activity isn't attributed to the prior user. (Identity
|
|
58
|
+
* can't be cleared on the existing row — the server merge only fills nulls — so
|
|
59
|
+
* we start a new session instead.) */
|
|
60
|
+
reset(): void;
|
|
61
|
+
/** Set the raw userId on meta and compute its hash (async). */
|
|
62
|
+
private applyUserId;
|
|
63
|
+
private mergeMetadata;
|
|
64
|
+
/** Emit a custom event (e.g. conversions). */
|
|
65
|
+
track(name: string, props?: Record<string, unknown>): void;
|
|
66
|
+
/** Mark a conversion. Conversion is client-defined, so the NAME matters —
|
|
67
|
+
* "checkout", "signup", "trial_started" — and each is denormalised onto the
|
|
68
|
+
* session for per-client filtering. Sets props.conversion so the legacy
|
|
69
|
+
* 'conversion' flag still lights up. Sugar over track(name, {conversion:true}). */
|
|
70
|
+
conversion(name: string, props?: Record<string, unknown>): void;
|
|
71
|
+
/** Attach one or more free-form, searchable labels to the current session.
|
|
72
|
+
* Tags are session-level (deduped server-side) — use them to slice sessions
|
|
73
|
+
* however a client likes ("vip", "ab_variant_b", "mobile_app"). */
|
|
74
|
+
tag(...names: string[]): void;
|
|
75
|
+
/** Begin a named sub-event span. Pairs with end() / endTask(name) to produce a
|
|
76
|
+
* searchable, duration-bearing span on the session timeline. Returns a handle
|
|
77
|
+
* so callers can `const t = Obsrviq.startTask('checkout'); …; t.end()`. */
|
|
78
|
+
startTask(name: string, props?: Record<string, unknown>): {
|
|
79
|
+
end: (endProps?: Record<string, unknown>) => void;
|
|
80
|
+
};
|
|
81
|
+
/** Close a named sub-event span opened by startTask(name). Safe to call even
|
|
82
|
+
* if the start was never seen (renders as an orphan end on the timeline). */
|
|
83
|
+
endTask(name: string, props?: Record<string, unknown>): void;
|
|
84
|
+
/** Gate recording (PRIV-2). true starts; false stops + flushes. Dismisses the
|
|
85
|
+
* built-in consent prompt if one is showing. */
|
|
86
|
+
setConsent(granted: boolean): void;
|
|
87
|
+
/** Hard stop + flush. */
|
|
88
|
+
stop(): void;
|
|
89
|
+
/** Force a flush now (returns when send is attempted). */
|
|
90
|
+
flush(): Promise<boolean>;
|
|
91
|
+
getDiagnostics(): Diagnostics;
|
|
92
|
+
private teardown;
|
|
93
|
+
}
|
|
94
|
+
declare const Obsrviq: ObsrviqClient;
|
|
95
|
+
|
|
96
|
+
export { type Diagnostics, Obsrviq, Obsrviq as default };
|