没绑定过手机号的 允许会话十次,弹出验证码弹窗
This commit is contained in:
160
index.html
160
index.html
@@ -7,12 +7,11 @@
|
||||
<meta http-equiv="Cache-Control" content="no-store, no-cache, must-revalidate">
|
||||
<meta http-equiv="Pragma" content="no-cache">
|
||||
<meta http-equiv="Expires" content="Thu, 01 Jan 1970 00:00:00 GMT">
|
||||
<meta property=" og:title" content="sata光伏智能体">
|
||||
<meta property="og:description"
|
||||
content="SataBot Agent 是一款由人工智能驱动的光伏场景AI应用程序。SataBot Agent AI 能力基于飒沓自研大模型的 AIGC 服务构建。">
|
||||
<meta property=" og:title" content="飒沓光伏智能体">
|
||||
<meta property="og:description" content="SataBot Agent 是一款由人工智能驱动的光伏场景AI应用程序。">
|
||||
<meta property="og:image" content="http://1.95.137.212:19000/mbzn/16%3A33%3A49123.png">
|
||||
|
||||
<title>sata光伏智能体</title>
|
||||
<title>飒沓光伏智能体</title>
|
||||
<link rel="icon" href="./img/logo.png" type="image/x-icon">
|
||||
<!-- <script src=" https://res.wx.qq.com/connect/zh_CN/htmledition/js/wxopensdk.js"> -->
|
||||
<!-- </script> -->
|
||||
@@ -1745,10 +1744,10 @@
|
||||
if (document.querySelector('.popbox .loading-box')) {
|
||||
document.querySelector('.popbox .loading-box').classList.add('none');
|
||||
}
|
||||
userId = res.userId;
|
||||
setCookie('token', res.token);
|
||||
// userId = res.userId;
|
||||
// setCookie('token', res.token);
|
||||
unshowpop();
|
||||
getSessionList();
|
||||
// getSessionList();
|
||||
}
|
||||
|
||||
// } else {
|
||||
@@ -2408,6 +2407,8 @@
|
||||
})
|
||||
return null;
|
||||
});
|
||||
|
||||
console.log(res, 7665)
|
||||
if (!res) {
|
||||
|
||||
|
||||
@@ -2423,6 +2424,24 @@
|
||||
|
||||
return null;
|
||||
}
|
||||
if (res.status === 400) {
|
||||
clearInterval(interval);
|
||||
if (controller) {
|
||||
controller.abort();
|
||||
controller = null;
|
||||
}
|
||||
if (sendBtnImg.classList.contains("none")) {
|
||||
sendBtnImg.classList.remove('none');
|
||||
|
||||
}
|
||||
stopBtn.forEach(e => {
|
||||
if (!e.classList.contains("none")) {
|
||||
e.classList.add('none')
|
||||
}
|
||||
})
|
||||
showpop();
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
sendParam.imageBase64 = '';
|
||||
@@ -2478,6 +2497,28 @@
|
||||
})
|
||||
// renderChatList();
|
||||
msgDiv.innerHTML = marked.parse(aiMsg.content);
|
||||
// const shareSpan = document.createElement("span");
|
||||
// shareSpan.style.display = "inline-flex";
|
||||
// shareSpan.style.alignItems = "center";
|
||||
// shareSpan.style.cursor = "pointer";
|
||||
// shareSpan.style.marginTop = "8px";
|
||||
// shareSpan.style.opacity = "0.75";
|
||||
// shareSpan.style.transition = "opacity .2s";
|
||||
// shareSpan.title = "分享";
|
||||
|
||||
// shareSpan.onmouseenter = () => shareSpan.style.opacity = "1";
|
||||
// shareSpan.onmouseleave = () => shareSpan.style.opacity = "0.75";
|
||||
|
||||
// shareSpan.innerHTML = `
|
||||
// <svg t="1764059839985" class="icon" viewBox="0 0 1236 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="5667" width="20" height="20"><path d="M741.743 1018.343c-28.287 0-50.917-11.315-73.547-28.288-22.63-22.63-39.602-50.917-39.602-84.862V792.044c-124.464 0-328.133 33.945-435.624 181.039-16.973 28.287-56.575 45.26-90.52 50.917H85.478C28.903 1012.685-5.042 961.768 0.616 905.193c28.287-243.27 113.15-418.652 260.243-537.458 107.492-84.862 231.956-130.122 367.735-141.437V118.807c0-50.917 22.63-96.177 67.89-113.15C736.086-5.657 781.345 0 815.29 33.945l362.077 367.735c28.288 22.63 45.26 56.574 50.918 96.176 5.657 39.603-5.658 79.205-33.945 107.492-5.658 5.658-11.315 16.972-22.63 22.63l-350.762 356.42c-22.63 22.63-50.918 33.945-79.205 33.945z m-90.52-339.448h90.52v226.298l356.42-367.734 5.658-5.658c5.657-5.657 5.657-16.972 5.657-22.63 0-11.315-5.657-16.972-11.315-22.63l-5.657-5.657-356.42-362.077V333.79l-79.205 5.658c-118.806 0-231.956 39.602-328.132 113.149-113.15 90.519-186.696 237.613-209.326 429.967 141.436-175.382 390.364-203.669 531.8-203.669z" p-id="5668"></path></svg>
|
||||
// `;
|
||||
|
||||
// shareSpan.onclick = () => {
|
||||
// generatePoster(aiMsg.content);
|
||||
// };
|
||||
|
||||
// if (role === "assistant") {
|
||||
// 分享按钮
|
||||
const shareSpan = document.createElement("span");
|
||||
shareSpan.style.display = "inline-flex";
|
||||
shareSpan.style.alignItems = "center";
|
||||
@@ -2491,74 +2532,49 @@
|
||||
shareSpan.onmouseleave = () => shareSpan.style.opacity = "0.75";
|
||||
|
||||
shareSpan.innerHTML = `
|
||||
<svg t="1764059839985" class="icon" viewBox="0 0 1236 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="5667" width="20" height="20"><path d="M741.743 1018.343c-28.287 0-50.917-11.315-73.547-28.288-22.63-22.63-39.602-50.917-39.602-84.862V792.044c-124.464 0-328.133 33.945-435.624 181.039-16.973 28.287-56.575 45.26-90.52 50.917H85.478C28.903 1012.685-5.042 961.768 0.616 905.193c28.287-243.27 113.15-418.652 260.243-537.458 107.492-84.862 231.956-130.122 367.735-141.437V118.807c0-50.917 22.63-96.177 67.89-113.15C736.086-5.657 781.345 0 815.29 33.945l362.077 367.735c28.288 22.63 45.26 56.574 50.918 96.176 5.657 39.603-5.658 79.205-33.945 107.492-5.658 5.658-11.315 16.972-22.63 22.63l-350.762 356.42c-22.63 22.63-50.918 33.945-79.205 33.945z m-90.52-339.448h90.52v226.298l356.42-367.734 5.658-5.658c5.657-5.657 5.657-16.972 5.657-22.63 0-11.315-5.657-16.972-11.315-22.63l-5.657-5.657-356.42-362.077V333.79l-79.205 5.658c-118.806 0-231.956 39.602-328.132 113.149-113.15 90.519-186.696 237.613-209.326 429.967 141.436-175.382 390.364-203.669 531.8-203.669z" p-id="5668"></path></svg>
|
||||
<svg t="1764059839985" class="icon" viewBox="0 0 1236 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="5667" width="20" height="20"><path d="M741.743 1018.343c-28.287 0-50.917-11.315-73.547-28.288-22.63-22.63-39.602-50.917-39.602-84.862V792.044c-124.464 0-328.133 33.945-435.624 181.039-16.973 28.287-56.575 45.26-90.52 50.917H85.478C28.903 1012.685-5.042 961.768 0.616 905.193c28.287-243.27 113.15-418.652 260.243-537.458 107.492-84.862 231.956-130.122 367.735-141.437V118.807c0-50.917 22.63-96.177 67.89-113.15C736.086-5.657 781.345 0 815.29 33.945l362.077 367.735c28.288 22.63 45.26 56.574 50.918 96.176 5.657 39.603-5.658 79.205-33.945 107.492-5.658 5.658-11.315 16.972-22.63 22.63l-350.762 356.42c-22.63 22.63-50.918 33.945-79.205 33.945z m-90.52-339.448h90.52v226.298l356.42-367.734 5.658-5.658c5.657-5.657 5.657-16.972 5.657-22.63 0-11.315-5.657-16.972-11.315-22.63l-5.657-5.657-356.42-362.077V333.79l-79.205 5.658c-118.806 0-231.956 39.602-328.132 113.149-113.15 90.519-186.696 237.613-209.326 429.967 141.436-175.382 390.364-203.669 531.8-203.669z" p-id="5668" fill="#878895"></path></svg>
|
||||
`;
|
||||
|
||||
|
||||
shareSpan.onclick = () => {
|
||||
generatePoster(aiMsg.content);
|
||||
generatePoster(content, imgUrl);
|
||||
};
|
||||
|
||||
if (role === "assistant") {
|
||||
// 分享按钮
|
||||
const shareSpan = document.createElement("span");
|
||||
shareSpan.style.display = "inline-flex";
|
||||
shareSpan.style.alignItems = "center";
|
||||
shareSpan.style.cursor = "pointer";
|
||||
shareSpan.style.marginTop = "8px";
|
||||
shareSpan.style.opacity = "0.75";
|
||||
shareSpan.style.transition = "opacity .2s";
|
||||
shareSpan.title = "分享";
|
||||
// msg.appendChild(shareSpan);
|
||||
|
||||
shareSpan.onmouseenter = () => shareSpan.style.opacity = "1";
|
||||
shareSpan.onmouseleave = () => shareSpan.style.opacity = "0.75";
|
||||
// ======================
|
||||
// ✅ 复制按钮(新增)
|
||||
// ======================
|
||||
const copySpan = document.createElement("span");
|
||||
copySpan.style.display = "inline-flex";
|
||||
copySpan.style.alignItems = "center";
|
||||
copySpan.style.cursor = "pointer";
|
||||
copySpan.style.marginTop = "8px";
|
||||
copySpan.style.marginLeft = "10px";
|
||||
copySpan.style.opacity = "0.75";
|
||||
copySpan.style.transition = "opacity .2s";
|
||||
copySpan.title = "复制内容";
|
||||
|
||||
shareSpan.innerHTML = `
|
||||
<svg viewBox="0 0 1236 1024" width="20" height="20">
|
||||
<path d="M741.743 1018.343c-28.287 0-50.917-11.315-73.547-28.288-22.63-22.63-39.602-50.917-39.602-84.862V792.044c-124.464 0-328.133 33.945-435.624 181.039-16.973 28.287-56.575 45.26-90.52 50.917H85.478C28.903 1012.685-5.042 961.768 0.616 905.193c28.287-243.27 113.15-418.652 260.243-537.458 107.492-84.862 231.956-130.122 367.735-141.437V118.807c0-50.917 22.63-96.177 67.89-113.15C736.086-5.657 781.345 0 815.29 33.945l362.077 367.735c28.288 22.63 45.26 56.574 50.918 96.176 5.657 39.603-5.658 79.205-33.945 107.492-5.658 5.658-11.315 16.972-22.63 22.63l-350.762 356.42c-22.63 22.63-50.918 33.945-79.205 33.945z" />
|
||||
</svg>
|
||||
copySpan.onmouseenter = () => copySpan.style.opacity = "1";
|
||||
copySpan.onmouseleave = () => copySpan.style.opacity = "0.75";
|
||||
|
||||
copySpan.innerHTML = `
|
||||
<svg t="1764549398754" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="5335" width="20" height="20"><path d="M639.9 256.3H192.4c-35.2 0-63.9 28.8-63.9 63.9v575.4c0 35.2 28.8 63.9 63.9 63.9h447.5c35.2 0 63.9-28.8 63.9-63.9V320.2c0-35.1-28.8-63.9-63.9-63.9z m0 639.3H192.4V320.2h447.5v575.4z" fill="#878895" p-id="5336"></path><path d="M831.6 64.5H384.1c-35.2 0-63.9 28.8-63.9 63.9v63.9h63.9v-63.9h447.5v575.4h-63.9v63.9h63.9c35.2 0 63.9-28.8 63.9-63.9V128.4c0.1-35.1-28.7-63.9-63.9-63.9z" fill="#878895" p-id="5337"></path></svg>
|
||||
`;
|
||||
|
||||
shareSpan.onclick = () => {
|
||||
generatePoster(content, imgUrl);
|
||||
};
|
||||
copySpan.onclick = async () => {
|
||||
try {
|
||||
await navigator.clipboard.writeText(content);
|
||||
copySpan.style.opacity = "1";
|
||||
showToast("复制成功")
|
||||
copySpan.title = "复制成功";
|
||||
setTimeout(() => { copySpan.title = "复制内容"; }, 1200);
|
||||
} catch (err) {
|
||||
alert("复制失败,可能不支持 HTTPS 环境以外复制。");
|
||||
}
|
||||
};
|
||||
|
||||
msg.appendChild(shareSpan);
|
||||
|
||||
// ======================
|
||||
// ✅ 复制按钮(新增)
|
||||
// ======================
|
||||
const copySpan = document.createElement("span");
|
||||
copySpan.style.display = "inline-flex";
|
||||
copySpan.style.alignItems = "center";
|
||||
copySpan.style.cursor = "pointer";
|
||||
copySpan.style.marginTop = "8px";
|
||||
copySpan.style.marginLeft = "10px";
|
||||
copySpan.style.opacity = "0.75";
|
||||
copySpan.style.transition = "opacity .2s";
|
||||
copySpan.title = "复制内容";
|
||||
|
||||
copySpan.onmouseenter = () => copySpan.style.opacity = "1";
|
||||
copySpan.onmouseleave = () => copySpan.style.opacity = "0.75";
|
||||
|
||||
copySpan.innerHTML = `
|
||||
<svg viewBox="0 0 1024 1024" width="20" height="20">
|
||||
<path d="M320 128h448v64H320zM256 256h512v64H256zM192 384h640v64H192zM256 512h512v64H256zM320 640h448v64H320z"/>
|
||||
</svg>
|
||||
`;
|
||||
|
||||
copySpan.onclick = async () => {
|
||||
try {
|
||||
await navigator.clipboard.writeText(content);
|
||||
copySpan.style.opacity = "1";
|
||||
showToast("复制成功")
|
||||
copySpan.title = "复制成功";
|
||||
setTimeout(() => { copySpan.title = "复制内容"; }, 1200);
|
||||
} catch (err) {
|
||||
alert("复制失败,可能不支持 HTTPS 环境以外复制。");
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
// }
|
||||
|
||||
setTimeout(() => {
|
||||
msgDiv.appendChild(shareSpan);
|
||||
@@ -3202,6 +3218,20 @@
|
||||
showToast("提交成功", 'success')
|
||||
}
|
||||
});
|
||||
|
||||
window.addEventListener('beforeunload', function (event) {
|
||||
const data = {
|
||||
userId: userId
|
||||
}
|
||||
const res = fetch('https://serviceai.satabot.com/updateAiChatNum', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify(data)
|
||||
});
|
||||
|
||||
console.log(res, 98888);
|
||||
debugger;
|
||||
})
|
||||
</script>
|
||||
</body>
|
||||
|
||||
|
||||
76
middle.html
76
middle.html
@@ -4,12 +4,11 @@
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width,initial-scale=1.0,user-scalable=no"">
|
||||
<title>新能源智能体</title>
|
||||
<meta property=" og:title" content="新能源智能体">
|
||||
<meta property="og:description"
|
||||
content="SataBot Agent 是一款由人工智能驱动的光伏场景AI应用程序。SataBot Agent AI 能力基于飒沓自研大模型的 AIGC 服务构建。">
|
||||
<title>飒沓光伏智能体</title>
|
||||
<meta property=" og:title" content="飒沓光伏智能体">
|
||||
<meta property="og:description" content="SataBot Agent 是一款由人工智能驱动的光伏场景AI应用程序。">
|
||||
<meta property="og:image" content="http://1.95.137.212:19000/mbzn/16%3A33%3A49123.png">
|
||||
<script src=" https://unpkg.com/vconsole@latest/dist/vconsole.min.js">
|
||||
<!-- <script src=" https://unpkg.com/vconsole@latest/dist/vconsole.min.js"> -->
|
||||
</script>
|
||||
<link rel="prefetch" href="index.html">
|
||||
|
||||
@@ -702,7 +701,9 @@
|
||||
retryFetch(`https://servicestream.satabot.com/api/wechat/access_token?code=${code}`)
|
||||
.then(res => {
|
||||
userInfo = res;
|
||||
console.log(res);
|
||||
localStorage.setItem("userInfo", JSON.stringify(res));
|
||||
// test(res.openid)
|
||||
getUserByOpenId(res.openid)
|
||||
}).catch(() => {
|
||||
if (!document.querySelector('.weixin-loading').classList.contains("none")) {
|
||||
@@ -713,6 +714,57 @@
|
||||
})
|
||||
|
||||
}
|
||||
function test(id) {
|
||||
console.log(id);
|
||||
const data = {
|
||||
sourceType: 5,
|
||||
openId: id,
|
||||
phonenumber: '',
|
||||
smsCode: '',
|
||||
loginPlatForm: 2
|
||||
|
||||
}
|
||||
retryFetch("https://serviceri.satabot.com/login", {
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json"
|
||||
},
|
||||
body: JSON.stringify(data)
|
||||
})
|
||||
.then(res => {
|
||||
if (res.code == 300) {
|
||||
// showpop(); // 弹出验证码
|
||||
// const _token = "BEAR DHWJDBJDKWJDBDNJEWHBD"
|
||||
|
||||
// setCookie('token', _token);
|
||||
// const encodedUserId = btoa(unescape(encodeURIComponent("138")));
|
||||
// localStorage.setItem('userId', encodedUserId);
|
||||
// const t = Date.now();
|
||||
// document.body.classList.add('fadeout');
|
||||
|
||||
// window.location.replace(`https://ai.satabot.com?userId=${encodedUserId}&headingimg=${userInfo.headimgurl}&nickname=${userInfo.nickname}&localtoken=${_token}&t=${t}`)
|
||||
} else if (res.code == 200) {
|
||||
setCookie('token', res.token);
|
||||
const encodedUserId = btoa(unescape(encodeURIComponent(res.userId)));
|
||||
localStorage.setItem('userId', encodedUserId);
|
||||
const t = Date.now();
|
||||
document.body.classList.add('fadeout');
|
||||
|
||||
window.location.replace(`https://ai.satabot.com?userId=${encodedUserId}&headingimg=${userInfo.headimgurl}&nickname=${userInfo.nickname}&localtoken=${res.token}&t=${t}`)
|
||||
}
|
||||
})
|
||||
.catch(err => {
|
||||
console.error("登录请求失败(重试10次后):", err);
|
||||
showToast("网络异常,请稍后再试");
|
||||
})
|
||||
.finally(() => {
|
||||
|
||||
console.log("登录请求结束");
|
||||
// if (!document.querySelector('.weixin-loading').classList.contains("none")) {
|
||||
// document.querySelector('.weixin-loading').classList.add("none");
|
||||
// }
|
||||
});
|
||||
}
|
||||
|
||||
function getUserByOpenId(id) {
|
||||
const data = {
|
||||
@@ -733,15 +785,15 @@
|
||||
.then(res => {
|
||||
if (res.code == 300) {
|
||||
// showpop(); // 弹出验证码
|
||||
const _token = "BEAR DHWJDBJDKWJDBDNJEWHBD"
|
||||
// const _token = "BEAR DHWJDBJDKWJDBDNJEWHBD"
|
||||
|
||||
setCookie('token', _token);
|
||||
const encodedUserId = btoa(unescape(encodeURIComponent("138")));
|
||||
localStorage.setItem('userId', encodedUserId);
|
||||
const t = Date.now();
|
||||
document.body.classList.add('fadeout');
|
||||
// setCookie('token', _token);
|
||||
// const encodedUserId = btoa(unescape(encodeURIComponent("138")));
|
||||
// localStorage.setItem('userId', encodedUserId);
|
||||
// const t = Date.now();
|
||||
// document.body.classList.add('fadeout');
|
||||
|
||||
window.location.replace(`https://ai.satabot.com?userId=${encodedUserId}&headingimg=${userInfo.headimgurl}&nickname=${userInfo.nickname}&localtoken=${_token}&t=${t}`)
|
||||
// window.location.replace(`https://ai.satabot.com?userId=${encodedUserId}&headingimg=${userInfo.headimgurl}&nickname=${userInfo.nickname}&localtoken=${_token}&t=${t}`)
|
||||
} else if (res.code == 200) {
|
||||
setCookie('token', res.token);
|
||||
const encodedUserId = btoa(unescape(encodeURIComponent(res.userId)));
|
||||
|
||||
9
node_modules/.package-lock.json
generated
vendored
9
node_modules/.package-lock.json
generated
vendored
@@ -233,6 +233,15 @@
|
||||
"integrity": "sha512-klpgFSWLW1ZEs8svjfb7g4qWY0YS5imI82dTg+QahUvJ8YqAY0P10Uk8tTyh9ZGuYEZEMaeJYCF5BFuX552hsw==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/async-mutex": {
|
||||
"version": "0.5.0",
|
||||
"resolved": "https://registry.npmmirror.com/async-mutex/-/async-mutex-0.5.0.tgz",
|
||||
"integrity": "sha512-1A94B18jkJ3DYq284ohPxoXbfTA5HsQ7/Mf4DEhcyLx3Bz27Rh59iScbB6EPiP+B+joue6YCxcMXSbFC1tZKwA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"tslib": "^2.4.0"
|
||||
}
|
||||
},
|
||||
"node_modules/asynckit": {
|
||||
"version": "0.4.0",
|
||||
"resolved": "https://registry.npmmirror.com/asynckit/-/asynckit-0.4.0.tgz",
|
||||
|
||||
21
node_modules/async-mutex/LICENSE
generated
vendored
Normal file
21
node_modules/async-mutex/LICENSE
generated
vendored
Normal file
@@ -0,0 +1,21 @@
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2016 Christian Speckner <cnspeckn@googlemail.com>
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
540
node_modules/async-mutex/README.md
generated
vendored
Normal file
540
node_modules/async-mutex/README.md
generated
vendored
Normal file
@@ -0,0 +1,540 @@
|
||||
[](https://github.com/DirtyHairy/async-mutex/actions?query=workflow%3A%22Build+and+Tests%22)
|
||||
[](https://badge.fury.io/js/async-mutex)
|
||||
[](https://coveralls.io/github/DirtyHairy/async-mutex?branch=master)
|
||||
|
||||
# What is it?
|
||||
|
||||
This package implements primitives for synchronizing asynchronous operations in
|
||||
Javascript.
|
||||
|
||||
## Mutex
|
||||
|
||||
The term "mutex" usually refers to a data structure used to synchronize
|
||||
concurrent processes running on different threads. For example, before accessing
|
||||
a non-threadsafe resource, a thread will lock the mutex. This is guaranteed
|
||||
to block the thread until no other thread holds a lock on the mutex and thus
|
||||
enforces exclusive access to the resource. Once the operation is complete, the
|
||||
thread releases the lock, allowing other threads to acquire a lock and access the
|
||||
resource.
|
||||
|
||||
While Javascript is strictly single-threaded, the asynchronous nature of its
|
||||
execution model allows for race conditions that require similar synchronization
|
||||
primitives. Consider for example a library communicating with a web worker that
|
||||
needs to exchange several subsequent messages with the worker in order to achieve
|
||||
a task. As these messages are exchanged in an asynchronous manner, it is perfectly
|
||||
possible that the library is called again during this process. Depending on the
|
||||
way state is handled during the async process, this will lead to race conditions
|
||||
that are hard to fix and even harder to track down.
|
||||
|
||||
This library solves the problem by applying the concept of mutexes to Javascript.
|
||||
Locking the mutex will return a promise that resolves once the mutex becomes
|
||||
available. Once the async process is complete (usually taking multiple
|
||||
spins of the event loop), a callback supplied to the caller should be called in order
|
||||
to release the mutex, allowing the next scheduled worker to execute.
|
||||
|
||||
# Semaphore
|
||||
|
||||
Imagine a situation where you need to control access to several instances of
|
||||
a shared resource. For example, you might want to distribute images between several
|
||||
worker processes that perform transformations, or you might want to create a web
|
||||
crawler that performs a defined number of requests in parallel.
|
||||
|
||||
A semaphore is a data structure that is initialized with an arbitrary integer value and that
|
||||
can be locked multiple times.
|
||||
As long as the semaphore value is positive, locking it will return the current value
|
||||
and the locking process will continue execution immediately; the semaphore will
|
||||
be decremented upon locking. Releasing the lock will increment the semaphore again.
|
||||
|
||||
Once the semaphore has reached zero, the next process that attempts to acquire a lock
|
||||
will be suspended until another process releases its lock and this increments the semaphore
|
||||
again.
|
||||
|
||||
This library provides a semaphore implementation for Javascript that is similar to the
|
||||
mutex implementation described above.
|
||||
|
||||
# How to use it?
|
||||
|
||||
## Installation
|
||||
|
||||
You can install the library into your project via npm
|
||||
|
||||
npm install async-mutex
|
||||
|
||||
The library is written in TypeScript and will work in any environment that
|
||||
supports ES5, ES6 promises and `Array.isArray`. On ancient browsers,
|
||||
a shim can be used (e.g. [core-js](https://github.com/zloirock/core-js)).
|
||||
No external typings are required for using this library with
|
||||
TypeScript (version >= 2).
|
||||
|
||||
Starting with Node 12.16 and 13.7, native ES6 style imports are supported.
|
||||
|
||||
**WARNING:** Node 13 versions < 13.2.0 fail to import this package correctly.
|
||||
Node 12 and earlier are fine, as are newer versions of Node 13.
|
||||
|
||||
## Importing
|
||||
|
||||
**CommonJS:**
|
||||
```javascript
|
||||
var Mutex = require('async-mutex').Mutex;
|
||||
var Semaphore = require('async-mutex').Semaphore;
|
||||
var withTimeout = require('async-mutex').withTimeout;
|
||||
```
|
||||
|
||||
**ES6:**
|
||||
```javascript
|
||||
import {Mutex, Semaphore, withTimeout} from 'async-mutex';
|
||||
```
|
||||
|
||||
**TypeScript:**
|
||||
```typescript
|
||||
import {Mutex, MutexInterface, Semaphore, SemaphoreInterface, withTimeout} from 'async-mutex';
|
||||
```
|
||||
|
||||
With the latest version of Node, native ES6 style imports are supported.
|
||||
|
||||
## Mutex API
|
||||
|
||||
### Creating
|
||||
|
||||
```typescript
|
||||
const mutex = new Mutex();
|
||||
```
|
||||
|
||||
Create a new mutex.
|
||||
|
||||
### Synchronized code execution
|
||||
|
||||
Promise style:
|
||||
```typescript
|
||||
mutex
|
||||
.runExclusive(() => {
|
||||
// ...
|
||||
})
|
||||
.then((result) => {
|
||||
// ...
|
||||
});
|
||||
```
|
||||
|
||||
async/await:
|
||||
```typescript
|
||||
await mutex.runExclusive(async () => {
|
||||
// ...
|
||||
});
|
||||
```
|
||||
|
||||
`runExclusive` schedules the supplied callback to be run once the mutex is unlocked.
|
||||
The function may return a promise. Once the promise is resolved or rejected (or immediately after
|
||||
execution if an immediate value was returned),
|
||||
the mutex is released. `runExclusive` returns a promise that adopts the state of the function result.
|
||||
|
||||
The mutex is released and the result rejected if an exception occurs during execution
|
||||
of the callback.
|
||||
|
||||
### Manual locking / releasing
|
||||
|
||||
Promise style:
|
||||
```typescript
|
||||
mutex
|
||||
.acquire()
|
||||
.then(function(release) {
|
||||
// ...
|
||||
|
||||
release();
|
||||
});
|
||||
```
|
||||
|
||||
async/await:
|
||||
```typescript
|
||||
const release = await mutex.acquire();
|
||||
try {
|
||||
// ...
|
||||
} finally {
|
||||
release();
|
||||
}
|
||||
```
|
||||
|
||||
`acquire` returns an (ES6) promise that will resolve as soon as the mutex is
|
||||
available. The promise resolves with a function `release` that
|
||||
must be called once the mutex should be released again. The `release` callback
|
||||
is idempotent.
|
||||
|
||||
**IMPORTANT:** Failure to call `release` will hold the mutex locked and will
|
||||
likely deadlock the application. Make sure to call `release` under all circumstances
|
||||
and handle exceptions accordingly.
|
||||
|
||||
### Unscoped release
|
||||
|
||||
As an alternative to calling the `release` callback returned by `acquire`, the mutex
|
||||
can be released by calling `release` directly on it:
|
||||
|
||||
```typescript
|
||||
mutex.release();
|
||||
```
|
||||
|
||||
### Checking whether the mutex is locked
|
||||
|
||||
```typescript
|
||||
mutex.isLocked();
|
||||
```
|
||||
|
||||
### Cancelling pending locks
|
||||
|
||||
Pending locks can be cancelled by calling `cancel()` on the mutex. This will reject
|
||||
all pending locks with `E_CANCELED`:
|
||||
|
||||
Promise style:
|
||||
```typescript
|
||||
import {E_CANCELED} from 'async-mutex';
|
||||
|
||||
mutex
|
||||
.runExclusive(() => {
|
||||
// ...
|
||||
})
|
||||
.then(() => {
|
||||
// ...
|
||||
})
|
||||
.catch(e => {
|
||||
if (e === E_CANCELED) {
|
||||
// ...
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
async/await:
|
||||
```typescript
|
||||
import {E_CANCELED} from 'async-mutex';
|
||||
|
||||
try {
|
||||
await mutex.runExclusive(() => {
|
||||
// ...
|
||||
});
|
||||
} catch (e) {
|
||||
if (e === E_CANCELED) {
|
||||
// ...
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
This works with `acquire`, too:
|
||||
if `acquire` is used for locking, the resulting promise will reject with `E_CANCELED`.
|
||||
|
||||
The error that is thrown can be customized by passing a different error to the `Mutex`
|
||||
constructor:
|
||||
|
||||
```typescript
|
||||
const mutex = new Mutex(new Error('fancy custom error'));
|
||||
```
|
||||
|
||||
Note that while all pending locks are cancelled, a currently held lock will not be
|
||||
revoked. In consequence, the mutex may not be available even after `cancel()` has been called.
|
||||
|
||||
### Waiting until the mutex is available
|
||||
|
||||
You can wait until the mutex is available without locking it by calling `waitForUnlock()`.
|
||||
This will return a promise that resolve once the mutex can be acquired again. This operation
|
||||
will not lock the mutex, and there is no guarantee that the mutex will still be available
|
||||
once an async barrier has been encountered.
|
||||
|
||||
Promise style:
|
||||
```typescript
|
||||
mutex
|
||||
.waitForUnlock()
|
||||
.then(() => {
|
||||
// ...
|
||||
});
|
||||
```
|
||||
|
||||
Async/await:
|
||||
```typescript
|
||||
await mutex.waitForUnlock();
|
||||
// ...
|
||||
```
|
||||
|
||||
|
||||
## Semaphore API
|
||||
|
||||
### Creating
|
||||
|
||||
```typescript
|
||||
const semaphore = new Semaphore(initialValue);
|
||||
```
|
||||
|
||||
Creates a new semaphore. `initialValue` is an arbitrary integer that defines the
|
||||
initial value of the semaphore.
|
||||
|
||||
### Synchronized code execution
|
||||
|
||||
Promise style:
|
||||
```typescript
|
||||
semaphore
|
||||
.runExclusive(function(value) {
|
||||
// ...
|
||||
})
|
||||
.then(function(result) {
|
||||
// ...
|
||||
});
|
||||
```
|
||||
|
||||
async/await:
|
||||
```typescript
|
||||
await semaphore.runExclusive(async (value) => {
|
||||
// ...
|
||||
});
|
||||
```
|
||||
|
||||
`runExclusive` schedules the supplied callback to be run once the semaphore is available.
|
||||
The callback will receive the current value of the semaphore as its argument.
|
||||
The function may return a promise. Once the promise is resolved or rejected (or immediately after
|
||||
execution if an immediate value was returned),
|
||||
the semaphore is released. `runExclusive` returns a promise that adopts the state of the function result.
|
||||
|
||||
The semaphore is released and the result rejected if an exception occurs during execution
|
||||
of the callback.
|
||||
|
||||
`runExclusive` accepts a first optional argument `weight`. Specifying a `weight` will decrement the
|
||||
semaphore by the specified value, and the callback will only be invoked once the semaphore's
|
||||
value greater or equal to `weight`.
|
||||
|
||||
`runExclusive` accepts a second optional argument `priority`. Specifying a greater value for `priority`
|
||||
tells the scheduler to run this task before other tasks. `priority` can be any real number. The default
|
||||
is zero.
|
||||
|
||||
### Manual locking / releasing
|
||||
|
||||
Promise style:
|
||||
```typescript
|
||||
semaphore
|
||||
.acquire()
|
||||
.then(function([value, release]) {
|
||||
// ...
|
||||
|
||||
release();
|
||||
});
|
||||
```
|
||||
|
||||
async/await:
|
||||
```typescript
|
||||
const [value, release] = await semaphore.acquire();
|
||||
try {
|
||||
// ...
|
||||
} finally {
|
||||
release();
|
||||
}
|
||||
```
|
||||
|
||||
`acquire` returns an (ES6) promise that will resolve as soon as the semaphore is
|
||||
available. The promise resolves to an array with the
|
||||
first entry being the current value of the semaphore, and the second value a
|
||||
function that must be called to release the semaphore once the critical operation
|
||||
has completed. The `release` callback is idempotent.
|
||||
|
||||
**IMPORTANT:** Failure to call `release` will hold the semaphore locked and will
|
||||
likely deadlock the application. Make sure to call `release` under all circumstances
|
||||
and handle exceptions accordingly.
|
||||
|
||||
`acquire` accepts a first optional argument `weight`. Specifying a `weight` will decrement the
|
||||
semaphore by the specified value, and the semaphore will only be acquired once its
|
||||
value is greater or equal to `weight`.
|
||||
|
||||
`acquire` accepts a second optional argument `priority`. Specifying a greater value for `priority`
|
||||
tells the scheduler to release the semaphore to the caller before other callers. `priority` can be
|
||||
any real number. The default is zero.
|
||||
|
||||
### Unscoped release
|
||||
|
||||
As an alternative to calling the `release` callback returned by `acquire`, the semaphore
|
||||
can be released by calling `release` directly on it:
|
||||
|
||||
```typescript
|
||||
semaphore.release();
|
||||
```
|
||||
|
||||
`release` accepts an optional argument `weight` and increments the semaphore accordingly.
|
||||
|
||||
**IMPORTANT:** Releasing a previously acquired semaphore with the releaser that was
|
||||
returned by acquire will automatically increment the semaphore by the correct weight. If
|
||||
you release by calling the unscoped `release` you have to supply the correct weight
|
||||
yourself!
|
||||
|
||||
### Getting the semaphore value
|
||||
|
||||
```typescript
|
||||
semaphore.getValue()
|
||||
```
|
||||
|
||||
### Checking whether the semaphore is locked
|
||||
|
||||
```typescript
|
||||
semaphore.isLocked();
|
||||
```
|
||||
|
||||
The semaphore is considered to be locked if its value is either zero or negative.
|
||||
|
||||
### Setting the semaphore value
|
||||
|
||||
The value of a semaphore can be set directly to a desired value. A positive value will
|
||||
cause the semaphore to schedule any pending waiters accordingly.
|
||||
|
||||
```typescript
|
||||
semaphore.setValue();
|
||||
```
|
||||
|
||||
### Cancelling pending locks
|
||||
|
||||
Pending locks can be cancelled by calling `cancel()` on the semaphore. This will reject
|
||||
all pending locks with `E_CANCELED`:
|
||||
|
||||
Promise style:
|
||||
```typescript
|
||||
import {E_CANCELED} from 'async-mutex';
|
||||
|
||||
semaphore
|
||||
.runExclusive(() => {
|
||||
// ...
|
||||
})
|
||||
.then(() => {
|
||||
// ...
|
||||
})
|
||||
.catch(e => {
|
||||
if (e === E_CANCELED) {
|
||||
// ...
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
async/await:
|
||||
```typescript
|
||||
import {E_CANCELED} from 'async-mutex';
|
||||
|
||||
try {
|
||||
await semaphore.runExclusive(() => {
|
||||
// ...
|
||||
});
|
||||
} catch (e) {
|
||||
if (e === E_CANCELED) {
|
||||
// ...
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
This works with `acquire`, too:
|
||||
if `acquire` is used for locking, the resulting promise will reject with `E_CANCELED`.
|
||||
|
||||
The error that is thrown can be customized by passing a different error to the `Semaphore`
|
||||
constructor:
|
||||
|
||||
```typescript
|
||||
const semaphore = new Semaphore(2, new Error('fancy custom error'));
|
||||
```
|
||||
|
||||
Note that while all pending locks are cancelled, any currently held locks will not be
|
||||
revoked. In consequence, the semaphore may not be available even after `cancel()` has been called.
|
||||
|
||||
### Waiting until the semaphore is available
|
||||
|
||||
You can wait until the semaphore is available without locking it by calling `waitForUnlock()`.
|
||||
This will return a promise that resolve once the semaphore can be acquired again. This operation
|
||||
will not lock the semaphore, and there is no guarantee that the semaphore will still be available
|
||||
once an async barrier has been encountered.
|
||||
|
||||
Promise style:
|
||||
```typescript
|
||||
semaphore
|
||||
.waitForUnlock()
|
||||
.then(() => {
|
||||
// ...
|
||||
});
|
||||
```
|
||||
|
||||
Async/await:
|
||||
```typescript
|
||||
await semaphore.waitForUnlock();
|
||||
// ...
|
||||
```
|
||||
|
||||
`waitForUnlock` accepts optional arguments `weight` and `priority`. The promise will resolve as soon
|
||||
as it is possible to `acquire` the semaphore with the given weight and priority. Scheduled tasks with
|
||||
the greatest `priority` values execute first.
|
||||
|
||||
|
||||
## Limiting the time waiting for a mutex or semaphore to become available
|
||||
|
||||
Sometimes it is desirable to limit the time a program waits for a mutex or
|
||||
semaphore to become available. The `withTimeout` decorator can be applied
|
||||
to both semaphores and mutexes and changes the behavior of `acquire` and
|
||||
`runExclusive` accordingly.
|
||||
|
||||
```typescript
|
||||
import {withTimeout, E_TIMEOUT} from 'async-mutex';
|
||||
|
||||
const mutexWithTimeout = withTimeout(new Mutex(), 100);
|
||||
const semaphoreWithTimeout = withTimeout(new Semaphore(5), 100);
|
||||
```
|
||||
|
||||
The API of the decorated mutex or semaphore is unchanged.
|
||||
|
||||
The second argument of `withTimeout` is the timeout in milliseconds. After the
|
||||
timeout is exceeded, the promise returned by `acquire` and `runExclusive` will
|
||||
reject with `E_TIMEOUT`. The latter will not run the provided callback in case
|
||||
of an timeout.
|
||||
|
||||
The third argument of `withTimeout` is optional and can be used to
|
||||
customize the error with which the promise is rejected.
|
||||
|
||||
```typescript
|
||||
const mutexWithTimeout = withTimeout(new Mutex(), 100, new Error('new fancy error'));
|
||||
const semaphoreWithTimeout = withTimeout(new Semaphore(5), 100, new Error('new fancy error'));
|
||||
```
|
||||
|
||||
### Failing early if the mutex or semaphore is not available
|
||||
|
||||
A shortcut exists for the case where you do not want to wait for a lock to
|
||||
be available at all. The `tryAcquire` decorator can be applied to both mutexes
|
||||
and semaphores and changes the behavior of `acquire` and `runExclusive` to
|
||||
immediately throw `E_ALREADY_LOCKED` if the mutex is not available.
|
||||
|
||||
Promise style:
|
||||
```typescript
|
||||
import {tryAcquire, E_ALREADY_LOCKED} from 'async-mutex';
|
||||
|
||||
tryAcquire(semaphoreOrMutex)
|
||||
.runExclusive(() => {
|
||||
// ...
|
||||
})
|
||||
.then(() => {
|
||||
// ...
|
||||
})
|
||||
.catch(e => {
|
||||
if (e === E_ALREADY_LOCKED) {
|
||||
// ...
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
async/await:
|
||||
```typescript
|
||||
import {tryAcquire, E_ALREADY_LOCKED} from 'async-mutex';
|
||||
|
||||
try {
|
||||
await tryAcquire(semaphoreOrMutex).runExclusive(() => {
|
||||
// ...
|
||||
});
|
||||
} catch (e) {
|
||||
if (e === E_ALREADY_LOCKED) {
|
||||
// ...
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Again, the error can be customized by providing a custom error as second argument to
|
||||
`tryAcquire`.
|
||||
|
||||
```typescript
|
||||
tryAcquire(semaphoreOrMutex, new Error('new fancy error'))
|
||||
.runExclusive(() => {
|
||||
// ...
|
||||
});
|
||||
```
|
||||
# License
|
||||
|
||||
Feel free to use this library under the conditions of the MIT license.
|
||||
41
node_modules/async-mutex/es6/Mutex.js
generated
vendored
Normal file
41
node_modules/async-mutex/es6/Mutex.js
generated
vendored
Normal file
@@ -0,0 +1,41 @@
|
||||
import { __awaiter, __generator } from "tslib";
|
||||
import Semaphore from './Semaphore';
|
||||
var Mutex = /** @class */ (function () {
|
||||
function Mutex(cancelError) {
|
||||
this._semaphore = new Semaphore(1, cancelError);
|
||||
}
|
||||
Mutex.prototype.acquire = function () {
|
||||
return __awaiter(this, arguments, void 0, function (priority) {
|
||||
var _a, releaser;
|
||||
if (priority === void 0) { priority = 0; }
|
||||
return __generator(this, function (_b) {
|
||||
switch (_b.label) {
|
||||
case 0: return [4 /*yield*/, this._semaphore.acquire(1, priority)];
|
||||
case 1:
|
||||
_a = _b.sent(), releaser = _a[1];
|
||||
return [2 /*return*/, releaser];
|
||||
}
|
||||
});
|
||||
});
|
||||
};
|
||||
Mutex.prototype.runExclusive = function (callback, priority) {
|
||||
if (priority === void 0) { priority = 0; }
|
||||
return this._semaphore.runExclusive(function () { return callback(); }, 1, priority);
|
||||
};
|
||||
Mutex.prototype.isLocked = function () {
|
||||
return this._semaphore.isLocked();
|
||||
};
|
||||
Mutex.prototype.waitForUnlock = function (priority) {
|
||||
if (priority === void 0) { priority = 0; }
|
||||
return this._semaphore.waitForUnlock(1, priority);
|
||||
};
|
||||
Mutex.prototype.release = function () {
|
||||
if (this._semaphore.isLocked())
|
||||
this._semaphore.release();
|
||||
};
|
||||
Mutex.prototype.cancel = function () {
|
||||
return this._semaphore.cancel();
|
||||
};
|
||||
return Mutex;
|
||||
}());
|
||||
export default Mutex;
|
||||
1
node_modules/async-mutex/es6/MutexInterface.js
generated
vendored
Normal file
1
node_modules/async-mutex/es6/MutexInterface.js
generated
vendored
Normal file
@@ -0,0 +1 @@
|
||||
export {};
|
||||
153
node_modules/async-mutex/es6/Semaphore.js
generated
vendored
Normal file
153
node_modules/async-mutex/es6/Semaphore.js
generated
vendored
Normal file
@@ -0,0 +1,153 @@
|
||||
import { __awaiter, __generator } from "tslib";
|
||||
import { E_CANCELED } from './errors';
|
||||
var Semaphore = /** @class */ (function () {
|
||||
function Semaphore(_value, _cancelError) {
|
||||
if (_cancelError === void 0) { _cancelError = E_CANCELED; }
|
||||
this._value = _value;
|
||||
this._cancelError = _cancelError;
|
||||
this._queue = [];
|
||||
this._weightedWaiters = [];
|
||||
}
|
||||
Semaphore.prototype.acquire = function (weight, priority) {
|
||||
var _this = this;
|
||||
if (weight === void 0) { weight = 1; }
|
||||
if (priority === void 0) { priority = 0; }
|
||||
if (weight <= 0)
|
||||
throw new Error("invalid weight ".concat(weight, ": must be positive"));
|
||||
return new Promise(function (resolve, reject) {
|
||||
var task = { resolve: resolve, reject: reject, weight: weight, priority: priority };
|
||||
var i = findIndexFromEnd(_this._queue, function (other) { return priority <= other.priority; });
|
||||
if (i === -1 && weight <= _this._value) {
|
||||
// Needs immediate dispatch, skip the queue
|
||||
_this._dispatchItem(task);
|
||||
}
|
||||
else {
|
||||
_this._queue.splice(i + 1, 0, task);
|
||||
}
|
||||
});
|
||||
};
|
||||
Semaphore.prototype.runExclusive = function (callback_1) {
|
||||
return __awaiter(this, arguments, void 0, function (callback, weight, priority) {
|
||||
var _a, value, release;
|
||||
if (weight === void 0) { weight = 1; }
|
||||
if (priority === void 0) { priority = 0; }
|
||||
return __generator(this, function (_b) {
|
||||
switch (_b.label) {
|
||||
case 0: return [4 /*yield*/, this.acquire(weight, priority)];
|
||||
case 1:
|
||||
_a = _b.sent(), value = _a[0], release = _a[1];
|
||||
_b.label = 2;
|
||||
case 2:
|
||||
_b.trys.push([2, , 4, 5]);
|
||||
return [4 /*yield*/, callback(value)];
|
||||
case 3: return [2 /*return*/, _b.sent()];
|
||||
case 4:
|
||||
release();
|
||||
return [7 /*endfinally*/];
|
||||
case 5: return [2 /*return*/];
|
||||
}
|
||||
});
|
||||
});
|
||||
};
|
||||
Semaphore.prototype.waitForUnlock = function (weight, priority) {
|
||||
var _this = this;
|
||||
if (weight === void 0) { weight = 1; }
|
||||
if (priority === void 0) { priority = 0; }
|
||||
if (weight <= 0)
|
||||
throw new Error("invalid weight ".concat(weight, ": must be positive"));
|
||||
if (this._couldLockImmediately(weight, priority)) {
|
||||
return Promise.resolve();
|
||||
}
|
||||
else {
|
||||
return new Promise(function (resolve) {
|
||||
if (!_this._weightedWaiters[weight - 1])
|
||||
_this._weightedWaiters[weight - 1] = [];
|
||||
insertSorted(_this._weightedWaiters[weight - 1], { resolve: resolve, priority: priority });
|
||||
});
|
||||
}
|
||||
};
|
||||
Semaphore.prototype.isLocked = function () {
|
||||
return this._value <= 0;
|
||||
};
|
||||
Semaphore.prototype.getValue = function () {
|
||||
return this._value;
|
||||
};
|
||||
Semaphore.prototype.setValue = function (value) {
|
||||
this._value = value;
|
||||
this._dispatchQueue();
|
||||
};
|
||||
Semaphore.prototype.release = function (weight) {
|
||||
if (weight === void 0) { weight = 1; }
|
||||
if (weight <= 0)
|
||||
throw new Error("invalid weight ".concat(weight, ": must be positive"));
|
||||
this._value += weight;
|
||||
this._dispatchQueue();
|
||||
};
|
||||
Semaphore.prototype.cancel = function () {
|
||||
var _this = this;
|
||||
this._queue.forEach(function (entry) { return entry.reject(_this._cancelError); });
|
||||
this._queue = [];
|
||||
};
|
||||
Semaphore.prototype._dispatchQueue = function () {
|
||||
this._drainUnlockWaiters();
|
||||
while (this._queue.length > 0 && this._queue[0].weight <= this._value) {
|
||||
this._dispatchItem(this._queue.shift());
|
||||
this._drainUnlockWaiters();
|
||||
}
|
||||
};
|
||||
Semaphore.prototype._dispatchItem = function (item) {
|
||||
var previousValue = this._value;
|
||||
this._value -= item.weight;
|
||||
item.resolve([previousValue, this._newReleaser(item.weight)]);
|
||||
};
|
||||
Semaphore.prototype._newReleaser = function (weight) {
|
||||
var _this = this;
|
||||
var called = false;
|
||||
return function () {
|
||||
if (called)
|
||||
return;
|
||||
called = true;
|
||||
_this.release(weight);
|
||||
};
|
||||
};
|
||||
Semaphore.prototype._drainUnlockWaiters = function () {
|
||||
if (this._queue.length === 0) {
|
||||
for (var weight = this._value; weight > 0; weight--) {
|
||||
var waiters = this._weightedWaiters[weight - 1];
|
||||
if (!waiters)
|
||||
continue;
|
||||
waiters.forEach(function (waiter) { return waiter.resolve(); });
|
||||
this._weightedWaiters[weight - 1] = [];
|
||||
}
|
||||
}
|
||||
else {
|
||||
var queuedPriority_1 = this._queue[0].priority;
|
||||
for (var weight = this._value; weight > 0; weight--) {
|
||||
var waiters = this._weightedWaiters[weight - 1];
|
||||
if (!waiters)
|
||||
continue;
|
||||
var i = waiters.findIndex(function (waiter) { return waiter.priority <= queuedPriority_1; });
|
||||
(i === -1 ? waiters : waiters.splice(0, i))
|
||||
.forEach((function (waiter) { return waiter.resolve(); }));
|
||||
}
|
||||
}
|
||||
};
|
||||
Semaphore.prototype._couldLockImmediately = function (weight, priority) {
|
||||
return (this._queue.length === 0 || this._queue[0].priority < priority) &&
|
||||
weight <= this._value;
|
||||
};
|
||||
return Semaphore;
|
||||
}());
|
||||
function insertSorted(a, v) {
|
||||
var i = findIndexFromEnd(a, function (other) { return v.priority <= other.priority; });
|
||||
a.splice(i + 1, 0, v);
|
||||
}
|
||||
function findIndexFromEnd(a, predicate) {
|
||||
for (var i = a.length - 1; i >= 0; i--) {
|
||||
if (predicate(a[i])) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
export default Semaphore;
|
||||
1
node_modules/async-mutex/es6/SemaphoreInterface.js
generated
vendored
Normal file
1
node_modules/async-mutex/es6/SemaphoreInterface.js
generated
vendored
Normal file
@@ -0,0 +1 @@
|
||||
export {};
|
||||
3
node_modules/async-mutex/es6/errors.js
generated
vendored
Normal file
3
node_modules/async-mutex/es6/errors.js
generated
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
export var E_TIMEOUT = new Error('timeout while waiting for mutex to become available');
|
||||
export var E_ALREADY_LOCKED = new Error('mutex already locked');
|
||||
export var E_CANCELED = new Error('request for lock canceled');
|
||||
5
node_modules/async-mutex/es6/index.js
generated
vendored
Normal file
5
node_modules/async-mutex/es6/index.js
generated
vendored
Normal file
@@ -0,0 +1,5 @@
|
||||
export { default as Mutex } from './Mutex';
|
||||
export { default as Semaphore } from './Semaphore';
|
||||
export { withTimeout } from './withTimeout';
|
||||
export { tryAcquire } from './tryAcquire';
|
||||
export * from './errors';
|
||||
8
node_modules/async-mutex/es6/tryAcquire.js
generated
vendored
Normal file
8
node_modules/async-mutex/es6/tryAcquire.js
generated
vendored
Normal file
@@ -0,0 +1,8 @@
|
||||
import { E_ALREADY_LOCKED } from './errors';
|
||||
import { withTimeout } from './withTimeout';
|
||||
// eslint-disable-next-lisne @typescript-eslint/explicit-module-boundary-types
|
||||
export function tryAcquire(sync, alreadyAcquiredError) {
|
||||
if (alreadyAcquiredError === void 0) { alreadyAcquiredError = E_ALREADY_LOCKED; }
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
return withTimeout(sync, 0, alreadyAcquiredError);
|
||||
}
|
||||
124
node_modules/async-mutex/es6/withTimeout.js
generated
vendored
Normal file
124
node_modules/async-mutex/es6/withTimeout.js
generated
vendored
Normal file
@@ -0,0 +1,124 @@
|
||||
import { __awaiter, __generator } from "tslib";
|
||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||
import { E_TIMEOUT } from './errors';
|
||||
export function withTimeout(sync, timeout, timeoutError) {
|
||||
var _this = this;
|
||||
if (timeoutError === void 0) { timeoutError = E_TIMEOUT; }
|
||||
return {
|
||||
acquire: function (weightOrPriority, priority) {
|
||||
var weight;
|
||||
if (isSemaphore(sync)) {
|
||||
weight = weightOrPriority;
|
||||
}
|
||||
else {
|
||||
weight = undefined;
|
||||
priority = weightOrPriority;
|
||||
}
|
||||
if (weight !== undefined && weight <= 0) {
|
||||
throw new Error("invalid weight ".concat(weight, ": must be positive"));
|
||||
}
|
||||
return new Promise(function (resolve, reject) { return __awaiter(_this, void 0, void 0, function () {
|
||||
var isTimeout, handle, ticket, release, e_1;
|
||||
return __generator(this, function (_a) {
|
||||
switch (_a.label) {
|
||||
case 0:
|
||||
isTimeout = false;
|
||||
handle = setTimeout(function () {
|
||||
isTimeout = true;
|
||||
reject(timeoutError);
|
||||
}, timeout);
|
||||
_a.label = 1;
|
||||
case 1:
|
||||
_a.trys.push([1, 3, , 4]);
|
||||
return [4 /*yield*/, (isSemaphore(sync)
|
||||
? sync.acquire(weight, priority)
|
||||
: sync.acquire(priority))];
|
||||
case 2:
|
||||
ticket = _a.sent();
|
||||
if (isTimeout) {
|
||||
release = Array.isArray(ticket) ? ticket[1] : ticket;
|
||||
release();
|
||||
}
|
||||
else {
|
||||
clearTimeout(handle);
|
||||
resolve(ticket);
|
||||
}
|
||||
return [3 /*break*/, 4];
|
||||
case 3:
|
||||
e_1 = _a.sent();
|
||||
if (!isTimeout) {
|
||||
clearTimeout(handle);
|
||||
reject(e_1);
|
||||
}
|
||||
return [3 /*break*/, 4];
|
||||
case 4: return [2 /*return*/];
|
||||
}
|
||||
});
|
||||
}); });
|
||||
},
|
||||
runExclusive: function (callback, weight, priority) {
|
||||
return __awaiter(this, void 0, void 0, function () {
|
||||
var release, ticket;
|
||||
return __generator(this, function (_a) {
|
||||
switch (_a.label) {
|
||||
case 0:
|
||||
release = function () { return undefined; };
|
||||
_a.label = 1;
|
||||
case 1:
|
||||
_a.trys.push([1, , 7, 8]);
|
||||
return [4 /*yield*/, this.acquire(weight, priority)];
|
||||
case 2:
|
||||
ticket = _a.sent();
|
||||
if (!Array.isArray(ticket)) return [3 /*break*/, 4];
|
||||
release = ticket[1];
|
||||
return [4 /*yield*/, callback(ticket[0])];
|
||||
case 3: return [2 /*return*/, _a.sent()];
|
||||
case 4:
|
||||
release = ticket;
|
||||
return [4 /*yield*/, callback()];
|
||||
case 5: return [2 /*return*/, _a.sent()];
|
||||
case 6: return [3 /*break*/, 8];
|
||||
case 7:
|
||||
release();
|
||||
return [7 /*endfinally*/];
|
||||
case 8: return [2 /*return*/];
|
||||
}
|
||||
});
|
||||
});
|
||||
},
|
||||
release: function (weight) {
|
||||
sync.release(weight);
|
||||
},
|
||||
cancel: function () {
|
||||
return sync.cancel();
|
||||
},
|
||||
waitForUnlock: function (weightOrPriority, priority) {
|
||||
var weight;
|
||||
if (isSemaphore(sync)) {
|
||||
weight = weightOrPriority;
|
||||
}
|
||||
else {
|
||||
weight = undefined;
|
||||
priority = weightOrPriority;
|
||||
}
|
||||
if (weight !== undefined && weight <= 0) {
|
||||
throw new Error("invalid weight ".concat(weight, ": must be positive"));
|
||||
}
|
||||
return new Promise(function (resolve, reject) {
|
||||
var handle = setTimeout(function () { return reject(timeoutError); }, timeout);
|
||||
(isSemaphore(sync)
|
||||
? sync.waitForUnlock(weight, priority)
|
||||
: sync.waitForUnlock(priority)).then(function () {
|
||||
clearTimeout(handle);
|
||||
resolve();
|
||||
});
|
||||
});
|
||||
},
|
||||
isLocked: function () { return sync.isLocked(); },
|
||||
getValue: function () { return sync.getValue(); },
|
||||
setValue: function (value) { return sync.setValue(value); },
|
||||
};
|
||||
}
|
||||
function isSemaphore(sync) {
|
||||
return sync.getValue !== undefined;
|
||||
}
|
||||
291
node_modules/async-mutex/index.mjs
generated
vendored
Normal file
291
node_modules/async-mutex/index.mjs
generated
vendored
Normal file
@@ -0,0 +1,291 @@
|
||||
const E_TIMEOUT = new Error('timeout while waiting for mutex to become available');
|
||||
const E_ALREADY_LOCKED = new Error('mutex already locked');
|
||||
const E_CANCELED = new Error('request for lock canceled');
|
||||
|
||||
var __awaiter$2 = (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());
|
||||
});
|
||||
};
|
||||
class Semaphore {
|
||||
constructor(_value, _cancelError = E_CANCELED) {
|
||||
this._value = _value;
|
||||
this._cancelError = _cancelError;
|
||||
this._queue = [];
|
||||
this._weightedWaiters = [];
|
||||
}
|
||||
acquire(weight = 1, priority = 0) {
|
||||
if (weight <= 0)
|
||||
throw new Error(`invalid weight ${weight}: must be positive`);
|
||||
return new Promise((resolve, reject) => {
|
||||
const task = { resolve, reject, weight, priority };
|
||||
const i = findIndexFromEnd(this._queue, (other) => priority <= other.priority);
|
||||
if (i === -1 && weight <= this._value) {
|
||||
// Needs immediate dispatch, skip the queue
|
||||
this._dispatchItem(task);
|
||||
}
|
||||
else {
|
||||
this._queue.splice(i + 1, 0, task);
|
||||
}
|
||||
});
|
||||
}
|
||||
runExclusive(callback_1) {
|
||||
return __awaiter$2(this, arguments, void 0, function* (callback, weight = 1, priority = 0) {
|
||||
const [value, release] = yield this.acquire(weight, priority);
|
||||
try {
|
||||
return yield callback(value);
|
||||
}
|
||||
finally {
|
||||
release();
|
||||
}
|
||||
});
|
||||
}
|
||||
waitForUnlock(weight = 1, priority = 0) {
|
||||
if (weight <= 0)
|
||||
throw new Error(`invalid weight ${weight}: must be positive`);
|
||||
if (this._couldLockImmediately(weight, priority)) {
|
||||
return Promise.resolve();
|
||||
}
|
||||
else {
|
||||
return new Promise((resolve) => {
|
||||
if (!this._weightedWaiters[weight - 1])
|
||||
this._weightedWaiters[weight - 1] = [];
|
||||
insertSorted(this._weightedWaiters[weight - 1], { resolve, priority });
|
||||
});
|
||||
}
|
||||
}
|
||||
isLocked() {
|
||||
return this._value <= 0;
|
||||
}
|
||||
getValue() {
|
||||
return this._value;
|
||||
}
|
||||
setValue(value) {
|
||||
this._value = value;
|
||||
this._dispatchQueue();
|
||||
}
|
||||
release(weight = 1) {
|
||||
if (weight <= 0)
|
||||
throw new Error(`invalid weight ${weight}: must be positive`);
|
||||
this._value += weight;
|
||||
this._dispatchQueue();
|
||||
}
|
||||
cancel() {
|
||||
this._queue.forEach((entry) => entry.reject(this._cancelError));
|
||||
this._queue = [];
|
||||
}
|
||||
_dispatchQueue() {
|
||||
this._drainUnlockWaiters();
|
||||
while (this._queue.length > 0 && this._queue[0].weight <= this._value) {
|
||||
this._dispatchItem(this._queue.shift());
|
||||
this._drainUnlockWaiters();
|
||||
}
|
||||
}
|
||||
_dispatchItem(item) {
|
||||
const previousValue = this._value;
|
||||
this._value -= item.weight;
|
||||
item.resolve([previousValue, this._newReleaser(item.weight)]);
|
||||
}
|
||||
_newReleaser(weight) {
|
||||
let called = false;
|
||||
return () => {
|
||||
if (called)
|
||||
return;
|
||||
called = true;
|
||||
this.release(weight);
|
||||
};
|
||||
}
|
||||
_drainUnlockWaiters() {
|
||||
if (this._queue.length === 0) {
|
||||
for (let weight = this._value; weight > 0; weight--) {
|
||||
const waiters = this._weightedWaiters[weight - 1];
|
||||
if (!waiters)
|
||||
continue;
|
||||
waiters.forEach((waiter) => waiter.resolve());
|
||||
this._weightedWaiters[weight - 1] = [];
|
||||
}
|
||||
}
|
||||
else {
|
||||
const queuedPriority = this._queue[0].priority;
|
||||
for (let weight = this._value; weight > 0; weight--) {
|
||||
const waiters = this._weightedWaiters[weight - 1];
|
||||
if (!waiters)
|
||||
continue;
|
||||
const i = waiters.findIndex((waiter) => waiter.priority <= queuedPriority);
|
||||
(i === -1 ? waiters : waiters.splice(0, i))
|
||||
.forEach((waiter => waiter.resolve()));
|
||||
}
|
||||
}
|
||||
}
|
||||
_couldLockImmediately(weight, priority) {
|
||||
return (this._queue.length === 0 || this._queue[0].priority < priority) &&
|
||||
weight <= this._value;
|
||||
}
|
||||
}
|
||||
function insertSorted(a, v) {
|
||||
const i = findIndexFromEnd(a, (other) => v.priority <= other.priority);
|
||||
a.splice(i + 1, 0, v);
|
||||
}
|
||||
function findIndexFromEnd(a, predicate) {
|
||||
for (let i = a.length - 1; i >= 0; i--) {
|
||||
if (predicate(a[i])) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
var __awaiter$1 = (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());
|
||||
});
|
||||
};
|
||||
class Mutex {
|
||||
constructor(cancelError) {
|
||||
this._semaphore = new Semaphore(1, cancelError);
|
||||
}
|
||||
acquire() {
|
||||
return __awaiter$1(this, arguments, void 0, function* (priority = 0) {
|
||||
const [, releaser] = yield this._semaphore.acquire(1, priority);
|
||||
return releaser;
|
||||
});
|
||||
}
|
||||
runExclusive(callback, priority = 0) {
|
||||
return this._semaphore.runExclusive(() => callback(), 1, priority);
|
||||
}
|
||||
isLocked() {
|
||||
return this._semaphore.isLocked();
|
||||
}
|
||||
waitForUnlock(priority = 0) {
|
||||
return this._semaphore.waitForUnlock(1, priority);
|
||||
}
|
||||
release() {
|
||||
if (this._semaphore.isLocked())
|
||||
this._semaphore.release();
|
||||
}
|
||||
cancel() {
|
||||
return this._semaphore.cancel();
|
||||
}
|
||||
}
|
||||
|
||||
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());
|
||||
});
|
||||
};
|
||||
function withTimeout(sync, timeout, timeoutError = E_TIMEOUT) {
|
||||
return {
|
||||
acquire: (weightOrPriority, priority) => {
|
||||
let weight;
|
||||
if (isSemaphore(sync)) {
|
||||
weight = weightOrPriority;
|
||||
}
|
||||
else {
|
||||
weight = undefined;
|
||||
priority = weightOrPriority;
|
||||
}
|
||||
if (weight !== undefined && weight <= 0) {
|
||||
throw new Error(`invalid weight ${weight}: must be positive`);
|
||||
}
|
||||
return new Promise((resolve, reject) => __awaiter(this, void 0, void 0, function* () {
|
||||
let isTimeout = false;
|
||||
const handle = setTimeout(() => {
|
||||
isTimeout = true;
|
||||
reject(timeoutError);
|
||||
}, timeout);
|
||||
try {
|
||||
const ticket = yield (isSemaphore(sync)
|
||||
? sync.acquire(weight, priority)
|
||||
: sync.acquire(priority));
|
||||
if (isTimeout) {
|
||||
const release = Array.isArray(ticket) ? ticket[1] : ticket;
|
||||
release();
|
||||
}
|
||||
else {
|
||||
clearTimeout(handle);
|
||||
resolve(ticket);
|
||||
}
|
||||
}
|
||||
catch (e) {
|
||||
if (!isTimeout) {
|
||||
clearTimeout(handle);
|
||||
reject(e);
|
||||
}
|
||||
}
|
||||
}));
|
||||
},
|
||||
runExclusive(callback, weight, priority) {
|
||||
return __awaiter(this, void 0, void 0, function* () {
|
||||
let release = () => undefined;
|
||||
try {
|
||||
const ticket = yield this.acquire(weight, priority);
|
||||
if (Array.isArray(ticket)) {
|
||||
release = ticket[1];
|
||||
return yield callback(ticket[0]);
|
||||
}
|
||||
else {
|
||||
release = ticket;
|
||||
return yield callback();
|
||||
}
|
||||
}
|
||||
finally {
|
||||
release();
|
||||
}
|
||||
});
|
||||
},
|
||||
release(weight) {
|
||||
sync.release(weight);
|
||||
},
|
||||
cancel() {
|
||||
return sync.cancel();
|
||||
},
|
||||
waitForUnlock: (weightOrPriority, priority) => {
|
||||
let weight;
|
||||
if (isSemaphore(sync)) {
|
||||
weight = weightOrPriority;
|
||||
}
|
||||
else {
|
||||
weight = undefined;
|
||||
priority = weightOrPriority;
|
||||
}
|
||||
if (weight !== undefined && weight <= 0) {
|
||||
throw new Error(`invalid weight ${weight}: must be positive`);
|
||||
}
|
||||
return new Promise((resolve, reject) => {
|
||||
const handle = setTimeout(() => reject(timeoutError), timeout);
|
||||
(isSemaphore(sync)
|
||||
? sync.waitForUnlock(weight, priority)
|
||||
: sync.waitForUnlock(priority)).then(() => {
|
||||
clearTimeout(handle);
|
||||
resolve();
|
||||
});
|
||||
});
|
||||
},
|
||||
isLocked: () => sync.isLocked(),
|
||||
getValue: () => sync.getValue(),
|
||||
setValue: (value) => sync.setValue(value),
|
||||
};
|
||||
}
|
||||
function isSemaphore(sync) {
|
||||
return sync.getValue !== undefined;
|
||||
}
|
||||
|
||||
// eslint-disable-next-lisne @typescript-eslint/explicit-module-boundary-types
|
||||
function tryAcquire(sync, alreadyAcquiredError = E_ALREADY_LOCKED) {
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
return withTimeout(sync, 0, alreadyAcquiredError);
|
||||
}
|
||||
|
||||
export { E_ALREADY_LOCKED, E_CANCELED, E_TIMEOUT, Mutex, Semaphore, tryAcquire, withTimeout };
|
||||
12
node_modules/async-mutex/lib/Mutex.d.ts
generated
vendored
Normal file
12
node_modules/async-mutex/lib/Mutex.d.ts
generated
vendored
Normal file
@@ -0,0 +1,12 @@
|
||||
import MutexInterface from './MutexInterface';
|
||||
declare class Mutex implements MutexInterface {
|
||||
constructor(cancelError?: Error);
|
||||
acquire(priority?: number): Promise<MutexInterface.Releaser>;
|
||||
runExclusive<T>(callback: MutexInterface.Worker<T>, priority?: number): Promise<T>;
|
||||
isLocked(): boolean;
|
||||
waitForUnlock(priority?: number): Promise<void>;
|
||||
release(): void;
|
||||
cancel(): void;
|
||||
private _semaphore;
|
||||
}
|
||||
export default Mutex;
|
||||
43
node_modules/async-mutex/lib/Mutex.js
generated
vendored
Normal file
43
node_modules/async-mutex/lib/Mutex.js
generated
vendored
Normal file
@@ -0,0 +1,43 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
var tslib_1 = require("tslib");
|
||||
var Semaphore_1 = require("./Semaphore");
|
||||
var Mutex = /** @class */ (function () {
|
||||
function Mutex(cancelError) {
|
||||
this._semaphore = new Semaphore_1.default(1, cancelError);
|
||||
}
|
||||
Mutex.prototype.acquire = function () {
|
||||
return tslib_1.__awaiter(this, arguments, void 0, function (priority) {
|
||||
var _a, releaser;
|
||||
if (priority === void 0) { priority = 0; }
|
||||
return tslib_1.__generator(this, function (_b) {
|
||||
switch (_b.label) {
|
||||
case 0: return [4 /*yield*/, this._semaphore.acquire(1, priority)];
|
||||
case 1:
|
||||
_a = _b.sent(), releaser = _a[1];
|
||||
return [2 /*return*/, releaser];
|
||||
}
|
||||
});
|
||||
});
|
||||
};
|
||||
Mutex.prototype.runExclusive = function (callback, priority) {
|
||||
if (priority === void 0) { priority = 0; }
|
||||
return this._semaphore.runExclusive(function () { return callback(); }, 1, priority);
|
||||
};
|
||||
Mutex.prototype.isLocked = function () {
|
||||
return this._semaphore.isLocked();
|
||||
};
|
||||
Mutex.prototype.waitForUnlock = function (priority) {
|
||||
if (priority === void 0) { priority = 0; }
|
||||
return this._semaphore.waitForUnlock(1, priority);
|
||||
};
|
||||
Mutex.prototype.release = function () {
|
||||
if (this._semaphore.isLocked())
|
||||
this._semaphore.release();
|
||||
};
|
||||
Mutex.prototype.cancel = function () {
|
||||
return this._semaphore.cancel();
|
||||
};
|
||||
return Mutex;
|
||||
}());
|
||||
exports.default = Mutex;
|
||||
17
node_modules/async-mutex/lib/MutexInterface.d.ts
generated
vendored
Normal file
17
node_modules/async-mutex/lib/MutexInterface.d.ts
generated
vendored
Normal file
@@ -0,0 +1,17 @@
|
||||
interface MutexInterface {
|
||||
acquire(priority?: number): Promise<MutexInterface.Releaser>;
|
||||
runExclusive<T>(callback: MutexInterface.Worker<T>, priority?: number): Promise<T>;
|
||||
waitForUnlock(priority?: number): Promise<void>;
|
||||
isLocked(): boolean;
|
||||
release(): void;
|
||||
cancel(): void;
|
||||
}
|
||||
declare namespace MutexInterface {
|
||||
interface Releaser {
|
||||
(): void;
|
||||
}
|
||||
interface Worker<T> {
|
||||
(): Promise<T> | T;
|
||||
}
|
||||
}
|
||||
export default MutexInterface;
|
||||
2
node_modules/async-mutex/lib/MutexInterface.js
generated
vendored
Normal file
2
node_modules/async-mutex/lib/MutexInterface.js
generated
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
22
node_modules/async-mutex/lib/Semaphore.d.ts
generated
vendored
Normal file
22
node_modules/async-mutex/lib/Semaphore.d.ts
generated
vendored
Normal file
@@ -0,0 +1,22 @@
|
||||
import SemaphoreInterface from './SemaphoreInterface';
|
||||
declare class Semaphore implements SemaphoreInterface {
|
||||
private _value;
|
||||
private _cancelError;
|
||||
constructor(_value: number, _cancelError?: Error);
|
||||
acquire(weight?: number, priority?: number): Promise<[number, SemaphoreInterface.Releaser]>;
|
||||
runExclusive<T>(callback: SemaphoreInterface.Worker<T>, weight?: number, priority?: number): Promise<T>;
|
||||
waitForUnlock(weight?: number, priority?: number): Promise<void>;
|
||||
isLocked(): boolean;
|
||||
getValue(): number;
|
||||
setValue(value: number): void;
|
||||
release(weight?: number): void;
|
||||
cancel(): void;
|
||||
private _dispatchQueue;
|
||||
private _dispatchItem;
|
||||
private _newReleaser;
|
||||
private _drainUnlockWaiters;
|
||||
private _couldLockImmediately;
|
||||
private _queue;
|
||||
private _weightedWaiters;
|
||||
}
|
||||
export default Semaphore;
|
||||
155
node_modules/async-mutex/lib/Semaphore.js
generated
vendored
Normal file
155
node_modules/async-mutex/lib/Semaphore.js
generated
vendored
Normal file
@@ -0,0 +1,155 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
var tslib_1 = require("tslib");
|
||||
var errors_1 = require("./errors");
|
||||
var Semaphore = /** @class */ (function () {
|
||||
function Semaphore(_value, _cancelError) {
|
||||
if (_cancelError === void 0) { _cancelError = errors_1.E_CANCELED; }
|
||||
this._value = _value;
|
||||
this._cancelError = _cancelError;
|
||||
this._queue = [];
|
||||
this._weightedWaiters = [];
|
||||
}
|
||||
Semaphore.prototype.acquire = function (weight, priority) {
|
||||
var _this = this;
|
||||
if (weight === void 0) { weight = 1; }
|
||||
if (priority === void 0) { priority = 0; }
|
||||
if (weight <= 0)
|
||||
throw new Error("invalid weight ".concat(weight, ": must be positive"));
|
||||
return new Promise(function (resolve, reject) {
|
||||
var task = { resolve: resolve, reject: reject, weight: weight, priority: priority };
|
||||
var i = findIndexFromEnd(_this._queue, function (other) { return priority <= other.priority; });
|
||||
if (i === -1 && weight <= _this._value) {
|
||||
// Needs immediate dispatch, skip the queue
|
||||
_this._dispatchItem(task);
|
||||
}
|
||||
else {
|
||||
_this._queue.splice(i + 1, 0, task);
|
||||
}
|
||||
});
|
||||
};
|
||||
Semaphore.prototype.runExclusive = function (callback_1) {
|
||||
return tslib_1.__awaiter(this, arguments, void 0, function (callback, weight, priority) {
|
||||
var _a, value, release;
|
||||
if (weight === void 0) { weight = 1; }
|
||||
if (priority === void 0) { priority = 0; }
|
||||
return tslib_1.__generator(this, function (_b) {
|
||||
switch (_b.label) {
|
||||
case 0: return [4 /*yield*/, this.acquire(weight, priority)];
|
||||
case 1:
|
||||
_a = _b.sent(), value = _a[0], release = _a[1];
|
||||
_b.label = 2;
|
||||
case 2:
|
||||
_b.trys.push([2, , 4, 5]);
|
||||
return [4 /*yield*/, callback(value)];
|
||||
case 3: return [2 /*return*/, _b.sent()];
|
||||
case 4:
|
||||
release();
|
||||
return [7 /*endfinally*/];
|
||||
case 5: return [2 /*return*/];
|
||||
}
|
||||
});
|
||||
});
|
||||
};
|
||||
Semaphore.prototype.waitForUnlock = function (weight, priority) {
|
||||
var _this = this;
|
||||
if (weight === void 0) { weight = 1; }
|
||||
if (priority === void 0) { priority = 0; }
|
||||
if (weight <= 0)
|
||||
throw new Error("invalid weight ".concat(weight, ": must be positive"));
|
||||
if (this._couldLockImmediately(weight, priority)) {
|
||||
return Promise.resolve();
|
||||
}
|
||||
else {
|
||||
return new Promise(function (resolve) {
|
||||
if (!_this._weightedWaiters[weight - 1])
|
||||
_this._weightedWaiters[weight - 1] = [];
|
||||
insertSorted(_this._weightedWaiters[weight - 1], { resolve: resolve, priority: priority });
|
||||
});
|
||||
}
|
||||
};
|
||||
Semaphore.prototype.isLocked = function () {
|
||||
return this._value <= 0;
|
||||
};
|
||||
Semaphore.prototype.getValue = function () {
|
||||
return this._value;
|
||||
};
|
||||
Semaphore.prototype.setValue = function (value) {
|
||||
this._value = value;
|
||||
this._dispatchQueue();
|
||||
};
|
||||
Semaphore.prototype.release = function (weight) {
|
||||
if (weight === void 0) { weight = 1; }
|
||||
if (weight <= 0)
|
||||
throw new Error("invalid weight ".concat(weight, ": must be positive"));
|
||||
this._value += weight;
|
||||
this._dispatchQueue();
|
||||
};
|
||||
Semaphore.prototype.cancel = function () {
|
||||
var _this = this;
|
||||
this._queue.forEach(function (entry) { return entry.reject(_this._cancelError); });
|
||||
this._queue = [];
|
||||
};
|
||||
Semaphore.prototype._dispatchQueue = function () {
|
||||
this._drainUnlockWaiters();
|
||||
while (this._queue.length > 0 && this._queue[0].weight <= this._value) {
|
||||
this._dispatchItem(this._queue.shift());
|
||||
this._drainUnlockWaiters();
|
||||
}
|
||||
};
|
||||
Semaphore.prototype._dispatchItem = function (item) {
|
||||
var previousValue = this._value;
|
||||
this._value -= item.weight;
|
||||
item.resolve([previousValue, this._newReleaser(item.weight)]);
|
||||
};
|
||||
Semaphore.prototype._newReleaser = function (weight) {
|
||||
var _this = this;
|
||||
var called = false;
|
||||
return function () {
|
||||
if (called)
|
||||
return;
|
||||
called = true;
|
||||
_this.release(weight);
|
||||
};
|
||||
};
|
||||
Semaphore.prototype._drainUnlockWaiters = function () {
|
||||
if (this._queue.length === 0) {
|
||||
for (var weight = this._value; weight > 0; weight--) {
|
||||
var waiters = this._weightedWaiters[weight - 1];
|
||||
if (!waiters)
|
||||
continue;
|
||||
waiters.forEach(function (waiter) { return waiter.resolve(); });
|
||||
this._weightedWaiters[weight - 1] = [];
|
||||
}
|
||||
}
|
||||
else {
|
||||
var queuedPriority_1 = this._queue[0].priority;
|
||||
for (var weight = this._value; weight > 0; weight--) {
|
||||
var waiters = this._weightedWaiters[weight - 1];
|
||||
if (!waiters)
|
||||
continue;
|
||||
var i = waiters.findIndex(function (waiter) { return waiter.priority <= queuedPriority_1; });
|
||||
(i === -1 ? waiters : waiters.splice(0, i))
|
||||
.forEach((function (waiter) { return waiter.resolve(); }));
|
||||
}
|
||||
}
|
||||
};
|
||||
Semaphore.prototype._couldLockImmediately = function (weight, priority) {
|
||||
return (this._queue.length === 0 || this._queue[0].priority < priority) &&
|
||||
weight <= this._value;
|
||||
};
|
||||
return Semaphore;
|
||||
}());
|
||||
function insertSorted(a, v) {
|
||||
var i = findIndexFromEnd(a, function (other) { return v.priority <= other.priority; });
|
||||
a.splice(i + 1, 0, v);
|
||||
}
|
||||
function findIndexFromEnd(a, predicate) {
|
||||
for (var i = a.length - 1; i >= 0; i--) {
|
||||
if (predicate(a[i])) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
exports.default = Semaphore;
|
||||
19
node_modules/async-mutex/lib/SemaphoreInterface.d.ts
generated
vendored
Normal file
19
node_modules/async-mutex/lib/SemaphoreInterface.d.ts
generated
vendored
Normal file
@@ -0,0 +1,19 @@
|
||||
interface SemaphoreInterface {
|
||||
acquire(weight?: number, priority?: number): Promise<[number, SemaphoreInterface.Releaser]>;
|
||||
runExclusive<T>(callback: SemaphoreInterface.Worker<T>, weight?: number, priority?: number): Promise<T>;
|
||||
waitForUnlock(weight?: number, priority?: number): Promise<void>;
|
||||
isLocked(): boolean;
|
||||
getValue(): number;
|
||||
setValue(value: number): void;
|
||||
release(weight?: number): void;
|
||||
cancel(): void;
|
||||
}
|
||||
declare namespace SemaphoreInterface {
|
||||
interface Releaser {
|
||||
(): void;
|
||||
}
|
||||
interface Worker<T> {
|
||||
(value: number): Promise<T> | T;
|
||||
}
|
||||
}
|
||||
export default SemaphoreInterface;
|
||||
2
node_modules/async-mutex/lib/SemaphoreInterface.js
generated
vendored
Normal file
2
node_modules/async-mutex/lib/SemaphoreInterface.js
generated
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
3
node_modules/async-mutex/lib/errors.d.ts
generated
vendored
Normal file
3
node_modules/async-mutex/lib/errors.d.ts
generated
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
export declare const E_TIMEOUT: Error;
|
||||
export declare const E_ALREADY_LOCKED: Error;
|
||||
export declare const E_CANCELED: Error;
|
||||
6
node_modules/async-mutex/lib/errors.js
generated
vendored
Normal file
6
node_modules/async-mutex/lib/errors.js
generated
vendored
Normal file
@@ -0,0 +1,6 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.E_CANCELED = exports.E_ALREADY_LOCKED = exports.E_TIMEOUT = void 0;
|
||||
exports.E_TIMEOUT = new Error('timeout while waiting for mutex to become available');
|
||||
exports.E_ALREADY_LOCKED = new Error('mutex already locked');
|
||||
exports.E_CANCELED = new Error('request for lock canceled');
|
||||
7
node_modules/async-mutex/lib/index.d.ts
generated
vendored
Normal file
7
node_modules/async-mutex/lib/index.d.ts
generated
vendored
Normal file
@@ -0,0 +1,7 @@
|
||||
export { default as Mutex } from './Mutex';
|
||||
export { default as MutexInterface } from './MutexInterface';
|
||||
export { default as Semaphore } from './Semaphore';
|
||||
export { default as SemaphoreInterface } from './SemaphoreInterface';
|
||||
export { withTimeout } from './withTimeout';
|
||||
export { tryAcquire } from './tryAcquire';
|
||||
export * from './errors';
|
||||
13
node_modules/async-mutex/lib/index.js
generated
vendored
Normal file
13
node_modules/async-mutex/lib/index.js
generated
vendored
Normal file
@@ -0,0 +1,13 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.tryAcquire = exports.withTimeout = exports.Semaphore = exports.Mutex = void 0;
|
||||
var tslib_1 = require("tslib");
|
||||
var Mutex_1 = require("./Mutex");
|
||||
Object.defineProperty(exports, "Mutex", { enumerable: true, get: function () { return Mutex_1.default; } });
|
||||
var Semaphore_1 = require("./Semaphore");
|
||||
Object.defineProperty(exports, "Semaphore", { enumerable: true, get: function () { return Semaphore_1.default; } });
|
||||
var withTimeout_1 = require("./withTimeout");
|
||||
Object.defineProperty(exports, "withTimeout", { enumerable: true, get: function () { return withTimeout_1.withTimeout; } });
|
||||
var tryAcquire_1 = require("./tryAcquire");
|
||||
Object.defineProperty(exports, "tryAcquire", { enumerable: true, get: function () { return tryAcquire_1.tryAcquire; } });
|
||||
tslib_1.__exportStar(require("./errors"), exports);
|
||||
4
node_modules/async-mutex/lib/tryAcquire.d.ts
generated
vendored
Normal file
4
node_modules/async-mutex/lib/tryAcquire.d.ts
generated
vendored
Normal file
@@ -0,0 +1,4 @@
|
||||
import MutexInterface from './MutexInterface';
|
||||
import SemaphoreInterface from './SemaphoreInterface';
|
||||
export declare function tryAcquire(mutex: MutexInterface, alreadyAcquiredError?: Error): MutexInterface;
|
||||
export declare function tryAcquire(semaphore: SemaphoreInterface, alreadyAcquiredError?: Error): SemaphoreInterface;
|
||||
12
node_modules/async-mutex/lib/tryAcquire.js
generated
vendored
Normal file
12
node_modules/async-mutex/lib/tryAcquire.js
generated
vendored
Normal file
@@ -0,0 +1,12 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.tryAcquire = void 0;
|
||||
var errors_1 = require("./errors");
|
||||
var withTimeout_1 = require("./withTimeout");
|
||||
// eslint-disable-next-lisne @typescript-eslint/explicit-module-boundary-types
|
||||
function tryAcquire(sync, alreadyAcquiredError) {
|
||||
if (alreadyAcquiredError === void 0) { alreadyAcquiredError = errors_1.E_ALREADY_LOCKED; }
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
return (0, withTimeout_1.withTimeout)(sync, 0, alreadyAcquiredError);
|
||||
}
|
||||
exports.tryAcquire = tryAcquire;
|
||||
4
node_modules/async-mutex/lib/withTimeout.d.ts
generated
vendored
Normal file
4
node_modules/async-mutex/lib/withTimeout.d.ts
generated
vendored
Normal file
@@ -0,0 +1,4 @@
|
||||
import MutexInterface from './MutexInterface';
|
||||
import SemaphoreInterface from './SemaphoreInterface';
|
||||
export declare function withTimeout(mutex: MutexInterface, timeout: number, timeoutError?: Error): MutexInterface;
|
||||
export declare function withTimeout(semaphore: SemaphoreInterface, timeout: number, timeoutError?: Error): SemaphoreInterface;
|
||||
128
node_modules/async-mutex/lib/withTimeout.js
generated
vendored
Normal file
128
node_modules/async-mutex/lib/withTimeout.js
generated
vendored
Normal file
@@ -0,0 +1,128 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.withTimeout = void 0;
|
||||
var tslib_1 = require("tslib");
|
||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||
var errors_1 = require("./errors");
|
||||
function withTimeout(sync, timeout, timeoutError) {
|
||||
var _this = this;
|
||||
if (timeoutError === void 0) { timeoutError = errors_1.E_TIMEOUT; }
|
||||
return {
|
||||
acquire: function (weightOrPriority, priority) {
|
||||
var weight;
|
||||
if (isSemaphore(sync)) {
|
||||
weight = weightOrPriority;
|
||||
}
|
||||
else {
|
||||
weight = undefined;
|
||||
priority = weightOrPriority;
|
||||
}
|
||||
if (weight !== undefined && weight <= 0) {
|
||||
throw new Error("invalid weight ".concat(weight, ": must be positive"));
|
||||
}
|
||||
return new Promise(function (resolve, reject) { return tslib_1.__awaiter(_this, void 0, void 0, function () {
|
||||
var isTimeout, handle, ticket, release, e_1;
|
||||
return tslib_1.__generator(this, function (_a) {
|
||||
switch (_a.label) {
|
||||
case 0:
|
||||
isTimeout = false;
|
||||
handle = setTimeout(function () {
|
||||
isTimeout = true;
|
||||
reject(timeoutError);
|
||||
}, timeout);
|
||||
_a.label = 1;
|
||||
case 1:
|
||||
_a.trys.push([1, 3, , 4]);
|
||||
return [4 /*yield*/, (isSemaphore(sync)
|
||||
? sync.acquire(weight, priority)
|
||||
: sync.acquire(priority))];
|
||||
case 2:
|
||||
ticket = _a.sent();
|
||||
if (isTimeout) {
|
||||
release = Array.isArray(ticket) ? ticket[1] : ticket;
|
||||
release();
|
||||
}
|
||||
else {
|
||||
clearTimeout(handle);
|
||||
resolve(ticket);
|
||||
}
|
||||
return [3 /*break*/, 4];
|
||||
case 3:
|
||||
e_1 = _a.sent();
|
||||
if (!isTimeout) {
|
||||
clearTimeout(handle);
|
||||
reject(e_1);
|
||||
}
|
||||
return [3 /*break*/, 4];
|
||||
case 4: return [2 /*return*/];
|
||||
}
|
||||
});
|
||||
}); });
|
||||
},
|
||||
runExclusive: function (callback, weight, priority) {
|
||||
return tslib_1.__awaiter(this, void 0, void 0, function () {
|
||||
var release, ticket;
|
||||
return tslib_1.__generator(this, function (_a) {
|
||||
switch (_a.label) {
|
||||
case 0:
|
||||
release = function () { return undefined; };
|
||||
_a.label = 1;
|
||||
case 1:
|
||||
_a.trys.push([1, , 7, 8]);
|
||||
return [4 /*yield*/, this.acquire(weight, priority)];
|
||||
case 2:
|
||||
ticket = _a.sent();
|
||||
if (!Array.isArray(ticket)) return [3 /*break*/, 4];
|
||||
release = ticket[1];
|
||||
return [4 /*yield*/, callback(ticket[0])];
|
||||
case 3: return [2 /*return*/, _a.sent()];
|
||||
case 4:
|
||||
release = ticket;
|
||||
return [4 /*yield*/, callback()];
|
||||
case 5: return [2 /*return*/, _a.sent()];
|
||||
case 6: return [3 /*break*/, 8];
|
||||
case 7:
|
||||
release();
|
||||
return [7 /*endfinally*/];
|
||||
case 8: return [2 /*return*/];
|
||||
}
|
||||
});
|
||||
});
|
||||
},
|
||||
release: function (weight) {
|
||||
sync.release(weight);
|
||||
},
|
||||
cancel: function () {
|
||||
return sync.cancel();
|
||||
},
|
||||
waitForUnlock: function (weightOrPriority, priority) {
|
||||
var weight;
|
||||
if (isSemaphore(sync)) {
|
||||
weight = weightOrPriority;
|
||||
}
|
||||
else {
|
||||
weight = undefined;
|
||||
priority = weightOrPriority;
|
||||
}
|
||||
if (weight !== undefined && weight <= 0) {
|
||||
throw new Error("invalid weight ".concat(weight, ": must be positive"));
|
||||
}
|
||||
return new Promise(function (resolve, reject) {
|
||||
var handle = setTimeout(function () { return reject(timeoutError); }, timeout);
|
||||
(isSemaphore(sync)
|
||||
? sync.waitForUnlock(weight, priority)
|
||||
: sync.waitForUnlock(priority)).then(function () {
|
||||
clearTimeout(handle);
|
||||
resolve();
|
||||
});
|
||||
});
|
||||
},
|
||||
isLocked: function () { return sync.isLocked(); },
|
||||
getValue: function () { return sync.getValue(); },
|
||||
setValue: function (value) { return sync.setValue(value); },
|
||||
};
|
||||
}
|
||||
exports.withTimeout = withTimeout;
|
||||
function isSemaphore(sync) {
|
||||
return sync.getValue !== undefined;
|
||||
}
|
||||
88
node_modules/async-mutex/package.json
generated
vendored
Normal file
88
node_modules/async-mutex/package.json
generated
vendored
Normal file
@@ -0,0 +1,88 @@
|
||||
{
|
||||
"name": "async-mutex",
|
||||
"version": "0.5.0",
|
||||
"description": "A mutex for guarding async workflows",
|
||||
"scripts": {
|
||||
"lint": "eslint src/**/*.ts test/**/*.ts",
|
||||
"build": "tsc && tsc -p tsconfig.es6.json && tsc -p tsconfig.mjs.json && rollup -o index.mjs mjs/index.js",
|
||||
"prepublishOnly": "yarn test && yarn build",
|
||||
"test": "yarn lint && nyc --reporter=text --reporter=html --reporter=lcov mocha test/*.ts",
|
||||
"coveralls": "cat ./coverage/lcov.info | coveralls"
|
||||
},
|
||||
"author": "Christian Speckner <cnspeckn@googlemail.com> (https://github.com/DirtyHairy/)",
|
||||
"license": "MIT",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/DirtyHairy/async-mutex"
|
||||
},
|
||||
"prettier": {
|
||||
"printWidth": 120,
|
||||
"tabWidth": 4,
|
||||
"singleQuote": true,
|
||||
"parser": "typescript"
|
||||
},
|
||||
"importSort": {
|
||||
".js, .jsx, .ts, .tsx": {
|
||||
"style": "eslint",
|
||||
"parser": "typescript"
|
||||
}
|
||||
},
|
||||
"eslintConfig": {
|
||||
"root": true,
|
||||
"parser": "@typescript-eslint/parser",
|
||||
"plugins": [
|
||||
"@typescript-eslint"
|
||||
],
|
||||
"extends": [
|
||||
"eslint:recommended",
|
||||
"plugin:@typescript-eslint/eslint-recommended",
|
||||
"plugin:@typescript-eslint/recommended"
|
||||
],
|
||||
"rules": {
|
||||
"eqeqeq": "error",
|
||||
"@typescript-eslint/no-namespace": "off",
|
||||
"no-async-promise-executor": "off"
|
||||
}
|
||||
},
|
||||
"keywords": [
|
||||
"mutex",
|
||||
"async"
|
||||
],
|
||||
"files": [
|
||||
"lib",
|
||||
"es6",
|
||||
"index.mjs"
|
||||
],
|
||||
"devDependencies": {
|
||||
"@sinonjs/fake-timers": "^11.2.2",
|
||||
"@types/mocha": "^10.0.6",
|
||||
"@types/node": "^20.11.25",
|
||||
"@types/sinonjs__fake-timers": "^8.1.2",
|
||||
"@typescript-eslint/eslint-plugin": "^7.2.0",
|
||||
"@typescript-eslint/parser": "^7.2.0",
|
||||
"coveralls": "^3.1.1",
|
||||
"eslint": "^8.57.0",
|
||||
"import-sort-style-eslint": "^6.0.0",
|
||||
"mocha": "^10.3.0",
|
||||
"nyc": "^15.1.0",
|
||||
"prettier": "^3.2.5",
|
||||
"prettier-plugin-import-sort": "^0.0.7",
|
||||
"rollup": "^4.12.1",
|
||||
"ts-node": "^10.9.1",
|
||||
"typescript": "^5.4.2"
|
||||
},
|
||||
"main": "lib/index.js",
|
||||
"module": "es6/index.js",
|
||||
"types": "lib/index.d.ts",
|
||||
"exports": {
|
||||
".": {
|
||||
"import": "./index.mjs",
|
||||
"require": "./lib/index.js",
|
||||
"default": "./lib/index.js"
|
||||
},
|
||||
"./package.json": "./package.json"
|
||||
},
|
||||
"dependencies": {
|
||||
"tslib": "^2.4.0"
|
||||
}
|
||||
}
|
||||
18
package-lock.json
generated
18
package-lock.json
generated
@@ -12,6 +12,7 @@
|
||||
"@azure/core-auth": "^1.10.1",
|
||||
"@azure/identity": "^4.12.0",
|
||||
"@azure/openai": "^2.0.0",
|
||||
"async-mutex": "^0.5.0",
|
||||
"axios": "^1.13.2",
|
||||
"body-parser": "^2.2.0",
|
||||
"cors": "^2.8.5",
|
||||
@@ -257,6 +258,15 @@
|
||||
"integrity": "sha512-klpgFSWLW1ZEs8svjfb7g4qWY0YS5imI82dTg+QahUvJ8YqAY0P10Uk8tTyh9ZGuYEZEMaeJYCF5BFuX552hsw==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/async-mutex": {
|
||||
"version": "0.5.0",
|
||||
"resolved": "https://registry.npmmirror.com/async-mutex/-/async-mutex-0.5.0.tgz",
|
||||
"integrity": "sha512-1A94B18jkJ3DYq284ohPxoXbfTA5HsQ7/Mf4DEhcyLx3Bz27Rh59iScbB6EPiP+B+joue6YCxcMXSbFC1tZKwA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"tslib": "^2.4.0"
|
||||
}
|
||||
},
|
||||
"node_modules/asynckit": {
|
||||
"version": "0.4.0",
|
||||
"resolved": "https://registry.npmmirror.com/asynckit/-/asynckit-0.4.0.tgz",
|
||||
@@ -2030,6 +2040,14 @@
|
||||
"resolved": "https://registry.npmjs.org/append-field/-/append-field-1.0.0.tgz",
|
||||
"integrity": "sha512-klpgFSWLW1ZEs8svjfb7g4qWY0YS5imI82dTg+QahUvJ8YqAY0P10Uk8tTyh9ZGuYEZEMaeJYCF5BFuX552hsw=="
|
||||
},
|
||||
"async-mutex": {
|
||||
"version": "0.5.0",
|
||||
"resolved": "https://registry.npmmirror.com/async-mutex/-/async-mutex-0.5.0.tgz",
|
||||
"integrity": "sha512-1A94B18jkJ3DYq284ohPxoXbfTA5HsQ7/Mf4DEhcyLx3Bz27Rh59iScbB6EPiP+B+joue6YCxcMXSbFC1tZKwA==",
|
||||
"requires": {
|
||||
"tslib": "^2.4.0"
|
||||
}
|
||||
},
|
||||
"asynckit": {
|
||||
"version": "0.4.0",
|
||||
"resolved": "https://registry.npmmirror.com/asynckit/-/asynckit-0.4.0.tgz",
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
"@azure/core-auth": "^1.10.1",
|
||||
"@azure/identity": "^4.12.0",
|
||||
"@azure/openai": "^2.0.0",
|
||||
"async-mutex": "^0.5.0",
|
||||
"axios": "^1.13.2",
|
||||
"body-parser": "^2.2.0",
|
||||
"cors": "^2.8.5",
|
||||
|
||||
89
server2.js
89
server2.js
@@ -874,12 +874,86 @@ app.post("/session", async (req, res) => {
|
||||
|
||||
const stopFlags = new Map();
|
||||
|
||||
|
||||
|
||||
const { Mutex } = require('async-mutex');
|
||||
const aiChatNum = new Map(); // userId -> number
|
||||
const mutexes = new Map(); // userId -> Mutex
|
||||
|
||||
function getMutex(userId) {
|
||||
if (!mutexes.has(userId)) mutexes.set(userId, new Mutex());
|
||||
return mutexes.get(userId);
|
||||
}
|
||||
|
||||
async function setQuota(userId,num) {
|
||||
const m = getMutex(userId);
|
||||
return await m.runExclusive(async () => {
|
||||
console.log("更新用户对话次数:", userId, num);
|
||||
aiChatNum.set(userId, num);
|
||||
});
|
||||
}
|
||||
|
||||
async function hasQuota(userId) {
|
||||
const m = getMutex(userId);
|
||||
return await m.runExclusive(async () => {
|
||||
console.log("更新用户对话次数:", userId, num);
|
||||
return aiChatNum.has(userId);
|
||||
});
|
||||
}
|
||||
|
||||
async function getQuota(userId) {
|
||||
const m = getMutex(userId);
|
||||
return await m.runExclusive(async () => {
|
||||
console.log("更新用户对话次数:", userId, num);
|
||||
return aiChatNum.get(userId);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
async function removeQuota(userId) {
|
||||
const m = getMutex(userId);
|
||||
return await m.runExclusive(async () => {
|
||||
aiChatNum.delete(userId);
|
||||
});
|
||||
}
|
||||
|
||||
// ====================== API ======================
|
||||
app.post("/updateAiChatNum", async (req, res) => {
|
||||
const { userId } = req.body;
|
||||
console.log(userId,98888888888);
|
||||
if(!hasQuota(userId)){
|
||||
return res.status(400).json({ error: "用户不存在" });
|
||||
}else{
|
||||
// 删除当前内存map存放的可用数量
|
||||
removeQuota(userId);
|
||||
await pool.query(
|
||||
"update sys_user set ai_chat_num = ? where user_id= ?",
|
||||
[aiChatNum.get(userId),userId]
|
||||
);
|
||||
res.json({ code: 0, message: "更新成功" });
|
||||
}
|
||||
})
|
||||
|
||||
app.post("/analyze", upload.single("file"), async (req, res) => {
|
||||
try
|
||||
{
|
||||
|
||||
const { requestId } = req.body;
|
||||
const { userId } = req.body;
|
||||
if(!hasQuota(userId)){
|
||||
const [userRemainChatNum] = await pool.query(
|
||||
"SELECT ai_chat_num from sys_user where user_id= ?",
|
||||
[userId]
|
||||
);
|
||||
if (userRemainChatNum.length>0) {
|
||||
setQuota(userId,userRemainChatNum[0].ai_chat_num);
|
||||
}
|
||||
}else{
|
||||
if(getQuota(userId)<=0 && getQuota(userId)!==-1){
|
||||
return res.status(400).json({ error: "对话次数已用完,请完成账号注册" });
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
const { requestId } = req.body;
|
||||
|
||||
|
||||
res.setHeader("Content-Type", "text/plain; charset=utf-8");
|
||||
@@ -907,7 +981,7 @@ app.post("/analyze", upload.single("file"), async (req, res) => {
|
||||
console.log("缺少必要参数");
|
||||
return res.status(400).json({ error: "缺少必要参数" });
|
||||
}
|
||||
const { userPrompt, imageBase64, userId, sessionId } = req.body;
|
||||
const { userPrompt, imageBase64, sessionId } = req.body;
|
||||
const [rows] = await pool.query(
|
||||
`
|
||||
SELECT EXISTS(
|
||||
@@ -1171,6 +1245,15 @@ const existingTitle = rows[0].hasTitle === 1; // true / false
|
||||
}
|
||||
console.log("====== 分析完成 ======");
|
||||
console.timeEnd("analyze");
|
||||
|
||||
// 更新用户剩余对话次数
|
||||
let userChatNum = aiChatNum.get(userId);
|
||||
|
||||
if(userChatNum!==-1){
|
||||
userChatNum = Math.max(0, userChatNum - 1); // 防止变成负数
|
||||
setQuota(userId,userChatNum);
|
||||
console.log(`用户剩余AI 对话次数: ${userChatNum}`);
|
||||
}
|
||||
}
|
||||
catch (err)
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user