@splitsoftware/browser-rum-agent
Advanced tools
Comparing version 0.4.0-rc.0 to 0.4.0
@@ -1,4 +0,4 @@ | ||
0.4.0 (XXX XX, 2023) | ||
- BREAKING CHANGES: | ||
- Removed `webVitals` event collector. It is now registered by default. | ||
0.4.0 (December 22, 2023) | ||
- Updated `SplitRumAgent.track` and `SplitRumAgent.trackError` methods to return a boolean value. This value indicates whether the event was successfully queued or not (i.e., it was dropped). An event is dropped if it has an invalid eventTypeId, value or properties, if the queue is full, or if user consent is declined. | ||
- Updated internal implementation to avoid throwing errors in non-browser environments (i.e. environments where `window` and `document` objects are not available), enhancing support for testing frameworks like Jest. | ||
@@ -5,0 +5,0 @@ 0.3.2 (October 19, 2023) |
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.routeChanges = exports.tti = exports.SplitRumAgent = void 0; | ||
exports.webVitals = exports.routeChanges = exports.tti = exports.SplitRumAgent = void 0; | ||
// Create SplitRumAgent namespace and export it for ESM/CJS imports | ||
@@ -13,1 +13,3 @@ // Must use a named import to create the SplitRumAgent namespace (side effects) | ||
Object.defineProperty(exports, "routeChanges", { enumerable: true, get: function () { return routeChanges_1.routeChanges; } }); | ||
var webVitals_1 = require("./metrics/webVitals"); | ||
Object.defineProperty(exports, "webVitals", { enumerable: true, get: function () { return webVitals_1.webVitals; } }); |
@@ -11,3 +11,3 @@ "use strict"; | ||
var errorData = (0, getErrorData_1.getErrorData)(error); | ||
if (errorData) { | ||
return errorData ? | ||
ctx.track({ | ||
@@ -20,4 +20,4 @@ eventTypeId: 'error', | ||
} | ||
}); | ||
} | ||
}) : | ||
false; | ||
} | ||
@@ -29,3 +29,3 @@ /** | ||
function handleCustomErrors(e, ctx) { | ||
trackError(e ? e : constants_1.UNAVAILABLE, ctx); | ||
return trackError(e ? e : constants_1.UNAVAILABLE, ctx); | ||
} | ||
@@ -32,0 +32,0 @@ exports.handleCustomErrors = handleCustomErrors; |
@@ -39,23 +39,3 @@ "use strict"; | ||
/** | ||
* Collects Web-Vitals metrics (https://www.npmjs.com/package/web-vitals). | ||
* | ||
* By default it collects all web-vitals metrics, but you can specify which ones to collect by passing a `reportOptions` object. | ||
* | ||
* Collected event format: | ||
* ``` | ||
* { | ||
* eventTypeId: 'webvitals.cls' | 'webvitals.fcp' | 'webvitals.fid' | 'webvitals.inp' | 'webvitals.lcp' | 'webvitals.ttfb', | ||
* value: number, | ||
* properties: { | ||
* rating: 'good' | 'needsImprovement' | 'poor', | ||
* navigationType: 'navigate' | 'reload' | 'back_forward' | 'back-forward-cache' | 'prerender' | 'restore' | ||
* } | ||
* } | ||
* ``` | ||
* | ||
* @param {IWebVitalsOptions} options | ||
* @default | ||
* ``` | ||
* { reportOptions: { onCLS: true, onFCP: true, onFID: true, onINP: true, onLCP: true, onTTFB: true } } | ||
* ``` | ||
* web-vitals metrics | ||
*/ | ||
@@ -62,0 +42,0 @@ function webVitals(options) { |
@@ -5,3 +5,4 @@ "use strict"; | ||
index_1.SplitRumAgent.routeChanges = index_1.routeChanges; | ||
index_1.SplitRumAgent.webVitals = index_1.webVitals; | ||
// eslint-disable-next-line import/no-default-export | ||
exports.default = index_1.SplitRumAgent; |
@@ -14,7 +14,6 @@ "use strict"; | ||
var constants_1 = require("./constants"); | ||
var validateEvent_1 = require("./validateEvent"); | ||
// Built-in event collectors | ||
var errors_1 = require("../metrics/errors"); | ||
var navigation_1 = require("../metrics/navigation"); | ||
var webVitals_1 = require("../metrics/webVitals"); | ||
var validateEvent_1 = require("./validateEvent"); | ||
// Browser support: | ||
@@ -24,3 +23,9 @@ // - ES5 syntax | ||
// - feature detection of Promise, History and Performance APIs is done by event collector modules. These features are required for some modules to collect data, but not for the RUM Agent to work. | ||
(function (w) { | ||
// In Browser, we pollute the global `window` object with the agent. | ||
// In NodeJS, we only export it and no event collectors are registered. | ||
var isBrowser = typeof window === 'object' && typeof document === 'object'; | ||
var globalRef = isBrowser ? window : {}; | ||
(function (w, isBrowser) { | ||
if (w.SplitRumAgent) | ||
return; | ||
var _config = { | ||
@@ -39,3 +44,3 @@ i: [], | ||
function calculateAndFlush() { | ||
if (!_isConfigured && _config.a && _config.i.length) { | ||
if (isBrowser && !_isConfigured && _config.a && _config.i.length) { | ||
_isConfigured = true; | ||
@@ -47,109 +52,110 @@ // always async never inasync ;) | ||
var context = { track: queue_1.track }; | ||
var collectors = [ | ||
(0, navigation_1.onNavigationTimingMetrics)(context), | ||
(0, errors_1.onError)(context), | ||
(0, webVitals_1.webVitals)()(context) | ||
]; | ||
// Post events periodically | ||
(0, whenLoaded_1.whenLoaded)(queue_1.setSchedule); | ||
// If there's a pagehide or visibilitychange event, calculate what's possible and send events using beacon by default | ||
(0, whenLoaded_1.onPageHideOrVisibilityChange)(function () { | ||
collectors.forEach(function (collector) { | ||
if (collector && typeof collector.flush === 'function') | ||
collector.flush(); | ||
}); | ||
(0, queue_1.flush)(true); | ||
}); | ||
var collectors = []; | ||
// SplitRumAgent would be our namespace | ||
if (!w.SplitRumAgent) | ||
w.SplitRumAgent = { | ||
// Initialization params | ||
setup: function (sdkKey, config) { | ||
if ((0, isObject_1.isObject)(config)) { | ||
(0, assign_1.objectAssign)(_config, config); | ||
} | ||
if ((0, isString_1.isString)(sdkKey)) { | ||
_config.a = sdkKey; | ||
calculateAndFlush(); | ||
} | ||
else { | ||
_config.log.error('SDK key must be a string.'); | ||
} | ||
// allow for chaining. | ||
return w.SplitRumAgent; | ||
}, | ||
// Configure identities | ||
addIdentities: function (identities) { | ||
if ((0, isArray_1.isArray)(identities)) { | ||
identities.forEach(function (identity) { return w.SplitRumAgent.addIdentity(identity); }); | ||
} | ||
else { | ||
_config.log.error('Identities must be an array of objects with key and trafficType.'); | ||
} | ||
}, | ||
addIdentity: function (identity) { | ||
var validatedIdentity = (0, validateIdentity_1.validateIdentity)(identity, _config.log); | ||
if (validatedIdentity && !_config.i.some(function (i) { return (0, validateIdentity_1.areEqual)(i, validatedIdentity); })) { | ||
_config.i.push(validatedIdentity); | ||
calculateAndFlush(); | ||
} | ||
}, | ||
removeIdentities: function () { | ||
_config.i = []; | ||
}, | ||
removeIdentity: function (identity) { | ||
var validatedIdentity = (0, validateIdentity_1.validateIdentity)(identity, _config.log); | ||
if (validatedIdentity) { | ||
_config.i = _config.i.filter(function (i) { return !(0, validateIdentity_1.areEqual)(i, validatedIdentity); }); | ||
} | ||
}, | ||
getIdentities: function () { | ||
return _config.i; | ||
}, | ||
// Custom error handler. | ||
trackError: function (e) { | ||
(0, errors_1.handleCustomErrors)(e, context); | ||
}, | ||
// Track custom events. | ||
track: queue_1.track, | ||
// For internal use. | ||
// @TODO review how to expose config | ||
__getConfig: function () { | ||
return _config; | ||
}, | ||
// register event collector | ||
register: function (eventCollector) { | ||
if (typeof eventCollector !== 'function') { | ||
_config.log.error('register method must be called with an event collector function.'); | ||
} | ||
else { | ||
collectors.push(eventCollector(context)); | ||
} | ||
// allow for chaining. | ||
return w.SplitRumAgent; | ||
}, | ||
// Custom properties | ||
setProperties: function (properties) { | ||
(0, assign_1.objectAssign)(_config.p, (0, validateEvent_1.validateProperties)(properties, _config.log)); | ||
}, | ||
getProperties: function () { | ||
return _config.p; | ||
}, | ||
removeProperties: function () { | ||
_config.p = {}; | ||
}, | ||
// User consent | ||
setUserConsent: function (userConsent) { | ||
if (!(0, isBoolean_1.isBoolean)(userConsent)) { | ||
_config.log.warn('setUserConsent: provided param must be a boolean value'); | ||
return false; | ||
} | ||
_config.userConsent = userConsent ? constants_1.CONSENT_GRANTED : constants_1.CONSENT_DECLINED; | ||
return true; | ||
}, | ||
getUserConsent: function () { | ||
return _config.userConsent; | ||
var SplitRumAgent = { | ||
// Initialization params | ||
setup: function (sdkKey, config) { | ||
if ((0, isObject_1.isObject)(config)) { | ||
(0, assign_1.objectAssign)(_config, config); | ||
} | ||
}; | ||
})(window); | ||
exports.SplitRumAgent = window.SplitRumAgent; | ||
if ((0, isString_1.isString)(sdkKey)) { | ||
_config.a = sdkKey; | ||
calculateAndFlush(); | ||
} | ||
else { | ||
_config.log.error('SDK key must be a string.'); | ||
} | ||
// allow for chaining. | ||
return SplitRumAgent; | ||
}, | ||
// Configure identities | ||
addIdentities: function (identities) { | ||
if ((0, isArray_1.isArray)(identities)) { | ||
identities.forEach(function (identity) { return SplitRumAgent.addIdentity(identity); }); | ||
} | ||
else { | ||
_config.log.error('Identities must be an array of objects with key and trafficType.'); | ||
} | ||
}, | ||
addIdentity: function (identity) { | ||
var validatedIdentity = (0, validateIdentity_1.validateIdentity)(identity, _config.log); | ||
if (validatedIdentity && !_config.i.some(function (i) { return (0, validateIdentity_1.areEqual)(i, validatedIdentity); })) { | ||
_config.i.push(validatedIdentity); | ||
calculateAndFlush(); | ||
} | ||
}, | ||
removeIdentities: function () { | ||
_config.i = []; | ||
}, | ||
removeIdentity: function (identity) { | ||
var validatedIdentity = (0, validateIdentity_1.validateIdentity)(identity, _config.log); | ||
if (validatedIdentity) { | ||
_config.i = _config.i.filter(function (i) { return !(0, validateIdentity_1.areEqual)(i, validatedIdentity); }); | ||
} | ||
}, | ||
getIdentities: function () { | ||
return _config.i; | ||
}, | ||
// Custom error handler. | ||
trackError: function (e) { | ||
return (0, errors_1.handleCustomErrors)(e, context); | ||
}, | ||
// Track custom events. | ||
track: queue_1.track, | ||
// For internal use. | ||
// @TODO review how to expose config | ||
__getConfig: function () { | ||
return _config; | ||
}, | ||
// register event collector | ||
register: function (eventCollector) { | ||
if (typeof eventCollector !== 'function') { | ||
_config.log.error('register method must be called with an event collector function.'); | ||
} | ||
else { | ||
collectors.push(eventCollector(context)); | ||
} | ||
// allow for chaining. | ||
return SplitRumAgent; | ||
}, | ||
// Custom properties | ||
setProperties: function (properties) { | ||
(0, assign_1.objectAssign)(_config.p, (0, validateEvent_1.validateProperties)(properties, _config.log)); | ||
}, | ||
getProperties: function () { | ||
return _config.p; | ||
}, | ||
removeProperties: function () { | ||
_config.p = {}; | ||
}, | ||
// User consent | ||
setUserConsent: function (userConsent) { | ||
if (!(0, isBoolean_1.isBoolean)(userConsent)) { | ||
_config.log.warn('setUserConsent: provided param must be a boolean value'); | ||
return false; | ||
} | ||
_config.userConsent = userConsent ? constants_1.CONSENT_GRANTED : constants_1.CONSENT_DECLINED; | ||
return true; | ||
}, | ||
getUserConsent: function () { | ||
return _config.userConsent; | ||
} | ||
}; | ||
if (isBrowser) { | ||
// Built-in collectors | ||
SplitRumAgent.register(navigation_1.onNavigationTimingMetrics); | ||
SplitRumAgent.register(errors_1.onError); | ||
// Post events periodically | ||
(0, whenLoaded_1.whenLoaded)(queue_1.setSchedule); | ||
// If there's a pagehide or visibilitychange event, calculate what's possible and send events using beacon by default | ||
(0, whenLoaded_1.onPageHideOrVisibilityChange)(function () { | ||
collectors.forEach(function (collector) { | ||
if (collector && typeof collector.flush === 'function') | ||
collector.flush(); | ||
}); | ||
(0, queue_1.flush)(true); | ||
}); | ||
} | ||
w.SplitRumAgent = SplitRumAgent; | ||
})(globalRef, isBrowser); | ||
exports.SplitRumAgent = globalRef.SplitRumAgent; |
@@ -38,2 +38,2 @@ "use strict"; | ||
exports.userAgent = getUserAgent(); | ||
exports.languageVersion = 'jsrum-' + '0.4.0-rc.0'; | ||
exports.languageVersion = 'jsrum-' + '0.4.0'; |
@@ -47,3 +47,3 @@ "use strict"; | ||
if (!eventData || conf.userConsent === constants_1.CONSENT_DECLINED) | ||
return; | ||
return false; | ||
// If people configures the agent at a really late time, prevent having a giant queue and attempt to send them or drop. First events will be the most important ones anyways. | ||
@@ -53,3 +53,3 @@ if (exports.queue.length >= conf.queueSize) { | ||
conf.log.warn('Dropping event due to queue size limit.'); | ||
return; | ||
return false; | ||
} | ||
@@ -74,3 +74,4 @@ } | ||
(0, assign_1.assignIdentities)(eventData, window.SplitRumAgent.__getConfig().i, exports.queue); | ||
return true; | ||
} | ||
exports.track = track; |
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.onPageHideOrVisibilityChange = exports.whenLoaded = void 0; | ||
var callbacks = []; | ||
window.addEventListener('load', function () { | ||
// Defer, because we should wait until the handlers finish to allow customer logic run first. | ||
setTimeout(function () { | ||
callbacks.forEach(function (cb) { return cb(); }); | ||
}, 0); | ||
}); | ||
/** | ||
@@ -20,9 +13,12 @@ * Calls the callback function when the document is loaded or if it is already loaded. | ||
else { | ||
callbacks.push(callback); | ||
window.addEventListener('load', function () { | ||
// Defer, because we should wait until the handlers finish to allow customer logic run first. | ||
setTimeout(callback, 0); | ||
}); | ||
} | ||
} | ||
exports.whenLoaded = whenLoaded; | ||
// `unload` is not reliable, so we use `pagehide` if available: https://developer.chrome.com/articles/page-lifecycle-api/#the-unload-event | ||
var PAGE_TERMINATION_EVENT = typeof window.onpagehide !== 'undefined' ? 'pagehide' : 'unload'; | ||
function onPageHideOrVisibilityChange(callback) { | ||
// `unload` is not reliable, so we use `pagehide` if available: https://developer.chrome.com/articles/page-lifecycle-api/#the-unload-event | ||
var PAGE_TERMINATION_EVENT = typeof window.onpagehide !== 'undefined' ? 'pagehide' : 'unload'; | ||
// Flush data whenever the page is hidden or unloaded | ||
@@ -29,0 +25,0 @@ if (typeof document !== 'undefined' && document.addEventListener) { |
@@ -7,1 +7,2 @@ // Create SplitRumAgent namespace and export it for ESM/CJS imports | ||
export { routeChanges } from './metrics/routeChanges'; | ||
export { webVitals } from './metrics/webVitals'; |
@@ -8,3 +8,3 @@ import { getErrorData } from '../utils/getErrorData'; | ||
var errorData = getErrorData(error); | ||
if (errorData) { | ||
return errorData ? | ||
ctx.track({ | ||
@@ -17,4 +17,4 @@ eventTypeId: 'error', | ||
} | ||
}); | ||
} | ||
}) : | ||
false; | ||
} | ||
@@ -26,3 +26,3 @@ /** | ||
export function handleCustomErrors(e, ctx) { | ||
trackError(e ? e : UNAVAILABLE, ctx); | ||
return trackError(e ? e : UNAVAILABLE, ctx); | ||
} | ||
@@ -29,0 +29,0 @@ /** |
@@ -16,23 +16,3 @@ // webVitals plugin uses the web-vital standard build. | ||
/** | ||
* Collects Web-Vitals metrics (https://www.npmjs.com/package/web-vitals). | ||
* | ||
* By default it collects all web-vitals metrics, but you can specify which ones to collect by passing a `reportOptions` object. | ||
* | ||
* Collected event format: | ||
* ``` | ||
* { | ||
* eventTypeId: 'webvitals.cls' | 'webvitals.fcp' | 'webvitals.fid' | 'webvitals.inp' | 'webvitals.lcp' | 'webvitals.ttfb', | ||
* value: number, | ||
* properties: { | ||
* rating: 'good' | 'needsImprovement' | 'poor', | ||
* navigationType: 'navigate' | 'reload' | 'back_forward' | 'back-forward-cache' | 'prerender' | 'restore' | ||
* } | ||
* } | ||
* ``` | ||
* | ||
* @param {IWebVitalsOptions} options | ||
* @default | ||
* ``` | ||
* { reportOptions: { onCLS: true, onFCP: true, onFID: true, onINP: true, onLCP: true, onTTFB: true } } | ||
* ``` | ||
* web-vitals metrics | ||
*/ | ||
@@ -39,0 +19,0 @@ export function webVitals(options) { |
@@ -1,4 +0,5 @@ | ||
import { SplitRumAgent, routeChanges } from './index'; | ||
import { SplitRumAgent, routeChanges, webVitals } from './index'; | ||
SplitRumAgent.routeChanges = routeChanges; | ||
SplitRumAgent.webVitals = webVitals; | ||
// eslint-disable-next-line import/no-default-export | ||
export default SplitRumAgent; |
@@ -11,7 +11,6 @@ import { setSchedule, flush, track } from './queue'; | ||
import { CONSENT_DECLINED, CONSENT_GRANTED } from './constants'; | ||
import { validateProperties } from './validateEvent'; | ||
// Built-in event collectors | ||
import { handleCustomErrors, onError } from '../metrics/errors'; | ||
import { onNavigationTimingMetrics } from '../metrics/navigation'; | ||
import { webVitals } from '../metrics/webVitals'; | ||
import { validateProperties } from './validateEvent'; | ||
// Browser support: | ||
@@ -21,3 +20,9 @@ // - ES5 syntax | ||
// - feature detection of Promise, History and Performance APIs is done by event collector modules. These features are required for some modules to collect data, but not for the RUM Agent to work. | ||
(function (w) { | ||
// In Browser, we pollute the global `window` object with the agent. | ||
// In NodeJS, we only export it and no event collectors are registered. | ||
var isBrowser = typeof window === 'object' && typeof document === 'object'; | ||
var globalRef = isBrowser ? window : {}; | ||
(function (w, isBrowser) { | ||
if (w.SplitRumAgent) | ||
return; | ||
var _config = { | ||
@@ -36,3 +41,3 @@ i: [], | ||
function calculateAndFlush() { | ||
if (!_isConfigured && _config.a && _config.i.length) { | ||
if (isBrowser && !_isConfigured && _config.a && _config.i.length) { | ||
_isConfigured = true; | ||
@@ -44,109 +49,110 @@ // always async never inasync ;) | ||
var context = { track: track }; | ||
var collectors = [ | ||
onNavigationTimingMetrics(context), | ||
onError(context), | ||
webVitals()(context) | ||
]; | ||
// Post events periodically | ||
whenLoaded(setSchedule); | ||
// If there's a pagehide or visibilitychange event, calculate what's possible and send events using beacon by default | ||
onPageHideOrVisibilityChange(function () { | ||
collectors.forEach(function (collector) { | ||
if (collector && typeof collector.flush === 'function') | ||
collector.flush(); | ||
}); | ||
flush(true); | ||
}); | ||
var collectors = []; | ||
// SplitRumAgent would be our namespace | ||
if (!w.SplitRumAgent) | ||
w.SplitRumAgent = { | ||
// Initialization params | ||
setup: function (sdkKey, config) { | ||
if (isObject(config)) { | ||
objectAssign(_config, config); | ||
} | ||
if (isString(sdkKey)) { | ||
_config.a = sdkKey; | ||
calculateAndFlush(); | ||
} | ||
else { | ||
_config.log.error('SDK key must be a string.'); | ||
} | ||
// allow for chaining. | ||
return w.SplitRumAgent; | ||
}, | ||
// Configure identities | ||
addIdentities: function (identities) { | ||
if (isArray(identities)) { | ||
identities.forEach(function (identity) { return w.SplitRumAgent.addIdentity(identity); }); | ||
} | ||
else { | ||
_config.log.error('Identities must be an array of objects with key and trafficType.'); | ||
} | ||
}, | ||
addIdentity: function (identity) { | ||
var validatedIdentity = validateIdentity(identity, _config.log); | ||
if (validatedIdentity && !_config.i.some(function (i) { return areEqual(i, validatedIdentity); })) { | ||
_config.i.push(validatedIdentity); | ||
calculateAndFlush(); | ||
} | ||
}, | ||
removeIdentities: function () { | ||
_config.i = []; | ||
}, | ||
removeIdentity: function (identity) { | ||
var validatedIdentity = validateIdentity(identity, _config.log); | ||
if (validatedIdentity) { | ||
_config.i = _config.i.filter(function (i) { return !areEqual(i, validatedIdentity); }); | ||
} | ||
}, | ||
getIdentities: function () { | ||
return _config.i; | ||
}, | ||
// Custom error handler. | ||
trackError: function (e) { | ||
handleCustomErrors(e, context); | ||
}, | ||
// Track custom events. | ||
track: track, | ||
// For internal use. | ||
// @TODO review how to expose config | ||
__getConfig: function () { | ||
return _config; | ||
}, | ||
// register event collector | ||
register: function (eventCollector) { | ||
if (typeof eventCollector !== 'function') { | ||
_config.log.error('register method must be called with an event collector function.'); | ||
} | ||
else { | ||
collectors.push(eventCollector(context)); | ||
} | ||
// allow for chaining. | ||
return w.SplitRumAgent; | ||
}, | ||
// Custom properties | ||
setProperties: function (properties) { | ||
objectAssign(_config.p, validateProperties(properties, _config.log)); | ||
}, | ||
getProperties: function () { | ||
return _config.p; | ||
}, | ||
removeProperties: function () { | ||
_config.p = {}; | ||
}, | ||
// User consent | ||
setUserConsent: function (userConsent) { | ||
if (!isBoolean(userConsent)) { | ||
_config.log.warn('setUserConsent: provided param must be a boolean value'); | ||
return false; | ||
} | ||
_config.userConsent = userConsent ? CONSENT_GRANTED : CONSENT_DECLINED; | ||
return true; | ||
}, | ||
getUserConsent: function () { | ||
return _config.userConsent; | ||
var SplitRumAgent = { | ||
// Initialization params | ||
setup: function (sdkKey, config) { | ||
if (isObject(config)) { | ||
objectAssign(_config, config); | ||
} | ||
}; | ||
})(window); | ||
export var SplitRumAgent = window.SplitRumAgent; | ||
if (isString(sdkKey)) { | ||
_config.a = sdkKey; | ||
calculateAndFlush(); | ||
} | ||
else { | ||
_config.log.error('SDK key must be a string.'); | ||
} | ||
// allow for chaining. | ||
return SplitRumAgent; | ||
}, | ||
// Configure identities | ||
addIdentities: function (identities) { | ||
if (isArray(identities)) { | ||
identities.forEach(function (identity) { return SplitRumAgent.addIdentity(identity); }); | ||
} | ||
else { | ||
_config.log.error('Identities must be an array of objects with key and trafficType.'); | ||
} | ||
}, | ||
addIdentity: function (identity) { | ||
var validatedIdentity = validateIdentity(identity, _config.log); | ||
if (validatedIdentity && !_config.i.some(function (i) { return areEqual(i, validatedIdentity); })) { | ||
_config.i.push(validatedIdentity); | ||
calculateAndFlush(); | ||
} | ||
}, | ||
removeIdentities: function () { | ||
_config.i = []; | ||
}, | ||
removeIdentity: function (identity) { | ||
var validatedIdentity = validateIdentity(identity, _config.log); | ||
if (validatedIdentity) { | ||
_config.i = _config.i.filter(function (i) { return !areEqual(i, validatedIdentity); }); | ||
} | ||
}, | ||
getIdentities: function () { | ||
return _config.i; | ||
}, | ||
// Custom error handler. | ||
trackError: function (e) { | ||
return handleCustomErrors(e, context); | ||
}, | ||
// Track custom events. | ||
track: track, | ||
// For internal use. | ||
// @TODO review how to expose config | ||
__getConfig: function () { | ||
return _config; | ||
}, | ||
// register event collector | ||
register: function (eventCollector) { | ||
if (typeof eventCollector !== 'function') { | ||
_config.log.error('register method must be called with an event collector function.'); | ||
} | ||
else { | ||
collectors.push(eventCollector(context)); | ||
} | ||
// allow for chaining. | ||
return SplitRumAgent; | ||
}, | ||
// Custom properties | ||
setProperties: function (properties) { | ||
objectAssign(_config.p, validateProperties(properties, _config.log)); | ||
}, | ||
getProperties: function () { | ||
return _config.p; | ||
}, | ||
removeProperties: function () { | ||
_config.p = {}; | ||
}, | ||
// User consent | ||
setUserConsent: function (userConsent) { | ||
if (!isBoolean(userConsent)) { | ||
_config.log.warn('setUserConsent: provided param must be a boolean value'); | ||
return false; | ||
} | ||
_config.userConsent = userConsent ? CONSENT_GRANTED : CONSENT_DECLINED; | ||
return true; | ||
}, | ||
getUserConsent: function () { | ||
return _config.userConsent; | ||
} | ||
}; | ||
if (isBrowser) { | ||
// Built-in collectors | ||
SplitRumAgent.register(onNavigationTimingMetrics); | ||
SplitRumAgent.register(onError); | ||
// Post events periodically | ||
whenLoaded(setSchedule); | ||
// If there's a pagehide or visibilitychange event, calculate what's possible and send events using beacon by default | ||
onPageHideOrVisibilityChange(function () { | ||
collectors.forEach(function (collector) { | ||
if (collector && typeof collector.flush === 'function') | ||
collector.flush(); | ||
}); | ||
flush(true); | ||
}); | ||
} | ||
w.SplitRumAgent = SplitRumAgent; | ||
})(globalRef, isBrowser); | ||
export var SplitRumAgent = globalRef.SplitRumAgent; |
@@ -31,2 +31,2 @@ /* eslint-disable compat/compat */ | ||
export var userAgent = getUserAgent(); | ||
export var languageVersion = 'jsrum-' + '0.4.0-rc.0'; | ||
export var languageVersion = 'jsrum-' + '0.4.0'; |
@@ -42,3 +42,3 @@ import { getConnectionType, getUrl, userAgent } from './context'; | ||
if (!eventData || conf.userConsent === CONSENT_DECLINED) | ||
return; | ||
return false; | ||
// If people configures the agent at a really late time, prevent having a giant queue and attempt to send them or drop. First events will be the most important ones anyways. | ||
@@ -48,3 +48,3 @@ if (queue.length >= conf.queueSize) { | ||
conf.log.warn('Dropping event due to queue size limit.'); | ||
return; | ||
return false; | ||
} | ||
@@ -69,2 +69,3 @@ } | ||
assignIdentities(eventData, window.SplitRumAgent.__getConfig().i, queue); | ||
return true; | ||
} |
@@ -1,8 +0,1 @@ | ||
var callbacks = []; | ||
window.addEventListener('load', function () { | ||
// Defer, because we should wait until the handlers finish to allow customer logic run first. | ||
setTimeout(function () { | ||
callbacks.forEach(function (cb) { return cb(); }); | ||
}, 0); | ||
}); | ||
/** | ||
@@ -17,8 +10,11 @@ * Calls the callback function when the document is loaded or if it is already loaded. | ||
else { | ||
callbacks.push(callback); | ||
window.addEventListener('load', function () { | ||
// Defer, because we should wait until the handlers finish to allow customer logic run first. | ||
setTimeout(callback, 0); | ||
}); | ||
} | ||
} | ||
// `unload` is not reliable, so we use `pagehide` if available: https://developer.chrome.com/articles/page-lifecycle-api/#the-unload-event | ||
var PAGE_TERMINATION_EVENT = typeof window.onpagehide !== 'undefined' ? 'pagehide' : 'unload'; | ||
export function onPageHideOrVisibilityChange(callback) { | ||
// `unload` is not reliable, so we use `pagehide` if available: https://developer.chrome.com/articles/page-lifecycle-api/#the-unload-event | ||
var PAGE_TERMINATION_EVENT = typeof window.onpagehide !== 'undefined' ? 'pagehide' : 'unload'; | ||
// Flush data whenever the page is hidden or unloaded | ||
@@ -25,0 +21,0 @@ if (typeof document !== 'undefined' && document.addEventListener) { |
{ | ||
"name": "@splitsoftware/browser-rum-agent", | ||
"version": "0.4.0-rc.0", | ||
"version": "0.4.0", | ||
"description": "Split Software RUM Agent for Browsers.", | ||
@@ -5,0 +5,0 @@ "main": "cjs/index.js", |
@@ -0,1 +1,3 @@ | ||
import type { ReportOpts } from 'web-vitals' | ||
/** | ||
@@ -47,3 +49,3 @@ * Type of a property value | ||
export type EventCollector = (ctx: { | ||
track(event: EventData): void | ||
track(event: EventData): boolean | ||
}) => void | { | ||
@@ -192,4 +194,5 @@ flush(): void | ||
* @param {string | Error} error - The error message or Error object to track. | ||
* @returns {boolean} Whether the error event was tracked or not. The event will not be tracked if it is an invalid type (e.g., undefined), user consent is 'DECLINED' or the internal events queue is full and couldn't be flushed. | ||
*/ | ||
trackError(error: string | Error): void; | ||
trackError(error: string | Error): boolean; | ||
/** | ||
@@ -202,4 +205,5 @@ * Tracks a custom event. | ||
* @param {Properties=} properties - The properties of this event. Values can be string, number, boolean or null. | ||
* @returns {boolean} Whether the event was tracked or not. The event will not be tracked if it has invalid types (e.g., eventTypeId is not a string), user consent is 'DECLINED', or the internal events queue is full and couldn't be flushed. | ||
*/ | ||
track(eventTypeId: string, value?: number, properties?: Properties): void; | ||
track(eventTypeId: string, value?: number, properties?: Properties): boolean; | ||
/** | ||
@@ -210,4 +214,5 @@ * Tracks a custom event. | ||
* @param {EventData} event - The event object. | ||
* @returns {boolean} Whether the event was tracked or not. The event will not be tracked if it has invalid types (e.g., eventTypeId property is not a string), user consent is 'DECLINED', or the internal events queue is full and couldn't be flushed. | ||
*/ | ||
track(event: EventData): void; | ||
track(event: EventData): boolean; | ||
/** | ||
@@ -330,1 +335,50 @@ * Sets properties to append to events. | ||
export declare function routeChanges(options?: IRouteChangesOptions): EventCollector; | ||
export interface IWebVitalsOptions { | ||
reportOptions?: { | ||
onCLS?: boolean | ReportOpts, | ||
onFCP?: boolean | ReportOpts, | ||
onFID?: boolean | ReportOpts, | ||
onINP?: boolean | ReportOpts, | ||
onLCP?: boolean | ReportOpts, | ||
onTTFB?: boolean | ReportOpts, | ||
} | ||
} | ||
/** | ||
* Collects Web-Vitals metrics (https://www.npmjs.com/package/web-vitals). | ||
* | ||
* By default it collects all web-vitals metrics, but you can specify which ones to collect by passing a `reportOptions` object. | ||
* | ||
* For example: | ||
* ``` | ||
* SplitRumAgent.register(webVitals({ | ||
* reportOptions: { | ||
* // collects only the core web-vitals | ||
* onCLS: true, | ||
* onFID: true, | ||
* onLCP: true, | ||
* // other web-vital metrics are not collected | ||
* } | ||
* })); | ||
* ``` | ||
* | ||
* Collected event format: | ||
* ``` | ||
* { | ||
* eventTypeId: 'webvitals.cls' | 'webvitals.fcp' | 'webvitals.fid' | 'webvitals.inp' | 'webvitals.lcp' | 'webvitals.ttfb', | ||
* value: number, | ||
* properties: { | ||
* rating: 'good' | 'needsImprovement' | 'poor', | ||
* navigationType: 'navigate' | 'reload' | 'back_forward' | 'back-forward-cache' | 'prerender' | 'restore' | ||
* } | ||
* } | ||
* ``` | ||
* | ||
* @param {IWebVitalsOptions} options | ||
* @default | ||
* ``` | ||
* { reportOptions: { onCLS: true, onFCP: true, onFID: true, onINP: true, onLCP: true, onTTFB: true } } | ||
* ``` | ||
*/ | ||
export declare function webVitals(options?: IWebVitalsOptions): EventCollector; |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
125768
2685