@sentry/replay
Advanced tools
Comparing version 0.2.0-6 to 0.2.0-7
@@ -348,2 +348,3 @@ import * as Sentry from '@sentry/browser'; | ||
logger.log('Document has become active, but session has expired'); | ||
_this.loadSession({ expiry: VISIBILITY_CHANGE_TIMEOUT }); | ||
_this.triggerFullSnapshot(); | ||
@@ -378,4 +379,6 @@ } | ||
SentryReplay.attachmentUrlFromDsn = function (dsn, eventId) { | ||
var host = dsn.host, path = dsn.path, projectId = dsn.projectId, port = dsn.port, protocol = dsn.protocol, user = dsn.user; | ||
return "".concat(protocol, "://").concat(host).concat(port !== '' ? ":".concat(port) : '').concat(path !== '' ? "/".concat(path) : '', "/api/").concat(projectId, "/events/").concat(eventId, "/attachments/?sentry_key=").concat(user, "&sentry_version=7&sentry_client=replay"); | ||
var host = dsn.host, projectId = dsn.projectId, protocol = dsn.protocol, user = dsn.user; | ||
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"); | ||
}; | ||
@@ -421,9 +424,11 @@ SentryReplay.prototype.setupOnce = function () { | ||
} | ||
var uploadMaxDelayExceeded = isExpired(_this.initialEventTimestampSinceFlush, _this.options.uploadMaxDelay, now); | ||
// Do not finish the replay event if we receive a new replay event | ||
// unless `<uploadMaxDelay>` ms have elapsed since the last time we | ||
// finished the replay | ||
if (_this.timeout && !uploadMaxDelayExceeded) { | ||
if (_this.timeout) { | ||
window.clearTimeout(_this.timeout); | ||
} | ||
// If this is false, it means session is expired, create and a new session and wait for checkout | ||
if (!_this.checkAndHandleExpiredSession()) { | ||
logger.error(new Error('Received replay event after session expired.')); | ||
return; | ||
} | ||
// We need to clear existing events on a checkout, otherwise they are | ||
@@ -441,8 +446,15 @@ // incremental event updates and should be appended | ||
// A fullsnapshot happens on initial load and if we need to start a | ||
// new replay due to idle timeout. In the later case we will need to | ||
// create a new session before finishing the replay. | ||
_this.loadSession({ expiry: SESSION_IDLE_DURATION }); | ||
// new replay due to idle timeout. In the latter case, a new session *should* have been started | ||
// before triggering a new checkout | ||
_this.finishReplayEvent(); | ||
return; | ||
} | ||
var uploadMaxDelayExceeded = isExpired(_this.initialEventTimestampSinceFlush, _this.options.uploadMaxDelay, now); | ||
// If `uploadMaxDelayExceeded` is true, then we should finish the replay event immediately, | ||
// Otherwise schedule it to be finished in `this.options.uploadMinDelay` | ||
if (uploadMaxDelayExceeded) { | ||
logger.log('replay max delay exceeded, finishing replay event'); | ||
_this.finishReplayEvent(); | ||
return; | ||
} | ||
// Set timer to finish replay event and send replay attachment to | ||
@@ -452,3 +464,3 @@ // Sentry. Will be cancelled if an event happens before `uploadMinDelay` | ||
_this.timeout = window.setTimeout(function () { | ||
logger.log('rrweb timeout hit, finishing replay event'); | ||
logger.log('replay timeout exceeded, finishing replay event'); | ||
_this.finishReplayEvent(); | ||
@@ -458,3 +470,2 @@ }, _this.options.uploadMinDelay); | ||
this.addListeners(); | ||
// XXX: this needs to be in `setupOnce` vs `constructor`, otherwise SDK is | ||
// not fully initialized and the event will not get properly sent to Sentry | ||
@@ -464,2 +475,8 @@ this.createReplayEvent(); | ||
/** | ||
* Currently, this needs to be manually called (e.g. for tests). Sentry SDK does not support a teardown | ||
*/ | ||
SentryReplay.prototype.teardown = function () { | ||
this.removeListeners(); | ||
}; | ||
/** | ||
* Loads a session from storage, or creates a new one | ||
@@ -475,9 +492,31 @@ */ | ||
SentryReplay.prototype.addListeners = function () { | ||
var _this = this; | ||
document.addEventListener('visibilitychange', this.handleVisibilityChange); | ||
if ('PerformanceObserver' in window) { | ||
this.performanceObserver = new PerformanceObserver(this.handlePerformanceObserver); | ||
// Observe everything for now | ||
this.performanceObserver.observe({ | ||
entryTypes: __spreadArray([], PerformanceObserver.supportedEntryTypes, true), | ||
if (!('PerformanceObserver' in window)) { | ||
return; | ||
} | ||
this.performanceObserver = new PerformanceObserver(this.handlePerformanceObserver); | ||
// Observe almost everything for now (no mark/measure) | ||
[ | ||
'element', | ||
'event', | ||
'first-input', | ||
'largest-contentful-paint', | ||
'layout-shift', | ||
'longtask', | ||
'navigation', | ||
'paint', | ||
'resource', | ||
].forEach(function (type) { | ||
return _this.performanceObserver.observe({ | ||
type: type, | ||
buffered: true, | ||
}); | ||
}); | ||
}; | ||
SentryReplay.prototype.removeListeners = function () { | ||
document.removeEventListener('visibilitychange', this.handleVisibilityChange); | ||
if (this.performanceObserver) { | ||
this.performanceObserver.disconnect(); | ||
this.performanceObserver = null; | ||
} | ||
@@ -501,3 +540,3 @@ }; | ||
logger.log('CreateReplayEvent rootReplayId', this.session.id); | ||
this.replayEvent = Sentry.startTransaction({ | ||
this.replayEvent = Sentry.getCurrentHub().startTransaction({ | ||
name: REPLAY_EVENT_NAME, | ||
@@ -543,11 +582,29 @@ parentSpanId: this.session.spanId, | ||
}; | ||
/** | ||
* | ||
* | ||
* Returns true if session is not expired, false otherwise. | ||
*/ | ||
SentryReplay.prototype.checkAndHandleExpiredSession = function (expiry) { | ||
if (expiry === void 0) { expiry = SESSION_IDLE_DURATION; } | ||
var oldSessionId = this.session.id; | ||
// This will create a new session if expired, based on expiry length | ||
this.loadSession({ expiry: expiry }); | ||
// Session was expired if session ids do not match | ||
var isExpired = oldSessionId !== this.session.id; | ||
if (!isExpired) { | ||
return true; | ||
} | ||
// TODO: We could potentially figure out a way to save the last session, | ||
// and produce a checkout based on a previous checkout + updates, and then | ||
// replay the event on top. Or maybe replay the event on top of a refresh | ||
// snapshot. | ||
// For now create a new snapshot | ||
this.triggerFullSnapshot(); | ||
return false; | ||
}; | ||
SentryReplay.prototype.finishReplayEvent = function () { | ||
var _a; | ||
// Ensure that our existing session has not expired | ||
var isExpired = isSessionExpired(this.session, SESSION_IDLE_DURATION); | ||
if (isExpired) { | ||
// TBD: If it is expired, we do not send any events...we could send to | ||
// the expired session, but not sure if that's great | ||
console.error(new Error('Attempting to finish replay event after session expired.')); | ||
return; | ||
if (!this.checkAndHandleExpiredSession()) { | ||
logger.error(new Error('Attempting to finish replay event after session expired.')); | ||
} | ||
@@ -621,7 +678,7 @@ if (!this.session.id) { | ||
this.events = []; | ||
client = Sentry.getCurrentHub().getClient(); | ||
endpoint = SentryReplay.attachmentUrlFromDsn(client.getDsn(), eventId); | ||
_a.label = 1; | ||
case 1: | ||
_a.trys.push([1, 3, , 4]); | ||
client = Sentry.getCurrentHub().getClient(); | ||
endpoint = SentryReplay.attachmentUrlFromDsn(client.getDsn(), eventId); | ||
return [4 /*yield*/, this.sendReplayRequest(endpoint, events)]; | ||
@@ -628,0 +685,0 @@ case 2: |
@@ -372,2 +372,3 @@ 'use strict'; | ||
logger.log('Document has become active, but session has expired'); | ||
_this.loadSession({ expiry: VISIBILITY_CHANGE_TIMEOUT }); | ||
_this.triggerFullSnapshot(); | ||
@@ -402,4 +403,6 @@ } | ||
SentryReplay.attachmentUrlFromDsn = function (dsn, eventId) { | ||
var host = dsn.host, path = dsn.path, projectId = dsn.projectId, port = dsn.port, protocol = dsn.protocol, user = dsn.user; | ||
return "".concat(protocol, "://").concat(host).concat(port !== '' ? ":".concat(port) : '').concat(path !== '' ? "/".concat(path) : '', "/api/").concat(projectId, "/events/").concat(eventId, "/attachments/?sentry_key=").concat(user, "&sentry_version=7&sentry_client=replay"); | ||
var host = dsn.host, projectId = dsn.projectId, protocol = dsn.protocol, user = dsn.user; | ||
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"); | ||
}; | ||
@@ -445,9 +448,11 @@ SentryReplay.prototype.setupOnce = function () { | ||
} | ||
var uploadMaxDelayExceeded = isExpired(_this.initialEventTimestampSinceFlush, _this.options.uploadMaxDelay, now); | ||
// Do not finish the replay event if we receive a new replay event | ||
// unless `<uploadMaxDelay>` ms have elapsed since the last time we | ||
// finished the replay | ||
if (_this.timeout && !uploadMaxDelayExceeded) { | ||
if (_this.timeout) { | ||
window.clearTimeout(_this.timeout); | ||
} | ||
// If this is false, it means session is expired, create and a new session and wait for checkout | ||
if (!_this.checkAndHandleExpiredSession()) { | ||
logger.error(new Error('Received replay event after session expired.')); | ||
return; | ||
} | ||
// We need to clear existing events on a checkout, otherwise they are | ||
@@ -465,8 +470,15 @@ // incremental event updates and should be appended | ||
// A fullsnapshot happens on initial load and if we need to start a | ||
// new replay due to idle timeout. In the later case we will need to | ||
// create a new session before finishing the replay. | ||
_this.loadSession({ expiry: SESSION_IDLE_DURATION }); | ||
// new replay due to idle timeout. In the latter case, a new session *should* have been started | ||
// before triggering a new checkout | ||
_this.finishReplayEvent(); | ||
return; | ||
} | ||
var uploadMaxDelayExceeded = isExpired(_this.initialEventTimestampSinceFlush, _this.options.uploadMaxDelay, now); | ||
// If `uploadMaxDelayExceeded` is true, then we should finish the replay event immediately, | ||
// Otherwise schedule it to be finished in `this.options.uploadMinDelay` | ||
if (uploadMaxDelayExceeded) { | ||
logger.log('replay max delay exceeded, finishing replay event'); | ||
_this.finishReplayEvent(); | ||
return; | ||
} | ||
// Set timer to finish replay event and send replay attachment to | ||
@@ -476,3 +488,3 @@ // Sentry. Will be cancelled if an event happens before `uploadMinDelay` | ||
_this.timeout = window.setTimeout(function () { | ||
logger.log('rrweb timeout hit, finishing replay event'); | ||
logger.log('replay timeout exceeded, finishing replay event'); | ||
_this.finishReplayEvent(); | ||
@@ -482,3 +494,2 @@ }, _this.options.uploadMinDelay); | ||
this.addListeners(); | ||
// XXX: this needs to be in `setupOnce` vs `constructor`, otherwise SDK is | ||
// not fully initialized and the event will not get properly sent to Sentry | ||
@@ -488,2 +499,8 @@ this.createReplayEvent(); | ||
/** | ||
* Currently, this needs to be manually called (e.g. for tests). Sentry SDK does not support a teardown | ||
*/ | ||
SentryReplay.prototype.teardown = function () { | ||
this.removeListeners(); | ||
}; | ||
/** | ||
* Loads a session from storage, or creates a new one | ||
@@ -499,9 +516,31 @@ */ | ||
SentryReplay.prototype.addListeners = function () { | ||
var _this = this; | ||
document.addEventListener('visibilitychange', this.handleVisibilityChange); | ||
if ('PerformanceObserver' in window) { | ||
this.performanceObserver = new PerformanceObserver(this.handlePerformanceObserver); | ||
// Observe everything for now | ||
this.performanceObserver.observe({ | ||
entryTypes: __spreadArray([], PerformanceObserver.supportedEntryTypes, true), | ||
if (!('PerformanceObserver' in window)) { | ||
return; | ||
} | ||
this.performanceObserver = new PerformanceObserver(this.handlePerformanceObserver); | ||
// Observe almost everything for now (no mark/measure) | ||
[ | ||
'element', | ||
'event', | ||
'first-input', | ||
'largest-contentful-paint', | ||
'layout-shift', | ||
'longtask', | ||
'navigation', | ||
'paint', | ||
'resource', | ||
].forEach(function (type) { | ||
return _this.performanceObserver.observe({ | ||
type: type, | ||
buffered: true, | ||
}); | ||
}); | ||
}; | ||
SentryReplay.prototype.removeListeners = function () { | ||
document.removeEventListener('visibilitychange', this.handleVisibilityChange); | ||
if (this.performanceObserver) { | ||
this.performanceObserver.disconnect(); | ||
this.performanceObserver = null; | ||
} | ||
@@ -525,3 +564,3 @@ }; | ||
logger.log('CreateReplayEvent rootReplayId', this.session.id); | ||
this.replayEvent = Sentry__namespace.startTransaction({ | ||
this.replayEvent = Sentry__namespace.getCurrentHub().startTransaction({ | ||
name: REPLAY_EVENT_NAME, | ||
@@ -567,11 +606,29 @@ parentSpanId: this.session.spanId, | ||
}; | ||
/** | ||
* | ||
* | ||
* Returns true if session is not expired, false otherwise. | ||
*/ | ||
SentryReplay.prototype.checkAndHandleExpiredSession = function (expiry) { | ||
if (expiry === void 0) { expiry = SESSION_IDLE_DURATION; } | ||
var oldSessionId = this.session.id; | ||
// This will create a new session if expired, based on expiry length | ||
this.loadSession({ expiry: expiry }); | ||
// Session was expired if session ids do not match | ||
var isExpired = oldSessionId !== this.session.id; | ||
if (!isExpired) { | ||
return true; | ||
} | ||
// TODO: We could potentially figure out a way to save the last session, | ||
// and produce a checkout based on a previous checkout + updates, and then | ||
// replay the event on top. Or maybe replay the event on top of a refresh | ||
// snapshot. | ||
// For now create a new snapshot | ||
this.triggerFullSnapshot(); | ||
return false; | ||
}; | ||
SentryReplay.prototype.finishReplayEvent = function () { | ||
var _a; | ||
// Ensure that our existing session has not expired | ||
var isExpired = isSessionExpired(this.session, SESSION_IDLE_DURATION); | ||
if (isExpired) { | ||
// TBD: If it is expired, we do not send any events...we could send to | ||
// the expired session, but not sure if that's great | ||
console.error(new Error('Attempting to finish replay event after session expired.')); | ||
return; | ||
if (!this.checkAndHandleExpiredSession()) { | ||
logger.error(new Error('Attempting to finish replay event after session expired.')); | ||
} | ||
@@ -645,7 +702,7 @@ if (!this.session.id) { | ||
this.events = []; | ||
client = Sentry__namespace.getCurrentHub().getClient(); | ||
endpoint = SentryReplay.attachmentUrlFromDsn(client.getDsn(), eventId); | ||
_a.label = 1; | ||
case 1: | ||
_a.trys.push([1, 3, , 4]); | ||
client = Sentry__namespace.getCurrentHub().getClient(); | ||
endpoint = SentryReplay.attachmentUrlFromDsn(client.getDsn(), eventId); | ||
return [4 /*yield*/, this.sendReplayRequest(endpoint, events)]; | ||
@@ -652,0 +709,0 @@ case 2: |
{ | ||
"name": "@sentry/replay", | ||
"version": "0.2.0-6", | ||
"version": "0.2.0-7", | ||
"description": "User replays for Sentry", | ||
@@ -5,0 +5,0 @@ "main": "dist/index.js", |
Sorry, the diff of this file is not supported yet
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
145452
1596