Comparing version 0.0.10 to 0.0.11
@@ -1,3 +0,3 @@ | ||
export { initCrashDetection } from "./client-controller"; | ||
export { initClientWorker } from "./client-worker"; | ||
export { initDetectorWorker } from "./detector-worker"; | ||
export { initCrashDetection } from './init'; | ||
export { initClientWorker } from './init.client.worker'; | ||
export { initDetectorWorker } from './init.detector.worker'; |
426
dist/lib.js
@@ -15,6 +15,6 @@ (function webpackUniversalModuleDefinition(root, factory) { | ||
/***/ "./public/lib/client-controller.ts": | ||
/*!*****************************************!*\ | ||
!*** ./public/lib/client-controller.ts ***! | ||
\*****************************************/ | ||
/***/ "./public/lib/init.client.worker.ts": | ||
/*!******************************************!*\ | ||
!*** ./public/lib/init.client.worker.ts ***! | ||
\******************************************/ | ||
/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { | ||
@@ -24,162 +24,2 @@ | ||
/* harmony export */ __webpack_require__.d(__webpack_exports__, { | ||
/* harmony export */ initCrashDetection: () => (/* binding */ initCrashDetection) | ||
/* harmony export */ }); | ||
/* harmony import */ var _utils__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./utils */ "./public/lib/utils.ts"); | ||
// @ts-nocheck | ||
var __awaiter = (undefined && undefined.__awaiter) || function (thisArg, _arguments, P, generator) { | ||
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } | ||
return new (P || (P = Promise))(function (resolve, reject) { | ||
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } | ||
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } | ||
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } | ||
step((generator = generator.apply(thisArg, _arguments || [])).next()); | ||
}); | ||
}; | ||
var __generator = (undefined && undefined.__generator) || function (thisArg, body) { | ||
var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g = Object.create((typeof Iterator === "function" ? Iterator : Object).prototype); | ||
return g.next = verb(0), g["throw"] = verb(1), g["return"] = verb(2), typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g; | ||
function verb(n) { return function (v) { return step([n, v]); }; } | ||
function step(op) { | ||
if (f) throw new TypeError("Generator is already executing."); | ||
while (g && (g = 0, op[0] && (_ = 0)), _) try { | ||
if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t; | ||
if (y = 0, t) op = [op[0] & 2, t.value]; | ||
switch (op[0]) { | ||
case 0: case 1: t = op; break; | ||
case 4: _.label++; return { value: op[1], done: false }; | ||
case 5: _.label++; y = op[1]; op = [0]; continue; | ||
case 7: op = _.ops.pop(); _.trys.pop(); continue; | ||
default: | ||
if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; } | ||
if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; } | ||
if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; } | ||
if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; } | ||
if (t[2]) _.ops.pop(); | ||
_.trys.pop(); continue; | ||
} | ||
op = body.call(thisArg, _); | ||
} catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; } | ||
if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true }; | ||
} | ||
}; | ||
function initCrashDetection(options) { | ||
var worker; | ||
var detector; | ||
var info = {}; | ||
var db; | ||
var log = function (log) { | ||
var _a; | ||
log.id = info.id; | ||
(_a = options.log) === null || _a === void 0 ? void 0 : _a.call(options, log); | ||
}; | ||
function handleDetectorMessage(event) { | ||
return __awaiter(this, void 0, void 0, function () { | ||
var tab, success; | ||
return __generator(this, function (_a) { | ||
switch (_a.label) { | ||
case 0: | ||
if (!(event.data.event === 'crash-detected' && event.data.reporter.id === info.id)) return [3 /*break*/, 2]; | ||
log({ event: 'crash-detected' }); | ||
tab = event.data.tab; | ||
return [4 /*yield*/, options.reportCrash(tab)]; | ||
case 1: | ||
success = _a.sent(); | ||
log({ event: 'crash-reported', success: success }); | ||
if (success) { | ||
log({ event: 'crash-report-confirmed' }); | ||
detector.port.postMessage({ event: 'crash-reported', id: event.data.tab.id }); | ||
} | ||
_a.label = 2; | ||
case 2: return [2 /*return*/]; | ||
} | ||
}); | ||
}); | ||
} | ||
/** | ||
* Generate id for the tab | ||
*/ | ||
function initialize() { | ||
info.id = options.id; | ||
info.tabFirstActive = Date.now(); | ||
} | ||
/** | ||
* Update latest state of the tab | ||
*/ | ||
function updateInfo() { | ||
options.updateInfo(info); | ||
log({ event: 'updated' }); | ||
worker.postMessage({ | ||
event: 'update', | ||
info: info, | ||
}); | ||
} | ||
function registerWorkers() { | ||
worker = options.createClientWorker(); | ||
worker.addEventListener('message', updateInfo); | ||
detector = options.createDetectorWorker(); | ||
detector.port.addEventListener('message', handleDetectorMessage); | ||
detector.port.start(); | ||
} | ||
function unregisterWorkers() { | ||
worker.removeEventListener('message', updateInfo); | ||
detector.port.removeEventListener('message', handleDetectorMessage); | ||
} | ||
function startWhenReady() { | ||
// beforeunload is triggered only after at least one interaction | ||
log({ event: 'loaded' }); | ||
window.addEventListener('click', start); | ||
} | ||
function start() { | ||
return __awaiter(this, void 0, void 0, function () { | ||
return __generator(this, function (_a) { | ||
log({ event: 'started' }); | ||
window.removeEventListener('click', start); | ||
initialize(); | ||
registerWorkers(); | ||
window.addEventListener('beforeunload', function () { | ||
log({ event: 'unloaded' }); | ||
// to avoid any delays clean-up happens in the current tab as well | ||
var transaction = db.transaction(['tabs'], 'readwrite'); | ||
var store = transaction.objectStore('tabs'); | ||
store.delete(info.id); | ||
worker.postMessage({ | ||
event: 'close', | ||
info: info, | ||
}); | ||
unregisterWorkers(); | ||
log({ event: 'unloaded-done' }); | ||
}); | ||
worker.postMessage({ | ||
event: 'start', | ||
info: info, | ||
}); | ||
log({ event: 'started-done' }); | ||
return [2 /*return*/]; | ||
}); | ||
}); | ||
} | ||
// main entry point | ||
(0,_utils__WEBPACK_IMPORTED_MODULE_0__.getDb)(options.dbName).then(function (result) { return db = result; }); | ||
if (document.readyState === 'complete') { | ||
startWhenReady(); | ||
} | ||
else { | ||
window.addEventListener('load', function () { | ||
startWhenReady(); | ||
}); | ||
} | ||
} | ||
/***/ }), | ||
/***/ "./public/lib/client-worker.ts": | ||
/*!*************************************!*\ | ||
!*** ./public/lib/client-worker.ts ***! | ||
\*************************************/ | ||
/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { | ||
__webpack_require__.r(__webpack_exports__); | ||
/* harmony export */ __webpack_require__.d(__webpack_exports__, { | ||
/* harmony export */ initClientWorker: () => (/* binding */ initClientWorker) | ||
@@ -237,2 +77,11 @@ /* harmony export */ }); | ||
/** | ||
* Main logic of the Web Worker running with the tab. Create a separate file for the worker with code: | ||
* | ||
* initClientWorker({ | ||
* dbName: 'NAME OF YOUR DB', | ||
* pingInterval: 1000, | ||
* }); | ||
* | ||
*/ | ||
function initClientWorker(options) { | ||
@@ -290,6 +139,6 @@ var _this = this; | ||
/***/ "./public/lib/detector-worker.ts": | ||
/*!***************************************!*\ | ||
!*** ./public/lib/detector-worker.ts ***! | ||
\***************************************/ | ||
/***/ "./public/lib/init.detector.worker.ts": | ||
/*!********************************************!*\ | ||
!*** ./public/lib/init.detector.worker.ts ***! | ||
\********************************************/ | ||
/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { | ||
@@ -303,2 +152,13 @@ | ||
// @ts-nocheck | ||
var __assign = (undefined && undefined.__assign) || function () { | ||
__assign = Object.assign || function(t) { | ||
for (var s, i = 1, n = arguments.length; i < n; i++) { | ||
s = arguments[i]; | ||
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) | ||
t[p] = s[p]; | ||
} | ||
return t; | ||
}; | ||
return __assign.apply(this, arguments); | ||
}; | ||
var __awaiter = (undefined && undefined.__awaiter) || function (thisArg, _arguments, P, generator) { | ||
@@ -341,2 +201,13 @@ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } | ||
/** | ||
* Main logic of the Shared Worker responsible for detecting crashes. Create a separate file for the worker with code: | ||
* | ||
* initDetectorWorker({ | ||
* dbName: 'NAME OF YOUR DB', | ||
* staleThreshold: 60000, | ||
* crashThreshold: 5000, | ||
* interval: 5000, | ||
* }); | ||
* | ||
*/ | ||
function initDetectorWorker(options) { | ||
@@ -352,4 +223,8 @@ var db; | ||
} | ||
if (event.data.event === 'stale-tab-reported' && event.data.tab) { | ||
var transaction = db.transaction(['tabs'], 'readwrite'); | ||
var store = transaction.objectStore('tabs'); | ||
store.store(__assign(__assign({}, event.data.tab), { staleReported: true })); | ||
} | ||
} | ||
var INACTIVITY_THRESHOLD = options.inactivityThreshold; | ||
/** | ||
@@ -366,7 +241,12 @@ * Check which tabs have stopped sending updates but did not clear themselves properly | ||
var inactiveTabs = []; | ||
var staleTabs = []; | ||
tabs.forEach(function (tab) { | ||
var workerInactivity = Date.now() - tab.workerLastActive; | ||
if (workerInactivity > INACTIVITY_THRESHOLD) { | ||
var tabInactivity = Date.now() - tab.tabLastActive; | ||
if (workerInactivity > options.crashThreshold) { | ||
inactiveTabs.push(tab); | ||
} | ||
else if (tabInactivity > options.staleThreshold && !tab.staleReported) { | ||
staleTabs.push(tab); | ||
} | ||
else { | ||
@@ -382,10 +262,12 @@ activeTabs.push(tab); | ||
var reporter = activeTabs.pop(); | ||
tabs.forEach(function (tab) { | ||
var workerInactivity = Date.now() - tab.workerLastActive; | ||
if (workerInactivity > INACTIVITY_THRESHOLD) { | ||
openPorts.forEach(function (port) { | ||
port.postMessage({ event: 'crash-detected', tab: tab, reporter: reporter }); | ||
}); | ||
} | ||
inactiveTabs.forEach(function (tab) { | ||
openPorts.forEach(function (port) { | ||
port.postMessage({ event: 'crash-detected', tab: tab, reporter: reporter }); | ||
}); | ||
}); | ||
staleTabs.forEach(function (tab) { | ||
openPorts.forEach(function (port) { | ||
port.postMessage({ event: 'stale-tab-detected', tab: tab, reporter: reporter }); | ||
}); | ||
}); | ||
}; | ||
@@ -410,3 +292,3 @@ } | ||
port_1.start(); | ||
setInterval(checkStaleTabs, INACTIVITY_THRESHOLD); | ||
setInterval(checkStaleTabs, options.interval); | ||
started = true; | ||
@@ -424,2 +306,178 @@ _a.label = 2; | ||
/***/ "./public/lib/init.ts": | ||
/*!****************************!*\ | ||
!*** ./public/lib/init.ts ***! | ||
\****************************/ | ||
/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { | ||
__webpack_require__.r(__webpack_exports__); | ||
/* harmony export */ __webpack_require__.d(__webpack_exports__, { | ||
/* harmony export */ initCrashDetection: () => (/* binding */ initCrashDetection) | ||
/* harmony export */ }); | ||
/* harmony import */ var _utils__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./utils */ "./public/lib/utils.ts"); | ||
// @ts-nocheck | ||
var __awaiter = (undefined && undefined.__awaiter) || function (thisArg, _arguments, P, generator) { | ||
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } | ||
return new (P || (P = Promise))(function (resolve, reject) { | ||
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } | ||
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } | ||
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } | ||
step((generator = generator.apply(thisArg, _arguments || [])).next()); | ||
}); | ||
}; | ||
var __generator = (undefined && undefined.__generator) || function (thisArg, body) { | ||
var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g = Object.create((typeof Iterator === "function" ? Iterator : Object).prototype); | ||
return g.next = verb(0), g["throw"] = verb(1), g["return"] = verb(2), typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g; | ||
function verb(n) { return function (v) { return step([n, v]); }; } | ||
function step(op) { | ||
if (f) throw new TypeError("Generator is already executing."); | ||
while (g && (g = 0, op[0] && (_ = 0)), _) try { | ||
if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t; | ||
if (y = 0, t) op = [op[0] & 2, t.value]; | ||
switch (op[0]) { | ||
case 0: case 1: t = op; break; | ||
case 4: _.label++; return { value: op[1], done: false }; | ||
case 5: _.label++; y = op[1]; op = [0]; continue; | ||
case 7: op = _.ops.pop(); _.trys.pop(); continue; | ||
default: | ||
if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; } | ||
if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; } | ||
if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; } | ||
if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; } | ||
if (t[2]) _.ops.pop(); | ||
_.trys.pop(); continue; | ||
} | ||
op = body.call(thisArg, _); | ||
} catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; } | ||
if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true }; | ||
} | ||
}; | ||
/** | ||
* Main function to initialize crash detection. This should be run from the main thread of the tab. | ||
*/ | ||
function initCrashDetection(options) { | ||
var worker; | ||
var detector; | ||
var info = {}; | ||
var db; | ||
var log = function (log) { | ||
var _a; | ||
log.id = info.id; | ||
(_a = options.log) === null || _a === void 0 ? void 0 : _a.call(options, log); | ||
}; | ||
function handleDetectorMessage(event) { | ||
return __awaiter(this, void 0, void 0, function () { | ||
var tab, success, tab, success; | ||
return __generator(this, function (_a) { | ||
switch (_a.label) { | ||
case 0: | ||
if (!(event.data.event === 'crash-detected' && event.data.reporter.id === info.id)) return [3 /*break*/, 2]; | ||
log({ event: 'crash-detected' }); | ||
tab = event.data.tab; | ||
return [4 /*yield*/, options.reportCrash(tab)]; | ||
case 1: | ||
success = _a.sent(); | ||
log({ event: 'crash-reported', success: success }); | ||
if (success) { | ||
log({ event: 'crash-report-confirmed' }); | ||
detector.port.postMessage({ event: 'crash-reported', id: event.data.tab.id }); | ||
} | ||
_a.label = 2; | ||
case 2: | ||
if (!(options.reportStaleTab && event.data.event === 'stale-tab-detected' && event.data.reporter.id === info.id)) return [3 /*break*/, 4]; | ||
log({ event: 'stale-tab-detected' }); | ||
tab = event.data.tab; | ||
return [4 /*yield*/, options.reportStaleTab(tab)]; | ||
case 3: | ||
success = _a.sent(); | ||
log({ event: 'stale-tab-reported', success: success }); | ||
if (success) { | ||
log({ event: 'stale-tab-confirmed' }); | ||
detector.port.postMessage({ event: 'stale-tab-reported', id: event.data.tab.id }); | ||
} | ||
_a.label = 4; | ||
case 4: return [2 /*return*/]; | ||
} | ||
}); | ||
}); | ||
} | ||
/** | ||
* Generate id for the tab | ||
*/ | ||
function initialize() { | ||
info.id = options.id; | ||
info.tabFirstActive = Date.now(); | ||
} | ||
/** | ||
* Update latest state of the tab | ||
*/ | ||
function updateInfo() { | ||
options.updateInfo(info); | ||
//log({ event: 'updated' }); | ||
worker.postMessage({ | ||
event: 'update', | ||
info: info, | ||
}); | ||
} | ||
function registerWorkers() { | ||
worker = options.createClientWorker(); | ||
worker.addEventListener('message', updateInfo); | ||
detector = options.createDetectorWorker(); | ||
detector.port.addEventListener('message', handleDetectorMessage); | ||
detector.port.start(); | ||
} | ||
function unregisterWorkers() { | ||
worker.removeEventListener('message', updateInfo); | ||
detector.port.removeEventListener('message', handleDetectorMessage); | ||
} | ||
function startWhenReady() { | ||
// beforeunload is triggered only after at least one interaction | ||
log({ event: 'loaded' }); | ||
window.addEventListener('click', start); | ||
} | ||
function start() { | ||
return __awaiter(this, void 0, void 0, function () { | ||
return __generator(this, function (_a) { | ||
log({ event: 'started' }); | ||
window.removeEventListener('click', start); | ||
initialize(); | ||
registerWorkers(); | ||
window.addEventListener('beforeunload', function () { | ||
log({ event: 'unloaded' }); | ||
// to avoid any delays clean-up happens in the current tab as well | ||
var transaction = db.transaction(['tabs'], 'readwrite'); | ||
var store = transaction.objectStore('tabs'); | ||
store.delete(info.id); | ||
worker.postMessage({ | ||
event: 'close', | ||
info: info, | ||
}); | ||
unregisterWorkers(); | ||
log({ event: 'unloaded-done' }); | ||
}); | ||
worker.postMessage({ | ||
event: 'start', | ||
info: info, | ||
}); | ||
log({ event: 'started-done' }); | ||
return [2 /*return*/]; | ||
}); | ||
}); | ||
} | ||
// main entry point | ||
(0,_utils__WEBPACK_IMPORTED_MODULE_0__.getDb)(options.dbName).then(function (result) { return (db = result); }); | ||
if (document.readyState === 'complete') { | ||
startWhenReady(); | ||
} | ||
else { | ||
window.addEventListener('load', function () { | ||
startWhenReady(); | ||
}); | ||
} | ||
} | ||
/***/ }), | ||
/***/ "./public/lib/utils.ts": | ||
@@ -566,9 +624,9 @@ /*!*****************************!*\ | ||
/* harmony export */ __webpack_require__.d(__webpack_exports__, { | ||
/* harmony export */ initClientWorker: () => (/* reexport safe */ _client_worker__WEBPACK_IMPORTED_MODULE_1__.initClientWorker), | ||
/* harmony export */ initCrashDetection: () => (/* reexport safe */ _client_controller__WEBPACK_IMPORTED_MODULE_0__.initCrashDetection), | ||
/* harmony export */ initDetectorWorker: () => (/* reexport safe */ _detector_worker__WEBPACK_IMPORTED_MODULE_2__.initDetectorWorker) | ||
/* harmony export */ initClientWorker: () => (/* reexport safe */ _init_client_worker__WEBPACK_IMPORTED_MODULE_1__.initClientWorker), | ||
/* harmony export */ initCrashDetection: () => (/* reexport safe */ _init__WEBPACK_IMPORTED_MODULE_0__.initCrashDetection), | ||
/* harmony export */ initDetectorWorker: () => (/* reexport safe */ _init_detector_worker__WEBPACK_IMPORTED_MODULE_2__.initDetectorWorker) | ||
/* harmony export */ }); | ||
/* harmony import */ var _client_controller__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./client-controller */ "./public/lib/client-controller.ts"); | ||
/* harmony import */ var _client_worker__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./client-worker */ "./public/lib/client-worker.ts"); | ||
/* harmony import */ var _detector_worker__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./detector-worker */ "./public/lib/detector-worker.ts"); | ||
/* harmony import */ var _init__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./init */ "./public/lib/init.ts"); | ||
/* harmony import */ var _init_client_worker__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./init.client.worker */ "./public/lib/init.client.worker.ts"); | ||
/* harmony import */ var _init_detector_worker__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./init.detector.worker */ "./public/lib/init.detector.worker.ts"); | ||
@@ -575,0 +633,0 @@ |
{ | ||
"name": "crashme", | ||
"version": "0.0.10", | ||
"version": "0.0.11", | ||
"main": "dist/lib.js", | ||
@@ -18,2 +18,3 @@ "types": "dist/index.d.ts", | ||
"express": "^4.21.0", | ||
"prettier": "3.3.3", | ||
"ts-loader": "^9.5.1", | ||
@@ -20,0 +21,0 @@ "typescript": "^5.6.3", |
@@ -13,3 +13,3 @@ # Detecting Browser/Tab crashes POC | ||
6. Once a crash is detected it will be sent to the server and stored in local memory | ||
7. http://localhost:1234 will show crashes that were reported | ||
7. http://localhost:1234 will show crashes that were reported | ||
@@ -27,3 +27,3 @@ ## Resources | ||
### Detecting crashes before they occur | ||
### Detecting crashes before they occur | ||
@@ -35,7 +35,9 @@ The idea was to check if the page becomes unresponsive or is very close to hitting memory limits and report it over HTTP to persisted storage. | ||
1. Memory usage can be checked with `window.performance.memory` | ||
- Pros: | ||
- It provides total JS heap size, used heap size and the limit | ||
- Cons: | ||
- Browser may dynamically change limits and allocate additional memory | ||
- Available only in Chrome | ||
- Pros: | ||
- It provides total JS heap size, used heap size and the limit | ||
- Cons: | ||
- Browser may dynamically change limits and allocate additional memory | ||
- Available only in Chrome | ||
2. When browser slows down. This could be checked by a ping mechanism using web/service workers. | ||
@@ -68,6 +70,6 @@ - Pros: | ||
- Browser session storage | ||
- cons: used by Sentry (see resources) but it's isolated to a single tab so to make it work it would require user to refresh the tab (not close it) after the crash which may not happen every time (https://developer.mozilla.org/en-US/docs/Web/API/Window/sessionStorage) | ||
- cons: used by Sentry (see resources) but it's isolated to a single tab so to make it work it would require user to refresh the tab (not close it) after the crash which may not happen every time (https://developer.mozilla.org/en-US/docs/Web/API/Window/sessionStorage) | ||
- Browser indexed db | ||
- pros: can be shared between workers and allows transactional updates | ||
- External storage (over HTTP): | ||
- pros: can be shared between workers and allows transactional updates | ||
- External storage (over HTTP): | ||
- cons: will stop reporting when the user if offline though the tab may not crashed | ||
@@ -88,5 +90,5 @@ | ||
- save inside the tab thread | ||
- cons: debugging the page will stop the thread and detector could say it crashed | ||
- cons: debugging the page will stop the thread and detector could say it crashed | ||
- save inside service/web worker | ||
- pros: worker will keep working even if the tab is paused; | ||
- pros: worker will keep working even if the tab is paused; | ||
@@ -103,3 +105,3 @@ Choice: Save inside a web worker | ||
### | ||
### | ||
@@ -128,4 +130,4 @@ ```mermaid | ||
4. WebWorker save the data with tabLastActive timestamps to the IndexedDB when receives a message from the tab. Saving is done in the worker to ensure it's a separate thread in cases the Client thread is paused because of debugging. | ||
4. WebWorker saves workerLastActive timestamp every second | ||
5. When Client is unloaded properly it sends the message to the WebWorker to remove the entry from IndexedDb | ||
5. WebWorker saves workerLastActive timestamp every second | ||
6. When Client is unloaded properly it sends the message to the WebWorker to remove the entry from IndexedDb | ||
@@ -143,2 +145,2 @@ A separate process check for stale tabs and reports back to the backend. It connects to the same IndexedDB | ||
workerLastActive timestamp is used to detect actual crash of a tab and tabLastActive is used for reporting. They may be out of step in Firefox which keeps the worked active after the tab crashes OR when thread on the tab is paused due to debugging (web worker will keep running) | ||
workerLastActive timestamp is used to detect actual crash of a tab and tabLastActive is used for reporting. They may be out of step in Firefox which keeps the worked active after the tab crashes OR when thread on the tab is paused due to debugging (web worker will keep running) |
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
73090
10
711
139
7