Files
FrenoCorp/node_modules/browser-tabs-lock/index.js
Michael Freno 7c684a42cc FRE-600: Fix code review blockers
- Consolidated duplicate UndoManagers to single instance
- Fixed connection promise to only resolve on 'connected' status
- Fixed WebSocketProvider import (WebsocketProvider)
- Added proper doc.destroy() cleanup
- Renamed isPresenceInitialized property to avoid conflict

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-04-25 00:08:01 -04:00

401 lines
19 KiB
JavaScript

"use strict";
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
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) : new P(function (resolve) { resolve(result.value); }).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
var __generator = (this && this.__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;
return g = { next: verb(0), "throw": verb(1), "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 (_) 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 };
}
};
var _this = this;
Object.defineProperty(exports, "__esModule", { value: true });
var processLock_1 = require("./processLock");
/**
* @author: SuperTokens (https://github.com/supertokens)
* This library was created as a part of a larger project, SuperTokens(https://supertokens.io) - the best session management solution.
* You can also check out our other projects on https://github.com/supertokens
*
* To contribute to this package visit https://github.com/supertokens/browser-tabs-lock
* If you face any problems you can file an issue on https://github.com/supertokens/browser-tabs-lock/issues
*
* If you have any questions or if you just want to say hi visit https://supertokens.io/discord
*/
/**
* @constant
* @type {string}
* @default
* @description All the locks taken by this package will have this as prefix
*/
var LOCK_STORAGE_KEY = 'browser-tabs-lock-key';
var DEFAULT_STORAGE_HANDLER = {
key: function (index) { return __awaiter(_this, void 0, void 0, function () {
return __generator(this, function (_a) {
throw new Error("Unsupported");
});
}); },
getItem: function (key) { return __awaiter(_this, void 0, void 0, function () {
return __generator(this, function (_a) {
throw new Error("Unsupported");
});
}); },
clear: function () { return __awaiter(_this, void 0, void 0, function () {
return __generator(this, function (_a) {
return [2 /*return*/, window.localStorage.clear()];
});
}); },
removeItem: function (key) { return __awaiter(_this, void 0, void 0, function () {
return __generator(this, function (_a) {
throw new Error("Unsupported");
});
}); },
setItem: function (key, value) { return __awaiter(_this, void 0, void 0, function () {
return __generator(this, function (_a) {
throw new Error("Unsupported");
});
}); },
keySync: function (index) {
return window.localStorage.key(index);
},
getItemSync: function (key) {
return window.localStorage.getItem(key);
},
clearSync: function () {
return window.localStorage.clear();
},
removeItemSync: function (key) {
return window.localStorage.removeItem(key);
},
setItemSync: function (key, value) {
return window.localStorage.setItem(key, value);
},
};
/**
* @function delay
* @param {number} milliseconds - How long the delay should be in terms of milliseconds
* @returns {Promise<void>}
*/
function delay(milliseconds) {
return new Promise(function (resolve) { return setTimeout(resolve, milliseconds); });
}
/**
* @function generateRandomString
* @params {number} length - How long the random string should be
* @returns {string}
* @description returns random string whose length is equal to the length passed as parameter
*/
function generateRandomString(length) {
var CHARS = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXTZabcdefghiklmnopqrstuvwxyz';
var randomstring = '';
for (var i = 0; i < length; i++) {
var INDEX = Math.floor(Math.random() * CHARS.length);
randomstring += CHARS[INDEX];
}
return randomstring;
}
/**
* @function getLockId
* @returns {string}
* @description Generates an id which will be unique for the browser tab
*/
function getLockId() {
return Date.now().toString() + generateRandomString(15);
}
var SuperTokensLock = /** @class */ (function () {
function SuperTokensLock(storageHandler) {
this.acquiredIatSet = new Set();
this.storageHandler = undefined;
this.id = getLockId();
this.acquireLock = this.acquireLock.bind(this);
this.releaseLock = this.releaseLock.bind(this);
this.releaseLock__private__ = this.releaseLock__private__.bind(this);
this.waitForSomethingToChange = this.waitForSomethingToChange.bind(this);
this.refreshLockWhileAcquired = this.refreshLockWhileAcquired.bind(this);
this.storageHandler = storageHandler;
if (SuperTokensLock.waiters === undefined) {
SuperTokensLock.waiters = [];
}
}
/**
* @async
* @memberOf Lock
* @function acquireLock
* @param {string} lockKey - Key for which the lock is being acquired
* @param {number} [timeout=5000] - Maximum time for which the function will wait to acquire the lock
* @returns {Promise<boolean>}
* @description Will return true if lock is being acquired, else false.
* Also the lock can be acquired for maximum 10 secs
*/
SuperTokensLock.prototype.acquireLock = function (lockKey, timeout) {
if (timeout === void 0) { timeout = 5000; }
return __awaiter(this, void 0, void 0, function () {
var iat, MAX_TIME, STORAGE_KEY, STORAGE, lockObj, TIMEOUT_KEY, lockObjPostDelay, parsedLockObjPostDelay;
return __generator(this, function (_a) {
switch (_a.label) {
case 0:
iat = Date.now() + generateRandomString(4);
MAX_TIME = Date.now() + timeout;
STORAGE_KEY = LOCK_STORAGE_KEY + "-" + lockKey;
STORAGE = this.storageHandler === undefined ? DEFAULT_STORAGE_HANDLER : this.storageHandler;
_a.label = 1;
case 1:
if (!(Date.now() < MAX_TIME)) return [3 /*break*/, 8];
return [4 /*yield*/, delay(30)];
case 2:
_a.sent();
lockObj = STORAGE.getItemSync(STORAGE_KEY);
if (!(lockObj === null)) return [3 /*break*/, 5];
TIMEOUT_KEY = this.id + "-" + lockKey + "-" + iat;
// there is a problem if setItem happens at the exact same time for 2 different processes.. so we add some random delay here.
return [4 /*yield*/, delay(Math.floor(Math.random() * 25))];
case 3:
// there is a problem if setItem happens at the exact same time for 2 different processes.. so we add some random delay here.
_a.sent();
STORAGE.setItemSync(STORAGE_KEY, JSON.stringify({
id: this.id,
iat: iat,
timeoutKey: TIMEOUT_KEY,
timeAcquired: Date.now(),
timeRefreshed: Date.now()
}));
return [4 /*yield*/, delay(30)];
case 4:
_a.sent(); // this is to prevent race conditions. This time must be more than the time it takes for storage.setItem
lockObjPostDelay = STORAGE.getItemSync(STORAGE_KEY);
if (lockObjPostDelay !== null) {
parsedLockObjPostDelay = JSON.parse(lockObjPostDelay);
if (parsedLockObjPostDelay.id === this.id && parsedLockObjPostDelay.iat === iat) {
this.acquiredIatSet.add(iat);
this.refreshLockWhileAcquired(STORAGE_KEY, iat);
return [2 /*return*/, true];
}
}
return [3 /*break*/, 7];
case 5:
SuperTokensLock.lockCorrector(this.storageHandler === undefined ? DEFAULT_STORAGE_HANDLER : this.storageHandler);
return [4 /*yield*/, this.waitForSomethingToChange(MAX_TIME)];
case 6:
_a.sent();
_a.label = 7;
case 7:
iat = Date.now() + generateRandomString(4);
return [3 /*break*/, 1];
case 8: return [2 /*return*/, false];
}
});
});
};
SuperTokensLock.prototype.refreshLockWhileAcquired = function (storageKey, iat) {
return __awaiter(this, void 0, void 0, function () {
var _this = this;
return __generator(this, function (_a) {
setTimeout(function () { return __awaiter(_this, void 0, void 0, function () {
var STORAGE, lockObj, parsedLockObj;
return __generator(this, function (_a) {
switch (_a.label) {
case 0: return [4 /*yield*/, processLock_1.default().lock(iat)];
case 1:
_a.sent();
if (!this.acquiredIatSet.has(iat)) {
processLock_1.default().unlock(iat);
return [2 /*return*/];
}
STORAGE = this.storageHandler === undefined ? DEFAULT_STORAGE_HANDLER : this.storageHandler;
lockObj = STORAGE.getItemSync(storageKey);
if (lockObj !== null) {
parsedLockObj = JSON.parse(lockObj);
parsedLockObj.timeRefreshed = Date.now();
STORAGE.setItemSync(storageKey, JSON.stringify(parsedLockObj));
processLock_1.default().unlock(iat);
}
else {
processLock_1.default().unlock(iat);
return [2 /*return*/];
}
this.refreshLockWhileAcquired(storageKey, iat);
return [2 /*return*/];
}
});
}); }, 1000);
return [2 /*return*/];
});
});
};
SuperTokensLock.prototype.waitForSomethingToChange = function (MAX_TIME) {
return __awaiter(this, void 0, void 0, function () {
return __generator(this, function (_a) {
switch (_a.label) {
case 0: return [4 /*yield*/, new Promise(function (resolve) {
var resolvedCalled = false;
var startedAt = Date.now();
var MIN_TIME_TO_WAIT = 50; // ms
var removedListeners = false;
function stopWaiting() {
if (!removedListeners) {
window.removeEventListener('storage', stopWaiting);
SuperTokensLock.removeFromWaiting(stopWaiting);
clearTimeout(timeOutId);
removedListeners = true;
}
if (!resolvedCalled) {
resolvedCalled = true;
var timeToWait = MIN_TIME_TO_WAIT - (Date.now() - startedAt);
if (timeToWait > 0) {
setTimeout(resolve, timeToWait);
}
else {
resolve(null);
}
}
}
window.addEventListener('storage', stopWaiting);
SuperTokensLock.addToWaiting(stopWaiting);
var timeOutId = setTimeout(stopWaiting, Math.max(0, MAX_TIME - Date.now()));
})];
case 1:
_a.sent();
return [2 /*return*/];
}
});
});
};
SuperTokensLock.addToWaiting = function (func) {
this.removeFromWaiting(func);
if (SuperTokensLock.waiters === undefined) {
return;
}
SuperTokensLock.waiters.push(func);
};
SuperTokensLock.removeFromWaiting = function (func) {
if (SuperTokensLock.waiters === undefined) {
return;
}
SuperTokensLock.waiters = SuperTokensLock.waiters.filter(function (i) { return i !== func; });
};
SuperTokensLock.notifyWaiters = function () {
if (SuperTokensLock.waiters === undefined) {
return;
}
var waiters = SuperTokensLock.waiters.slice(); // so that if Lock.waiters is changed it's ok.
waiters.forEach(function (i) { return i(); });
};
/**
* @function releaseLock
* @memberOf Lock
* @param {string} lockKey - Key for which lock is being released
* @returns {void}
* @description Release a lock.
*/
SuperTokensLock.prototype.releaseLock = function (lockKey) {
return __awaiter(this, void 0, void 0, function () {
return __generator(this, function (_a) {
switch (_a.label) {
case 0: return [4 /*yield*/, this.releaseLock__private__(lockKey)];
case 1: return [2 /*return*/, _a.sent()];
}
});
});
};
/**
* @function releaseLock
* @memberOf Lock
* @param {string} lockKey - Key for which lock is being released
* @returns {void}
* @description Release a lock.
*/
SuperTokensLock.prototype.releaseLock__private__ = function (lockKey) {
return __awaiter(this, void 0, void 0, function () {
var STORAGE, STORAGE_KEY, lockObj, parsedlockObj;
return __generator(this, function (_a) {
switch (_a.label) {
case 0:
STORAGE = this.storageHandler === undefined ? DEFAULT_STORAGE_HANDLER : this.storageHandler;
STORAGE_KEY = LOCK_STORAGE_KEY + "-" + lockKey;
lockObj = STORAGE.getItemSync(STORAGE_KEY);
if (lockObj === null) {
return [2 /*return*/];
}
parsedlockObj = JSON.parse(lockObj);
if (!(parsedlockObj.id === this.id)) return [3 /*break*/, 2];
return [4 /*yield*/, processLock_1.default().lock(parsedlockObj.iat)];
case 1:
_a.sent();
this.acquiredIatSet.delete(parsedlockObj.iat);
STORAGE.removeItemSync(STORAGE_KEY);
processLock_1.default().unlock(parsedlockObj.iat);
SuperTokensLock.notifyWaiters();
_a.label = 2;
case 2: return [2 /*return*/];
}
});
});
};
/**
* @function lockCorrector
* @returns {void}
* @description If a lock is acquired by a tab and the tab is closed before the lock is
* released, this function will release those locks
*/
SuperTokensLock.lockCorrector = function (storageHandler) {
var MIN_ALLOWED_TIME = Date.now() - 5000;
var STORAGE = storageHandler;
var KEYS = [];
var currIndex = 0;
while (true) {
var key = STORAGE.keySync(currIndex);
if (key === null) {
break;
}
KEYS.push(key);
currIndex++;
}
var notifyWaiters = false;
for (var i = 0; i < KEYS.length; i++) {
var LOCK_KEY = KEYS[i];
if (LOCK_KEY.includes(LOCK_STORAGE_KEY)) {
var lockObj = STORAGE.getItemSync(LOCK_KEY);
if (lockObj !== null) {
var parsedlockObj = JSON.parse(lockObj);
if ((parsedlockObj.timeRefreshed === undefined && parsedlockObj.timeAcquired < MIN_ALLOWED_TIME) ||
(parsedlockObj.timeRefreshed !== undefined && parsedlockObj.timeRefreshed < MIN_ALLOWED_TIME)) {
STORAGE.removeItemSync(LOCK_KEY);
notifyWaiters = true;
}
}
}
}
if (notifyWaiters) {
SuperTokensLock.notifyWaiters();
}
};
SuperTokensLock.waiters = undefined;
return SuperTokensLock;
}());
exports.default = SuperTokensLock;