@rushobservability/rum-sdk 0.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.
Files changed (47) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +70 -0
  3. package/dist/browser.d.ts +11 -0
  4. package/dist/browser.d.ts.map +1 -0
  5. package/dist/browser.js +63 -0
  6. package/dist/browser.js.map +1 -0
  7. package/dist/core.d.ts +6 -0
  8. package/dist/core.d.ts.map +1 -0
  9. package/dist/core.js +115 -0
  10. package/dist/core.js.map +1 -0
  11. package/dist/errors.d.ts +2 -0
  12. package/dist/errors.d.ts.map +1 -0
  13. package/dist/errors.js +32 -0
  14. package/dist/errors.js.map +1 -0
  15. package/dist/index.d.ts +16 -0
  16. package/dist/index.d.ts.map +1 -0
  17. package/dist/index.js +46 -0
  18. package/dist/index.js.map +1 -0
  19. package/dist/interactions.d.ts +2 -0
  20. package/dist/interactions.d.ts.map +1 -0
  21. package/dist/interactions.js +24 -0
  22. package/dist/interactions.js.map +1 -0
  23. package/dist/pageview.d.ts +2 -0
  24. package/dist/pageview.d.ts.map +1 -0
  25. package/dist/pageview.js +51 -0
  26. package/dist/pageview.js.map +1 -0
  27. package/dist/replay.d.ts +9 -0
  28. package/dist/replay.d.ts.map +1 -0
  29. package/dist/replay.js +62 -0
  30. package/dist/replay.js.map +1 -0
  31. package/dist/resources.d.ts +2 -0
  32. package/dist/resources.d.ts.map +1 -0
  33. package/dist/resources.js +97 -0
  34. package/dist/resources.js.map +1 -0
  35. package/dist/session.d.ts +3 -0
  36. package/dist/session.d.ts.map +1 -0
  37. package/dist/session.js +39 -0
  38. package/dist/session.js.map +1 -0
  39. package/dist/types.d.ts +63 -0
  40. package/dist/types.d.ts.map +1 -0
  41. package/dist/types.js +2 -0
  42. package/dist/types.js.map +1 -0
  43. package/dist/vitals.d.ts +2 -0
  44. package/dist/vitals.d.ts.map +1 -0
  45. package/dist/vitals.js +22 -0
  46. package/dist/vitals.js.map +1 -0
  47. package/package.json +38 -0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Rush Observability
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,70 @@
1
+ # @rushobservability/rum-sdk
2
+
3
+ Real User Monitoring (RUM) browser SDK for [Rush Observability](https://github.com/RushObservability). Lightweight, dependency-light, and framework-agnostic — it captures Web Vitals, JS errors, page views, interactions, resource timing, and optional session replay, then ships them to your Rush ingest endpoint.
4
+
5
+ ## Install
6
+
7
+ ```bash
8
+ npm install @rushobservability/rum-sdk
9
+ ```
10
+
11
+ ## Usage
12
+
13
+ ```ts
14
+ import RushRUM from '@rushobservability/rum-sdk'
15
+
16
+ RushRUM.init({
17
+ endpoint: 'https://your-rush-host/rum/ingest',
18
+ app: { name: 'my-web-app', version: '1.4.2' },
19
+ environment: 'production',
20
+
21
+ // What to collect (defaults shown)
22
+ trackWebVitals: true, // LCP, INP, CLS, FCP, TTFB (via web-vitals)
23
+ trackErrors: true, // uncaught errors + unhandled rejections
24
+ trackPageViews: true, // SPA-aware page views
25
+ trackInteractions: false, // click/input interaction events
26
+ trackResources: false, // resource timing entries
27
+ trackSessionReplay: false,// DOM session replay (via rrweb)
28
+
29
+ // Optional
30
+ sampleRate: 1.0, // 0..1
31
+ user: () => ({ id: currentUserId }), // attach a user id
32
+ propagateTraces: { origins: [/^https:\/\/api\.example\.com/] },
33
+ })
34
+
35
+ // Custom events
36
+ RushRUM.trackEvent('checkout_completed', { plan: 'pro', amount: 49 })
37
+
38
+ // Flush the queue manually (e.g. before a hard navigation)
39
+ RushRUM.flush()
40
+ ```
41
+
42
+ ### Configuration
43
+
44
+ | Option | Type | Default | Description |
45
+ |---|---|---|---|
46
+ | `endpoint` | `string` | — (required) | Rush RUM ingest URL |
47
+ | `app` | `{ name, version? }` | — (required) | Application identity |
48
+ | `environment` | `string` | — | e.g. `production`, `staging` |
49
+ | `sampleRate` | `number` | `1.0` | Fraction of sessions to record (0–1) |
50
+ | `user` | `() => { id? } \| null` | — | Resolver for the current user id |
51
+ | `trackWebVitals` | `boolean` | `true` | Core Web Vitals |
52
+ | `trackErrors` | `boolean` | `true` | Uncaught errors + promise rejections |
53
+ | `trackPageViews` | `boolean` | `true` | SPA-aware page views |
54
+ | `trackInteractions` | `boolean` | `false` | Click/input events |
55
+ | `trackResources` | `boolean` | `false` | Resource timing |
56
+ | `trackSessionReplay` | `boolean` | `false` | DOM session replay (rrweb) |
57
+ | `propagateTraces` | `{ origins: RegExp[] }` | — | Inject trace headers on matching XHR/fetch origins |
58
+ | `replayEndpoint` | `string` | `<endpoint>/rum/replay/ingest` | Override the replay ingest URL |
59
+
60
+ ## Build
61
+
62
+ ```bash
63
+ npm install
64
+ npm run build # tsc → dist/
65
+ npm run typecheck # type-check only
66
+ ```
67
+
68
+ ## License
69
+
70
+ [MIT](LICENSE)
@@ -0,0 +1,11 @@
1
+ export interface BrowserInfo {
2
+ browserName: string;
3
+ browserVersion: string;
4
+ osName: string;
5
+ osVersion: string;
6
+ deviceType: 'desktop' | 'mobile' | 'tablet';
7
+ screenWidth: number;
8
+ screenHeight: number;
9
+ }
10
+ export declare function detectBrowser(): BrowserInfo;
11
+ //# sourceMappingURL=browser.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"browser.d.ts","sourceRoot":"","sources":["../src/browser.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,WAAW;IAC1B,WAAW,EAAE,MAAM,CAAA;IACnB,cAAc,EAAE,MAAM,CAAA;IACtB,MAAM,EAAE,MAAM,CAAA;IACd,SAAS,EAAE,MAAM,CAAA;IACjB,UAAU,EAAE,SAAS,GAAG,QAAQ,GAAG,QAAQ,CAAA;IAC3C,WAAW,EAAE,MAAM,CAAA;IACnB,YAAY,EAAE,MAAM,CAAA;CACrB;AAED,wBAAgB,aAAa,IAAI,WAAW,CA0D3C"}
@@ -0,0 +1,63 @@
1
+ export function detectBrowser() {
2
+ const ua = navigator.userAgent;
3
+ let browserName = 'Unknown';
4
+ let browserVersion = '';
5
+ if (ua.includes('Firefox/')) {
6
+ browserName = 'Firefox';
7
+ browserVersion = ua.split('Firefox/')[1]?.split(' ')[0] ?? '';
8
+ }
9
+ else if (ua.includes('Edg/')) {
10
+ browserName = 'Edge';
11
+ browserVersion = ua.split('Edg/')[1]?.split(' ')[0] ?? '';
12
+ }
13
+ else if (ua.includes('Chrome/')) {
14
+ browserName = 'Chrome';
15
+ browserVersion = ua.split('Chrome/')[1]?.split(' ')[0] ?? '';
16
+ }
17
+ else if (ua.includes('Safari/') && !ua.includes('Chrome')) {
18
+ browserName = 'Safari';
19
+ browserVersion = ua.split('Version/')[1]?.split(' ')[0] ?? '';
20
+ }
21
+ let osName = 'Unknown';
22
+ let osVersion = '';
23
+ if (ua.includes('Windows')) {
24
+ osName = 'Windows';
25
+ const m = ua.match(/Windows NT (\d+\.\d+)/);
26
+ osVersion = m?.[1] ?? '';
27
+ }
28
+ else if (ua.includes('Mac OS X')) {
29
+ osName = 'macOS';
30
+ const m = ua.match(/Mac OS X (\d+[._]\d+[._]?\d*)/);
31
+ osVersion = m?.[1]?.replace(/_/g, '.') ?? '';
32
+ }
33
+ else if (ua.includes('Linux')) {
34
+ osName = 'Linux';
35
+ }
36
+ else if (ua.includes('Android')) {
37
+ osName = 'Android';
38
+ const m = ua.match(/Android (\d+(\.\d+)?)/);
39
+ osVersion = m?.[1] ?? '';
40
+ }
41
+ else if (/iPhone|iPad|iPod/.test(ua)) {
42
+ osName = 'iOS';
43
+ const m = ua.match(/OS (\d+_\d+)/);
44
+ osVersion = m?.[1]?.replace('_', '.') ?? '';
45
+ }
46
+ let deviceType = 'desktop';
47
+ if (/Mobi|Android.*Mobile/.test(ua)) {
48
+ deviceType = 'mobile';
49
+ }
50
+ else if (/iPad|Android(?!.*Mobile)|Tablet/.test(ua)) {
51
+ deviceType = 'tablet';
52
+ }
53
+ return {
54
+ browserName,
55
+ browserVersion,
56
+ osName,
57
+ osVersion,
58
+ deviceType,
59
+ screenWidth: window.screen.width,
60
+ screenHeight: window.screen.height,
61
+ };
62
+ }
63
+ //# sourceMappingURL=browser.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"browser.js","sourceRoot":"","sources":["../src/browser.ts"],"names":[],"mappings":"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"}
package/dist/core.d.ts ADDED
@@ -0,0 +1,6 @@
1
+ import type { RumEvent, RushRUMConfig } from './types';
2
+ export declare function configure(cfg: RushRUMConfig): void;
3
+ export declare function getConfig(): RushRUMConfig | null;
4
+ export declare function pushEvent(event: RumEvent): void;
5
+ export declare function flush(): void;
6
+ //# sourceMappingURL=core.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"core.d.ts","sourceRoot":"","sources":["../src/core.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAuB,aAAa,EAAE,MAAM,SAAS,CAAA;AAY3E,wBAAgB,SAAS,CAAC,GAAG,EAAE,aAAa,GAAG,IAAI,CAKlD;AAED,wBAAgB,SAAS,IAAI,aAAa,GAAG,IAAI,CAEhD;AAED,wBAAgB,SAAS,CAAC,KAAK,EAAE,QAAQ,GAAG,IAAI,CAY/C;AA+BD,wBAAgB,KAAK,IAAI,IAAI,CAoB5B"}
package/dist/core.js ADDED
@@ -0,0 +1,115 @@
1
+ import { getSessionId } from './session';
2
+ import { detectBrowser } from './browser';
3
+ const MAX_BATCH = 30;
4
+ const FLUSH_INTERVAL_MS = 250;
5
+ let config = null;
6
+ let queue = [];
7
+ let flushTimer = null;
8
+ let browserInfo = null;
9
+ export function configure(cfg) {
10
+ config = cfg;
11
+ browserInfo = detectBrowser();
12
+ startFlushTimer();
13
+ setupBeaconFlush();
14
+ }
15
+ export function getConfig() {
16
+ return config;
17
+ }
18
+ export function pushEvent(event) {
19
+ if (!config)
20
+ return;
21
+ if (config.sampleRate !== undefined && config.sampleRate < 1) {
22
+ if (Math.random() > config.sampleRate)
23
+ return;
24
+ }
25
+ event.timestamp = event.timestamp ?? Date.now() * 1000000; // ns
26
+ queue.push(event);
27
+ if (queue.length >= MAX_BATCH) {
28
+ flush();
29
+ }
30
+ }
31
+ function buildMeta() {
32
+ const cfg = config;
33
+ const bi = browserInfo;
34
+ let userId = '';
35
+ try {
36
+ const u = cfg.user?.();
37
+ userId = u?.id ?? '';
38
+ }
39
+ catch { /* ignore */ }
40
+ return {
41
+ app_name: cfg.app.name,
42
+ app_version: cfg.app.version ?? '',
43
+ environment: cfg.environment ?? '',
44
+ session_id: getSessionId(),
45
+ user_id: userId,
46
+ page_url: location.href,
47
+ page_path: location.pathname,
48
+ view_name: document.title,
49
+ referrer: document.referrer,
50
+ browser_name: bi.browserName,
51
+ browser_version: bi.browserVersion,
52
+ os_name: bi.osName,
53
+ os_version: bi.osVersion,
54
+ device_type: bi.deviceType,
55
+ screen_width: bi.screenWidth,
56
+ screen_height: bi.screenHeight,
57
+ };
58
+ }
59
+ export function flush() {
60
+ if (!config || queue.length === 0)
61
+ return;
62
+ const events = queue.splice(0, MAX_BATCH);
63
+ const payload = {
64
+ meta: buildMeta(),
65
+ events,
66
+ };
67
+ const body = JSON.stringify(payload);
68
+ // Try fetch first, fallback silently
69
+ fetch(config.endpoint, {
70
+ method: 'POST',
71
+ headers: { 'Content-Type': 'application/json' },
72
+ body,
73
+ keepalive: true,
74
+ }).catch(() => {
75
+ // Silently drop — RUM data is best-effort
76
+ });
77
+ }
78
+ function startFlushTimer() {
79
+ if (flushTimer)
80
+ return;
81
+ flushTimer = setInterval(() => {
82
+ if (queue.length > 0)
83
+ flush();
84
+ }, FLUSH_INTERVAL_MS);
85
+ }
86
+ function setupBeaconFlush() {
87
+ if (typeof document === 'undefined')
88
+ return;
89
+ const beaconFlush = () => {
90
+ if (!config || queue.length === 0)
91
+ return;
92
+ const events = queue.splice(0);
93
+ const payload = { meta: buildMeta(), events };
94
+ const body = JSON.stringify(payload);
95
+ if (navigator.sendBeacon) {
96
+ navigator.sendBeacon(config.endpoint, body);
97
+ }
98
+ else {
99
+ // Sync XHR as last resort (not recommended but works for unload)
100
+ try {
101
+ const xhr = new XMLHttpRequest();
102
+ xhr.open('POST', config.endpoint, false);
103
+ xhr.setRequestHeader('Content-Type', 'application/json');
104
+ xhr.send(body);
105
+ }
106
+ catch { /* ignore */ }
107
+ }
108
+ };
109
+ document.addEventListener('visibilitychange', () => {
110
+ if (document.visibilityState === 'hidden')
111
+ beaconFlush();
112
+ });
113
+ window.addEventListener('pagehide', beaconFlush);
114
+ }
115
+ //# sourceMappingURL=core.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"core.js","sourceRoot":"","sources":["../src/core.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export declare function initErrors(): void;
2
+ //# sourceMappingURL=errors.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"AAEA,wBAAgB,UAAU,IAAI,IAAI,CA+BjC"}
package/dist/errors.js ADDED
@@ -0,0 +1,32 @@
1
+ import { pushEvent } from './core';
2
+ export function initErrors() {
3
+ window.addEventListener('error', (event) => {
4
+ pushEvent({
5
+ event_type: 'error',
6
+ error_message: event.message || 'Unknown error',
7
+ error_stack: event.error?.stack ?? '',
8
+ error_type: event.error?.name ?? 'Error',
9
+ });
10
+ });
11
+ window.addEventListener('unhandledrejection', (event) => {
12
+ const reason = event.reason;
13
+ let message = 'Unhandled promise rejection';
14
+ let stack = '';
15
+ let errorType = 'UnhandledRejection';
16
+ if (reason instanceof Error) {
17
+ message = reason.message;
18
+ stack = reason.stack ?? '';
19
+ errorType = reason.name;
20
+ }
21
+ else if (typeof reason === 'string') {
22
+ message = reason;
23
+ }
24
+ pushEvent({
25
+ event_type: 'error',
26
+ error_message: message,
27
+ error_stack: stack,
28
+ error_type: errorType,
29
+ });
30
+ });
31
+ }
32
+ //# sourceMappingURL=errors.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"errors.js","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,QAAQ,CAAA;AAElC,MAAM,UAAU,UAAU;IACxB,MAAM,CAAC,gBAAgB,CAAC,OAAO,EAAE,CAAC,KAAiB,EAAE,EAAE;QACrD,SAAS,CAAC;YACR,UAAU,EAAE,OAAO;YACnB,aAAa,EAAE,KAAK,CAAC,OAAO,IAAI,eAAe;YAC/C,WAAW,EAAE,KAAK,CAAC,KAAK,EAAE,KAAK,IAAI,EAAE;YACrC,UAAU,EAAE,KAAK,CAAC,KAAK,EAAE,IAAI,IAAI,OAAO;SACzC,CAAC,CAAA;IACJ,CAAC,CAAC,CAAA;IAEF,MAAM,CAAC,gBAAgB,CAAC,oBAAoB,EAAE,CAAC,KAA4B,EAAE,EAAE;QAC7E,MAAM,MAAM,GAAG,KAAK,CAAC,MAAM,CAAA;QAC3B,IAAI,OAAO,GAAG,6BAA6B,CAAA;QAC3C,IAAI,KAAK,GAAG,EAAE,CAAA;QACd,IAAI,SAAS,GAAG,oBAAoB,CAAA;QAEpC,IAAI,MAAM,YAAY,KAAK,EAAE,CAAC;YAC5B,OAAO,GAAG,MAAM,CAAC,OAAO,CAAA;YACxB,KAAK,GAAG,MAAM,CAAC,KAAK,IAAI,EAAE,CAAA;YAC1B,SAAS,GAAG,MAAM,CAAC,IAAI,CAAA;QACzB,CAAC;aAAM,IAAI,OAAO,MAAM,KAAK,QAAQ,EAAE,CAAC;YACtC,OAAO,GAAG,MAAM,CAAA;QAClB,CAAC;QAED,SAAS,CAAC;YACR,UAAU,EAAE,OAAO;YACnB,aAAa,EAAE,OAAO;YACtB,WAAW,EAAE,KAAK;YAClB,UAAU,EAAE,SAAS;SACtB,CAAC,CAAA;IACJ,CAAC,CAAC,CAAA;AACJ,CAAC"}
@@ -0,0 +1,16 @@
1
+ import type { RushRUMConfig } from './types';
2
+ import { flush } from './core';
3
+ export type { RushRUMConfig, RumEvent, RumPayload } from './types';
4
+ export declare const RushRUM: {
5
+ init(config: RushRUMConfig): void;
6
+ /**
7
+ * Send a custom event.
8
+ */
9
+ trackEvent(name: string, attributes?: Record<string, unknown>): void;
10
+ /**
11
+ * Force flush the event queue.
12
+ */
13
+ flush: typeof flush;
14
+ };
15
+ export default RushRUM;
16
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,SAAS,CAAA;AAC5C,OAAO,EAAa,KAAK,EAAa,MAAM,QAAQ,CAAA;AAQpD,YAAY,EAAE,aAAa,EAAE,QAAQ,EAAE,UAAU,EAAE,MAAM,SAAS,CAAA;AAElE,eAAO,MAAM,OAAO;iBACL,aAAa,GAAG,IAAI;IAuBjC;;OAEG;qBACc,MAAM,eAAe,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI;IAQpE;;OAEG;;CAEJ,CAAA;AAED,eAAe,OAAO,CAAA"}
package/dist/index.js ADDED
@@ -0,0 +1,46 @@
1
+ import { configure, flush, pushEvent } from './core';
2
+ import { initVitals } from './vitals';
3
+ import { initErrors } from './errors';
4
+ import { initPageViews } from './pageview';
5
+ import { initInteractions } from './interactions';
6
+ import { initResources } from './resources';
7
+ import { initReplay } from './replay';
8
+ export const RushRUM = {
9
+ init(config) {
10
+ configure(config);
11
+ if (config.trackWebVitals !== false) {
12
+ initVitals();
13
+ }
14
+ if (config.trackErrors !== false) {
15
+ initErrors();
16
+ }
17
+ if (config.trackPageViews !== false) {
18
+ initPageViews();
19
+ }
20
+ if (config.trackInteractions === true) {
21
+ initInteractions();
22
+ }
23
+ if (config.trackResources === true) {
24
+ initResources();
25
+ }
26
+ if (config.trackSessionReplay === true) {
27
+ initReplay(config);
28
+ }
29
+ },
30
+ /**
31
+ * Send a custom event.
32
+ */
33
+ trackEvent(name, attributes) {
34
+ pushEvent({
35
+ event_type: 'custom',
36
+ event_name: name,
37
+ attributes: attributes ? JSON.stringify(attributes) : undefined,
38
+ });
39
+ },
40
+ /**
41
+ * Force flush the event queue.
42
+ */
43
+ flush,
44
+ };
45
+ export default RushRUM;
46
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,SAAS,EAAE,KAAK,EAAE,SAAS,EAAE,MAAM,QAAQ,CAAA;AACpD,OAAO,EAAE,UAAU,EAAE,MAAM,UAAU,CAAA;AACrC,OAAO,EAAE,UAAU,EAAE,MAAM,UAAU,CAAA;AACrC,OAAO,EAAE,aAAa,EAAE,MAAM,YAAY,CAAA;AAC1C,OAAO,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAA;AACjD,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAA;AAC3C,OAAO,EAAE,UAAU,EAAE,MAAM,UAAU,CAAA;AAIrC,MAAM,CAAC,MAAM,OAAO,GAAG;IACrB,IAAI,CAAC,MAAqB;QACxB,SAAS,CAAC,MAAM,CAAC,CAAA;QAEjB,IAAI,MAAM,CAAC,cAAc,KAAK,KAAK,EAAE,CAAC;YACpC,UAAU,EAAE,CAAA;QACd,CAAC;QACD,IAAI,MAAM,CAAC,WAAW,KAAK,KAAK,EAAE,CAAC;YACjC,UAAU,EAAE,CAAA;QACd,CAAC;QACD,IAAI,MAAM,CAAC,cAAc,KAAK,KAAK,EAAE,CAAC;YACpC,aAAa,EAAE,CAAA;QACjB,CAAC;QACD,IAAI,MAAM,CAAC,iBAAiB,KAAK,IAAI,EAAE,CAAC;YACtC,gBAAgB,EAAE,CAAA;QACpB,CAAC;QACD,IAAI,MAAM,CAAC,cAAc,KAAK,IAAI,EAAE,CAAC;YACnC,aAAa,EAAE,CAAA;QACjB,CAAC;QACD,IAAI,MAAM,CAAC,kBAAkB,KAAK,IAAI,EAAE,CAAC;YACvC,UAAU,CAAC,MAAM,CAAC,CAAA;QACpB,CAAC;IACH,CAAC;IAED;;OAEG;IACH,UAAU,CAAC,IAAY,EAAE,UAAoC;QAC3D,SAAS,CAAC;YACR,UAAU,EAAE,QAAQ;YACpB,UAAU,EAAE,IAAI;YAChB,UAAU,EAAE,UAAU,CAAC,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,SAAS;SAChE,CAAC,CAAA;IACJ,CAAC;IAED;;OAEG;IACH,KAAK;CACN,CAAA;AAED,eAAe,OAAO,CAAA"}
@@ -0,0 +1,2 @@
1
+ export declare function initInteractions(): void;
2
+ //# sourceMappingURL=interactions.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"interactions.d.ts","sourceRoot":"","sources":["../src/interactions.ts"],"names":[],"mappings":"AAIA,wBAAgB,gBAAgB,IAAI,IAAI,CAqBvC"}
@@ -0,0 +1,24 @@
1
+ import { pushEvent } from './core';
2
+ const INTERACTIVE_SELECTORS = 'button, a, [role="button"], input[type="submit"], input[type="button"]';
3
+ export function initInteractions() {
4
+ document.addEventListener('click', (event) => {
5
+ const target = event.target;
6
+ if (!target)
7
+ return;
8
+ const interactive = target.closest(INTERACTIVE_SELECTORS);
9
+ if (!interactive)
10
+ return;
11
+ const tag = interactive.tagName.toLowerCase();
12
+ const text = (interactive.textContent ?? '').trim().slice(0, 100);
13
+ const id = interactive.id ? `#${interactive.id}` : '';
14
+ const classes = interactive.className
15
+ ? `.${Array.from(interactive.classList).slice(0, 3).join('.')}`
16
+ : '';
17
+ pushEvent({
18
+ event_type: 'interaction',
19
+ interaction_type: 'click',
20
+ interaction_target: `${tag}${id}${classes}${text ? ` "${text}"` : ''}`,
21
+ });
22
+ }, { capture: true, passive: true });
23
+ }
24
+ //# sourceMappingURL=interactions.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"interactions.js","sourceRoot":"","sources":["../src/interactions.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,QAAQ,CAAA;AAElC,MAAM,qBAAqB,GAAG,wEAAwE,CAAA;AAEtG,MAAM,UAAU,gBAAgB;IAC9B,QAAQ,CAAC,gBAAgB,CAAC,OAAO,EAAE,CAAC,KAAiB,EAAE,EAAE;QACvD,MAAM,MAAM,GAAG,KAAK,CAAC,MAAwB,CAAA;QAC7C,IAAI,CAAC,MAAM;YAAE,OAAM;QAEnB,MAAM,WAAW,GAAG,MAAM,CAAC,OAAO,CAAC,qBAAqB,CAAC,CAAA;QACzD,IAAI,CAAC,WAAW;YAAE,OAAM;QAExB,MAAM,GAAG,GAAG,WAAW,CAAC,OAAO,CAAC,WAAW,EAAE,CAAA;QAC7C,MAAM,IAAI,GAAG,CAAC,WAAW,CAAC,WAAW,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC,CAAA;QACjE,MAAM,EAAE,GAAG,WAAW,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,WAAW,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,CAAA;QACrD,MAAM,OAAO,GAAG,WAAW,CAAC,SAAS;YACnC,CAAC,CAAC,IAAI,KAAK,CAAC,IAAI,CAAC,WAAW,CAAC,SAAS,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE;YAC/D,CAAC,CAAC,EAAE,CAAA;QAEN,SAAS,CAAC;YACR,UAAU,EAAE,aAAa;YACzB,gBAAgB,EAAE,OAAO;YACzB,kBAAkB,EAAE,GAAG,GAAG,GAAG,EAAE,GAAG,OAAO,GAAG,IAAI,CAAC,CAAC,CAAC,KAAK,IAAI,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE;SACvE,CAAC,CAAA;IACJ,CAAC,EAAE,EAAE,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC,CAAA;AACtC,CAAC"}
@@ -0,0 +1,2 @@
1
+ export declare function initPageViews(): void;
2
+ //# sourceMappingURL=pageview.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pageview.d.ts","sourceRoot":"","sources":["../src/pageview.ts"],"names":[],"mappings":"AAEA,wBAAgB,aAAa,IAAI,IAAI,CAsBpC"}
@@ -0,0 +1,51 @@
1
+ import { pushEvent } from './core';
2
+ export function initPageViews() {
3
+ // Track initial page load
4
+ trackPageView();
5
+ // SPA navigation: history.pushState / replaceState
6
+ const origPush = history.pushState.bind(history);
7
+ const origReplace = history.replaceState.bind(history);
8
+ history.pushState = function (...args) {
9
+ origPush(...args);
10
+ trackPageView();
11
+ };
12
+ history.replaceState = function (...args) {
13
+ origReplace(...args);
14
+ trackPageView();
15
+ };
16
+ // Back/forward navigation
17
+ window.addEventListener('popstate', () => {
18
+ trackPageView();
19
+ });
20
+ }
21
+ function generateTraceId() {
22
+ const bytes = new Uint8Array(16);
23
+ crypto.getRandomValues(bytes);
24
+ return Array.from(bytes).map((b) => b.toString(16).padStart(2, '0')).join('');
25
+ }
26
+ function generateSpanId() {
27
+ const bytes = new Uint8Array(8);
28
+ crypto.getRandomValues(bytes);
29
+ return Array.from(bytes).map((b) => b.toString(16).padStart(2, '0')).join('');
30
+ }
31
+ function trackPageView() {
32
+ const traceId = generateTraceId();
33
+ const spanId = generateSpanId();
34
+ pushEvent({
35
+ event_type: 'pageview',
36
+ event_name: document.title,
37
+ duration_ms: getNavigationLoadTime(),
38
+ trace_id: traceId,
39
+ span_id: spanId,
40
+ });
41
+ }
42
+ function getNavigationLoadTime() {
43
+ if (typeof performance === 'undefined')
44
+ return 0;
45
+ const entries = performance.getEntriesByType('navigation');
46
+ if (entries.length > 0) {
47
+ return entries[0].loadEventEnd - entries[0].startTime;
48
+ }
49
+ return 0;
50
+ }
51
+ //# sourceMappingURL=pageview.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pageview.js","sourceRoot":"","sources":["../src/pageview.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,QAAQ,CAAA;AAElC,MAAM,UAAU,aAAa;IAC3B,0BAA0B;IAC1B,aAAa,EAAE,CAAA;IAEf,mDAAmD;IACnD,MAAM,QAAQ,GAAG,OAAO,CAAC,SAAS,CAAC,IAAI,CAAC,OAAO,CAAC,CAAA;IAChD,MAAM,WAAW,GAAG,OAAO,CAAC,YAAY,CAAC,IAAI,CAAC,OAAO,CAAC,CAAA;IAEtD,OAAO,CAAC,SAAS,GAAG,UAAU,GAAG,IAAiC;QAChE,QAAQ,CAAC,GAAG,IAAI,CAAC,CAAA;QACjB,aAAa,EAAE,CAAA;IACjB,CAAC,CAAA;IAED,OAAO,CAAC,YAAY,GAAG,UAAU,GAAG,IAAoC;QACtE,WAAW,CAAC,GAAG,IAAI,CAAC,CAAA;QACpB,aAAa,EAAE,CAAA;IACjB,CAAC,CAAA;IAED,0BAA0B;IAC1B,MAAM,CAAC,gBAAgB,CAAC,UAAU,EAAE,GAAG,EAAE;QACvC,aAAa,EAAE,CAAA;IACjB,CAAC,CAAC,CAAA;AACJ,CAAC;AAED,SAAS,eAAe;IACtB,MAAM,KAAK,GAAG,IAAI,UAAU,CAAC,EAAE,CAAC,CAAA;IAChC,MAAM,CAAC,eAAe,CAAC,KAAK,CAAC,CAAA;IAC7B,OAAO,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC,QAAQ,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC,CAAA;AAC/E,CAAC;AAED,SAAS,cAAc;IACrB,MAAM,KAAK,GAAG,IAAI,UAAU,CAAC,CAAC,CAAC,CAAA;IAC/B,MAAM,CAAC,eAAe,CAAC,KAAK,CAAC,CAAA;IAC7B,OAAO,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC,QAAQ,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC,CAAA;AAC/E,CAAC;AAED,SAAS,aAAa;IACpB,MAAM,OAAO,GAAG,eAAe,EAAE,CAAA;IACjC,MAAM,MAAM,GAAG,cAAc,EAAE,CAAA;IAE/B,SAAS,CAAC;QACR,UAAU,EAAE,UAAU;QACtB,UAAU,EAAE,QAAQ,CAAC,KAAK;QAC1B,WAAW,EAAE,qBAAqB,EAAE;QACpC,QAAQ,EAAE,OAAO;QACjB,OAAO,EAAE,MAAM;KAChB,CAAC,CAAA;AACJ,CAAC;AAED,SAAS,qBAAqB;IAC5B,IAAI,OAAO,WAAW,KAAK,WAAW;QAAE,OAAO,CAAC,CAAA;IAChD,MAAM,OAAO,GAAG,WAAW,CAAC,gBAAgB,CAAC,YAAY,CAAkC,CAAA;IAC3F,IAAI,OAAO,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACvB,OAAO,OAAO,CAAC,CAAC,CAAC,CAAC,YAAY,GAAG,OAAO,CAAC,CAAC,CAAC,CAAC,SAAS,CAAA;IACvD,CAAC;IACD,OAAO,CAAC,CAAA;AACV,CAAC"}
@@ -0,0 +1,9 @@
1
+ /**
2
+ * Session replay recorder — captures rrweb DOM snapshots and mutation events
3
+ * and streams them in chunks to the Rush replay ingest endpoint.
4
+ *
5
+ * Must be called only after `configure()` (i.e. inside `RushRUM.init`).
6
+ */
7
+ import type { RushRUMConfig } from './types';
8
+ export declare function initReplay(config: RushRUMConfig): void;
9
+ //# sourceMappingURL=replay.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"replay.d.ts","sourceRoot":"","sources":["../src/replay.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,SAAS,CAAA;AAyC5C,wBAAgB,UAAU,CAAC,MAAM,EAAE,aAAa,GAAG,IAAI,CAyBtD"}
package/dist/replay.js ADDED
@@ -0,0 +1,62 @@
1
+ import { getSessionId } from './session';
2
+ const CHUNK_SIZE = 50; // flush after this many events
3
+ const FLUSH_INTERVAL_MS = 5000; // flush every 5 s regardless
4
+ let _config = null;
5
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
6
+ let _buffer = [];
7
+ let _chunkIdx = 0;
8
+ let _flushTimer = null;
9
+ function replayEndpoint() {
10
+ const cfg = _config;
11
+ if (cfg.replayEndpoint)
12
+ return cfg.replayEndpoint;
13
+ // Derive from the event ingest endpoint: replace /rum/ingest with /rum/replay/ingest
14
+ return cfg.endpoint.replace(/\/rum\/ingest$/, '/rum/replay/ingest');
15
+ }
16
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
17
+ function sendChunk(events) {
18
+ if (!_config || events.length === 0)
19
+ return;
20
+ const payload = {
21
+ session_id: getSessionId(),
22
+ app_name: _config.app.name,
23
+ chunk_idx: _chunkIdx++,
24
+ events,
25
+ };
26
+ fetch(replayEndpoint(), {
27
+ method: 'POST',
28
+ headers: { 'Content-Type': 'application/json' },
29
+ body: JSON.stringify(payload),
30
+ keepalive: true,
31
+ }).catch(() => { });
32
+ }
33
+ function flushBuffer() {
34
+ if (_buffer.length === 0)
35
+ return;
36
+ sendChunk(_buffer.splice(0));
37
+ }
38
+ export function initReplay(config) {
39
+ _config = config;
40
+ // Dynamically import rrweb so it doesn't bloat pages that don't enable replay
41
+ import('rrweb').then(({ record }) => {
42
+ record({
43
+ emit(event) {
44
+ _buffer.push(event);
45
+ if (_buffer.length >= CHUNK_SIZE) {
46
+ flushBuffer();
47
+ }
48
+ },
49
+ maskAllInputs: true, // mask form fields for privacy
50
+ maskTextSelector: '[data-pii]', // opt-in masking of PII text nodes
51
+ });
52
+ }).catch(() => {
53
+ // rrweb not installed — replay silently disabled
54
+ });
55
+ _flushTimer = setInterval(flushBuffer, FLUSH_INTERVAL_MS);
56
+ document.addEventListener('visibilitychange', () => {
57
+ if (document.visibilityState === 'hidden')
58
+ flushBuffer();
59
+ });
60
+ window.addEventListener('pagehide', flushBuffer);
61
+ }
62
+ //# sourceMappingURL=replay.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"replay.js","sourceRoot":"","sources":["../src/replay.ts"],"names":[],"mappings":"AAOA,OAAO,EAAE,YAAY,EAAE,MAAM,WAAW,CAAA;AAExC,MAAM,UAAU,GAAG,EAAE,CAAA,CAAU,+BAA+B;AAC9D,MAAM,iBAAiB,GAAG,IAAK,CAAA,CAAC,6BAA6B;AAE7D,IAAI,OAAO,GAAyB,IAAI,CAAA;AACxC,8DAA8D;AAC9D,IAAI,OAAO,GAAU,EAAE,CAAA;AACvB,IAAI,SAAS,GAAG,CAAC,CAAA;AACjB,IAAI,WAAW,GAA0C,IAAI,CAAA;AAE7D,SAAS,cAAc;IACrB,MAAM,GAAG,GAAG,OAAQ,CAAA;IACpB,IAAI,GAAG,CAAC,cAAc;QAAE,OAAO,GAAG,CAAC,cAAc,CAAA;IACjD,qFAAqF;IACrF,OAAO,GAAG,CAAC,QAAQ,CAAC,OAAO,CAAC,gBAAgB,EAAE,oBAAoB,CAAC,CAAA;AACrE,CAAC;AAED,8DAA8D;AAC9D,SAAS,SAAS,CAAC,MAAa;IAC9B,IAAI,CAAC,OAAO,IAAI,MAAM,CAAC,MAAM,KAAK,CAAC;QAAE,OAAM;IAC3C,MAAM,OAAO,GAAG;QACd,UAAU,EAAE,YAAY,EAAE;QAC1B,QAAQ,EAAE,OAAO,CAAC,GAAG,CAAC,IAAI;QAC1B,SAAS,EAAE,SAAS,EAAE;QACtB,MAAM;KACP,CAAA;IACD,KAAK,CAAC,cAAc,EAAE,EAAE;QACtB,MAAM,EAAE,MAAM;QACd,OAAO,EAAE,EAAE,cAAc,EAAE,kBAAkB,EAAE;QAC/C,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC;QAC7B,SAAS,EAAE,IAAI;KAChB,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE,GAAqB,CAAC,CAAC,CAAA;AACvC,CAAC;AAED,SAAS,WAAW;IAClB,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC;QAAE,OAAM;IAChC,SAAS,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAA;AAC9B,CAAC;AAED,MAAM,UAAU,UAAU,CAAC,MAAqB;IAC9C,OAAO,GAAG,MAAM,CAAA;IAEhB,8EAA8E;IAC9E,MAAM,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,CAAC,EAAE,MAAM,EAAE,EAAE,EAAE;QAClC,MAAM,CAAC;YACL,IAAI,CAAC,KAAK;gBACR,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAA;gBACnB,IAAI,OAAO,CAAC,MAAM,IAAI,UAAU,EAAE,CAAC;oBACjC,WAAW,EAAE,CAAA;gBACf,CAAC;YACH,CAAC;YACD,aAAa,EAAE,IAAI,EAAM,+BAA+B;YACxD,gBAAgB,EAAE,YAAY,EAAE,mCAAmC;SACpE,CAAC,CAAA;IACJ,CAAC,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE;QACZ,iDAAiD;IACnD,CAAC,CAAC,CAAA;IAEF,WAAW,GAAG,WAAW,CAAC,WAAW,EAAE,iBAAiB,CAAC,CAAA;IAEzD,QAAQ,CAAC,gBAAgB,CAAC,kBAAkB,EAAE,GAAG,EAAE;QACjD,IAAI,QAAQ,CAAC,eAAe,KAAK,QAAQ;YAAE,WAAW,EAAE,CAAA;IAC1D,CAAC,CAAC,CAAA;IACF,MAAM,CAAC,gBAAgB,CAAC,UAAU,EAAE,WAAW,CAAC,CAAA;AAClD,CAAC"}
@@ -0,0 +1,2 @@
1
+ export declare function initResources(): void;
2
+ //# sourceMappingURL=resources.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"resources.d.ts","sourceRoot":"","sources":["../src/resources.ts"],"names":[],"mappings":"AAEA,wBAAgB,aAAa,IAAI,IAAI,CAGpC"}
@@ -0,0 +1,97 @@
1
+ import { pushEvent, getConfig } from './core';
2
+ export function initResources() {
3
+ patchFetch();
4
+ patchXHR();
5
+ }
6
+ function shouldTraceOrigin(url) {
7
+ const cfg = getConfig();
8
+ if (!cfg?.propagateTraces?.origins)
9
+ return false;
10
+ return cfg.propagateTraces.origins.some((re) => re.test(url));
11
+ }
12
+ function generateTraceId() {
13
+ const bytes = new Uint8Array(16);
14
+ crypto.getRandomValues(bytes);
15
+ return Array.from(bytes).map((b) => b.toString(16).padStart(2, '0')).join('');
16
+ }
17
+ function generateSpanId() {
18
+ const bytes = new Uint8Array(8);
19
+ crypto.getRandomValues(bytes);
20
+ return Array.from(bytes).map((b) => b.toString(16).padStart(2, '0')).join('');
21
+ }
22
+ function patchFetch() {
23
+ const origFetch = window.fetch.bind(window);
24
+ window.fetch = async function (input, init) {
25
+ const url = typeof input === 'string' ? input : input instanceof URL ? input.href : input.url;
26
+ const start = performance.now();
27
+ let traceId = '';
28
+ let spanId = '';
29
+ if (shouldTraceOrigin(url)) {
30
+ traceId = generateTraceId();
31
+ spanId = generateSpanId();
32
+ const headers = new Headers(init?.headers);
33
+ headers.set('traceparent', `00-${traceId}-${spanId}-01`);
34
+ init = { ...init, headers };
35
+ }
36
+ try {
37
+ const response = await origFetch(input, init);
38
+ const durationMs = performance.now() - start;
39
+ pushEvent({
40
+ event_type: 'resource',
41
+ event_name: url,
42
+ duration_ms: durationMs,
43
+ trace_id: traceId,
44
+ span_id: spanId,
45
+ attributes: JSON.stringify({ status: response.status, method: init?.method ?? 'GET' }),
46
+ });
47
+ return response;
48
+ }
49
+ catch (err) {
50
+ const durationMs = performance.now() - start;
51
+ pushEvent({
52
+ event_type: 'resource',
53
+ event_name: url,
54
+ duration_ms: durationMs,
55
+ trace_id: traceId,
56
+ span_id: spanId,
57
+ error_message: err instanceof Error ? err.message : 'fetch failed',
58
+ error_type: 'NetworkError',
59
+ });
60
+ throw err;
61
+ }
62
+ };
63
+ }
64
+ function patchXHR() {
65
+ const origOpen = XMLHttpRequest.prototype.open;
66
+ const origSend = XMLHttpRequest.prototype.send;
67
+ XMLHttpRequest.prototype.open = function (method, url, ...rest) {
68
+ this.__wide_url = typeof url === 'string' ? url : url.href;
69
+ this.__wide_method = method;
70
+ return origOpen.apply(this, [method, url, ...rest]);
71
+ };
72
+ XMLHttpRequest.prototype.send = function (body) {
73
+ const url = this.__wide_url ?? '';
74
+ const method = this.__wide_method ?? 'GET';
75
+ const start = performance.now();
76
+ let traceId = '';
77
+ let spanId = '';
78
+ if (shouldTraceOrigin(url)) {
79
+ traceId = generateTraceId();
80
+ spanId = generateSpanId();
81
+ this.setRequestHeader('traceparent', `00-${traceId}-${spanId}-01`);
82
+ }
83
+ this.addEventListener('loadend', () => {
84
+ const durationMs = performance.now() - start;
85
+ pushEvent({
86
+ event_type: 'resource',
87
+ event_name: url,
88
+ duration_ms: durationMs,
89
+ trace_id: traceId,
90
+ span_id: spanId,
91
+ attributes: JSON.stringify({ status: this.status, method }),
92
+ });
93
+ });
94
+ return origSend.call(this, body);
95
+ };
96
+ }
97
+ //# sourceMappingURL=resources.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"resources.js","sourceRoot":"","sources":["../src/resources.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,3 @@
1
+ export declare function getSessionId(): string;
2
+ export declare function touchSession(): void;
3
+ //# sourceMappingURL=session.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"session.d.ts","sourceRoot":"","sources":["../src/session.ts"],"names":[],"mappings":"AAeA,wBAAgB,YAAY,IAAI,MAAM,CAmBrC;AAED,wBAAgB,YAAY,IAAI,IAAI,CAInC"}
@@ -0,0 +1,39 @@
1
+ const SESSION_KEY = 'rush_rum_sid';
2
+ const SESSION_TS_KEY = 'rush_rum_sts';
3
+ const TIMEOUT_MS = 30 * 60 * 1000; // 30 minutes
4
+ function generateId() {
5
+ if (typeof crypto !== 'undefined' && crypto.randomUUID) {
6
+ return crypto.randomUUID();
7
+ }
8
+ return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, (c) => {
9
+ const r = (Math.random() * 16) | 0;
10
+ const v = c === 'x' ? r : (r & 0x3) | 0x8;
11
+ return v.toString(16);
12
+ });
13
+ }
14
+ export function getSessionId() {
15
+ try {
16
+ const stored = sessionStorage.getItem(SESSION_KEY);
17
+ const ts = sessionStorage.getItem(SESSION_TS_KEY);
18
+ const now = Date.now();
19
+ if (stored && ts && now - Number(ts) < TIMEOUT_MS) {
20
+ sessionStorage.setItem(SESSION_TS_KEY, String(now));
21
+ return stored;
22
+ }
23
+ const id = generateId();
24
+ sessionStorage.setItem(SESSION_KEY, id);
25
+ sessionStorage.setItem(SESSION_TS_KEY, String(now));
26
+ return id;
27
+ }
28
+ catch {
29
+ // sessionStorage not available (e.g. incognito overflow)
30
+ return generateId();
31
+ }
32
+ }
33
+ export function touchSession() {
34
+ try {
35
+ sessionStorage.setItem(SESSION_TS_KEY, String(Date.now()));
36
+ }
37
+ catch { /* ignore */ }
38
+ }
39
+ //# sourceMappingURL=session.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"session.js","sourceRoot":"","sources":["../src/session.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,GAAG,cAAc,CAAA;AAClC,MAAM,cAAc,GAAG,cAAc,CAAA;AACrC,MAAM,UAAU,GAAG,EAAE,GAAG,EAAE,GAAG,IAAI,CAAA,CAAC,aAAa;AAE/C,SAAS,UAAU;IACjB,IAAI,OAAO,MAAM,KAAK,WAAW,IAAI,MAAM,CAAC,UAAU,EAAE,CAAC;QACvD,OAAO,MAAM,CAAC,UAAU,EAAE,CAAA;IAC5B,CAAC;IACD,OAAO,sCAAsC,CAAC,OAAO,CAAC,OAAO,EAAE,CAAC,CAAC,EAAE,EAAE;QACnE,MAAM,CAAC,GAAG,CAAC,IAAI,CAAC,MAAM,EAAE,GAAG,EAAE,CAAC,GAAG,CAAC,CAAA;QAClC,MAAM,CAAC,GAAG,CAAC,KAAK,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,GAAG,CAAC,GAAG,GAAG,CAAA;QACzC,OAAO,CAAC,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAA;IACvB,CAAC,CAAC,CAAA;AACJ,CAAC;AAED,MAAM,UAAU,YAAY;IAC1B,IAAI,CAAC;QACH,MAAM,MAAM,GAAG,cAAc,CAAC,OAAO,CAAC,WAAW,CAAC,CAAA;QAClD,MAAM,EAAE,GAAG,cAAc,CAAC,OAAO,CAAC,cAAc,CAAC,CAAA;QACjD,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,EAAE,CAAA;QAEtB,IAAI,MAAM,IAAI,EAAE,IAAI,GAAG,GAAG,MAAM,CAAC,EAAE,CAAC,GAAG,UAAU,EAAE,CAAC;YAClD,cAAc,CAAC,OAAO,CAAC,cAAc,EAAE,MAAM,CAAC,GAAG,CAAC,CAAC,CAAA;YACnD,OAAO,MAAM,CAAA;QACf,CAAC;QAED,MAAM,EAAE,GAAG,UAAU,EAAE,CAAA;QACvB,cAAc,CAAC,OAAO,CAAC,WAAW,EAAE,EAAE,CAAC,CAAA;QACvC,cAAc,CAAC,OAAO,CAAC,cAAc,EAAE,MAAM,CAAC,GAAG,CAAC,CAAC,CAAA;QACnD,OAAO,EAAE,CAAA;IACX,CAAC;IAAC,MAAM,CAAC;QACP,yDAAyD;QACzD,OAAO,UAAU,EAAE,CAAA;IACrB,CAAC;AACH,CAAC;AAED,MAAM,UAAU,YAAY;IAC1B,IAAI,CAAC;QACH,cAAc,CAAC,OAAO,CAAC,cAAc,EAAE,MAAM,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC,CAAC,CAAA;IAC5D,CAAC;IAAC,MAAM,CAAC,CAAC,YAAY,CAAC,CAAC;AAC1B,CAAC"}
@@ -0,0 +1,63 @@
1
+ export interface RushRUMConfig {
2
+ endpoint: string;
3
+ app: {
4
+ name: string;
5
+ version?: string;
6
+ };
7
+ environment?: string;
8
+ user?: () => {
9
+ id?: string;
10
+ } | null;
11
+ sampleRate?: number;
12
+ trackWebVitals?: boolean;
13
+ trackErrors?: boolean;
14
+ trackInteractions?: boolean;
15
+ trackResources?: boolean;
16
+ trackPageViews?: boolean;
17
+ propagateTraces?: {
18
+ origins: RegExp[];
19
+ };
20
+ trackSessionReplay?: boolean;
21
+ /** Override the replay ingest endpoint (defaults to endpoint with /rum/replay/ingest) */
22
+ replayEndpoint?: string;
23
+ }
24
+ export interface RumMeta {
25
+ app_name: string;
26
+ app_version: string;
27
+ environment: string;
28
+ session_id: string;
29
+ user_id: string;
30
+ page_url: string;
31
+ page_path: string;
32
+ view_name: string;
33
+ referrer: string;
34
+ browser_name: string;
35
+ browser_version: string;
36
+ os_name: string;
37
+ os_version: string;
38
+ device_type: string;
39
+ screen_width: number;
40
+ screen_height: number;
41
+ }
42
+ export interface RumEvent {
43
+ event_type: string;
44
+ event_name?: string;
45
+ timestamp?: number;
46
+ vital_name?: string;
47
+ vital_value?: number;
48
+ vital_rating?: string;
49
+ error_message?: string;
50
+ error_stack?: string;
51
+ error_type?: string;
52
+ interaction_target?: string;
53
+ interaction_type?: string;
54
+ duration_ms?: number;
55
+ trace_id?: string;
56
+ span_id?: string;
57
+ attributes?: string;
58
+ }
59
+ export interface RumPayload {
60
+ meta: RumMeta;
61
+ events: RumEvent[];
62
+ }
63
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,aAAa;IAC5B,QAAQ,EAAE,MAAM,CAAA;IAChB,GAAG,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,OAAO,CAAC,EAAE,MAAM,CAAA;KAAE,CAAA;IACvC,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,IAAI,CAAC,EAAE,MAAM;QAAE,EAAE,CAAC,EAAE,MAAM,CAAA;KAAE,GAAG,IAAI,CAAA;IACnC,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,cAAc,CAAC,EAAE,OAAO,CAAA;IACxB,WAAW,CAAC,EAAE,OAAO,CAAA;IACrB,iBAAiB,CAAC,EAAE,OAAO,CAAA;IAC3B,cAAc,CAAC,EAAE,OAAO,CAAA;IACxB,cAAc,CAAC,EAAE,OAAO,CAAA;IACxB,eAAe,CAAC,EAAE;QAAE,OAAO,EAAE,MAAM,EAAE,CAAA;KAAE,CAAA;IACvC,kBAAkB,CAAC,EAAE,OAAO,CAAA;IAC5B,yFAAyF;IACzF,cAAc,CAAC,EAAE,MAAM,CAAA;CACxB;AAED,MAAM,WAAW,OAAO;IACtB,QAAQ,EAAE,MAAM,CAAA;IAChB,WAAW,EAAE,MAAM,CAAA;IACnB,WAAW,EAAE,MAAM,CAAA;IACnB,UAAU,EAAE,MAAM,CAAA;IAClB,OAAO,EAAE,MAAM,CAAA;IACf,QAAQ,EAAE,MAAM,CAAA;IAChB,SAAS,EAAE,MAAM,CAAA;IACjB,SAAS,EAAE,MAAM,CAAA;IACjB,QAAQ,EAAE,MAAM,CAAA;IAChB,YAAY,EAAE,MAAM,CAAA;IACpB,eAAe,EAAE,MAAM,CAAA;IACvB,OAAO,EAAE,MAAM,CAAA;IACf,UAAU,EAAE,MAAM,CAAA;IAClB,WAAW,EAAE,MAAM,CAAA;IACnB,YAAY,EAAE,MAAM,CAAA;IACpB,aAAa,EAAE,MAAM,CAAA;CACtB;AAED,MAAM,WAAW,QAAQ;IACvB,UAAU,EAAE,MAAM,CAAA;IAClB,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,YAAY,CAAC,EAAE,MAAM,CAAA;IACrB,aAAa,CAAC,EAAE,MAAM,CAAA;IACtB,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,kBAAkB,CAAC,EAAE,MAAM,CAAA;IAC3B,gBAAgB,CAAC,EAAE,MAAM,CAAA;IACzB,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,UAAU,CAAC,EAAE,MAAM,CAAA;CACpB;AAED,MAAM,WAAW,UAAU;IACzB,IAAI,EAAE,OAAO,CAAA;IACb,MAAM,EAAE,QAAQ,EAAE,CAAA;CACnB"}
package/dist/types.js ADDED
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":""}
@@ -0,0 +1,2 @@
1
+ export declare function initVitals(): void;
2
+ //# sourceMappingURL=vitals.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"vitals.d.ts","sourceRoot":"","sources":["../src/vitals.ts"],"names":[],"mappings":"AAEA,wBAAgB,UAAU,IAAI,IAAI,CAoBjC"}
package/dist/vitals.js ADDED
@@ -0,0 +1,22 @@
1
+ import { pushEvent } from './core';
2
+ export function initVitals() {
3
+ // Dynamic import so the SDK works even if web-vitals isn't available
4
+ import('web-vitals').then(({ onLCP, onCLS, onINP, onFCP, onTTFB }) => {
5
+ const report = (name) => (metric) => {
6
+ pushEvent({
7
+ event_type: 'web_vital',
8
+ vital_name: name,
9
+ vital_value: metric.value,
10
+ vital_rating: metric.rating,
11
+ });
12
+ };
13
+ onLCP(report('LCP'));
14
+ onCLS(report('CLS'));
15
+ onINP(report('INP'));
16
+ onFCP(report('FCP'));
17
+ onTTFB(report('TTFB'));
18
+ }).catch(() => {
19
+ // web-vitals not available — skip
20
+ });
21
+ }
22
+ //# sourceMappingURL=vitals.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"vitals.js","sourceRoot":"","sources":["../src/vitals.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,QAAQ,CAAA;AAElC,MAAM,UAAU,UAAU;IACxB,qEAAqE;IACrE,MAAM,CAAC,YAAY,CAAC,CAAC,IAAI,CAAC,CAAC,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,MAAM,EAAE,EAAE,EAAE;QACnE,MAAM,MAAM,GAAG,CAAC,IAAY,EAAE,EAAE,CAAC,CAAC,MAAyC,EAAE,EAAE;YAC7E,SAAS,CAAC;gBACR,UAAU,EAAE,WAAW;gBACvB,UAAU,EAAE,IAAI;gBAChB,WAAW,EAAE,MAAM,CAAC,KAAK;gBACzB,YAAY,EAAE,MAAM,CAAC,MAAM;aAC5B,CAAC,CAAA;QACJ,CAAC,CAAA;QAED,KAAK,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAA;QACpB,KAAK,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAA;QACpB,KAAK,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAA;QACpB,KAAK,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAA;QACpB,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,CAAA;IACxB,CAAC,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE;QACZ,kCAAkC;IACpC,CAAC,CAAC,CAAA;AACJ,CAAC"}
package/package.json ADDED
@@ -0,0 +1,38 @@
1
+ {
2
+ "name": "@rushobservability/rum-sdk",
3
+ "version": "0.1.0",
4
+ "description": "Rush RUM (Real User Monitoring) browser SDK",
5
+ "type": "module",
6
+ "repository": {
7
+ "type": "git",
8
+ "url": "git+https://github.com/RushObservability/rum-sdk.git"
9
+ },
10
+ "homepage": "https://github.com/RushObservability/rum-sdk#readme",
11
+ "publishConfig": {
12
+ "access": "public"
13
+ },
14
+ "main": "dist/index.js",
15
+ "module": "dist/index.js",
16
+ "types": "dist/index.d.ts",
17
+ "exports": {
18
+ ".": {
19
+ "import": "./dist/index.js",
20
+ "types": "./dist/index.d.ts"
21
+ }
22
+ },
23
+ "files": [
24
+ "dist"
25
+ ],
26
+ "scripts": {
27
+ "build": "tsc",
28
+ "typecheck": "tsc --noEmit"
29
+ },
30
+ "dependencies": {
31
+ "rrweb": "^2.0.0-alpha.20",
32
+ "web-vitals": "^4.2.0"
33
+ },
34
+ "devDependencies": {
35
+ "typescript": "^5.5.0"
36
+ },
37
+ "license": "MIT"
38
+ }