Set up the source tree for the typescript rewrite. Nothing really here just yet, just filler

This commit is contained in:
Elijah R 2024-02-01 17:53:44 -05:00
parent f99e8bdb2e
commit a6a28ef518
17 changed files with 129 additions and 1375 deletions

4
.gitignore vendored
View File

@ -1,3 +1,3 @@
node_modules/
dist/*.js
dist/*.js.LICENSE.txt
dist/
.parcel-cache/

View File

@ -1,17 +0,0 @@
{
"presets": [
[
"@babel/preset-env",
{
"targets": {
"edge": "17",
"firefox": "60",
"chrome": "67",
"safari": "11.1"
},
"useBuiltIns": "usage",
"corejs": "3.6.5"
}
]
]
}

View File

@ -4,21 +4,22 @@
"description": "kill me",
"private": true,
"scripts": {
"build": "webpack --config webpack.config.js",
"serve": "serve dist/"
"build": "parcel build --dist-dir dist src/html/index.html",
"serve": "parcel src/html/index.html"
},
"author": "Elijah R",
"license": "GPL-3.0",
"dependencies": {
"@hcaptcha/types": "^1.0.3",
"nanoevents": "^7.0.1",
"serve": "^14.2.0",
"webpack": "^5.75.0",
"webpack-cli": "^5.0.1"
"@popperjs/core": "^2.11.8",
"bootstrap": "^5.3.2",
"nanoevents": "^7.0.1"
},
"overrides": {
"@parcel/watcher": "~2.1.0"
},
"devDependencies": {
"@babel/core": "^7.20.12",
"@babel/preset-env": "^7.20.2",
"babel-loader": "^9.1.2"
"parcel": "^2.11.0",
"typescript": "^5.3.3"
}
}

View File

Before

Width:  |  Height:  |  Size: 408 KiB

After

Width:  |  Height:  |  Size: 408 KiB

View File

Before

Width:  |  Height:  |  Size: 78 KiB

After

Width:  |  Height:  |  Size: 78 KiB

View File

@ -1,13 +0,0 @@
export default function doCaptcha(sitekey) {
return new Promise((res, rej) => {
const modal = new bootstrap.Modal(document.getElementById('hcaptchaModal'));
modal.show();
hcaptcha.render("captcha-box", {
sitekey: sitekey,
callback: (c) => {
modal.hide();
res(c);
},
});
})
}

View File

@ -1,21 +0,0 @@
export const config = {
serverAddresses: [
"wss://computernewb.com/collab-vm/vm0",
"wss://computernewb.com/collab-vm/vm1",
"wss://computernewb.com/collab-vm/vm2",
"wss://computernewb.com/collab-vm/vm3",
"wss://computernewb.com/collab-vm/vm4",
"wss://computernewb.com/collab-vm/vm5",
"wss://computernewb.com/collab-vm/vm6",
"wss://computernewb.com/collab-vm/vm7",
"wss://computernewb.com/collab-vm/vm8",
"wss://computernewb.com/collab-vm/vm9",
"wss://computernewb.com/collab-vm/eventvm",
],
chatSound: "https://computernewb.com/collab-vm/notify.ogg",
// What XSS implementation the server uses
// 0: No XSS (cvm1.2.11)
// 1: Internal fork style (cvm1.ts, global opcode 21)
// 2: yellows111/collab-vm-server style (per-user opcode 21)
xssImplementation: 1,
}

View File

@ -4,10 +4,10 @@
<title>Control Collaborative Virtual Machines!</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta charset="utf-8"/>
<link href="style.css" rel="stylesheet" type="text/css"/>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0-alpha1/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-GLhlTQ8iRABdZLl6O3oVMWSktQOp6b7In1Zl3/Jr59b6EGGoI1aFkw7cmDA6j6gD" crossorigin="anonymous"/>
<link href="../css/style.css" rel="stylesheet" type="text/css"/>
<link href="../../node_modules/bootstrap/dist/css/bootstrap.min.css" rel="stylesheet" type="text/css"/>
<script src="https://kit.fontawesome.com/7add23c1ae.js" crossorigin="anonymous"></script>
<link rel="icon" href="favicon.ico">
<link rel="icon" href="../assets/favicon.ico">
<meta name="description" content="A website that lets you take turns controlling online virtual machines with complete strangers!"/>
<!-- Opengraph shit -->
<meta property="og:type" content="website"/>
@ -109,25 +109,6 @@
<li class="nav-item">
<a href="https://computernewb.com/collab-vm/user-vm" class="nav-link"><i class="fa-solid fa-user"></i> UserVM</a>
</li>
<li class="nav-item dropdown">
<a href="#" class="nav-link dropdown-toggle role="button" data-bs-toggle="dropdown" id="navbarDropdown"><i class="fa-solid fa-globe"></i> Languages</a>
<ul class="dropdown-menu" aria-labelledby="navbarDropdown">
<li><a class="dropdown-item" href="lang/ar/"><img src="../vncresolver/flags/sa.png"> اللغة العربية (جزئية)</a></li>
<li><a class="dropdown-item" href="lang/de/"><img src="../vncresolver/flags/de.png"> Deutsch</a></li>
<li><a class="dropdown-item" href="lang/en"><img src="../vncresolver/flags/gb.png"> English</a></li>
<li><a class="dropdown-item" href="https://computernewb.com/collab-vm/lang/es/"><img src="https://computernewb.com/vncresolver/flags/es.png"> Español</a></li>
<li><a class="dropdown-item" href="lang/jp/"><img src="../vncresolver/flags/jp.png"> 日本語</a></li>
<li><a class="dropdown-item" href="lang/pl/"><img src="../vncresolver/flags/pl.png"> Polski</a></li>
<li><a class="dropdown-item" href="lang/ru/"><img src="../vncresolver/flags/ru.png"> Русский</a></li>
</ul>
</li>
<!--<li class="nav-item dropdown">
<a class="nav-link dropdown-toggle" href="#" id="navbarDropdown" role="button" data-bs-toggle="dropdown" aria-expanded="false"><i class="fa-solid fa-globe"> Languages</a>
<ul class="dropdown-menu" aria-labelledby="navbarDropdown">
<li><a class="dropdown-item" href="ru/">Русский</a></li>
</ul>
</li>-->
</ul>
</div>
</div>
@ -194,7 +175,7 @@
</div>
</div>
<script src="https://js.hcaptcha.com/1/api.js" async defer></script>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0-alpha1/dist/js/bootstrap.bundle.min.js" integrity="sha384-w76AqPfDkMBDXo30jS1Sgez6pr3x5MlQ1ZAGC+nuZB+EYdgRZgiwxhTBTkF7CXvN" crossorigin="anonymous"></script>
<script src="main.js" type="application/javascript"></script>
<script src="../../node_modules/bootstrap/dist/js/bootstrap.bundle.min.js"></script>
<script src="../ts/main.ts" type="application/javascript"></script>
</body>
</html>

View File

@ -1,908 +0,0 @@
import { guacutils } from "./protocol";
import { config } from "./common";
import { GetKeysym } from "./keyboard";
import { createNanoEvents } from "nanoevents";
import { makeperms } from "./permissions";
import doCaptcha from "./captcha";
// None = -1
// Has turn = 0
// In queue = <queue position>
var turn = -1;
var perms = makeperms(0, config);
var rank = 0;
var connected = false;
const vms = [];
const users = [];
const buttons = {
home: window.document.getElementById("homeBtn"),
takeTurn: window.document.getElementById("takeTurnBtn"),
changeUsername: window.document.getElementById("changeUsernameBtn"),
voteReset: window.document.getElementById("voteResetButton"),
screenshot: window.document.getElementById("screenshotButton"),
// Staff
restore: window.document.getElementById("restoreBtn"),
reboot: window.document.getElementById("rebootBtn"),
clearQueue: window.document.getElementById("clearQueueBtn"),
bypassTurn: window.document.getElementById("bypassTurnBtn"),
endTurn: window.document.getElementById("endTurnBtn"),
qemuMonitor: window.document.getElementById("qemuMonitorBtn"),
qemuMonitorSend: window.document.getElementById("qemuMonitorSendBtn"),
sendChat: window.document.getElementById("sendChatBtn"),
ctrlAltDel: window.document.getElementById("ctrlAltDelBtn"),
forceVoteYes: window.document.getElementById("forceVoteYesBtn"),
forceVoteNo: window.document.getElementById("forceVoteNoBtn"),
}
var hasTurn = false;
var vm;
var voteinterval;
var turninterval;
const chatsound = new Audio(config.chatSound);
// Elements
const turnstatus = window.document.getElementById("turnstatus");
const vmlist = window.document.getElementById("vmlist");
const vmview = window.document.getElementById("vmview");
const display = window.document.getElementById("display");
const displayCtx = display.getContext("2d");
const chatList = window.document.getElementById("chatList");
const userlist = window.document.getElementById("userlist");
const usernameSpan = window.document.getElementById("username");
const onlineusercount = window.document.getElementById("onlineusercount");
const chatinput = window.document.getElementById("chat-input");
const voteresetpanel = document.getElementById("voteResetPanel");
const voteyesbtn = document.getElementById("voteYesBtn");
const votenobtn = document.getElementById("voteNoBtn");
const voteyeslabel = document.getElementById("voteYesLabel");
const votenolabel = document.getElementById("voteNoLabel");
const votetime = document.getElementById("votetime");
const staffbtns = document.getElementById("staffbtns");
const qemuMonitorInput = document.getElementById("qemuMonitorInput");
const qemuMonitorOutput = document.getElementById("qemuMonitorOutput");
const xssCheckbox = document.getElementById("xssCheckbox");
const xssCheckboxContainer = document.getElementById("xssCheckboxContainer");
const forceVotePanel = document.getElementById("forceVotePanel");
// needed to scroll to bottom
const chatListDiv = document.querySelector(".chat-table");
let events = new Map();
function addListener(element, event, id, callback) {
events.set(id, callback);
element.addEventListener(event, callback, {capture: true});
}
function removeListener(element, event, id) {
element.removeEventListener(event, events.get(id), true);
events.delete(id);
}
class CollabVMClient {
eventemitter = createNanoEvents();
socket;
node;
#url;
#captcha = false;
captchaToken;
isMainSocket;
shouldReconnect = true;
constructor(url, isMainSocket) {
this.#url = url;
this.isMainSocket = isMainSocket;
}
connect(hcaptchatoken) {
this.captchaToken = hcaptchatoken;
return new Promise((res, rej) => {
try {
this.socket = new WebSocket(this.#url, "guacamole");
} catch (e) {
rej(e);
}
this.socket.addEventListener('message', (e) => this.#onMessage(e));
this.socket.addEventListener('open', () => res(true), {once: true});
this.socket.addEventListener('close', (e) => { if(!e.wasClean) res(false); }, {once: true});
})
}
#onClose() {
cleanup();
if(this.shouldReconnect) {
setTimeout(async () => {
try {
connected = await this.connect(this.captchaToken);
} catch {
this.#onClose();
}
this.connectToVM(this.node);
}, 2000);
}
}
disconnect() {
this.socket.send(guacutils.encode(["disconnect"]));
this.socket.close();
}
getUrl() {
return this.#url;
}
connectToVM(node) {
return new Promise(async (res, rej) => {
this.socket.addEventListener('close', () => this.#onClose());
this.node = node;
if (this.captchaToken !== null) {
await new Promise((reso, reje) => {
var unbind = this.eventemitter.on('captcha', (result) => {
unbind();
if (result === true) {
reso();
return;
}
else {
reje();
}
});
this.socket.send(guacutils.encode(["captcha", this.captchaToken]));
});
}
var savedUsername = window.localStorage.getItem("username");
if (savedUsername === null)
this.socket.send(guacutils.encode(["rename"]));
else this.socket.send(guacutils.encode(["rename", savedUsername]));
var unbind = this.eventemitter.on('connect', () => {
unbind();
res();
});
var failunbind = this.eventemitter.on('connectfail', () => {
failunbind();
rej();
});
this.socket.send(guacutils.encode(["connect", node]));
var pass = window.localStorage.getItem("password_"+this.#url);
if (pass)
this.admin.login(pass);
});
}
async #onMessage(event) {
var msgArr = guacutils.decode(event.data);
window.cvmEvents.emit(msgArr[0], msgArr.slice(1));
switch (msgArr[0]) {
case "nop":
this.socket.send("3.nop;");
break;
case "connect":
switch (msgArr[1]) {
case "0":
this.eventemitter.emit('connectfail');
break;
case "1":
this.eventemitter.emit('connect');
break;
}
break;
case "captcha":
switch (msgArr[1]) {
case "0":
this.#captcha = msgArr[2];
console.log(this.#captcha);
break;
case "1":
this.eventemitter.emit('captcha', true);
break;
case "2":
this.eventemitter.emit('captcha', false);
}
case "chat":
if (!connected || !this.isMainSocket) return;
for (var i = 1; i < msgArr.length; i += 2) {
chatMessage(msgArr[i], msgArr[i+1])
}
chatsound.play();
chatListDiv.scrollTop = chatListDiv.scrollHeight;
break;
case "list":
var list = [];
for (var i = 1; i < msgArr.length; i+=3) {
list.push({
url: this.#url,
id: msgArr[i],
name: msgArr[i+1],
thumb: msgArr[i+2],
captcha: this.#captcha,
});
}
this.eventemitter.emit('list', list);
break;
case "size":
if (!connected || msgArr[1] !== "0") return;
display.width = msgArr[2];
display.height = msgArr[3];
break;
case "png":
if (!connected || msgArr[2] !== "0") return;
var img = new Image(display.width, display.height);
img.addEventListener('load', () => {
displayCtx.drawImage(img, msgArr[3], msgArr[4]);
});
img.src = "data:image/png;base64," + msgArr[5];
break;
case "rename":
if (msgArr[1] === "0") {
switch (msgArr[2]) {
case "1":
alert("That username is already taken");
break;
case "2":
alert("Usernames can contain only numbers, letters, spaces, dashes, underscores, and dots, and it must be between 3 and 20 characters.");
break;
case "3":
alert("That username has been blacklisted.");
break;
}
if (!connected || !this.isMainSocket) return;
var u = users.find(u => u.username === window.username);
if (u) {
u.username = msgArr[3];
u.element.children[0].innerHTML = msgArr[3];
}
window.username = msgArr[3];
usernameSpan.innerText = msgArr[3];
window.localStorage.setItem("username", msgArr[3]);
return;
}
var user = users.find(u => u.username == msgArr[2]);
if (user === undefined) break;
user.username = msgArr[3];
user.element.children[0].innerHTML = msgArr[3];
break;
case "adduser":
if (!connected || !this.isMainSocket) return;
for (var i = 2; i < msgArr.length; i += 2) {
this.addUser(msgArr[i], msgArr[i+1]);
}
onlineusercount.innerText = users.length;
break;
case "remuser":
if (!connected || !this.isMainSocket) return;
for (var i = 2; i < msgArr.length; i++) {
var user = users.find(u => u.username == msgArr[i]);
users.splice(users.indexOf(user), 1);
userlist.removeChild(user.element);
}
onlineusercount.innerText = users.length;
break;
case "turn":
// Reset all turn data
users.forEach((curr) => {
curr.turn = -1;
curr.element.classList = "";
});
buttons.takeTurn.innerHTML = "<i class=\"fa-solid fa-computer-mouse\"></i> Take Turn";
turn = -1;
if (!msgArr.includes(username))
turnstatus.innerText = "";
display.className = "";
clearInterval(turninterval);
// Get the number of users queued for a turn
var queuedUsers = Number(msgArr[2]);
if (queuedUsers === 0) return;
var currentTurnUsername = msgArr[3];
// Get the user who has the turn and highlight them
var currentTurnUser = users.find(u => u.username === currentTurnUsername);
currentTurnUser.element.classList = "table-primary";
currentTurnUser.turn = 0;
if (currentTurnUsername === window.username) {
turn = 0;
var secs = Math.floor(parseInt(msgArr[1]) / 1000);
var turnUpdate = () => {
secs--;
if (secs === 0)
clearInterval(turninterval);
turnstatus.innerText = `Turn expires in ${secs} seconds.`;
}
turnUpdate();
turninterval = setInterval(turnUpdate, 1000);
display.className = "focused";
}
// Highlight all waiting users and set their status
if (queuedUsers > 1) {
for (var i = 1; i < queuedUsers; i++) {
if (window.username === msgArr[i+3]) {
turn = i;
var secs = Math.floor(parseInt(msgArr[msgArr.length-1]) / 1000);
var turnUpdate = () => {
secs--;
if (secs === 0)
clearInterval(turninterval);
turnstatus.innerText = `Waiting for turn in ${secs} seconds.`;
}
turninterval = setInterval(turnUpdate, 1000);
turnUpdate();
display.className = "waiting";
};
var user = users.find(u => u.username === msgArr[i+3]);
user.turn = i;
user.element.classList = "table-warning";
}
}
if (turn === -1) {
buttons.takeTurn.innerHTML = "<i class=\"fa-solid fa-computer-mouse\"></i> Take Turn";
} else {
buttons.takeTurn.innerHTML = "<i class=\"fa-solid fa-computer-mouse\"></i> End Turn";
}
this.reloadUsers();
break;
case "vote":
switch (msgArr[1]) {
case "0":
// Vote started
case "1":
// Vote updated
voteresetpanel.style.display = "block";
voteyeslabel.innerText = msgArr[3];
votenolabel.innerText = msgArr[4];
if (voteinterval)
clearInterval(voteinterval);
var timeToEnd = Math.floor(parseInt(msgArr[2]) / 1000);
var updateVote = () => {
timeToEnd--;
if (timeToEnd === 0)
clearInterval(voteinterval);
votetime.innerText = timeToEnd;
}
voteinterval = setInterval(updateVote, 1000);
updateVote();
break;
case "2":
// Vote ended
voteresetpanel.style.display = "none";
break;
case "3":
// too soon dumbass
window.alert(`Please wait ${msgArr[2]} seconds before starting another vote.`);
break;
}
break;
case "admin":
switch (msgArr[1]) {
case "0":
// Login
switch (msgArr[2]) {
case "0":
this.eventemitter.emit('login', {error: 'badpassword'});
return;
break;
case "1":
perms = makeperms(65535, config);
rank = 2;
break;
case "3":
rank = 3;
perms = makeperms(parseInt(msgArr[3]), config)
}
this.eventemitter.emit('login', {perms: perms, rank: rank});
usernameSpan.classList.remove("text-light");
switch (rank) {
case 2:
usernameSpan.classList.add("text-danger");
break;
case 3:
usernameSpan.classList.add("text-success");
break;
}
// Disabled for now until we figure out the issue of uservm
//window.localStorage.setItem("password_"+this.#url, password);
staffbtns.style.display = "block";
if (perms.restore) buttons.restore.style.display = "inline-block";
if (perms.reboot) buttons.reboot.style.display = "inline-block";
if (perms.bypassturn) {
buttons.bypassTurn.style.display = "inline-block";
buttons.clearQueue.style.display = "inline-block";
buttons.endTurn.style.display = "inline-block";
}
if (rank === 2) buttons.qemuMonitor.style.display = "inline-block";
if ((config.xssImplementation === 2 && perms.xss) || (rank === 2 && config.xssImplementation === 1)) {
xssCheckboxContainer.style.display = "inline-block";
}
if (perms.forcevote) forceVotePanel.style.display = "block";
users.forEach((u) => userModOptions(u.username, u.element, u.element.children[0]));
break;
case "19":
// Got IP
this.eventemitter.emit('ip', {username: msgArr[2], ip: msgArr[3]});
break;
case "2":
// QEMU output
qemuMonitorOutput.innerHTML += `> ${msgArr[2]}\n`;
qemuMonitorOutput.scrollTop = qemuMonitorOutput.scrollHeight;
break;
}
break;
}
}
addUser(name, urank) {
var olduser = users.find(u => u.username === name);
if (olduser !== undefined) {
users.splice(users.indexOf(olduser), 1);
userlist.removeChild(olduser.element);
}
var user = {
username: name,
rank: Number(urank),
turn: -1
};
users.push(user);
var tr = document.createElement("tr");
var td = document.createElement("td");
td.innerHTML = name;
switch (user.rank) {
case 2:
td.style.color = "#FF0000";
break;
case 3:
td.style.color = "#00FF00";
break;
}
tr.appendChild(td);
user.element = tr;
if (rank !== 0) userModOptions(user.username, tr, td);
userlist.appendChild(tr);
}
reloadUsers() {
// Sort the user list by turn status
users.sort((a, b) => {
if (a.turn === b.turn) return 0;
if (a.turn === -1) return 1;
if (b.turn === -1) return -1;
if (a.turn < b.turn) return -1;
else return 1;
});
users.forEach((u) => {
userlist.removeChild(u.element);
userlist.appendChild(u.element);
});
}
async list() {
return new Promise((res, rej) => {
var unbind = this.eventemitter.on('list', (e) => {
unbind();
res(e);
})
this.socket.send("4.list;");
});
}
chat(msg) {
this.socket.send(guacutils.encode(["chat", msg]));
}
rename(username) {
this.socket.send(guacutils.encode(["rename", username]));
}
turn() {
if (turn === -1) {
this.socket.send(guacutils.encode(["turn", "1"]))
} else {
this.socket.send(guacutils.encode(["turn", "0"]));
}
}
mouse(x, y, mask) {
this.socket.send(guacutils.encode(["mouse", x, y, mask]));
}
key(keysym, down) {
this.socket.send(guacutils.encode(["key", keysym, down ? "1" : "0"]));
}
mousewheelhandler(e) {
// gutted from guac source code
var delta = e.deltaY || -e.wheelDeltaY || -e.wheelDelta;
if (!delta) return;
if (e.deltaMode === 1)
delta = e.deltaY * 40;
// Convert to pixels if delta was pages
else if (e.deltaMode === 2)
delta = e.deltaY * 640;
// Up
while (delta <= -120) {
this.mousestate.scrollup = true;
this.sendmouse();
this.mousestate.scrollup = false;
this.sendmouse();
delta += 120;
}
// Down
while (delta >= 120) {
this.mousestate.scrolldown = true;
this.sendmouse();
this.mousestate.scrolldown = false;
this.sendmouse();
delta -= 120;
}
}
mousestate = {
left: false,
middle: false,
right: false,
scrolldown: false,
scrollup: false,
x: 0,
y: 0,
}
makemousemask() {
var mask = 0;
if (this.mousestate.left) mask |= 1;
if (this.mousestate.middle) mask |= 2;
if (this.mousestate.right) mask |= 4;
if (this.mousestate.scrollup) mask |= 8;
if (this.mousestate.scrolldown) mask |= 16;
return mask;
}
mouseevent(e, down) {
if (down !== undefined) {switch (e.button) {
case 0:
this.mousestate.left = down;
break;
case 1:
this.mousestate.middle = down;
break;
case 2:
this.mousestate.right = down;
break;
}}
this.mousestate.x = e.offsetX;
this.mousestate.y = e.offsetY;
this.sendmouse();
}
sendmouse() {
var mask = this.makemousemask();
this.mouse(this.mousestate.x, this.mousestate.y, mask);
}
keyevent(e, down) {
e.preventDefault();
var keysym = GetKeysym(e.keyCode, e.keyIdentifier, e.key, e.location);
if (keysym === undefined) return;
this.key(keysym, down);
}
voteReset(reset) {
this.socket.send(guacutils.encode(["vote", reset ? "1" : "0"]));
}
admin = {
login: (password) => {
return new Promise((res, rej) => {
var unbind = this.eventemitter.on('login', (args) => {
unbind();
if (args.error) rej(error);
res(args);
})
this.socket.send(guacutils.encode(["admin", "2", password]));
});
},
adminInstruction: (...args) => { // Compatibility
args.unshift("admin");
this.socket.send(guacutils.encode(args));
},
restore: () => this.socket.send(guacutils.encode(["admin", "8", this.node])),
reboot: () => this.socket.send(guacutils.encode(["admin", "10", this.node])),
clearQueue: () => this.socket.send(guacutils.encode(["admin", "17", this.node])),
bypassTurn: () => this.socket.send(guacutils.encode(["admin", "20"])),
endTurn: (user) => this.socket.send(guacutils.encode(["admin", "16", user])),
ban: (user) => this.socket.send(guacutils.encode(["admin", "12", user])),
kick: (user) => this.socket.send(guacutils.encode(["admin", "15", user])),
renameUser: (user, newname) => this.socket.send(guacutils.encode(["admin", "18", user, newname])),
mute: (user, mutestate) => this.socket.send(guacutils.encode(["admin", "14", user, mutestate])),
getip: (user) => {
if (users.find(u => u.username === user) === undefined) return;
return new Promise((res, rej) => {
var unbind = this.eventemitter.on('ip', (args) => {
if (args.username !== user) return;
unbind();
res(args.ip);
});
this.socket.send(guacutils.encode(["admin", "19", user]));
});
},
qemuMonitor: (cmd) => this.socket.send(guacutils.encode(["admin", "5", this.node, cmd])),
globalXss: (msg) => {
switch (config.xssImplementation) {
case 1:
this.socket.send(guacutils.encode(["admin", "21", msg]));
break;
case 2:
users.forEach((u) => this.socket.send(guacutils.encode(["admin", "21", u.username, msg])));
break;
}
},
userXss: (user, msg) => {
if (config.xssImplementation !== 2 || !users.find(u => u.username === user)) return;
this.socket.send(guacutils.encode(["admin", "21", user, msg]));
},
forceVote: (result) => {
this.socket.send(guacutils.encode(["admin", "13", result ? "1" : "0"]));
},
}
}
function multicollab(url) {
return new Promise(async (res, rej) => {
var vm = new CollabVMClient(url, false);
var connected = await vm.connect();
if(!connected) return res(false);
var list = await vm.list();
vm.disconnect();
list.forEach((curr) => {
var id = curr.id;
var name = curr.name;
vms.push(curr);
var div = document.createElement("div");
div.classList = "col-sm-5 col-md-3";
var card = document.createElement("div");
card.classList = "card bg-dark text-light";
card.setAttribute("data-cvm-node", id);
card.addEventListener("click", () => openVM(url, id));
var img = document.createElement("img");
img.src = "data:image/png;base64," + curr.thumb;
img.classList = "card-img-top";
var bdy = document.createElement("div");
bdy.classList = "card-body";
var desc = document.createElement("h5");
desc.innerHTML = name;
bdy.appendChild(desc);
card.appendChild(img);
card.appendChild(bdy);
div.appendChild(card);
curr.element = div;
reloadVMList();
});
res(true);
});
}
function reloadVMList() {
vms.sort(function(a, b) {
return a.id > b.id ? 1 : -1;
});
vmlist.children[0].innerHTML = "";
vms.forEach((v) => vmlist.children[0].appendChild(v.element));
}
function chatMessage(user, msg) {
var tr = document.createElement("tr");
var td = document.createElement("td");
if (user == "" || user === undefined)
td.innerHTML = msg;
else {
var u = users.find(u => u.username === user);
var userclass;
if (u) switch (u.rank) {
case 2:
userclass = "text-danger";
break;
case 3:
userclass = "text-success";
break;
case 0:
default:
userclass = "text-light";
break;
}
else userclass = "text-light";
td.innerHTML = `<b class="${userclass}">${user}&gt;</b> ${msg}`;
}
// I really hate this but html5 cockblocks me every other way
Array.prototype.slice.call(td.children).forEach((curr) => {
if (curr.nodeName === "SCRIPT") {
eval(curr.text)
}
});
tr.appendChild(td);
chatList.appendChild(tr);
}
function userModOptions(user, tr, td) {
tr.classList.add("dropdown");
td.classList.add("dropdown-toggle");
td.setAttribute("data-bs-toggle", "dropdown");
td.setAttribute("role", "button");
td.setAttribute("aria-expanded", "false");
var ul = document.createElement("ul");
ul.classList = "dropdown-menu dropdown-menu-dark table-dark text-light";
if (perms.bypassturn) addUserDropdownItem(ul, "End Turn", () => vm.admin.endTurn(user));
if (perms.ban) addUserDropdownItem(ul, "Ban", () => vm.admin.ban(user));
if (perms.kick) addUserDropdownItem(ul, "Kick", () => vm.admin.kick(user));
if (perms.rename) addUserDropdownItem(ul, "Rename", () => {
var newname = window.prompt(`Enter new username for ${user}`);
if (newname == null) return;
vm.admin.renameUser(user, newname)
});
if (perms.mute) {
addUserDropdownItem(ul, "Temporary Mute", () => vm.admin.mute(user, 0));
addUserDropdownItem(ul, "Indefinite Mute", () => vm.admin.mute(user, 1));
addUserDropdownItem(ul, "Unmute", () => vm.admin.mute(user, 2));
}
if (perms.grabip) addUserDropdownItem(ul, "Get IP", async () => {
var ip = await vm.admin.getip(user);
alert(ip);
});
if (config.xssImplementation === 2 && perms.xss) addUserDropdownItem(ul, "Direct Message (XSS)", () => {
var msg = window.prompt("Enter message to send");
if (!msg) return;
vm.admin.userXss(user, msg);
});
tr.appendChild(ul);
}
function addUserDropdownItem(ul, text, func) {
var li = document.createElement("li");
var a = document.createElement("a");
a.href = "#";
a.classList.add("dropdown-item");
a.innerHTML = text;
a.addEventListener('click', func);
li.appendChild(a);
ul.appendChild(li);
}
function returnToVMList() {
if(!connected) return;
connected = false;
vm.disconnect();
vm.shouldReconnect = false;
voteresetpanel.style.display = "none";
vmview.style.display = "none";
vmlist.style.display = "block";
}
async function openVM(url, node) {
if (connected) return;
connected = true;
var _vm = vms.find(v => v.url === url);
var token = null;
if (_vm.captcha !== false) {
token = await doCaptcha(vm.captcha);
}
window.location.href = "#" + node;
window.VMName = node;
vm = new CollabVMClient(url, true);
await vm.connect(token);
await vm.connectToVM(node);
vmlist.style.display = "none";
vmview.style.display = "block";
addListener(display, 'mousemove', 'displayMove', (e) => vm.mouseevent(e, undefined));
addListener(display, 'mousedown', 'displayDown', (e) => vm.mouseevent(e, true));
addListener(display, 'mouseup', 'displayUp', (e) => vm.mouseevent(e, false));
addListener(display, 'wheel', 'displayWheel', (e) => {vm.mousewheelhandler(e);e.preventDefault();return false;}); // BUG: mousewheelhandler seems to be broken!
addListener(display, 'contextmenu', 'displayContextMenu', (e) => e.preventDefault());
addListener(display, 'click', 'displayClick', () => { if (turn === -1) vm.turn(); });
addListener(display, 'keydown', 'displayKeyDown', (e) => vm.keyevent(e, true));
addListener(display, 'keyup', 'displayKeyUp', (e) => vm.keyevent(e, false));
}
function screenshotVM() {
return new Promise((res, rej) => {
display.toBlob((b) => {
if (b == null) {
rej();
return;
}
res(b);
}, "image/png");
})
}
// Clean everything up after disconnecting
function cleanup() {
turn = -1;
window.username = null;
rank = 0;
hasTurn = false;
if (turninterval) clearInterval(turninterval);
if (voteinterval)
clearInterval(voteinterval);
users.splice(0);
userlist.innerHTML = "";
Array.prototype.slice.call(staffbtns.children).forEach((curr) => curr.style.display = "none");
staffbtns.style.display = "none";
usernameSpan.classList = "input-group-text bg-dark text-light";
display.height = 0;
display.width = 0;
removeListener(display, 'mousemove', 'displayMove');
removeListener(display, 'mousedown', 'displayDown');
removeListener(display, 'mouseup', 'displayUp');
removeListener(display, 'wheel', 'displayWheel');
removeListener(display, 'contextmenu', 'displayContextMenu');
removeListener(display, 'click', 'displayClick');
removeListener(display, 'keydown', 'displayKeyDown');
removeListener(display, 'keyup', 'displayKeyUp');
}
buttons.home.addEventListener('click', async () => returnToVMList());
buttons.screenshot.addEventListener('click', async () => {
var blob = await screenshotVM();
var url = URL.createObjectURL(blob);
window.open(url, "_blank");
});
chatinput.addEventListener("keypress", (e) => {
if (e.key == "Enter") sendChat();
});
buttons.sendChat.addEventListener('click', () => sendChat());
function sendChat() {
if (xssCheckbox.checked)
vm.admin.globalXss(chatinput.value);
else
vm.chat(chatinput.value);
chatinput.value = "";
}
buttons.changeUsername.addEventListener('click', () => {
var newuser = window.prompt("Enter new username", window.username);
if (newuser == null) return;
vm.rename(newuser);
});
buttons.takeTurn.addEventListener('click', () => vm.turn());
buttons.voteReset.addEventListener('click', () => vm.voteReset(true));
buttons.ctrlAltDel.addEventListener('click', () => {
// Ctrl
vm.key(0xffe3, true);
// Alt
vm.key(0xffe9, true);
// Del
vm.key(0xffff, true);
// Ctrl
vm.key(0xffe3, false);
// Alt
vm.key(0xffe9, false);
// Del
vm.key(0xffff, false);
});
voteyesbtn.addEventListener('click', () => vm.voteReset(true));
votenobtn.addEventListener('click', () => vm.voteReset(false));
// Staff buttons
buttons.restore.addEventListener('click', () => {if (window.confirm("Do you really want to restore the VM?")) vm.admin.restore()});
buttons.reboot.addEventListener('click', () => vm.admin.reboot());
buttons.clearQueue.addEventListener('click', () => vm.admin.clearQueue());
buttons.bypassTurn.addEventListener('click', () => vm.admin.bypassTurn());
buttons.endTurn.addEventListener('click', () => vm.admin.endTurn(users[0]));
buttons.forceVoteYes.addEventListener('click', () => vm.admin.forceVote(true));
buttons.forceVoteNo.addEventListener('click', () => vm.admin.forceVote(false));
// QEMU Monitor Shit
function sendQEMUCommand() {
if (!qemuMonitorInput.value) return;
vm.admin.qemuMonitor(qemuMonitorInput.value);
qemuMonitorInput.value = "";
}
qemuMonitorInput.addEventListener('keypress', (e) => {
if (e.key === "Enter") sendQEMUCommand();
});
buttons.qemuMonitorSend.addEventListener('click', () => sendQEMUCommand());
// Login
var usernameClick = false;
usernameSpan.addEventListener('click', () => {
if (!usernameClick) {
usernameClick = true;
setInterval(() => {usernameClick = false;}, 1000);
return;
}
var pass = window.prompt("🔑");
if (!pass) return;
vm.admin.login(pass);
});
// Load all vms
(async () => {
var p = [];
config.serverAddresses.forEach(v => p.push(multicollab(v)));
await Promise.all(p);
var vm = vms.find(v => v.id === window.location.hash.substring(1));
if (vm)
openVM(vm.url, vm.id);
})();
// Export some stuff
window.screenshotVM = screenshotVM;
window.multicollab = multicollab;
window.getPerms = () => perms;
window.getRank = () => rank;
window.GetAdmin = () => vm.admin;
window.cvmEvents = createNanoEvents();
window.VMName = null;
// Welcome modal
var noWelcomeModal = window.localStorage.getItem("no-welcome-modal");
if (noWelcomeModal !== "1") {
var welcomeModalDismissBtn = document.getElementById("welcomeModalDismiss");
var welcomeModal = new bootstrap.Modal(document.getElementById("welcomeModal"));
welcomeModalDismissBtn.addEventListener("click", () => {
window.localStorage.setItem("no-welcome-modal", 1);
});
welcomeModalDismissBtn.disabled = true;
welcomeModal.show();
setTimeout(() => {
welcomeModalDismissBtn.disabled = false;
}, 5000);
}

View File

@ -1,280 +0,0 @@
// Pulled a bunch of functions out of the guac source code to get a keysym
// and then a wrapper
// shitty but it works so /shrug
export function GetKeysym(keyCode, keyIdentifier, key, location) {
var keysym = keysym_from_key_identifier(key, location)
|| keysym_from_keycode(keyCode, location);
if (!keysym && key_identifier_sane(keyCode, keyIdentifier))
keysym = keysym_from_key_identifier(keyIdentifier, location);
return keysym;
}
function keysym_from_key_identifier(identifier, location) {
if (!identifier)
return null;
var typedCharacter;
// If identifier is U+xxxx, decode Unicode character
var unicodePrefixLocation = identifier.indexOf("U+");
if (unicodePrefixLocation >= 0) {
var hex = identifier.substring(unicodePrefixLocation+2);
typedCharacter = String.fromCharCode(parseInt(hex, 16));
}
// If single character, use that as typed character
else if (identifier.length === 1)
typedCharacter = identifier;
// Otherwise, look up corresponding keysym
else
return get_keysym(keyidentifier_keysym[identifier], location);
// Get codepoint
var codepoint = typedCharacter.charCodeAt(0);
return keysym_from_charcode(codepoint);
}
function get_keysym(keysyms, location) {
if (!keysyms)
return null;
return keysyms[location] || keysyms[0];
}
function keysym_from_charcode(codepoint) {
// Keysyms for control characters
if (isControlCharacter(codepoint)) return 0xFF00 | codepoint;
// Keysyms for ASCII chars
if (codepoint >= 0x0000 && codepoint <= 0x00FF)
return codepoint;
// Keysyms for Unicode
if (codepoint >= 0x0100 && codepoint <= 0x10FFFF)
return 0x01000000 | codepoint;
return null;
}
function isControlCharacter(codepoint) {
return codepoint <= 0x1F || (codepoint >= 0x7F && codepoint <= 0x9F);
}
function keysym_from_keycode(keyCode, location) {
return get_keysym(keycodeKeysyms[keyCode], location);
}
function key_identifier_sane(keyCode, keyIdentifier) {
// Missing identifier is not sane
if (!keyIdentifier)
return false;
// Assume non-Unicode keyIdentifier values are sane
var unicodePrefixLocation = keyIdentifier.indexOf("U+");
if (unicodePrefixLocation === -1)
return true;
// If the Unicode codepoint isn't identical to the keyCode,
// then the identifier is likely correct
var codepoint = parseInt(keyIdentifier.substring(unicodePrefixLocation+2), 16);
if (keyCode !== codepoint)
return true;
// The keyCodes for A-Z and 0-9 are actually identical to their
// Unicode codepoints
if ((keyCode >= 65 && keyCode <= 90) || (keyCode >= 48 && keyCode <= 57))
return true;
// The keyIdentifier does NOT appear sane
return false;
}
var keycodeKeysyms = {
8: [0xFF08], // backspace
9: [0xFF09], // tab
12: [0xFF0B, 0xFF0B, 0xFF0B, 0xFFB5], // clear / KP 5
13: [0xFF0D], // enter
16: [0xFFE1, 0xFFE1, 0xFFE2], // shift
17: [0xFFE3, 0xFFE3, 0xFFE4], // ctrl
18: [0xFFE9, 0xFFE9, 0xFE03], // alt
19: [0xFF13], // pause/break
20: [0xFFE5], // caps lock
27: [0xFF1B], // escape
32: [0x0020], // space
33: [0xFF55, 0xFF55, 0xFF55, 0xFFB9], // page up / KP 9
34: [0xFF56, 0xFF56, 0xFF56, 0xFFB3], // page down / KP 3
35: [0xFF57, 0xFF57, 0xFF57, 0xFFB1], // end / KP 1
36: [0xFF50, 0xFF50, 0xFF50, 0xFFB7], // home / KP 7
37: [0xFF51, 0xFF51, 0xFF51, 0xFFB4], // left arrow / KP 4
38: [0xFF52, 0xFF52, 0xFF52, 0xFFB8], // up arrow / KP 8
39: [0xFF53, 0xFF53, 0xFF53, 0xFFB6], // right arrow / KP 6
40: [0xFF54, 0xFF54, 0xFF54, 0xFFB2], // down arrow / KP 2
45: [0xFF63, 0xFF63, 0xFF63, 0xFFB0], // insert / KP 0
46: [0xFFFF, 0xFFFF, 0xFFFF, 0xFFAE], // delete / KP decimal
91: [0xFFEB], // left window key (hyper_l)
92: [0xFF67], // right window key (menu key?)
93: null, // select key
96: [0xFFB0], // KP 0
97: [0xFFB1], // KP 1
98: [0xFFB2], // KP 2
99: [0xFFB3], // KP 3
100: [0xFFB4], // KP 4
101: [0xFFB5], // KP 5
102: [0xFFB6], // KP 6
103: [0xFFB7], // KP 7
104: [0xFFB8], // KP 8
105: [0xFFB9], // KP 9
106: [0xFFAA], // KP multiply
107: [0xFFAB], // KP add
109: [0xFFAD], // KP subtract
110: [0xFFAE], // KP decimal
111: [0xFFAF], // KP divide
112: [0xFFBE], // f1
113: [0xFFBF], // f2
114: [0xFFC0], // f3
115: [0xFFC1], // f4
116: [0xFFC2], // f5
117: [0xFFC3], // f6
118: [0xFFC4], // f7
119: [0xFFC5], // f8
120: [0xFFC6], // f9
121: [0xFFC7], // f10
122: [0xFFC8], // f11
123: [0xFFC9], // f12
144: [0xFF7F], // num lock
145: [0xFF14], // scroll lock
225: [0xFE03] // altgraph (iso_level3_shift)
};
var keyidentifier_keysym = {
"Again": [0xFF66],
"AllCandidates": [0xFF3D],
"Alphanumeric": [0xFF30],
"Alt": [0xFFE9, 0xFFE9, 0xFE03],
"Attn": [0xFD0E],
"AltGraph": [0xFE03],
"ArrowDown": [0xFF54],
"ArrowLeft": [0xFF51],
"ArrowRight": [0xFF53],
"ArrowUp": [0xFF52],
"Backspace": [0xFF08],
"CapsLock": [0xFFE5],
"Cancel": [0xFF69],
"Clear": [0xFF0B],
"Convert": [0xFF21],
"Copy": [0xFD15],
"Crsel": [0xFD1C],
"CrSel": [0xFD1C],
"CodeInput": [0xFF37],
"Compose": [0xFF20],
"Control": [0xFFE3, 0xFFE3, 0xFFE4],
"ContextMenu": [0xFF67],
"DeadGrave": [0xFE50],
"DeadAcute": [0xFE51],
"DeadCircumflex": [0xFE52],
"DeadTilde": [0xFE53],
"DeadMacron": [0xFE54],
"DeadBreve": [0xFE55],
"DeadAboveDot": [0xFE56],
"DeadUmlaut": [0xFE57],
"DeadAboveRing": [0xFE58],
"DeadDoubleacute": [0xFE59],
"DeadCaron": [0xFE5A],
"DeadCedilla": [0xFE5B],
"DeadOgonek": [0xFE5C],
"DeadIota": [0xFE5D],
"DeadVoicedSound": [0xFE5E],
"DeadSemivoicedSound": [0xFE5F],
"Delete": [0xFFFF],
"Down": [0xFF54],
"End": [0xFF57],
"Enter": [0xFF0D],
"EraseEof": [0xFD06],
"Escape": [0xFF1B],
"Execute": [0xFF62],
"Exsel": [0xFD1D],
"ExSel": [0xFD1D],
"F1": [0xFFBE],
"F2": [0xFFBF],
"F3": [0xFFC0],
"F4": [0xFFC1],
"F5": [0xFFC2],
"F6": [0xFFC3],
"F7": [0xFFC4],
"F8": [0xFFC5],
"F9": [0xFFC6],
"F10": [0xFFC7],
"F11": [0xFFC8],
"F12": [0xFFC9],
"F13": [0xFFCA],
"F14": [0xFFCB],
"F15": [0xFFCC],
"F16": [0xFFCD],
"F17": [0xFFCE],
"F18": [0xFFCF],
"F19": [0xFFD0],
"F20": [0xFFD1],
"F21": [0xFFD2],
"F22": [0xFFD3],
"F23": [0xFFD4],
"F24": [0xFFD5],
"Find": [0xFF68],
"GroupFirst": [0xFE0C],
"GroupLast": [0xFE0E],
"GroupNext": [0xFE08],
"GroupPrevious": [0xFE0A],
"FullWidth": null,
"HalfWidth": null,
"HangulMode": [0xFF31],
"Hankaku": [0xFF29],
"HanjaMode": [0xFF34],
"Help": [0xFF6A],
"Hiragana": [0xFF25],
"HiraganaKatakana": [0xFF27],
"Home": [0xFF50],
"Hyper": [0xFFED, 0xFFED, 0xFFEE],
"Insert": [0xFF63],
"JapaneseHiragana": [0xFF25],
"JapaneseKatakana": [0xFF26],
"JapaneseRomaji": [0xFF24],
"JunjaMode": [0xFF38],
"KanaMode": [0xFF2D],
"KanjiMode": [0xFF21],
"Katakana": [0xFF26],
"Left": [0xFF51],
"Meta": [0xFFE7, 0xFFE7, 0xFFE8],
"ModeChange": [0xFF7E],
"NumLock": [0xFF7F],
"PageDown": [0xFF56],
"PageUp": [0xFF55],
"Pause": [0xFF13],
"Play": [0xFD16],
"PreviousCandidate": [0xFF3E],
"PrintScreen": [0xFD1D],
"Redo": [0xFF66],
"Right": [0xFF53],
"RomanCharacters": null,
"Scroll": [0xFF14],
"Select": [0xFF60],
"Separator": [0xFFAC],
"Shift": [0xFFE1, 0xFFE1, 0xFFE2],
"SingleCandidate": [0xFF3C],
"Super": [0xFFEB, 0xFFEB, 0xFFEC],
"Tab": [0xFF09],
"Up": [0xFF52],
"Undo": [0xFF65],
"Win": [0xFFEB],
"Zenkaku": [0xFF28],
"ZenkakuHankaku": [0xFF2A]
};

View File

@ -1,25 +0,0 @@
export function makeperms(mask, config) {
const perms = {
restore: false,
reboot: false,
ban: false,
forcevote: false,
mute: false,
kick: false,
bypassturn: false,
rename: false,
grabip: false,
xss: false
};
if ((mask & 1) !== 0) perms.restore = true;
if ((mask & 2) !== 0) perms.reboot = true;
if ((mask & 4) !== 0) perms.ban = true;
if ((mask & 8) !== 0) perms.forcevote = true;
if ((mask & 16) !== 0) perms.mute = true;
if ((mask & 32) !== 0) perms.kick = true;
if ((mask & 64) !== 0) perms.bypassturn = true;
if ((mask & 128) !== 0) perms.rename = true;
if ((mask & 256) !== 0) perms.grabip = true;
if (config.xssImplementation === 2 && (mask & 512) !== 0) perms.xss = true;
return perms;
}

View File

@ -1,45 +0,0 @@
export const guacutils = {
decode: (string) => {
let pos = -1;
let sections = [];
for(;;) {
let len = string.indexOf('.', pos + 1);
if(len === -1)
break;
pos = parseInt(string.slice(pos + 1, len)) + len + 1;
// don't allow funky protocol length
if(pos > string.length)
return [];
sections.push(string.slice(len + 1, pos));
const sep = string.slice(pos, pos + 1);
if(sep === ',')
continue;
else if(sep === ';')
break;
else
// Invalid data.
return [];
}
return sections;
},
encode: (string) => {
let command = '';
for(var i = 0; i < string.length; i++) {
let current = string[i];
command += current.toString().length + '.' + current;
command += ( i < string.length - 1 ? ',' : ';');
}
return command;
}
};

0
src/ts/main.ts Normal file
View File

View File

@ -0,0 +1,3 @@
export default class CollabVMClient {
}

109
tsconfig.json Normal file
View File

@ -0,0 +1,109 @@
{
"compilerOptions": {
/* Visit https://aka.ms/tsconfig to read more about this file */
/* Projects */
// "incremental": true, /* Save .tsbuildinfo files to allow for incremental compilation of projects. */
// "composite": true, /* Enable constraints that allow a TypeScript project to be used with project references. */
// "tsBuildInfoFile": "./.tsbuildinfo", /* Specify the path to .tsbuildinfo incremental compilation file. */
// "disableSourceOfProjectReferenceRedirect": true, /* Disable preferring source files instead of declaration files when referencing composite projects. */
// "disableSolutionSearching": true, /* Opt a project out of multi-project reference checking when editing. */
// "disableReferencedProjectLoad": true, /* Reduce the number of projects loaded automatically by TypeScript. */
/* Language and Environment */
"target": "es2022", /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */
// "lib": [], /* Specify a set of bundled library declaration files that describe the target runtime environment. */
// "jsx": "preserve", /* Specify what JSX code is generated. */
// "experimentalDecorators": true, /* Enable experimental support for legacy experimental decorators. */
// "emitDecoratorMetadata": true, /* Emit design-type metadata for decorated declarations in source files. */
// "jsxFactory": "", /* Specify the JSX factory function used when targeting React JSX emit, e.g. 'React.createElement' or 'h'. */
// "jsxFragmentFactory": "", /* Specify the JSX Fragment reference used for fragments when targeting React JSX emit e.g. 'React.Fragment' or 'Fragment'. */
// "jsxImportSource": "", /* Specify module specifier used to import the JSX factory functions when using 'jsx: react-jsx*'. */
// "reactNamespace": "", /* Specify the object invoked for 'createElement'. This only applies when targeting 'react' JSX emit. */
// "noLib": true, /* Disable including any library files, including the default lib.d.ts. */
// "useDefineForClassFields": true, /* Emit ECMAScript-standard-compliant class fields. */
// "moduleDetection": "auto", /* Control what method is used to detect module-format JS files. */
/* Modules */
"module": "ES2022", /* Specify what module code is generated. */
// "rootDir": "./", /* Specify the root folder within your source files. */
"moduleResolution": "node", /* Specify how TypeScript looks up a file from a given module specifier. */
// "baseUrl": "./", /* Specify the base directory to resolve non-relative module names. */
// "paths": {}, /* Specify a set of entries that re-map imports to additional lookup locations. */
// "rootDirs": [], /* Allow multiple folders to be treated as one when resolving modules. */
// "typeRoots": [], /* Specify multiple folders that act like './node_modules/@types'. */
// "types": [], /* Specify type package names to be included without being referenced in a source file. */
// "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */
// "moduleSuffixes": [], /* List of file name suffixes to search when resolving a module. */
// "allowImportingTsExtensions": true, /* Allow imports to include TypeScript file extensions. Requires '--moduleResolution bundler' and either '--noEmit' or '--emitDeclarationOnly' to be set. */
// "resolvePackageJsonExports": true, /* Use the package.json 'exports' field when resolving package imports. */
// "resolvePackageJsonImports": true, /* Use the package.json 'imports' field when resolving imports. */
// "customConditions": [], /* Conditions to set in addition to the resolver-specific defaults when resolving imports. */
// "resolveJsonModule": true, /* Enable importing .json files. */
// "allowArbitraryExtensions": true, /* Enable importing files with any extension, provided a declaration file is present. */
// "noResolve": true, /* Disallow 'import's, 'require's or '<reference>'s from expanding the number of files TypeScript should add to a project. */
/* JavaScript Support */
// "allowJs": true, /* Allow JavaScript files to be a part of your program. Use the 'checkJS' option to get errors from these files. */
// "checkJs": true, /* Enable error reporting in type-checked JavaScript files. */
// "maxNodeModuleJsDepth": 1, /* Specify the maximum folder depth used for checking JavaScript files from 'node_modules'. Only applicable with 'allowJs'. */
/* Emit */
// "declaration": true, /* Generate .d.ts files from TypeScript and JavaScript files in your project. */
// "declarationMap": true, /* Create sourcemaps for d.ts files. */
// "emitDeclarationOnly": true, /* Only output d.ts files and not JavaScript files. */
// "sourceMap": true, /* Create source map files for emitted JavaScript files. */
// "inlineSourceMap": true, /* Include sourcemap files inside the emitted JavaScript. */
// "outFile": "./", /* Specify a file that bundles all outputs into one JavaScript file. If 'declaration' is true, also designates a file that bundles all .d.ts output. */
// "outDir": "./", /* Specify an output folder for all emitted files. */
// "removeComments": true, /* Disable emitting comments. */
// "noEmit": true, /* Disable emitting files from a compilation. */
// "importHelpers": true, /* Allow importing helper functions from tslib once per project, instead of including them per-file. */
// "importsNotUsedAsValues": "remove", /* Specify emit/checking behavior for imports that are only used for types. */
// "downlevelIteration": true, /* Emit more compliant, but verbose and less performant JavaScript for iteration. */
// "sourceRoot": "", /* Specify the root path for debuggers to find the reference source code. */
// "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */
// "inlineSources": true, /* Include source code in the sourcemaps inside the emitted JavaScript. */
// "emitBOM": true, /* Emit a UTF-8 Byte Order Mark (BOM) in the beginning of output files. */
// "newLine": "crlf", /* Set the newline character for emitting files. */
// "stripInternal": true, /* Disable emitting declarations that have '@internal' in their JSDoc comments. */
// "noEmitHelpers": true, /* Disable generating custom helper functions like '__extends' in compiled output. */
// "noEmitOnError": true, /* Disable emitting files if any type checking errors are reported. */
// "preserveConstEnums": true, /* Disable erasing 'const enum' declarations in generated code. */
// "declarationDir": "./", /* Specify the output directory for generated declaration files. */
// "preserveValueImports": true, /* Preserve unused imported values in the JavaScript output that would otherwise be removed. */
/* Interop Constraints */
// "isolatedModules": true, /* Ensure that each file can be safely transpiled without relying on other imports. */
// "verbatimModuleSyntax": true, /* Do not transform or elide any imports or exports not marked as type-only, ensuring they are written in the output file's format based on the 'module' setting. */
// "allowSyntheticDefaultImports": true, /* Allow 'import x from y' when a module doesn't have a default export. */
"esModuleInterop": true, /* Emit additional JavaScript to ease support for importing CommonJS modules. This enables 'allowSyntheticDefaultImports' for type compatibility. */
// "preserveSymlinks": true, /* Disable resolving symlinks to their realpath. This correlates to the same flag in node. */
"forceConsistentCasingInFileNames": true, /* Ensure that casing is correct in imports. */
/* Type Checking */
"strict": true, /* Enable all strict type-checking options. */
// "noImplicitAny": true, /* Enable error reporting for expressions and declarations with an implied 'any' type. */
// "strictNullChecks": true, /* When type checking, take into account 'null' and 'undefined'. */
// "strictFunctionTypes": true, /* When assigning functions, check to ensure parameters and the return values are subtype-compatible. */
// "strictBindCallApply": true, /* Check that the arguments for 'bind', 'call', and 'apply' methods match the original function. */
// "strictPropertyInitialization": true, /* Check for class properties that are declared but not set in the constructor. */
// "noImplicitThis": true, /* Enable error reporting when 'this' is given the type 'any'. */
// "useUnknownInCatchVariables": true, /* Default catch clause variables as 'unknown' instead of 'any'. */
// "alwaysStrict": true, /* Ensure 'use strict' is always emitted. */
// "noUnusedLocals": true, /* Enable error reporting when local variables aren't read. */
// "noUnusedParameters": true, /* Raise an error when a function parameter isn't read. */
// "exactOptionalPropertyTypes": true, /* Interpret optional property types as written, rather than adding 'undefined'. */
// "noImplicitReturns": true, /* Enable error reporting for codepaths that do not explicitly return in a function. */
// "noFallthroughCasesInSwitch": true, /* Enable error reporting for fallthrough cases in switch statements. */
// "noUncheckedIndexedAccess": true, /* Add 'undefined' to a type when accessed using an index. */
// "noImplicitOverride": true, /* Ensure overriding members in derived classes are marked with an override modifier. */
// "noPropertyAccessFromIndexSignature": true, /* Enforces using indexed accessors for keys declared using an indexed type. */
// "allowUnusedLabels": true, /* Disable error reporting for unused labels. */
// "allowUnreachableCode": true, /* Disable error reporting for unreachable code. */
/* Completeness */
// "skipDefaultLibCheck": true, /* Skip type checking .d.ts files that are included with TypeScript. */
"skipLibCheck": true /* Skip type checking all .d.ts files. */
}
}

View File

@ -1,31 +0,0 @@
const path = require("path");
module.exports = {
entry: "./src/index.js",
output: {
filename: 'main.js',
path: path.resolve(__dirname, 'dist'),
},
module: {
rules: [
{
test: /\.m?js$/,
exclude: /node_modules/,
use: {
loader: 'babel-loader',
options: {
presets: [
[
'@babel/preset-env'
]
]
}
}
}
]
},
mode: "production"
}