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

@sentry/replay

Package Overview
Dependencies
Maintainers
12
Versions
233
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@sentry/replay - npm Package Compare versions

Comparing version 0.2.0-10 to 0.2.0-11

183

dist/index.es.js
import * as Sentry from '@sentry/browser';
import { record } from 'rrweb';
import { logger as logger$1 } from '@sentry/utils';
import { getCurrentHub, captureEvent } from '@sentry/browser';
import { logger as logger$1, uuid4, addInstrumentationHandler, htmlTreeAsString } from '@sentry/utils';
import { record as record$1 } from 'rrweb';

@@ -158,3 +159,3 @@ /*! *****************************************************************************

// @ts-expect-error Not sure why this errors, Node should be correct (Argument of type 'Node' is not assignable to parameter of type 'INode')
nodeId: record.mirror.getId(entry.element),
nodeId: record$1.mirror.getId(entry.element),
},

@@ -250,21 +251,14 @@ };

var currentDate = new Date().getTime();
// Create root replay event, this is where attachments will be saved
var transaction = Sentry.getCurrentHub().startTransaction({
name: ROOT_REPLAY_NAME,
tags: {
isReplayRoot: 'yes',
},
});
// We have to finish the transaction to get an event ID to be able to
// upload an attachment for that event
// @ts-expect-error This returns an eventId (string), but is not typed as such
var id = transaction.finish();
logger.log("Creating new session: ".concat(id));
var hub = getCurrentHub();
var session = {
id: id,
spanId: transaction.spanId,
traceId: transaction.traceId,
id: uuid4(),
started: currentDate,
lastActivity: currentDate,
sequenceId: 0,
};
hub.captureEvent({
message: ROOT_REPLAY_NAME,
tags: { sequenceId: session.sequenceId },
}, { event_id: session.id });
logger.log("Creating new session: ".concat(session.id));
if (stickySession) {

@@ -330,2 +324,72 @@ saveSession(session);

function addInstrumentationListeners(scope, replay) {
scope.addScopeListener(scopeListenerCallback.bind(replay));
addInstrumentationHandler('dom', domCallback.bind(replay));
addInstrumentationHandler('xhr', xhrCallback.bind(replay));
addInstrumentationHandler('fetch', fetchCallback.bind(replay));
}
function scopeListenerCallback(scope) {
//@ts-expect-error using private val
var newBreadcrumb = scope._breadcrumbs[scope._breadcrumbs.length - 1];
if (['fetch', 'xhr', 'sentry.event'].includes(newBreadcrumb.category) ||
newBreadcrumb.category.startsWith('ui.')) {
return;
}
this.breadcrumbs.push(__assign({ type: 'default' }, newBreadcrumb));
}
function domCallback(handlerData) {
// Taken from https://github.com/getsentry/sentry-javascript/blob/master/packages/browser/src/integrations/breadcrumbs.ts#L112
var target;
var targetNode;
// Accessing event.target can throw (see getsentry/raven-js#838, #768)
try {
targetNode =
handlerData.event.target ||
handlerData.event;
target = htmlTreeAsString(targetNode);
}
catch (e) {
target = '<unknown>';
}
if (target.length === 0) {
return;
}
this.breadcrumbs.push({
timestamp: new Date().getTime() / 1000,
type: 'default',
category: "ui.".concat(handlerData.name),
message: target,
data: {
// @ts-expect-error Not sure why this errors, Node should be correct (Argument of type 'Node' is not assignable to parameter of type 'INode')
nodeId: targetNode ? record.mirror.getId(targetNode) : undefined,
},
});
}
function xhrCallback(handlerData) {
// TODO: add status code into data, etc.
if (handlerData.startTimestamp) {
handlerData.xhr.__sentry_xhr__.startTimestamp = handlerData.startTimestamp;
}
if (handlerData.endTimestamp) {
this.spans.push({
description: handlerData.args[1],
op: handlerData.args[0],
startTimestamp: handlerData.xhr.__sentry_xhr__.startTimestamp / 1000 ||
handlerData.endTimestamp / 1000.0,
endTimestamp: handlerData.endTimestamp / 1000.0,
});
}
}
function fetchCallback(handlerData) {
// TODO: add status code into data, etc.
if (handlerData.endTimestamp) {
this.spans.push({
description: handlerData.args[1],
op: handlerData.args[0],
startTimestamp: handlerData.startTimestamp / 1000,
endTimestamp: handlerData.endTimestamp / 1000,
});
}
}
var SentryReplay = /** @class */ (function () {

@@ -347,2 +411,3 @@ function SentryReplay(_a) {

this.performanceEvents = [];
this.breadcrumbs = [];
/**

@@ -353,2 +418,3 @@ * The timestamp of the first event since the last flush.

this.initialEventTimestampSinceFlush = null;
this.replaySpans = [];
this.performanceObserver = null;

@@ -404,6 +470,6 @@ /**

SentryReplay.attachmentUrlFromDsn = function (dsn, eventId) {
var host = dsn.host, projectId = dsn.projectId, protocol = dsn.protocol, user = dsn.user;
var host = dsn.host, projectId = dsn.projectId, protocol = dsn.protocol, publicKey = dsn.publicKey;
var port = dsn.port !== '' ? ":".concat(dsn.port) : '';
var path = dsn.path !== '' ? "/".concat(dsn.path) : '';
return "".concat(protocol, "://").concat(host).concat(port).concat(path, "/api/").concat(projectId, "/events/").concat(eventId, "/attachments/?sentry_key=").concat(user, "&sentry_version=7&sentry_client=replay");
return "".concat(protocol, "://").concat(host).concat(port).concat(path, "/api/").concat(projectId, "/events/").concat(eventId, "/attachments/?sentry_key=").concat(publicKey, "&sentry_version=7&sentry_client=replay");
};

@@ -424,2 +490,5 @@ SentryReplay.prototype.setupOnce = function () {

var _this = this;
var hub = Sentry.getCurrentHub();
var scope = hub.getStackTop().scope;
addInstrumentationListeners(scope, this);
this.loadSession({ expiry: SESSION_IDLE_DURATION });

@@ -440,5 +509,3 @@ // If there is no session, then something bad has happened - can't continue

});
// not fully initialized and the event will not get properly sent to Sentry
this.createReplayEvent();
record(__assign(__assign({}, this.rrwebRecordOptions), { emit: function (event, isCheckout) {
record$1(__assign(__assign({}, this.rrwebRecordOptions), { emit: function (event, isCheckout) {
// We want to batch uploads of replay events. Save events only if

@@ -551,24 +618,5 @@ // `<uploadMinDelay>` milliseconds have elapsed since the last event

logger.log('Taking full rrweb snapshot');
record.takeFullSnapshot(true);
record$1.takeFullSnapshot(true);
};
/**
* This is our pseudo replay event disguised as a transaction. It will be
* used to store performance entries and breadcrumbs for every incremental
* replay event.
**/
SentryReplay.prototype.createReplayEvent = function () {
var _this = this;
logger.log('CreateReplayEvent rootReplayId', this.session.id);
this.replayEvent = Sentry.getCurrentHub().startTransaction({
name: REPLAY_EVENT_NAME,
parentSpanId: this.session.spanId,
traceId: this.session.traceId,
tags: {
replayId: this.session.id,
},
});
Sentry.configureScope(function (scope) { return scope.setSpan(_this.replayEvent); });
return this.replayEvent;
};
/**
* Create a span for each performance entry. The parent transaction is `this.replayEvent`.

@@ -579,11 +627,10 @@ */

entries.forEach(function (_a) {
var _b;
var type = _a.type, start = _a.start, end = _a.end, name = _a.name, data = _a.data;
var span = (_b = _this.replayEvent) === null || _b === void 0 ? void 0 : _b.startChild({
_this.replaySpans.push({
op: type,
description: name,
startTimestamp: start,
endTimestamp: end,
data: data,
});
span.finish(end);
});

@@ -607,3 +654,2 @@ };

}
// This current implementation is to create spans on the transaction referenced in `this.replayEvent`
this.createPerformanceSpans(entryEvents);

@@ -635,3 +681,2 @@ };

SentryReplay.prototype.finishReplayEvent = function () {
var _a;
if (!this.checkAndHandleExpiredSession()) {

@@ -644,2 +689,11 @@ logger.error(new Error('Attempting to finish replay event after session expired.'));

}
// TEMP: keep sending a replay event just for the duration
captureEvent({
message: "".concat(REPLAY_EVENT_NAME, "-").concat(uuid4().substring(16)),
tags: {
replayId: this.session.id,
sequenceId: this.session.sequenceId++,
},
});
this.addPerformanceEntries();
this.sendReplay(this.session.id);

@@ -649,7 +703,2 @@ this.initialEventTimestampSinceFlush = null;

this.session.lastActivity = new Date().getTime();
// include performance entries
this.addPerformanceEntries();
// Close out existing replay event and create a new one
(_a = this.replayEvent) === null || _a === void 0 ? void 0 : _a.setStatus('ok').finish();
this.createReplayEvent();
};

@@ -665,9 +714,14 @@ /**

*/
SentryReplay.prototype.sendReplayRequest = function (endpoint, events) {
SentryReplay.prototype.sendReplayRequest = function (_a) {
var endpoint = _a.endpoint, events = _a.events, replaySpans = _a.replaySpans, breadcrumbs = _a.breadcrumbs;
return __awaiter(this, void 0, void 0, function () {
var stringifiedPayload, formData;
return __generator(this, function (_a) {
switch (_a.label) {
return __generator(this, function (_b) {
switch (_b.label) {
case 0:
stringifiedPayload = JSON.stringify({ events: events });
stringifiedPayload = JSON.stringify({
recording: events,
replaySpans: replaySpans,
breadcrumbs: breadcrumbs,
});
formData = new FormData();

@@ -690,3 +744,3 @@ formData.append('rrweb', new Blob([stringifiedPayload], {

case 1:
_a.sent();
_b.sent();
return [2 /*return*/];

@@ -702,3 +756,3 @@ }

return __awaiter(this, void 0, void 0, function () {
var events, client, endpoint, ex_1;
var events, replaySpans, breadcrumbs, client, endpoint, ex_1;
return __generator(this, function (_a) {

@@ -711,2 +765,6 @@ switch (_a.label) {

events = this.events;
replaySpans = this.replaySpans;
breadcrumbs = this.breadcrumbs;
this.replaySpans = [];
this.breadcrumbs = [];
this.events = [];

@@ -718,3 +776,8 @@ client = Sentry.getCurrentHub().getClient();

_a.trys.push([1, 3, , 4]);
return [4 /*yield*/, this.sendReplayRequest(endpoint, events)];
return [4 /*yield*/, this.sendReplayRequest({
endpoint: endpoint,
events: events,
replaySpans: replaySpans,
breadcrumbs: breadcrumbs,
})];
case 2:

@@ -721,0 +784,0 @@ _a.sent();

@@ -6,4 +6,4 @@ 'use strict';

var Sentry = require('@sentry/browser');
var utils = require('@sentry/utils');
var rrweb = require('rrweb');
var utils = require('@sentry/utils');

@@ -274,21 +274,14 @@ function _interopNamespace(e) {

var currentDate = new Date().getTime();
// Create root replay event, this is where attachments will be saved
var transaction = Sentry__namespace.getCurrentHub().startTransaction({
name: ROOT_REPLAY_NAME,
tags: {
isReplayRoot: 'yes',
},
});
// We have to finish the transaction to get an event ID to be able to
// upload an attachment for that event
// @ts-expect-error This returns an eventId (string), but is not typed as such
var id = transaction.finish();
logger.log("Creating new session: ".concat(id));
var hub = Sentry.getCurrentHub();
var session = {
id: id,
spanId: transaction.spanId,
traceId: transaction.traceId,
id: utils.uuid4(),
started: currentDate,
lastActivity: currentDate,
sequenceId: 0,
};
hub.captureEvent({
message: ROOT_REPLAY_NAME,
tags: { sequenceId: session.sequenceId },
}, { event_id: session.id });
logger.log("Creating new session: ".concat(session.id));
if (stickySession) {

@@ -354,2 +347,72 @@ saveSession(session);

function addInstrumentationListeners(scope, replay) {
scope.addScopeListener(scopeListenerCallback.bind(replay));
utils.addInstrumentationHandler('dom', domCallback.bind(replay));
utils.addInstrumentationHandler('xhr', xhrCallback.bind(replay));
utils.addInstrumentationHandler('fetch', fetchCallback.bind(replay));
}
function scopeListenerCallback(scope) {
//@ts-expect-error using private val
var newBreadcrumb = scope._breadcrumbs[scope._breadcrumbs.length - 1];
if (['fetch', 'xhr', 'sentry.event'].includes(newBreadcrumb.category) ||
newBreadcrumb.category.startsWith('ui.')) {
return;
}
this.breadcrumbs.push(__assign({ type: 'default' }, newBreadcrumb));
}
function domCallback(handlerData) {
// Taken from https://github.com/getsentry/sentry-javascript/blob/master/packages/browser/src/integrations/breadcrumbs.ts#L112
var target;
var targetNode;
// Accessing event.target can throw (see getsentry/raven-js#838, #768)
try {
targetNode =
handlerData.event.target ||
handlerData.event;
target = utils.htmlTreeAsString(targetNode);
}
catch (e) {
target = '<unknown>';
}
if (target.length === 0) {
return;
}
this.breadcrumbs.push({
timestamp: new Date().getTime() / 1000,
type: 'default',
category: "ui.".concat(handlerData.name),
message: target,
data: {
// @ts-expect-error Not sure why this errors, Node should be correct (Argument of type 'Node' is not assignable to parameter of type 'INode')
nodeId: targetNode ? record.mirror.getId(targetNode) : undefined,
},
});
}
function xhrCallback(handlerData) {
// TODO: add status code into data, etc.
if (handlerData.startTimestamp) {
handlerData.xhr.__sentry_xhr__.startTimestamp = handlerData.startTimestamp;
}
if (handlerData.endTimestamp) {
this.spans.push({
description: handlerData.args[1],
op: handlerData.args[0],
startTimestamp: handlerData.xhr.__sentry_xhr__.startTimestamp / 1000 ||
handlerData.endTimestamp / 1000.0,
endTimestamp: handlerData.endTimestamp / 1000.0,
});
}
}
function fetchCallback(handlerData) {
// TODO: add status code into data, etc.
if (handlerData.endTimestamp) {
this.spans.push({
description: handlerData.args[1],
op: handlerData.args[0],
startTimestamp: handlerData.startTimestamp / 1000,
endTimestamp: handlerData.endTimestamp / 1000,
});
}
}
var SentryReplay = /** @class */ (function () {

@@ -371,2 +434,3 @@ function SentryReplay(_a) {

this.performanceEvents = [];
this.breadcrumbs = [];
/**

@@ -377,2 +441,3 @@ * The timestamp of the first event since the last flush.

this.initialEventTimestampSinceFlush = null;
this.replaySpans = [];
this.performanceObserver = null;

@@ -428,6 +493,6 @@ /**

SentryReplay.attachmentUrlFromDsn = function (dsn, eventId) {
var host = dsn.host, projectId = dsn.projectId, protocol = dsn.protocol, user = dsn.user;
var host = dsn.host, projectId = dsn.projectId, protocol = dsn.protocol, publicKey = dsn.publicKey;
var port = dsn.port !== '' ? ":".concat(dsn.port) : '';
var path = dsn.path !== '' ? "/".concat(dsn.path) : '';
return "".concat(protocol, "://").concat(host).concat(port).concat(path, "/api/").concat(projectId, "/events/").concat(eventId, "/attachments/?sentry_key=").concat(user, "&sentry_version=7&sentry_client=replay");
return "".concat(protocol, "://").concat(host).concat(port).concat(path, "/api/").concat(projectId, "/events/").concat(eventId, "/attachments/?sentry_key=").concat(publicKey, "&sentry_version=7&sentry_client=replay");
};

@@ -448,2 +513,5 @@ SentryReplay.prototype.setupOnce = function () {

var _this = this;
var hub = Sentry__namespace.getCurrentHub();
var scope = hub.getStackTop().scope;
addInstrumentationListeners(scope, this);
this.loadSession({ expiry: SESSION_IDLE_DURATION });

@@ -464,4 +532,2 @@ // If there is no session, then something bad has happened - can't continue

});
// not fully initialized and the event will not get properly sent to Sentry
this.createReplayEvent();
rrweb.record(__assign(__assign({}, this.rrwebRecordOptions), { emit: function (event, isCheckout) {

@@ -578,21 +644,2 @@ // We want to batch uploads of replay events. Save events only if

/**
* This is our pseudo replay event disguised as a transaction. It will be
* used to store performance entries and breadcrumbs for every incremental
* replay event.
**/
SentryReplay.prototype.createReplayEvent = function () {
var _this = this;
logger.log('CreateReplayEvent rootReplayId', this.session.id);
this.replayEvent = Sentry__namespace.getCurrentHub().startTransaction({
name: REPLAY_EVENT_NAME,
parentSpanId: this.session.spanId,
traceId: this.session.traceId,
tags: {
replayId: this.session.id,
},
});
Sentry__namespace.configureScope(function (scope) { return scope.setSpan(_this.replayEvent); });
return this.replayEvent;
};
/**
* Create a span for each performance entry. The parent transaction is `this.replayEvent`.

@@ -603,11 +650,10 @@ */

entries.forEach(function (_a) {
var _b;
var type = _a.type, start = _a.start, end = _a.end, name = _a.name, data = _a.data;
var span = (_b = _this.replayEvent) === null || _b === void 0 ? void 0 : _b.startChild({
_this.replaySpans.push({
op: type,
description: name,
startTimestamp: start,
endTimestamp: end,
data: data,
});
span.finish(end);
});

@@ -631,3 +677,2 @@ };

}
// This current implementation is to create spans on the transaction referenced in `this.replayEvent`
this.createPerformanceSpans(entryEvents);

@@ -659,3 +704,2 @@ };

SentryReplay.prototype.finishReplayEvent = function () {
var _a;
if (!this.checkAndHandleExpiredSession()) {

@@ -668,2 +712,11 @@ logger.error(new Error('Attempting to finish replay event after session expired.'));

}
// TEMP: keep sending a replay event just for the duration
Sentry.captureEvent({
message: "".concat(REPLAY_EVENT_NAME, "-").concat(utils.uuid4().substring(16)),
tags: {
replayId: this.session.id,
sequenceId: this.session.sequenceId++,
},
});
this.addPerformanceEntries();
this.sendReplay(this.session.id);

@@ -673,7 +726,2 @@ this.initialEventTimestampSinceFlush = null;

this.session.lastActivity = new Date().getTime();
// include performance entries
this.addPerformanceEntries();
// Close out existing replay event and create a new one
(_a = this.replayEvent) === null || _a === void 0 ? void 0 : _a.setStatus('ok').finish();
this.createReplayEvent();
};

@@ -689,9 +737,14 @@ /**

*/
SentryReplay.prototype.sendReplayRequest = function (endpoint, events) {
SentryReplay.prototype.sendReplayRequest = function (_a) {
var endpoint = _a.endpoint, events = _a.events, replaySpans = _a.replaySpans, breadcrumbs = _a.breadcrumbs;
return __awaiter(this, void 0, void 0, function () {
var stringifiedPayload, formData;
return __generator(this, function (_a) {
switch (_a.label) {
return __generator(this, function (_b) {
switch (_b.label) {
case 0:
stringifiedPayload = JSON.stringify({ events: events });
stringifiedPayload = JSON.stringify({
recording: events,
replaySpans: replaySpans,
breadcrumbs: breadcrumbs,
});
formData = new FormData();

@@ -714,3 +767,3 @@ formData.append('rrweb', new Blob([stringifiedPayload], {

case 1:
_a.sent();
_b.sent();
return [2 /*return*/];

@@ -726,3 +779,3 @@ }

return __awaiter(this, void 0, void 0, function () {
var events, client, endpoint, ex_1;
var events, replaySpans, breadcrumbs, client, endpoint, ex_1;
return __generator(this, function (_a) {

@@ -735,2 +788,6 @@ switch (_a.label) {

events = this.events;
replaySpans = this.replaySpans;
breadcrumbs = this.breadcrumbs;
this.replaySpans = [];
this.breadcrumbs = [];
this.events = [];

@@ -742,3 +799,8 @@ client = Sentry__namespace.getCurrentHub().getClient();

_a.trys.push([1, 3, , 4]);
return [4 /*yield*/, this.sendReplayRequest(endpoint, events)];
return [4 /*yield*/, this.sendReplayRequest({
endpoint: endpoint,
events: events,
replaySpans: replaySpans,
breadcrumbs: breadcrumbs,
})];
case 2:

@@ -745,0 +807,0 @@ _a.sent();

{
"name": "@sentry/replay",
"version": "0.2.0-10",
"version": "0.2.0-11",
"description": "User replays for Sentry",

@@ -5,0 +5,0 @@ "main": "dist/index.js",

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