Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

@bugsnag/browser-performance

Package Overview
Dependencies
Maintainers
9
Versions
36
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@bugsnag/browser-performance - npm Package Compare versions

Comparing version 0.3.0 to 1.0.0

dist/network-request-callback.js

11

dist/auto-instrumentation/full-page-load-plugin.js

@@ -0,1 +1,2 @@

import { getPermittedAttributes } from '../send-page-attributes.js';
import { instrumentPageLoadPhaseSpans } from './page-load-phase-spans.js';

@@ -27,2 +28,3 @@

const span = this.spanFactory.startSpan('[FullPageLoad]', { startTime: 0, parentContext: null });
const permittedAttributes = getPermittedAttributes(configuration.sendPageAttributes);
const url = new URL(this.location.href);

@@ -37,6 +39,9 @@ this.onSettle((endTime) => {

span.setAttribute('bugsnag.span.category', 'full_page_load');
span.setAttribute('bugsnag.browser.page.referrer', this.document.referrer);
span.setAttribute('bugsnag.browser.page.title', this.document.title);
span.setAttribute('bugsnag.browser.page.url', url.toString());
span.setAttribute('bugsnag.browser.page.route', route);
if (permittedAttributes.referrer)
span.setAttribute('bugsnag.browser.page.referrer', this.document.referrer);
if (permittedAttributes.title)
span.setAttribute('bugsnag.browser.page.title', this.document.title);
if (permittedAttributes.url)
span.setAttribute('bugsnag.browser.page.url', url.toString());
this.webVitals.attachTo(span);

@@ -43,0 +48,0 @@ this.spanFactory.endSpan(span, endTime);

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

import { defaultNetworkRequestCallback } from '../network-request-callback.js';
const permittedPrefixes = ['http://', 'https://', '/', './', '../'];
class NetworkRequestPlugin {

@@ -7,9 +10,18 @@ constructor(spanFactory, fetchTracker, xhrTracker) {

this.configEndpoint = '';
this.networkRequestCallback = defaultNetworkRequestCallback;
this.logger = { debug: console.debug, warn: console.warn, info: console.info, error: console.error };
this.trackRequest = (startContext) => {
if (!this.shouldTrackRequest(startContext))
return;
const networkRequestInfo = this.networkRequestCallback({ url: startContext.url, type: startContext.type });
if (!networkRequestInfo)
return;
if (typeof networkRequestInfo.url !== 'string') {
this.logger.warn(`expected url to be a string following network request callback, got ${typeof networkRequestInfo.url}`);
return;
}
const span = this.spanFactory.startSpan(`[HTTP]/${startContext.method.toUpperCase()}`, { startTime: startContext.startTime, makeCurrentContext: false });
span.setAttribute('bugsnag.span.category', 'network');
span.setAttribute('http.url', startContext.url);
span.setAttribute('http.method', startContext.method);
span.setAttribute('http.url', networkRequestInfo.url);
return (endContext) => {

@@ -24,2 +36,3 @@ if (endContext.state === 'success') {

configure(configuration) {
this.logger = configuration.logger;
if (configuration.autoInstrumentNetworkRequests) {

@@ -29,6 +42,7 @@ this.configEndpoint = configuration.endpoint;

this.fetchTracker.onStart(this.trackRequest);
this.networkRequestCallback = configuration.networkRequestCallback;
}
}
shouldTrackRequest(startContext) {
return startContext.url !== this.configEndpoint;
return startContext.url !== this.configEndpoint && permittedPrefixes.some((prefix) => startContext.url.startsWith(prefix));
}

@@ -35,0 +49,0 @@ }

@@ -44,5 +44,19 @@ function getHttpVersion(protocol) {

if (parentContext) {
const url = new URL(entry.name);
url.search = '';
const name = url.href;
const networkRequestInfo = configuration.networkRequestCallback({ url: entry.name, type: entry.initiatorType });
if (!networkRequestInfo)
return;
if (typeof networkRequestInfo.url !== 'string') {
configuration.logger.warn(`expected url to be a string following network request callback, got ${typeof networkRequestInfo.url}`);
return;
}
let name = '';
try {
const url = new URL(networkRequestInfo.url);
url.search = '';
name = url.href;
}
catch (err) {
configuration.logger.warn(`Unable to parse URL returned from networkRequestCallback: ${networkRequestInfo.url}`);
return;
}
const span = this.spanFactory.startSpan(`[ResourceLoad]${name}`, {

@@ -54,3 +68,3 @@ parentContext,

span.setAttribute('bugsnag.span.category', 'resource_load');
span.setAttribute('http.url', entry.name);
span.setAttribute('http.url', networkRequestInfo.url);
const httpFlavor = getHttpVersion(entry.nextHopProtocol);

@@ -57,0 +71,0 @@ if (httpFlavor) {

import { validateSpanOptions, isString, coreSpanOptionSchema } from '@bugsnag/core-performance';
import { getPermittedAttributes } from '../send-page-attributes.js';
import { defaultRouteResolver } from '../default-routing-provider.js';

@@ -24,3 +26,5 @@ // exclude isFirstClass from the route change option schema

return;
let previousRoute = configuration.routingProvider.resolveRoute(new URL(this.location.href));
const previousUrl = new URL(this.location.href);
let previousRoute = configuration.routingProvider.resolveRoute(previousUrl) || defaultRouteResolver(previousUrl);
const permittedAttributes = getPermittedAttributes(configuration.sendPageAttributes);
configuration.routingProvider.listenForRouteChanges((url, trigger, options) => {

@@ -49,3 +53,3 @@ let absoluteUrl;

const cleanOptions = validateSpanOptions('[RouteChange]', routeChangeSpanOptions, routeChangeSpanOptionSchema, configuration.logger);
const route = configuration.routingProvider.resolveRoute(absoluteUrl);
const route = configuration.routingProvider.resolveRoute(absoluteUrl) || defaultRouteResolver(absoluteUrl);
// update the span name using the validated route

@@ -56,5 +60,6 @@ cleanOptions.name += route;

span.setAttribute('bugsnag.browser.page.route', route);
span.setAttribute('bugsnag.browser.page.url', url.toString());
span.setAttribute('bugsnag.browser.page.previous_route', previousRoute);
span.setAttribute('bugsnag.browser.page.route_change.trigger', cleanOptions.options.trigger);
if (permittedAttributes.url)
span.setAttribute('bugsnag.browser.page.url', url.toString());
previousRoute = route;

@@ -66,3 +71,4 @@ return {

end: (endTime) => {
span.setAttribute('bugsnag.browser.page.title', this.document.title);
if (permittedAttributes.title)
span.setAttribute('bugsnag.browser.page.title', this.document.title);
this.spanFactory.toPublicApi(span).end(endTime);

@@ -69,0 +75,0 @@ }

@@ -12,14 +12,14 @@ import { createClient } from '@bugsnag/core-performance';

import idGenerator from './id-generator.js';
import createOnSettle from './on-settle/index.js';
import makeBrowserPersistence from './persistence.js';
import createOnSettle from './on-settle/index.js';
import createFetchRequestTracker from './request-tracker/request-tracker-fetch.js';
import createXmlHttpRequestTracker from './request-tracker/request-tracker-xhr.js';
import createResourceAttributesSource from './resource-attributes-source.js';
import createSpanAttributesSource from './span-attributes-source.js';
import { createSpanAttributesSource } from './span-attributes-source.js';
import { WebVitals } from './web-vitals.js';
const backgroundingListener = createBrowserBackgroundingListener(document);
const spanAttributesSource = createSpanAttributesSource(document);
const clock = createClock(performance, backgroundingListener);
const persistence = makeBrowserPersistence(window);
const spanAttributesSource = createSpanAttributesSource(document.title, window.location.href);
const resourceAttributesSource = createResourceAttributesSource(navigator, persistence);

@@ -26,0 +26,0 @@ const fetchRequestTracker = createFetchRequestTracker(window, clock);

@@ -5,14 +5,22 @@ import { millisecondsToNanoseconds } from '@bugsnag/core-performance';

const MAX_CLOCK_DRIFT_MS = 300000;
function recalculateTimeOrigin(timeOrigin, performance) {
// if the machine has been sleeping the monatomic clock used by performance.now() may have been paused,
// so we need to check if this has drifted significantly from Date.now()
// if the drift is > 5 minutes re-set the clock's origin to bring it back in line with Date.now()
if (Math.abs(Date.now() - (timeOrigin + performance.now())) > MAX_CLOCK_DRIFT_MS) {
return Date.now() - performance.now();
}
return timeOrigin;
}
function createClock(performance, backgroundingListener) {
let calculatedTimeOrigin = performance.timeOrigin === undefined
const initialTimeOrigin = performance.timeOrigin === undefined
? performance.timing.navigationStart
: performance.timeOrigin;
// if the machine has been sleeping the monatomic clock used by performance.now() may have been paused,
// so when the app returns to the foreground we need to check if this has drifted significantly from Date.now()
// if the drift is > 5 minutes re-set the clock's origin to bring it back in line with Date.now()
// the performance clock could be shared between different tabs running in the same process
// so may already have diverged - for this reason we calculate a time origin when we first create the clock
// as well as when the app returns to the foreground
let calculatedTimeOrigin = recalculateTimeOrigin(initialTimeOrigin, performance);
backgroundingListener.onStateChange(state => {
if (state === 'in-foreground') {
if (Math.abs(Date.now() - (calculatedTimeOrigin + performance.now())) > MAX_CLOCK_DRIFT_MS) {
calculatedTimeOrigin = Date.now() - performance.now();
}
calculatedTimeOrigin = recalculateTimeOrigin(calculatedTimeOrigin, performance);
}

@@ -19,0 +27,0 @@ });

import { schema, isBoolean, isStringOrRegExpArray } from '@bugsnag/core-performance';
import { defaultNetworkRequestCallback, isNetworkRequestCallback } from './network-request-callback.js';
import { isRoutingProvider } from './routing-provider.js';
import { defaultSendPageAttributes, isSendPageAttributes } from './send-page-attributes.js';

@@ -29,2 +31,10 @@ function createSchema(hostname, defaultRoutingProvider) {

validate: isStringOrRegExpArray
}, networkRequestCallback: {
defaultValue: defaultNetworkRequestCallback,
message: 'should be a function',
validate: isNetworkRequestCallback
}, sendPageAttributes: {
defaultValue: defaultSendPageAttributes,
message: 'should be an object',
validate: isSendPageAttributes
} });

@@ -31,0 +41,0 @@ }

import getAbsoluteUrl from './request-tracker/url-helpers.js';
const defaultRouteResolver = (url) => url.pathname;
const defaultRouteResolver = (url) => url.pathname || '/';
const createDefaultRoutingProvider = (onSettle, location) => {

@@ -33,2 +33,2 @@ return class DefaultRoutingProvider {

export { createDefaultRoutingProvider };
export { createDefaultRoutingProvider, defaultRouteResolver };

@@ -8,3 +8,3 @@ import { RequestTracker } from './request-tracker.js';

const method = (!!init && init.method) || (inputIsRequest && input.method) || 'GET';
return { url: getAbsoluteUrl(url, baseUrl), method, startTime };
return { url: getAbsoluteUrl(url, baseUrl), method, startTime, type: 'fetch' };
}

@@ -11,0 +11,0 @@ function isRequest(input) {

@@ -24,2 +24,3 @@ import { RequestTracker } from './request-tracker.js';

const onRequestEnd = requestTracker.start({
type: 'xmlhttprequest',
method: requestData.method,

@@ -26,0 +27,0 @@ url: requestData.url,

@@ -8,3 +8,3 @@ import cuid from '@bugsnag/cuid';

return function resourceAttributesSource(config) {
const attributes = new ResourceAttributes(config.releaseStage, config.appVersion, 'bugsnag.performance.browser', '0.3.0');
const attributes = new ResourceAttributes(config.releaseStage, config.appVersion, 'bugsnag.performance.browser', '1.0.0');
attributes.set('browser.user_agent', navigator.userAgent);

@@ -11,0 +11,0 @@ // chromium only

@@ -1,11 +0,29 @@

const createSpanAttributesSource = (title, url) => {
return () => {
const spanAttributes = new Map();
spanAttributes.set('bugsnag.span.category', 'custom');
spanAttributes.set('bugsnag.browser.page.url', url);
spanAttributes.set('bugsnag.browser.page.title', title);
return spanAttributes;
const createSpanAttributesSource = (document) => {
const defaultAttributes = {
url: {
name: 'bugsnag.browser.page.url',
getValue: () => document.location.href,
permitted: false
},
title: {
name: 'bugsnag.browser.page.title',
getValue: () => document.title,
permitted: false
}
};
return {
configure(configuration) {
defaultAttributes.title.permitted = configuration.sendPageAttributes.title || false;
defaultAttributes.url.permitted = configuration.sendPageAttributes.url || false;
},
requestAttributes(span) {
for (const attribute of Object.values(defaultAttributes)) {
if (attribute.permitted) {
span.setAttribute(attribute.name, attribute.getValue());
}
}
}
};
};
export { createSpanAttributesSource as default };
export { createSpanAttributesSource, createSpanAttributesSource as default };

@@ -14,5 +14,5 @@ import { type BackgroundingListener, type InternalConfiguration, type Plugin, type SpanFactory } from '@bugsnag/core-performance';

private wasBackgrounded;
constructor(document: Document, location: Location, spanFactory: SpanFactory, webVitals: WebVitals, onSettle: OnSettle, backgroundingListener: BackgroundingListener, performance: PerformanceWithTiming);
constructor(document: Document, location: Location, spanFactory: SpanFactory<BrowserConfiguration>, webVitals: WebVitals, onSettle: OnSettle, backgroundingListener: BackgroundingListener, performance: PerformanceWithTiming);
configure(configuration: InternalConfiguration<BrowserConfiguration>): void;
}
//# sourceMappingURL=full-page-load-plugin.d.ts.map

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

import { type InternalConfiguration, type Plugin, type SpanFactory } from '@bugsnag/core-performance';
import { type BrowserConfiguration } from '../config';
import { type RequestTracker } from '../request-tracker/request-tracker';
import { type SpanFactory, type Plugin, type InternalConfiguration } from '@bugsnag/core-performance';
import { type BrowserConfiguration } from '../config';
export declare class NetworkRequestPlugin implements Plugin<BrowserConfiguration> {

@@ -9,3 +9,5 @@ private spanFactory;

private configEndpoint;
constructor(spanFactory: SpanFactory, fetchTracker: RequestTracker, xhrTracker: RequestTracker);
private networkRequestCallback;
private logger;
constructor(spanFactory: SpanFactory<BrowserConfiguration>, fetchTracker: RequestTracker, xhrTracker: RequestTracker);
configure(configuration: InternalConfiguration<BrowserConfiguration>): void;

@@ -12,0 +14,0 @@ private trackRequest;

import type { SpanContext, SpanFactory } from '@bugsnag/core-performance';
import { type BrowserConfiguration } from '../config';
import { type PerformanceWithTiming } from '../on-settle/load-event-end-settler';
export declare const instrumentPageLoadPhaseSpans: (spanFactory: SpanFactory, performance: PerformanceWithTiming, route: string, parentContext: SpanContext) => void;
export declare const instrumentPageLoadPhaseSpans: (spanFactory: SpanFactory<BrowserConfiguration>, performance: PerformanceWithTiming, route: string, parentContext: SpanContext) => void;
//# sourceMappingURL=page-load-phase-spans.d.ts.map

@@ -8,5 +8,5 @@ import { type SpanContextStorage, type InternalConfiguration, type Plugin, type SpanFactory } from '@bugsnag/core-performance';

private readonly PerformanceObserverClass;
constructor(spanFactory: SpanFactory, spanContextStorage: SpanContextStorage, PerformanceObserverClass: typeof PerformanceObserver);
constructor(spanFactory: SpanFactory<BrowserConfiguration>, spanContextStorage: SpanContextStorage, PerformanceObserverClass: typeof PerformanceObserver);
configure(configuration: InternalConfiguration<BrowserConfiguration>): void;
}
//# sourceMappingURL=resource-load-plugin.d.ts.map

@@ -7,5 +7,5 @@ import { type InternalConfiguration, type Plugin, type SpanFactory } from '@bugsnag/core-performance';

private readonly document;
constructor(spanFactory: SpanFactory, location: Location, document: Document);
constructor(spanFactory: SpanFactory<BrowserConfiguration>, location: Location, document: Document);
configure(configuration: InternalConfiguration<BrowserConfiguration>): void;
}
//# sourceMappingURL=route-change-plugin.d.ts.map
import { type ConfigOption, type Configuration, type CoreSchema } from '@bugsnag/core-performance';
import { type NetworkRequestCallback } from './network-request-callback';
import { type RoutingProvider } from './routing-provider';
import { type SendPageAttributes } from './send-page-attributes';
export interface BrowserSchema extends CoreSchema {

@@ -10,2 +12,4 @@ autoInstrumentFullPageLoads: ConfigOption<boolean>;

settleIgnoreUrls: ConfigOption<Array<string | RegExp>>;
networkRequestCallback: ConfigOption<NetworkRequestCallback>;
sendPageAttributes: ConfigOption<SendPageAttributes>;
}

@@ -19,4 +23,6 @@ export interface BrowserConfiguration extends Configuration {

settleIgnoreUrls?: Array<string | RegExp>;
networkRequestCallback?: NetworkRequestCallback;
sendPageAttributes?: SendPageAttributes;
}
export declare function createSchema(hostname: string, defaultRoutingProvider: RoutingProvider): BrowserSchema;
//# sourceMappingURL=config.d.ts.map
import { type OnSettle } from './on-settle';
import { type RouteResolver, type StartRouteChangeCallback } from './routing-provider';
export declare const defaultRouteResolver: RouteResolver;
export declare const createDefaultRoutingProvider: (onSettle: OnSettle, location: Location) => {

@@ -4,0 +5,0 @@ new (resolveRoute?: RouteResolver): {

@@ -5,2 +5,3 @@ export interface RequestStartContext {

startTime: number;
type: 'fetch' | 'xmlhttprequest';
}

@@ -7,0 +8,0 @@ export interface RequestEndContextSuccess {

import type { SpanAttributesSource } from '@bugsnag/core-performance';
declare const createSpanAttributesSource: (title: string, url: string) => SpanAttributesSource;
import { type BrowserConfiguration } from './config';
export declare const createSpanAttributesSource: (document: Document) => SpanAttributesSource<BrowserConfiguration>;
export default createSpanAttributesSource;
//# sourceMappingURL=span-attributes-source.d.ts.map
{
"name": "@bugsnag/browser-performance",
"version": "0.3.0",
"version": "1.0.0",
"description": "BugSnag performance monitoring for browsers",

@@ -24,3 +24,3 @@ "homepage": "https://www.bugsnag.com/",

"dependencies": {
"@bugsnag/core-performance": "^0.3.0",
"@bugsnag/core-performance": "^1.0.0",
"@bugsnag/cuid": "^3.0.2"

@@ -34,3 +34,3 @@ },

],
"gitHead": "076d6311e6534e8ed2bdacb82cd3bcfeb2033ffd"
"gitHead": "d30abd6e95ef5fe79582fd63186e243aa40269de"
}

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

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