Socket
Socket
Sign inDemoInstall

@splitsoftware/browser-rum-agent

Package Overview
Dependencies
Maintainers
8
Versions
35
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@splitsoftware/browser-rum-agent - npm Package Compare versions

Comparing version 0.5.1-rc.5 to 0.5.1-rc.6

cjs/utils/config.js

16

CHANGES.txt

@@ -1,14 +0,4 @@

0.6.0 (XXX XX, 2024)
- Added a new event collector named `openTelemetryCollector`. This collector integrates with the OpenTelemetry SDK and Splunk RUM for Web to automatically capture OpenTelemetry spans and forward them to Split as events. Read more in our docs.
- Added '@splitsoftware/browser-rum-agent/slim' sub-package to support a smaller bundle size by excluding some event collectors, like `errors` and `navigationTimingMetrics`, from being registered by default. This package is recommended for users who want to manually register only the event collectors they need.
In other words, importing:
```js
import { SplitRumAgent } from '@splitsoftware/browser-rum-agent';
```
is equivalent to:
```js
import { SplitRumAgent, errors, navigationTimingMetrics } from '@splitsoftware/browser-rum-agent/slim';
SplitRumAgent.register(errors());
SplitRumAgent.register(navigationTimingMetrics());
```
0.6.0 (March 21, 2024)
- Added `SplitRumAgent.flush` method to force the agent to send all queued events to the Split backend.
- Updated some transitive dependencies for vulnerability fixes.

@@ -15,0 +5,0 @@ 0.5.0 (March 4, 2024)

"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.webVitals = exports.tti = exports.routeChanges = exports.openTelemetryIntegration = exports.SplitRumAgent = void 0;
exports.webVitals = exports.routeChanges = exports.tti = exports.SplitRumAgent = void 0;
// Create SplitRumAgent namespace and export it for ESM/CJS imports
// Must use a named import to create the SplitRumAgent namespace (side effects)
var config_1 = require("./config");
var config_1 = require("./utils/config");
Object.defineProperty(exports, "SplitRumAgent", { enumerable: true, get: function () { return config_1.SplitRumAgent; } });
// Pluggable event collectors
var openTelemetry_1 = require("./metrics/openTelemetry");
Object.defineProperty(exports, "openTelemetryIntegration", { enumerable: true, get: function () { return openTelemetry_1.openTelemetryIntegration; } });
var tti_1 = require("./metrics/tti");
Object.defineProperty(exports, "tti", { enumerable: true, get: function () { return tti_1.tti; } });
var routeChanges_1 = require("./metrics/routeChanges");
Object.defineProperty(exports, "routeChanges", { enumerable: true, get: function () { return routeChanges_1.routeChanges; } });
var tti_1 = require("./metrics/tti");
Object.defineProperty(exports, "tti", { enumerable: true, get: function () { return tti_1.tti; } });
var webVitals_1 = require("./metrics/webVitals");
Object.defineProperty(exports, "webVitals", { enumerable: true, get: function () { return webVitals_1.webVitals; } });
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.errors = exports.handleCustomErrors = void 0;
exports.onError = exports.handleCustomErrors = void 0;
var getErrorData_1 = require("../utils/getErrorData");

@@ -33,29 +33,27 @@ var constants_1 = require("../utils/constants");

*/
function errors() {
return function onError(ctx) {
function handleUncaughtErrors(event) {
trackError(event ? event.error || event.message || constants_1.UNAVAILABLE : constants_1.UNAVAILABLE, ctx);
}
function handleRejectionsErrors(e) {
trackError(e ? e.reason : constants_1.UNAVAILABLE, ctx);
}
// If the user has placed the snippet in the <head>, remove listeners and process errors
var w = window; // @ts-expect-error object might be polluted
var g = w.__error;
if (g) {
w.removeEventListener('error', g.l1);
w.removeEventListener('unhandledrejection', g.l2);
// @TODO remove `setTimeout` eventually. ATM we have to run next code asynchronously, because `track` calls require global SplitRumAgent available
setTimeout(function () {
g.e1.forEach(handleUncaughtErrors);
g.e2.forEach(handleRejectionsErrors); // @ts-expect-error object might be polluted
delete w.__error;
}, 0);
}
// Capture browser uncaught errors.
w.addEventListener('error', handleUncaughtErrors);
// Capture browser promise rejection errors.
w.addEventListener('unhandledrejection', handleRejectionsErrors);
};
function onError(ctx) {
function handleUncaughtErrors(event) {
trackError(event ? event.error || event.message || constants_1.UNAVAILABLE : constants_1.UNAVAILABLE, ctx);
}
function handleRejectionsErrors(e) {
trackError(e ? e.reason : constants_1.UNAVAILABLE, ctx);
}
// In case customer placed the snippet in the head, remove listeners and process errors
var w = window; // @ts-expect-error object might be polluted
var g = w.__error;
if (g) {
w.removeEventListener('error', g.l1);
w.removeEventListener('unhandledrejection', g.l2);
// @TODO remove `setTimeout` eventually. ATM we have to run next code asynchronously, because `track` calls require global SplitRumAgent available
setTimeout(function () {
g.e1.forEach(handleUncaughtErrors);
g.e2.forEach(handleRejectionsErrors); // @ts-expect-error object might be polluted
delete w.__error;
}, 0);
}
// Capture browser uncaught errors.
w.addEventListener('error', handleUncaughtErrors);
// Capture browser promise rejection errors.
w.addEventListener('unhandledrejection', handleRejectionsErrors);
}
exports.errors = errors;
exports.onError = onError;
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.navigationTimingMetrics = void 0;
exports.onNavigationTimingMetrics = void 0;
// import { getTTFB } from './ttfb';

@@ -11,24 +11,23 @@ var ttdi_1 = require("./ttdi");

* Metrics:
* - time.to.first.byte
* - time.to.dom.interactive
* - page.load.time
*/
function navigationTimingMetrics() {
return function onNavigationTimingMetrics(ctx) {
var getMetrics = function () {
var ttdi = (0, ttdi_1.getTTDI)(),
// ttfb = getTTFB(),
plt = (0, plt_1.getPLT)();
if (ttdi)
ctx.track(ttdi);
// @TODO regarding `time.to.first.byte`, either rename to `server.time.to.first.byte`, update its definition (https://web.dev/ttfb/), or remove it. For now, the metric is also provided by webVitals collector.
// if (ttfb) ctx.track(ttfb);
if (plt)
ctx.track(plt);
};
(0, whenLoaded_1.whenLoaded)(getMetrics);
return {
flush: getMetrics
};
function onNavigationTimingMetrics(ctx) {
var getMetrics = function () {
var ttdi = (0, ttdi_1.getTTDI)(),
// ttfb = getTTFB(),
plt = (0, plt_1.getPLT)();
if (ttdi)
ctx.track(ttdi);
// @TODO regarding `time.to.first.byte`, either rename to `server.time.to.first.byte`, update its definition (https://web.dev/ttfb/), or remove it. For now, the metric is also provided by webVitals collector.
// if (ttfb) ctx.track(ttfb);
if (plt)
ctx.track(plt);
};
(0, whenLoaded_1.whenLoaded)(getMetrics);
return {
flush: getMetrics
};
}
exports.navigationTimingMetrics = navigationTimingMetrics;
exports.onNavigationTimingMetrics = onNavigationTimingMetrics;

@@ -38,2 +38,2 @@ "use strict";

exports.userAgent = getUserAgent();
exports.languageVersion = 'jsrum-' + '0.5.1-rc.5';
exports.languageVersion = 'jsrum-' + '0.5.1-rc.6';

@@ -18,6 +18,6 @@ "use strict";

/**
* Flush the queue of events.
* Flushes the queue of events.
*
* @param useBeacon - Whether to use navigator.sendBeacon or not. Defaults to false.
* @returns true if events were sent, or a falsy value otherwise (e.g., queue is empty, config is not ready, or there was an error sending events)
* @returns True if events were sent, or false otherwise (e.g., queue is empty, SDK key and identities were not set, user consent is not 'GRANTED', or there was an error sending events)
*/

@@ -27,6 +27,7 @@ function flush(useBeacon) {

// Only send events if we do have the config.
if (conf.a && conf.i.length && conf.userConsent === constants_1.CONSENT_GRANTED) {
if (exports.queue.length && conf.a && conf.i.length && conf.userConsent === constants_1.CONSENT_GRANTED) {
var toSend = exports.queue.splice(0, exports.queue.length);
return toSend.length && (0, transport_1.sendEvents)(toSend, useBeacon);
return (0, transport_1.sendEvents)(toSend, useBeacon);
}
return false;
}

@@ -57,4 +58,4 @@ exports.flush = flush;

}
if (!eventData.timestamp)
eventData.timestamp = Date.now(); // <-- This is not the most accurate but it shouldn't really matter much. The error margin should be small. We can actually calculate the timestamp later.
// @ts-expect-error adding timestamp to event
eventData.timestamp = Date.now(); // <-- This is not the most accurate but it shouldn't really matter much. The error margin should be small. We can actually calculate the timestamp later.
if (conf.prefix)

@@ -61,0 +62,0 @@ eventData.eventTypeId = conf.prefix + "." + eventData.eventTypeId;

// Create SplitRumAgent namespace and export it for ESM/CJS imports
// Must use a named import to create the SplitRumAgent namespace (side effects)
export { SplitRumAgent } from './config';
export { SplitRumAgent } from './utils/config';
// Pluggable event collectors
export { openTelemetryIntegration } from './metrics/openTelemetry';
export { tti } from './metrics/tti';
export { routeChanges } from './metrics/routeChanges';
export { tti } from './metrics/tti';
export { webVitals } from './metrics/webVitals';

@@ -29,28 +29,26 @@ import { getErrorData } from '../utils/getErrorData';

*/
export function errors() {
return function onError(ctx) {
function handleUncaughtErrors(event) {
trackError(event ? event.error || event.message || UNAVAILABLE : UNAVAILABLE, ctx);
}
function handleRejectionsErrors(e) {
trackError(e ? e.reason : UNAVAILABLE, ctx);
}
// If the user has placed the snippet in the <head>, remove listeners and process errors
var w = window; // @ts-expect-error object might be polluted
var g = w.__error;
if (g) {
w.removeEventListener('error', g.l1);
w.removeEventListener('unhandledrejection', g.l2);
// @TODO remove `setTimeout` eventually. ATM we have to run next code asynchronously, because `track` calls require global SplitRumAgent available
setTimeout(function () {
g.e1.forEach(handleUncaughtErrors);
g.e2.forEach(handleRejectionsErrors); // @ts-expect-error object might be polluted
delete w.__error;
}, 0);
}
// Capture browser uncaught errors.
w.addEventListener('error', handleUncaughtErrors);
// Capture browser promise rejection errors.
w.addEventListener('unhandledrejection', handleRejectionsErrors);
};
export function onError(ctx) {
function handleUncaughtErrors(event) {
trackError(event ? event.error || event.message || UNAVAILABLE : UNAVAILABLE, ctx);
}
function handleRejectionsErrors(e) {
trackError(e ? e.reason : UNAVAILABLE, ctx);
}
// In case customer placed the snippet in the head, remove listeners and process errors
var w = window; // @ts-expect-error object might be polluted
var g = w.__error;
if (g) {
w.removeEventListener('error', g.l1);
w.removeEventListener('unhandledrejection', g.l2);
// @TODO remove `setTimeout` eventually. ATM we have to run next code asynchronously, because `track` calls require global SplitRumAgent available
setTimeout(function () {
g.e1.forEach(handleUncaughtErrors);
g.e2.forEach(handleRejectionsErrors); // @ts-expect-error object might be polluted
delete w.__error;
}, 0);
}
// Capture browser uncaught errors.
w.addEventListener('error', handleUncaughtErrors);
// Capture browser promise rejection errors.
w.addEventListener('unhandledrejection', handleRejectionsErrors);
}

@@ -8,23 +8,22 @@ // import { getTTFB } from './ttfb';

* Metrics:
* - time.to.first.byte
* - time.to.dom.interactive
* - page.load.time
*/
export function navigationTimingMetrics() {
return function onNavigationTimingMetrics(ctx) {
var getMetrics = function () {
var ttdi = getTTDI(),
// ttfb = getTTFB(),
plt = getPLT();
if (ttdi)
ctx.track(ttdi);
// @TODO regarding `time.to.first.byte`, either rename to `server.time.to.first.byte`, update its definition (https://web.dev/ttfb/), or remove it. For now, the metric is also provided by webVitals collector.
// if (ttfb) ctx.track(ttfb);
if (plt)
ctx.track(plt);
};
whenLoaded(getMetrics);
return {
flush: getMetrics
};
export function onNavigationTimingMetrics(ctx) {
var getMetrics = function () {
var ttdi = getTTDI(),
// ttfb = getTTFB(),
plt = getPLT();
if (ttdi)
ctx.track(ttdi);
// @TODO regarding `time.to.first.byte`, either rename to `server.time.to.first.byte`, update its definition (https://web.dev/ttfb/), or remove it. For now, the metric is also provided by webVitals collector.
// if (ttfb) ctx.track(ttfb);
if (plt)
ctx.track(plt);
};
whenLoaded(getMetrics);
return {
flush: getMetrics
};
}

@@ -31,2 +31,2 @@ /* eslint-disable compat/compat */

export var userAgent = getUserAgent();
export var languageVersion = 'jsrum-' + '0.5.1-rc.5';
export var languageVersion = 'jsrum-' + '0.5.1-rc.6';

@@ -15,6 +15,6 @@ import { getConnectionType, getUrl, userAgent } from './context';

/**
* Flush the queue of events.
* Flushes the queue of events.
*
* @param useBeacon - Whether to use navigator.sendBeacon or not. Defaults to false.
* @returns true if events were sent, or a falsy value otherwise (e.g., queue is empty, config is not ready, or there was an error sending events)
* @returns True if events were sent, or false otherwise (e.g., queue is empty, SDK key and identities were not set, user consent is not 'GRANTED', or there was an error sending events)
*/

@@ -24,6 +24,7 @@ export function flush(useBeacon) {

// Only send events if we do have the config.
if (conf.a && conf.i.length && conf.userConsent === CONSENT_GRANTED) {
if (queue.length && conf.a && conf.i.length && conf.userConsent === CONSENT_GRANTED) {
var toSend = queue.splice(0, queue.length);
return toSend.length && sendEvents(toSend, useBeacon);
return sendEvents(toSend, useBeacon);
}
return false;
}

@@ -52,4 +53,4 @@ export function setSchedule() {

}
if (!eventData.timestamp)
eventData.timestamp = Date.now(); // <-- This is not the most accurate but it shouldn't really matter much. The error margin should be small. We can actually calculate the timestamp later.
// @ts-expect-error adding timestamp to event
eventData.timestamp = Date.now(); // <-- This is not the most accurate but it shouldn't really matter much. The error margin should be small. We can actually calculate the timestamp later.
if (conf.prefix)

@@ -56,0 +57,0 @@ eventData.eventTypeId = conf.prefix + "." + eventData.eventTypeId;

{
"name": "@splitsoftware/browser-rum-agent",
"version": "0.5.1-rc.5",
"version": "0.5.1-rc.6",
"description": "Split Software RUM Agent for Browsers.",

@@ -12,3 +12,2 @@ "main": "cjs/index.js",

"CHANGES.txt",
"slim",
"types",

@@ -43,3 +42,2 @@ "cjs",

"dependencies": {
"@opentelemetry/sdk-trace-base": "^1.22.0",
"web-vitals": "^3.5.0"

@@ -46,0 +44,0 @@ },

/* eslint-disable no-use-before-define */
import type { ReadableSpan, SpanExporter } from '@opentelemetry/sdk-trace-base';
import { Attributes } from '@opentelemetry/api';
import type { ReportOpts } from 'web-vitals';
// Interface from `@splunk/otel-web` package
export interface SplunkOtelWebExporterOptions {
/**
* Allows remapping Span's attributes right before they're serialized.
* One potential use case of this method is to remove PII from the attributes.
*/
onAttributesSerializing?: (attributes: Attributes, span: ReadableSpan) => Attributes;
}
/**

@@ -30,3 +19,2 @@ * Type of a property value.

properties?: Properties,
timestamp?: number,
};

@@ -244,2 +232,8 @@

getUserConsent(): ConsentStatus;
/**
* Flushes the queue of tracked events.
*
* @returns True if events were sent, or false otherwise (e.g., queue is empty, SDK key and identities were not set, user consent is not 'GRANTED', or there was an error sending events).
*/
flush(): boolean;
};

@@ -390,60 +384,1 @@

export declare function webVitals(options?: IWebVitalsOptions): EventCollector;
export interface IOpenTelemetryIntegrationOptions {
/**
* A function to be called before tracking an event.
* It receives the event object and should return the event object to be tracked, or `undefined` to prevent the event from being tracked.
* Useful to modify the event before it is tracked, or to prevent it from being tracked.
*
* @param event - The Split event to be tracked.
* @param span - The OpenTelemetry span object that originated the event.
* @returns The event object to be tracked, or `undefined` to prevent the event from being tracked.
*/
beforeTrack?: (event: EventData, span: ReadableSpan) => EventData | undefined
}
/**
* Collects spans from the OpenTelemetry SDK for Web or the Splunk RUM for Web, by creating an Span exporter ({@link https://opentelemetry.io/docs/languages/js/exporters/}),
* and converting them to Split events.
*
* Setup example with OpenTelemetry SDK for Web:
* ```
* import { WebTracerProvider } from '@opentelemetry/sdk-trace-web';
* import { BatchSpanProcessor } from '@opentelemetry/sdk-trace-base';
* import { SplitRumAgent, openTelemetryIntegration } from '@splitsoftware/browser-rum-agent';
*
* const integration = openTelemetryIntegration();
*
* const provider = new WebTracerProvider();
* provider.addSpanProcessor(new BatchSpanProcessor(integration.spanExporter));
* SplitRumAgent.register(integration.eventCollector);
* ```
*
* Setup example with Splunk RUM for Web {@link https://docs.splunk.com/observability/en/gdi/get-data-in/rum/browser/configure-rum-browser-instrumentation.html#exporter-settings}:
* ```
* import SplunkRum from '@splunk/otel-web';
* import { BatchSpanProcessor } from '@opentelemetry/sdk-trace-base';
* import { SplitRumAgent, openTelemetryIntegration } from '@splitsoftware/browser-rum-agent';
*
* const integration = openTelemetryIntegration();
*
* SplunkRum.init(...);
* SplunkRum.provider.addSpanProcessor(new BatchSpanProcessor(integration.spanExporter));
* SplitRumAgent.register(integration.eventCollector);
* ```
*
* or with Splunk exporter option:
* ```
* SplunkRum.init({
* exporter: integration.splunkExporter,
* ...
* });
* ```
*
* @param options - Collector options.
*/
export declare function openTelemetryIntegration(options?: IOpenTelemetryIntegrationOptions): {
spanExporter: SpanExporter,
splunkExporter: SplunkOtelWebExporterOptions,
eventCollector: EventCollector
}
SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc