Eski Node.js / TypeScript yapı dosyaları temizlendi

Silinen dizinler:
- Source/        — Node.js engine (Go engine tarafından ikame edildi)
- frontend/      — TypeScript SDK kaynağı (sdk/ ES modülleri ile değiştirildi)
- script/        — Parcel bundle çıktısı (artık yok; /sdk/ route'u devrede)

Silinen kök dosyalar:
- index.js       — Node.js giriş noktası (Source/index.js'e refer ediyordu)
- package.json / package-lock.json — Parcel/TypeScript build araçları
- tsconfig.json  — TypeScript derleyici ayarları

Taşınan:
- script/status.xml → public/status.xml (httpserver fallback olarak kullanıyor)

Güncelleme:
- httpserver.go  : /script/* route'u kaldırıldı; / artık /sdk/index.js'e yönlendiriyor
- config.go      : ScriptDir alanı ve MWSE_SCRIPT_DIR env değişkeni kaldırıldı
- contract_test  : ScriptDir → SDKDir
- .gitignore     : Node/Parcel artıkları için temizlendi ve kısaltıldı

go test -race ./... yeşil

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
abdussamedulutas 2026-06-17 12:28:37 +03:00
parent 28abefaaa9
commit 06ca31eecb
37 changed files with 34 additions and 9756 deletions

148
.gitignore vendored
View File

@ -1,147 +1,31 @@
# ---> Gitea CLI kimlik bilgisi (ASLA commit etme)
# Gitea CLI credentials — never commit
.gitea-auth.json
# ---> Go (engine rewrite)
# Go build outputs
/mwse
/mwse-engine
/mwse-loadtest
loadtest/mwse-loadtest
*.out
*.test
go.work
go.work.sum
loadtest/mwse-loadtest
# ---> Node
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
lerna-debug.log*
.pnpm-debug.log*
# Diagnostic reports (https://nodejs.org/api/report.html)
report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json
# Runtime data
pids
*.pid
*.seed
*.pid.lock
# Directory for instrumented libs generated by jscoverage/JSCover
lib-cov
# Coverage directory used by tools like istanbul
coverage
*.lcov
# nyc test coverage
.nyc_output
# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
.grunt
# Bower dependency directory (https://bower.io/)
bower_components
# node-waf configuration
.lock-wscript
# Compiled binary addons (https://nodejs.org/api/addons.html)
build/Release
# Dependency directories
# node_modules stays on disk (gitignored) so npm tools still work if needed,
# but nothing in the repo should require them anymore.
node_modules/
jspm_packages/
# Snowpack dependency directory (https://snowpack.dev/)
web_modules/
# TypeScript cache
package-lock.json
.parcel-cache/
*.tsbuildinfo
# Optional npm cache directory
.npm
# Optional eslint cache
.eslintcache
# Optional stylelint cache
.stylelintcache
# Microbundle cache
.rpt2_cache/
.rts2_cache_cjs/
.rts2_cache_es/
.rts2_cache_umd/
# Optional REPL history
.node_repl_history
# Output of 'npm pack'
*.tgz
# Yarn Integrity file
.yarn-integrity
# dotenv environment variable files
# Environment / secrets
.env
.env.development.local
.env.test.local
.env.production.local
.env.local
.env.*
!.env.example
# parcel-bundler cache (https://parceljs.org/)
.cache
.parcel-cache
# Editor / OS
.DS_Store
.vscode/
.idea/
*.swp
# Next.js build output
.next
out
# Nuxt.js build / generate output
.nuxt
dist
# Gatsby files
.cache/
# Comment in the public line in if your project uses Gatsby and not Next.js
# https://nextjs.org/blog/next-9-1#public-directory-support
# public
# vuepress build output
.vuepress/dist
# vuepress v2.x temp and cache directory
.temp
.cache
# Docusaurus cache and generated files
.docusaurus
# Serverless directories
.serverless/
# FuseBox cache
.fusebox/
# DynamoDB Local files
.dynamodb/
# TernJS port file
.tern-port
# Stores VSCode versions used for testing VSCode extensions
.vscode-test
# yarn v2
.yarn/cache
.yarn/unplugged
.yarn/build-state.yml
.yarn/install-state.gz
.pnp.*
.well-known
.well-known/*
.well-known/

View File

@ -1,213 +0,0 @@
function Client()
{
/**
* @type {string}
*/
this.id = null;
/**
* @type {import("websocket").connection}
*/
this.socket = null;
/**
* @type {Date}
*/
this.created_at = null;
/**
* @type {Map<string,any>}
*/
this.info = new Map();
/**
* @type {Map<string,any>}
*/
this.store = new Map();
/**
* @type {Set<string>}
*/
this.rooms = new Set();
/**
* @type {Set<string>}
*/
this.pairs = new Set();
this.requiredPair = false;
this.APNumber = 0;
this.APShortCode = 0;
this.APIPAddress = 0;
};
/**
* @type {Map<string, Client>}
*/
Client.clients = new Map();
/**
* @param {Client} client
*/
Client.prototype.peerRequest = function(client){
let info = {};
this.info.forEach((value, name) => info[name] = value);
this.pairs.add(client.id);
client.send([
{ from: this.id },
'request/pair'
]);
};
Client.prototype.match = function(filterObject){
let keys = Object.keys(filterObject);
let size = keys.length;
if(size > this.info.size)
{
return false
}
for (const key of keys)
{
if(this.info.has(key))
{
if(this.info.get(key) != filterObject[key])
{
return false
}
}else{
return false
}
};
return true
};
/**
* @param {Client|string} client
*/
Client.prototype.isSecure = function(client)
{
const { Room } = require("./Services/Room");
if(typeof client == "string")
{
if(Client.clients.has(client))
{
client = Client.clients.get(client);
}else return false;
}else if(!(client instanceof Client)){
console.error("isSecure Client bir client veri tipinde değil")
return false;
};
// Eşleştirilmiş kullanıcı
if(this.isPaired(client))
{
return true;
}
// Aynı odada bulunan kullanıcı
for (const id of this.rooms) {
let room = Room.rooms.get(id);
if(room)
{
if(room.clients.has(id))
{
return true
}
}
};
return false;
}
/**
* @returns {{pairs:Map<string, Client>,roompairs:Map<string, Client>,intersection:Map<string, Client>}}
*/
Client.prototype.getSucureClients = function()
{
const { Room } = require("./Services/Room");
let pairs = new Map();
let roompairs = new Map();
for (const id of this.pairs)
{
pairs.set(id, Client.clients.get(id))
}
// Aynı odada bulunan kullanıcı
for (const id of this.rooms) {
let room = Room.rooms.get(id);
if(room)
{
for (const [id, client] of room.clients)
{
if(id == this.id) continue;
roompairs.set(id, client)
};
}
};
return {
pairs,
roompairs,
intersection : new Map([
...pairs,
...roompairs
])
};
}
/**
* @param {Client} client
*/
Client.prototype.acceptPeerRequest = function(client){
this.pairs.add(client.id);
client.send([{
from: this.id
},'accepted/pair']);
};
/**
* @param {Client} client
*/
Client.prototype.rejectPeerRequest = function(client){
this.pairs.delete(client.id);
client.pairs.delete(this.id);
client.send([{
from: this.id
},'end/pair']);
};
/**
* @param {Client|string} client
* @returns {Boolean}
*/
Client.prototype.isPaired = function(client){
if(typeof client == "string")
{
return Client.clients.get(client)?.pairs.has(this.id) && this.pairs.has(client)
}
return client.pairs.has(this.id) && this.pairs.has(client.id);
};
/**
* @returns {string[]}
*/
Client.prototype.pairList = function(){
return [...this.pairs.values()].filter(e => this.isPaired(e));
};
Client.prototype.send = function(obj){
if(this.socket.connected){
this.socket.sendUTF(JSON.stringify(obj),err => {
if(err && this.socket)
{
console.error("I/O: Hatalı yazma işlemi yapıldı",err.message)
}
});
}else{
console.error("Bağlantısı kopmuş yazma işlemi")
}
};
Client.prototype.packWriteable = function(){
return !!this.store.get("packrecaive")
}
Client.prototype.packReadable = function(){
return !!this.store.get("packsending")
}
Client.prototype.peerInfoNotifiable = function(){
return !!this.store.get("notifyPairInfo")
}
Client.prototype.roomInfoNotifiable = function(){
return !!this.store.get("notifyRoomInfo")
}
exports.Client = Client;

View File

@ -1,62 +0,0 @@
"use strict";
const events = new Map();
function on(event, callback) {
if (!events.has(event)) {
events.set(event, []);
}
events.get(event).push(callback);
}
function emit(event, ...args) {
if (events.has(event)) {
for (const callback of events.get(event)) {
try {
callback(...args);
} catch (error) {
console.error(`Event error [${event}]:`, error);
}
}
}
}
function once(event, callback) {
const wrapper = (...args) => {
off(event, wrapper);
callback(...args);
};
on(event, wrapper);
}
function off(event, callback) {
if (events.has(event)) {
const callbacks = events.get(event);
const index = callbacks.indexOf(callback);
if (index > -1) {
callbacks.splice(index, 1);
}
}
}
function removeAllListeners(event) {
if (event) {
events.delete(event);
} else {
events.clear();
}
}
function listenerCount(event) {
return events.has(event) ? events.get(event).length : 0;
}
module.exports = {
on,
emit,
once,
off,
removeAllListeners,
listenerCount,
events
};

View File

@ -1,43 +0,0 @@
"use strict";
let http = require("http");
let express = require("express");
let compression = require("compression");
let {resolve} = require("path");
const { termoutput } = require("./config");
let app = express();
app.use(compression({ level: 9 }));
app.use(express.json());
app.use(express.urlencoded({ extended: true }));
let server = http.createServer(app);
server.listen(7707, '0.0.0.0', () => {
termoutput && console.log("HTTP Service Running...");
});
server.on("error", (err) => {
console.error(err);
});
exports.http = server;
const apiRouter = require("./api");
app.use("/api", apiRouter);
app.get("/script", (req, res) => {
res.sendFile(resolve("./script/index.js"));
});
app.use(express.static(resolve("./public")));
app.use("/script", express.static(resolve("./script")));
app.get("/", (req, res) => {
res.sendFile(resolve("./script/index.js"));
});
app.get("*", (req, res) => {
res.sendFile(resolve("./script/status.xml"));
});

View File

@ -1,54 +0,0 @@
"use strict";
const handlers = new Map();
function register(type, handler) {
handlers.set(type, handler);
}
function handle(client, message) {
const { type } = message;
if (!type) {
return { status: 'fail', message: 'MISSING_TYPE' };
}
const handler = handlers.get(type);
if (!handler) {
return { status: 'fail', message: 'UNKNOWN_TYPE' };
}
try {
const result = handler(client, message);
return result;
} catch (error) {
console.error(`Handler error [${type}]:`, error);
return { status: 'fail', message: 'HANDLER_ERROR', error: error.message };
}
}
function unregister(type) {
handlers.delete(type);
}
function clear() {
handlers.clear();
}
function hasHandler(type) {
return handlers.has(type);
}
function listHandlers() {
return [...handlers.keys()];
}
module.exports = {
register,
handle,
unregister,
clear,
hasHandler,
listHandlers
};

View File

@ -1,175 +0,0 @@
"use strict";
const { Client } = require("../Client.js");
const { on, emit, register } = require("../WebSocket");
on('disconnect', (xclient) => {
const { intersection, pairs } = xclient.getSucureClients();
for (const [clientid, client] of intersection) {
client?.send([{ id: xclient.id }, "peer/disconnect"]);
}
for (const [id, peer] of pairs) {
peer?.pairs.delete(xclient.id);
xclient.pairs.delete(id);
}
});
register('auth/pair-system', (client, msg) => {
if (msg.value == 'everybody') {
client.requiredPair = true;
return { status: 'success' };
}
if (msg.value == 'disable') {
client.requiredPair = false;
return { status: 'success' };
}
return { status: 'fail', message: 'INVALID_VALUE' };
});
register('my/socketid', (client, msg) => {
return client.id;
});
register('auth/public', (client, msg) => {
client.requiredPair = false;
return { value: 'success', mode: 'public' };
});
register('auth/private', (client, msg) => {
client.requiredPair = true;
return { value: 'success', mode: 'private' };
});
register('request/pair', (client, msg) => {
const { to } = msg;
if (!Client.clients.has(to)) {
return { status: 'fail', message: 'CLIENT_NOT_FOUND' };
}
const pairclient = Client.clients.get(to);
if (pairclient.pairs.has(client.id)) {
return { status: 'success', message: 'ALREADY-PAIRED' };
}
if (client.pairs.has(to)) {
return { status: 'fail', message: 'ALREADY-REQUESTED' };
}
client.peerRequest(pairclient);
return { status: 'success', message: 'REQUESTED' };
});
register('accept/pair', (client, msg) => {
const { to } = msg;
if (!Client.clients.has(to)) {
return { status: 'fail', message: 'CLIENT_NOT_FOUND' };
}
const pairclient = Client.clients.get(to);
if (pairclient.pairs.has(client.id)) {
return { status: 'success', message: 'ALREADY-PAIRED' };
}
if (!client.pairs.has(to)) {
return { status: 'fail', message: 'NOT_REQUESTED_PAIR' };
}
client.acceptPeerRequest(pairclient);
return { status: 'success' };
});
register('reject/pair', (client, msg) => {
const { to } = msg;
if (!Client.clients.has(to)) {
return { status: 'fail', message: 'CLIENT_NOT_FOUND' };
}
const pairclient = Client.clients.get(to);
if (pairclient.pairs.has(client.id)) {
return { status: 'success', message: 'ALREADY-PAIRED' };
}
if (!client.pairs.has(to)) {
return { status: 'fail', message: 'NOT_REQUESTED_PAIR' };
}
client.rejectPeerRequest(pairclient);
return { status: 'success' };
});
register('end/pair', (client, msg) => {
const { to } = msg;
if (!Client.clients.has(to)) {
return { status: 'fail', message: 'CLIENT_NOT_FOUND' };
}
const pairclient = Client.clients.get(to);
if (!pairclient.pairs.has(client.id)) {
return { status: 'success', message: 'NOT_PAIRED' };
}
client.rejectPeerRequest(pairclient);
return { status: 'success' };
});
register('pair/list', (client, msg) => {
return { type: 'pair/list', value: client.pairList() };
});
register('is/reachable', (client, msg) => {
const { to } = msg;
if (!Client.clients.has(to)) {
return false;
}
const otherPeer = Client.clients.get(to);
if (otherPeer.requiredPair && !otherPeer.pairs.has(to)) {
return false;
}
return true;
});
register('auth/info', (client, msg) => {
const { name, value } = msg;
client.info.set(name, value);
const clients = client.getSucureClients();
for (const [, spair] of clients.pairs) {
spair.send([{ from: client.id, name, value }, "pair/info"]);
}
for (const [, spair] of clients.roompairs) {
spair.send([{ from: client.id, name, value }, "pair/info"]);
}
return { status: 'success' };
});
register('peer/info', (client, msg) => {
const { peer } = msg;
if (!client.isSecure(peer)) {
return { status: "fail", message: "unaccessible user" };
}
const peerClient = Client.clients.get(peer);
const info = {};
peerClient.info.forEach((value, name) => { info[name] = value; });
return { status: "success", info };
});

View File

@ -1,101 +0,0 @@
"use strict";
const { Client } = require("../Client.js");
const { register } = require("../WebSocket");
const { Room } = require("./Room");
register('pack/to', (client, msg) => {
const { to, pack, handshake } = msg;
if (!client.packReadable()) {
return handshake ? { type: 'fail' } : undefined;
}
if (!Client.clients.has(to)) {
return handshake ? { type: 'fail' } : undefined;
}
const otherPeer = Client.clients.get(to);
if (otherPeer.requiredPair) {
if (!otherPeer.pairs.has(to)) {
return handshake ? { type: 'fail' } : undefined;
}
} else {
if (!otherPeer.pairs.has(to)) {
otherPeer.pairs.add(client.id);
client.pairs.add(otherPeer.id);
}
}
if (!otherPeer.packWriteable()) {
return handshake ? { type: 'fail' } : undefined;
}
otherPeer.send([{ from: client.id, pack }, 'pack']);
return handshake ? { type: 'success' } : undefined;
});
register('request/to', (client, msg) => {
const { to, pack } = msg;
if (!Client.clients.has(to)) {
return;
}
const otherPeer = Client.clients.get(to);
if (otherPeer.requiredPair) {
if (!otherPeer.pairs.has(to)) {
return;
}
} else {
otherPeer.pairs.add(client.id);
client.pairs.add(otherPeer.id);
}
otherPeer.send([{ from: client.id, pack }, 'request']);
});
register('response/to', (client, msg) => {
const { to, pack, id } = msg;
if (!Client.clients.has(to)) {
return;
}
const otherPeer = Client.clients.get(to);
if (otherPeer.requiredPair && !otherPeer.pairs.has(to)) {
return;
}
otherPeer.send([{ from: client.id, pack }, id]);
});
register('pack/room', (client, msg) => {
const { to, pack, handshake, wom } = msg;
if (!client.packReadable()) {
return handshake ? { type: 'fail' } : undefined;
}
if (!Room.rooms.has(to)) {
return handshake ? { type: 'fail' } : undefined;
}
if (!client.rooms.has(to)) {
return handshake ? { type: 'fail' } : undefined;
}
const room = Room.rooms.get(to);
room.send(
[{ from: to, pack, sender: client.id }, 'pack/room'],
wom ? client.id : undefined,
c => c.packWriteable()
);
return handshake ? { type: 'success' } : undefined;
});

View File

@ -1,269 +0,0 @@
"use strict";
const { Client } = require("../Client");
const { on, register } = require("../WebSocket");
class APNumber {
static busyNumbers = new Map();
static lock(client) {
let c = 24;
while (true) {
if (!APNumber.busyNumbers.has(c)) {
APNumber.busyNumbers.set(c, client);
process.send({
type: 'AP_NUMBER/LOCK',
uuid: client.id,
value: c
});
return c;
}
c++;
}
}
static release(num) {
process.send({
type: 'AP_NUMBER/RELEASE',
uuid: APNumber.busyNumbers.get(num).id,
value: num
});
APNumber.busyNumbers.delete(num);
}
static whois(num) {
return APNumber.busyNumbers.get(num)?.id;
}
}
class APShortCode {
static busyCodes = new Map();
static lock(client) {
let firstLetter = new ShortCodeLetter();
let secondLetter = new ShortCodeLetter();
let thirdLetter = new ShortCodeLetter();
while (1) {
let code = [firstLetter.code, secondLetter.code, thirdLetter.code].join('');
if (!APShortCode.busyCodes.has(code)) {
APShortCode.busyCodes.set(code, client);
process.send({
type: 'AP_SHORTCODE/LOCK',
uuid: APShortCode.busyCodes.get(code).id,
value: code
});
return code;
}
if (!thirdLetter.end()) {
thirdLetter.next();
} else {
thirdLetter.reset();
if (!secondLetter.end()) {
secondLetter.next();
} else {
secondLetter.reset();
if (!firstLetter.end()) {
firstLetter.next();
} else {
break;
}
}
}
}
}
static release(code) {
if (APShortCode.busyCodes.has(code)) {
process.send({
type: 'AP_SHORTCODE/RELEASE',
uuid: APShortCode.busyCodes.get(code).id,
value: code
});
APShortCode.busyCodes.delete(code);
}
}
static whois(num) {
return APShortCode.busyCodes.get(num)?.id;
}
}
class ShortCodeLetter {
chars = 'ABCDEFGHIKLMNOPRSTVXYZ'.split('');
now = 0;
code = 'A';
next() {
this.now++;
this.code = this.chars.at(this.now);
return this.code;
}
reset() {
this.now = 0;
this.code = 'A';
}
end() {
return !this.chars.at(this.now + 1);
}
}
class APIPAddress {
static busyIP = new Map();
static lock(client) {
let A = 10, B = 0, C = 0, D = 1;
while (1) {
let code = [A, B, C, D].join('.');
if (!APIPAddress.busyIP.has(code)) {
APIPAddress.busyIP.set(code, client);
process.send({
type: 'AP_IPADDRESS/LOCK',
uuid: APIPAddress.busyIP.get(code).id,
value: code
});
return code;
}
if (D != 255) { D++; continue; }
D = 0;
if (C != 255) { C++; continue; }
C = 0;
if (B != 255) { B++; continue; }
B = 0;
if (A != 255) { A++; continue; }
return;
}
}
static release(code) {
if (APIPAddress.busyIP.has(code)) {
process.send({
type: 'AP_IPADDRESS/RELEASE',
uuid: APIPAddress.busyIP.get(code).id,
value: code
});
APIPAddress.busyIP.delete(code);
}
}
static whois(num) {
return APIPAddress.busyIP.get(num)?.id;
}
}
exports.APNumber = APNumber;
exports.APShortCode = APShortCode;
exports.APIPAddress = APIPAddress;
register('alloc/APIPAddress', (client, msg) => {
if (client.APIPAddress) {
return { status: 'success', ip: client.APIPAddress };
}
let value = APIPAddress.lock(client);
client.APIPAddress = value;
return { status: 'success', ip: value };
});
register('alloc/APNumber', (client, msg) => {
if (client.APNumber) {
return { status: 'success', number: client.APNumber };
}
let value = APNumber.lock(client);
client.APNumber = value;
return { status: 'success', number: value };
});
register('alloc/APShortCode', (client, msg) => {
if (client.APShortCode) {
return { status: 'success', code: client.APShortCode };
}
let value = APShortCode.lock(client);
client.APShortCode = value;
return { status: 'success', code: value };
});
register('realloc/APIPAddress', (client, msg) => {
if (client.APIPAddress == 0) {
return { status: 'fail' };
}
APIPAddress.release(client.APIPAddress);
let value = APIPAddress.lock(client);
return { status: 'success', ip: value };
});
register('realloc/APNumber', (client, msg) => {
if (client.APNumber == 0) {
return { status: 'fail' };
}
APNumber.release(client.APNumber);
let value = APNumber.lock(client);
return { status: 'success', number: value };
});
register('realloc/APShortCode', (client, msg) => {
if (client.APShortCode == 0) {
return { status: 'fail' };
}
APShortCode.release(client.APShortCode);
let value = APShortCode.lock(client);
return { status: 'success', code: value };
});
register('release/APIPAddress', (client, msg) => {
APIPAddress.release(client.APIPAddress);
client.APIPAddress = undefined;
return { status: 'success' };
});
register('release/APNumber', (client, msg) => {
APNumber.release(client.APNumber);
client.APNumber = undefined;
return { status: 'success' };
});
register('release/APShortCode', (client, msg) => {
APShortCode.release(client.APShortCode);
client.APShortCode = undefined;
return { status: 'success' };
});
register('whois/APIPAddress', (client, msg) => {
let socketId = APIPAddress.whois(msg.whois);
if (socketId) {
return { status: 'success', socket: socketId };
}
return { status: 'fail' };
});
register('whois/APNumber', (client, msg) => {
let socketId = APNumber.whois(msg.whois);
if (socketId) {
return { status: 'success', socket: socketId };
}
return { status: 'fail' };
});
register('whois/APShortCode', (client, msg) => {
let socketId = APShortCode.whois(msg.whois);
if (socketId) {
return { status: 'success', socket: socketId };
}
return { status: 'fail' };
});
on('disconnect', (client) => {
if (client.APIPAddress != 0) {
APIPAddress.release(client.APIPAddress);
}
if (client.APNumber != 0) {
APNumber.release(client.APNumber);
}
if (client.APShortCode != 0) {
APShortCode.release(client.APShortCode);
}
});

View File

@ -1,549 +0,0 @@
const { Client } = require("../Client.js");
let { randomUUID, createHash } = require("crypto");
const joi = require("joi");
const { on, register } = require("../WebSocket");
const { termoutput } = require("../config.js");
let term = require("terminal-kit").terminal;
function Sha256(update) {
return createHash("sha256").update(update).digest("hex");
};
function Room()
{
/**
* @type {string}
*/
this.id = randomUUID();
/**
* @type {string}
*/
this.name = "";
/**
* @type {string}
*/
this.description = "";
/**
* @type {Client}
*/
this.owner = null;
/**
* @type {Date}
*/
this.createdAt = new Date();
/**
* @type {Map<string, Client>}
*/
this.clients = new Map();
/**
* @type {"public"|"private"}
*/
this.accessType = "";
/**
* @type {"free"|"invite"|"password"|"lock"}
*/
this.joinType = "invite";
/**
* @type {boolean}
*/
this.notifyActionInvite = false;
/**
* @type {boolean}
*/
this.notifyActionJoined = true;
/**
* @type {boolean}
*/
this.notifyActionEjected = true;
/**
* @type {string}
*/
this.credential = null;
/**
* @type {string[]}
*/
this.waitingInvited = new Set();
/**
* @type {Map<string,any>}
*/
this.info = new Map();
}
/**
* @param {Room} room
*/
Room.prototype.publish = function(){
Room.rooms.set(this.id, this);
termoutput && term.green("Room Published ").white(this.name," in ").yellow(this.clients.size).white(" clients")('\n');
};
/**
* @return {Client[]}
*/
Room.prototype.filterPeers = function(optiJson){
let peers = [];
this.clients.forEach(client => {
if(client.match(optiJson))
{
peers.push(client);
}
});
return peers;
};
Room.prototype.toJSON = function(detailed){
let obj = {};
obj.id = this.id;
obj.accessType = this.accessType;
obj.createdAt = this.createdAt;
obj.description = this.description;
obj.joinType = this.joinType;
obj.name = this.name;
obj.owner = this.owner.id;
obj.waitingInvited = [...this.waitingInvited];
if(detailed)
{
obj.credential = this.credential;
obj.notifyActionInvite = this.notifyActionInvite;
obj.notifyActionJoined = this.notifyActionJoined;
obj.notifyActionEjected = this.notifyActionEjected;
obj.clients = [...this.clients.keys()];
}
return obj;
};
Room.prototype.getInfo = function(){
let obj = {};
for (const [name, value] of this.info)
{
obj[name] = value;
}
return obj;
};
/**
* @param {Object} data
* @param {Room} room
*/
Room.fromJSON = function(data, room){
room = room || new Room();
let obj = {};
room.id = data.id;
room.accessType = data.accessType;
room.createdAt = data.createdAt;
room.description = data.description;
room.joinType = data.joinType;
room.name = data.name;
if(data.owner && Client.clients.has(data.owner))
{
room.owner = Client.clients.get(data.owner);
}
room.waitingInvited = new Set(data.waitingInvited);
obj.credential = data.credential;
obj.notifyActionInvite = data.notifyActionInvite;
obj.notifyActionJoined = data.notifyActionJoined;
obj.notifyActionEjected = data.notifyActionEjected;
obj.clients = new Map(
data.clients.map(e => ([
e, // map key
Client.clients.get(e) // map value
])
)
)
return room;
};
/**
*
* @param {any} obj
* @param {string} withOut
* @param {(client:Client) => boolean} map
*/
Room.prototype.send = function(obj, withOut, map){
for (const client of this.clients.values()) {
if(client.id != withOut)
{
(
map ? map(client) : 1
) && client.send(obj);
}
}
termoutput && term.green("Room bulk message ").white(this.name," in ").yellow(this.clients.size + "").white(" clients")('\n');
};
/**
* @param {Client} client
*/
Room.prototype.join = function(client){
if(this.notifyActionJoined)
{
this.send(
[
{
id: client.id,
roomid: this.id,
ownerid: this.owner.id
},
'room/joined'
],
void 0,
client => client.peerInfoNotifiable()
);
};
client.rooms.add(this.id);
this.clients.set(client.id, client);
termoutput && term.green("Client Room joined ").white(this.name," in ").yellow(this.clients.size + "").white(" clients")('\n');
};
Room.prototype.down = function(){
termoutput && term.red("Room is downed ").red(this.name," in ").yellow(this.clients.size + "").red(" clients")('\n');
this.send([{
roomid: this.id,
ownerid: this.owner.id
},'room/closed']);
Room.rooms.delete(this.id);
};
/**
* @param {Client} client
*/
Room.prototype.eject = function(client){
if(this.notifyActionEjected)
{
this.send(
[
{
id: client.id,
roomid: this.id,
ownerid: this.owner.id
},
'room/ejected'
],
client.id,
client => client.peerInfoNotifiable()
);
}
client.rooms.delete(this.id);
this.clients.delete(client.id);
if(this.clients.size == 0)
{
this.down();
termoutput && term.red("Client Room closed ").red(this.name," at 0 clients")('\n');
}
termoutput && term.red("Client Room ejected ").red(this.name," in ").yellow(this.clients.size + "").red(" clients")('\n');
};
/**
* @type {Map<string, Room>}
*/
Room.rooms = new Map();
on('connect', (client) => {
let room = new Room();
room.accessType = "private";
room.joinType = "notify";
room.description = 'Private room';
room.id = client.id;
room.name = "Your Room | " + client.id;
room.owner = client;
room.publish();
room.join(client);
});
on('disconnect', (client) => {
const room = Room.rooms.get(client.id);
if (room) room.eject(client);
for (const roomId of client.rooms) {
const r = Room.rooms.get(roomId);
if (r) r.eject(client);
}
});
let CreateRoomVerify = joi.object({
type: joi.any().required(),
accessType: joi.string().pattern(/^public$|private$/).required(),
notifyActionInvite: joi.boolean().required(),
notifyActionJoined: joi.boolean().required(),
notifyActionEjected: joi.boolean().required(),
joinType: joi.string().pattern(/^free$|^invite$|^password$|^lock$/).required(),
description: joi.string().required(),
name: joi.string().required(),
credential: joi.string().optional(),
ifexistsJoin: joi.boolean().optional(),
autoFetchInfo: joi.boolean().optional(),
});
register('myroom-info', (client, msg) => {
let room = Room.rooms.get(client.id);
return { status: "success", room: room.toJSON() };
});
register('room-peers', (client, msg) => {
const { roomId, filter } = msg;
if (!Room.rooms.has(roomId)) {
return { status: 'fail' };
}
const filteredPeers = Room.rooms.get(roomId).filterPeers(filter || {});
return { status: 'success', peers: filteredPeers.map(i => i.id) };
});
register('room/peer-count', (client, msg) => {
const { roomId, filter } = msg;
if (!Room.rooms.has(roomId)) {
return { status: 'fail' };
}
const filteredPeers = Room.rooms.get(roomId).filterPeers(filter || {});
return { status: 'success', count: filteredPeers.length };
});
register('room-info', (client, msg) => {
const { name } = msg;
for (const [roomId, room] of Room.rooms) {
if (name == room.name) {
return { status: "success", room: room.toJSON() };
}
}
return { status: "fail", message: "NOT-FOUND-ROOM" };
});
register('joinedrooms', (client, msg) => {
return [...client.rooms].map(e => Room.rooms.get(e).toJSON());
});
register('closeroom', (client, msg) => {
const { roomId } = msg;
if (!Room.rooms.has(roomId)) {
return { status: 'fail' };
}
const room = Room.rooms.get(roomId);
if (room.owner === client.id) {
room.down();
return { status: 'success' };
}
return { status: 'fail' };
});
register('create-room', (client, msg) => {
const { error } = CreateRoomValidate.validate(msg);
if (error) {
return { status: 'fail', messages: error.message };
}
const { name } = msg;
for (const [, room] of Room.rooms) {
if (name == room.name) {
return { status: "fail", message: "ALREADY-EXISTS" };
}
}
let room = new Room();
room.accessType = msg.accessType;
room.notifyActionInvite = msg.notifyActionInvite;
room.notifyActionJoined = msg.notifyActionJoined;
room.notifyActionEjected = msg.notifyActionEjected;
room.joinType = msg.joinType;
room.description = msg.description;
room.name = msg.name;
room.owner = client;
if (msg.credential) {
room.credential = Sha256(msg.credential + "");
}
room.publish();
room.join(client);
return { status: "success", room: room.toJSON() };
});
register('joinroom', (client, msg) => {
const { name, autoFetchInfo } = msg;
let roomId;
for (const [_roomId, room] of Room.rooms) {
if (name == room.name) {
roomId = _roomId;
break;
}
}
if (!Room.rooms.has(roomId)) {
return { status: "fail", message: "NOT-FOUND-ROOM" };
}
const room = Room.rooms.get(roomId);
if (room.joinType == "lock") {
return { status: "fail", message: "LOCKED-ROOM" };
}
if (room.joinType == "password") {
if (room.credential == Sha256(msg.credential + "")) {
let info = {};
if (autoFetchInfo) {
info.info = room.getInfo();
}
room.join(client);
return { status: "success", room: room.toJSON(), ...info };
}
return { status: "fail", message: "WRONG-PASSWORD", area: "credential" };
}
if (room.joinType == "free") {
let info = {};
if (autoFetchInfo) {
info.info = room.getInfo();
}
room.join(client);
return { status: "success", room: room.toJSON(), ...info };
}
if (room.joinType == "invite") {
room.waitingInvited.add(client.id);
if (room.notifyActionInvite) {
room.send([{ id: client.id }, "room/invite"]);
} else {
room.owner.send([{ id: client.id }, "room/invite"]);
}
}
return { status: "fail", message: "NOT-FOUND-ROOM" };
});
register('ejectroom', (client, msg) => {
const { roomId } = msg;
if (!Room.rooms.has(roomId)) {
return { status: "fail", message: "NOT-FOUND-ROOM" };
}
const room = Room.rooms.get(roomId);
if (!room.clients.has(client.id)) {
return { status: "fail", message: "ALREADY-ROOM-OUT" };
}
room.eject(client);
return { status: "success" };
});
register('accept/invite-room', (client, msg) => {
const { roomId, clientId } = msg;
if (!Room.rooms.has(roomId)) {
return { status: "fail", message: "NOT-FOUND-ROOM" };
}
const room = Room.rooms.get(roomId);
if (!client.rooms.has(room.id)) {
return { status: "fail", message: "FORBIDDEN-INVITE-ACTIONS" };
}
if (room.joinType == 'invite') {
return { status: "fail", message: "INVALID-DATA" };
}
if (!room.waitingInvited.includes(clientId)) {
return { status: "fail", message: "NO-WAITING-INVITED" };
}
if (!Client.clients.has(clientId)) {
return { status: "fail", message: "NO-CLIENT" };
}
const JoinClient = Client.clients.get(clientId);
room.join(JoinClient);
JoinClient.send([{ status: "accepted" }, 'room/invite/status']);
return { status: "success" };
});
register('reject/invite-room', (client, msg) => {
const { roomId, clientId } = msg;
if (!Room.rooms.has(roomId)) {
return { status: "fail", message: "NOT-FOUND-ROOM" };
}
const room = Room.rooms.get(roomId);
if (!client.rooms.has(room.id)) {
return { status: "fail", message: "FORBIDDEN-INVITE-ACTIONS" };
}
if (room.joinType == 'invite') {
return { status: "fail", message: "INVALID-DATA" };
}
if (!room.waitingInvited.includes(clientId)) {
return { status: "fail", message: "NO-WAITING-INVITED" };
}
if (!Client.clients.has(clientId)) {
return { status: "fail", message: "NO-CLIENT" };
}
const JoinClient = Client.clients.get(clientId);
room.waitingInvited = room.waitingInvited.filter(e => e != clientId);
room.send([{ id: clientId, roomId: room.id }, 'room/invite/status']);
JoinClient.send([{ status: "rejected" }, 'room/invite/status']);
return { status: "success" };
});
register('room/list', (client, msg) => {
const rooms = [];
for (const [id, room] of Room.rooms) {
if (room.accessType == "public") {
rooms.push({
name: room.name,
joinType: room.joinType,
description: room.description,
id
});
}
}
return { type: 'public/rooms', rooms };
});
register('room/info', (client, msg) => {
const { roomId, name } = msg;
if (!Room.rooms.has(roomId)) {
return { status: "fail", message: "NOT-FOUND-ROOM" };
}
const room = Room.rooms.get(roomId);
if (!client.rooms.has(room.id)) {
return { status: "fail", message: "NO-JOINED-ROOM" };
}
if (name) {
return { status: "success", value: room.info.get(name) };
}
return { status: "success", value: room.getInfo() };
});
register('room/setinfo', (client, msg) => {
const { roomId, name, value } = msg;
if (!Room.rooms.has(roomId)) {
return { status: "fail", message: "NOT-FOUND-ROOM" };
}
const room = Room.rooms.get(roomId);
if (!client.rooms.has(room.id)) {
return { status: "fail", message: "NO-JOINED-ROOM" };
}
room.info.set(name, value);
room.send(
[{ name, value, roomId: room.id }, "room/info"],
client.id,
c => c.roomInfoNotifiable()
);
return { status: "success" };
});
exports.Room = Room;

View File

@ -1,43 +0,0 @@
"use strict";
const { on, emit, register } = require("../WebSocket");
const defaults = {
notifyPairInfo: true,
packrecaive: true,
packsending: true,
notifyRoomInfo: true
};
on('connect', (client) => {
for (const [name, value] of Object.entries(defaults)) {
client.store.set(name, value);
}
});
register('connection/pairinfo', (client, msg) => {
client.store.set("notifyPairInfo", !!msg.value);
return { status: 'success' };
});
register('connection/roominfo', (client, msg) => {
client.store.set("notifyRoomInfo", !!msg.value);
return { status: 'success' };
});
register('connection/packrecaive', (client, msg) => {
client.store.set("packrecaive", !!msg.value);
return { status: 'success' };
});
register('connection/packsending', (client, msg) => {
client.store.set("packsending", !!msg.value);
return { status: 'success' };
});
register('connection/reset', (client, msg) => {
for (const [name, value] of Object.entries(defaults)) {
client.store.set(name, value);
}
return { status: 'success' };
});

View File

@ -1,7 +0,0 @@
"use strict";
const { on } = require("../WebSocket");
on('connect', (client) => {
client.send([{ type: 'id', value: client.id }, 'id']);
});

View File

@ -1,94 +0,0 @@
"use strict";
let websocket = require("websocket");
let http = null;
let wsServer = null;
let {randomUUID} = require("crypto");
const { Client } = require("./Client.js");
const { termoutput } = require("./config");
const EventEmitter = require("./EventEmitter");
const MessageRouter = require("./MessageRouter");
function init(server) {
http = server;
termoutput && console.log("Web Socket Protocol is ready");
http.addListener("upgrade", () => {
termoutput && console.log("HTTP Upgrading to WebSocket");
});
wsServer = new websocket.server({
httpServer: http,
autoAcceptConnections: true
});
wsServer.addListener("connect", (socket) => {
let client = new Client();
let id = randomUUID();
socket.id = id;
client.id = id;
client.socket = socket;
client.created_at = new Date();
Client.clients.set(id, client);
EventEmitter.emit('connect', client);
let pingTimer = setInterval(() => socket.ping('saQut'), 10_000);
socket.addListener("pong", (validationText) => {
if (validationText.toString('utf8') != "saQut") {
socket.close();
}
});
socket.addListener("message", ({ type, utf8Data }) => {
if (type == "utf8") {
try {
const json = JSON.parse(utf8Data);
const [message, id, action] = json;
let response;
if (typeof id === 'number' || typeof id === 'string') {
response = MessageRouter.handle(client, message);
if (action === 'R') {
client.send([response, id, 'E']);
} else if (action === 'S') {
client.send([response, id, 'C']);
}
} else {
const result = MessageRouter.handle(client, message);
if (result && result.broadcast) {
EventEmitter.emit('broadcast', result.broadcast, client);
}
}
} catch (error) {
EventEmitter.emit('messageError', client, utf8Data);
}
}
});
socket.addListener("close", () => {
EventEmitter.emit('disconnect', client);
Client.clients.delete(id);
clearInterval(pingTimer);
});
});
}
const on = EventEmitter.on;
const emit = EventEmitter.emit;
const off = EventEmitter.off;
const register = MessageRouter.register;
const handle = MessageRouter.handle;
exports.init = init;
exports.on = on;
exports.emit = emit;
exports.off = off;
exports.register = register;
exports.handle = handle;

View File

@ -1,235 +0,0 @@
"use strict";
const express = require("express");
const router = express.Router();
const { Client } = require("./Client");
const { Room } = require("./Services/Room");
const apiKeys = new Map();
const webhooks = new Map();
function auth(req, res, next) {
const key = req.headers['x-api-key'];
if (!key) {
return res.status(401).json({ status: 'fail', message: 'API_KEY_REQUIRED' });
}
if (!apiKeys.has(key)) {
return res.status(401).json({ status: 'fail', message: 'INVALID_API_KEY' });
}
req.server = apiKeys.get(key);
next();
}
router.post('/auth/key', (req, res) => {
const { domain } = req.body;
if (!domain) {
return res.json({ status: 'fail', message: 'DOMAIN_REQUIRED' });
}
const key = require("crypto").randomUUID();
apiKeys.set(key, { domain, key });
res.json({ status: 'success', key });
});
router.post('/client/:id/send', auth, (req, res) => {
const { id } = req.params;
const { pack } = req.body;
const client = Client.clients.get(id);
if (!client) {
return res.json({ status: 'fail', message: 'CLIENT_NOT_FOUND' });
}
if (!pack) {
return res.json({ status: 'fail', message: 'PACK_REQUIRED' });
}
const fromServer = req.server;
client.send([{ from: 'server', fromServer, pack }, 'server/pack']);
res.json({ status: 'success' });
});
router.post('/room/:id/send', auth, (req, res) => {
const { id } = req.params;
const { pack, wom } = req.body;
const room = Room.rooms.get(id);
if (!room) {
return res.json({ status: 'fail', message: 'ROOM_NOT_FOUND' });
}
if (!pack) {
return res.json({ status: 'fail', message: 'PACK_REQUIRED' });
}
const fromServer = req.server;
room.send(
[{ from: 'server', fromServer, pack, roomId: id }, 'server/pack/room'],
wom ? undefined : 'server',
() => true
);
res.json({ status: 'success' });
});
router.post('/room/create', auth, (req, res) => {
const { name, accessType, joinType, description, credential } = req.body;
if (!name) {
return res.json({ status: 'fail', message: 'NAME_REQUIRED' });
}
for (const [, room] of Room.rooms) {
if (room.name === name) {
return res.json({ status: 'fail', message: 'ROOM_ALREADY_EXISTS' });
}
}
const room = new Room();
room.name = name;
room.accessType = accessType || 'public';
room.joinType = joinType || 'free';
room.description = description || '';
room.owner = { id: 'server', isServer: true };
if (credential) {
const { createHash } = require("crypto");
room.credential = createHash("sha256").update(credential).digest("hex");
}
room.publish();
res.json({ status: 'success', room: room.toJSON() });
});
router.post('/room/:id/join', auth, (req, res) => {
const { id } = req.params;
const { credential } = req.body;
const room = Room.rooms.get(id);
if (!room) {
return res.json({ status: 'fail', message: 'ROOM_NOT_FOUND' });
}
if (room.joinType === 'lock') {
return res.json({ status: 'fail', message: 'ROOM_LOCKED' });
}
if (room.joinType === 'password') {
const { createHash } = require("crypto");
const hash = createHash(credential || '').digest("hex");
if (room.credential !== hash) {
return res.json({ status: 'fail', message: 'WRONG_PASSWORD' });
}
}
const fakeClient = {
id: 'server-joined',
isServer: true,
rooms: new Set(),
send: (msg) => {
const fromServer = req.server;
room.send([{ from: 'server', fromServer, ...msg[0] }, msg[1]], 'server');
}
};
room.join(fakeClient);
res.json({ status: 'success', room: room.toJSON() });
});
router.delete('/room/:id/leave', auth, (req, res) => {
const { id } = req.params;
const room = Room.rooms.get(id);
if (!room) {
return res.json({ status: 'fail', message: 'ROOM_NOT_FOUND' });
}
const fakeClient = { id: 'server-joined', isServer: true };
room.eject(fakeClient);
res.json({ status: 'success' });
});
router.get('/room/:id', (req, res) => {
const { id } = req.params;
const room = Room.rooms.get(id);
if (!room) {
return res.json({ status: 'fail', message: 'ROOM_NOT_FOUND' });
}
res.json({ status: 'success', room: room.toJSON() });
});
router.get('/rooms', (req, res) => {
const rooms = [];
for (const [id, room] of Room.rooms) {
rooms.push({
id,
name: room.name,
accessType: room.accessType,
joinType: room.joinType,
description: room.description,
clientCount: room.clients.size
});
}
res.json({ status: 'success', rooms });
});
router.get('/clients', (req, res) => {
const clients = [];
for (const [id, client] of Client.clients) {
clients.push({
id,
rooms: [...client.rooms],
pairs: [...client.pairs]
});
}
res.json({ status: 'success', clients });
});
router.post('/webhook', auth, (req, res) => {
const { url, events } = req.body;
if (!url) {
return res.json({ status: 'fail', message: 'URL_REQUIRED' });
}
const server = req.server;
webhooks.set(server.domain, { url, events: events || ['server/pack', 'server/pack/room'] });
res.json({ status: 'success' });
});
function triggerWebhook(event, data) {
for (const [, webhook] of webhooks) {
if (webhook.events.includes(event)) {
fetch(webhook.url, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ event, data })
}).catch(console.error);
}
}
}
module.exports = router;
module.exports.triggerWebhook = triggerWebhook;

View File

@ -1 +0,0 @@
exports.termoutput = false;

View File

@ -1,26 +0,0 @@
require("./HTTPServer.js");
const { http } = require("./HTTPServer");
const WebSocket = require("./WebSocket");
WebSocket.init(http);
require("./Services/YourID.js");
require("./Services/Auth.js");
require("./Services/Room.js");
require("./Services/DataTransfer.js");
require("./Services/IPPressure.js");
require("./Services/Session.js");
process.on('unhandledRejection',(reason, promise)=>{
console.log("Process unhandledRejection",{reason, promise})
});
process.on('rejectionHandled',(promise)=>{
console.log("Process rejectionHandled",{promise})
});
process.on('multipleResolves',(type, promise, value)=>{
console.log("Process multipleResolves",{type, promise, value})
});
process.on('warning',(err)=>{
console.log("Process warning", err)
});

View File

@ -1,136 +0,0 @@
import MWSE from "frontend";
export interface IConnection{
endpoint: string;
autoReconnect?: boolean | {
timeout: number;
}
}
export class Connection
{
public ws! : WebSocket;
public endpoint : URL;
public autoPair : boolean = false;
public connected : boolean = false;
public autoReconnect : boolean = true;
public autoReconnectTimeout : number = 3000;
public autoReconnectTimer? : number;
constructor(mwse:MWSE, options: IConnection){
if(options.endpoint == "auto")
{
const RootURL : string = ( <HTMLScriptElement> document.currentScript).src
let scriptPath = new URL(RootURL);
let isSecurity = scriptPath.protocol == "https:";
let dumeUrl = scriptPath.pathname.split('/').slice(0,-1).join('/') + '/';
let wsSocket = new URL(dumeUrl, scriptPath);
wsSocket.protocol = isSecurity ? 'wss:' : 'ws:';
this.endpoint = new URL(wsSocket.href);
}else{
try{
// Testing
this.endpoint = new URL(options.endpoint);
}catch{
throw new Error("endpoint is required")
}
}
if(typeof options.autoReconnect == "boolean")
{
this.autoReconnect = true;
}else if(options.autoReconnect)
{
this.autoReconnect = true;
this.autoReconnectTimeout = options.autoReconnect.timeout;
}
}
public connect()
{
if(this.autoReconnectTimer)
{
clearTimeout(this.autoReconnectTimer)
};
this.ws = new WebSocket(this.endpoint.href);
this.addWSEvents();
}
public disconnect()
{
/**
* Eğer bilinerek elle kapatıldıysa otomatik tekrar bağlanmasının
* önüne geçmek için autoReconnect bayrağını her zaman kapalı tutmak gerekir
*/
this.autoReconnect = false;
this.ws.close();
}
public addWSEvents()
{
this.ws.addEventListener("open", () => this.eventOpen());
this.ws.addEventListener("close", () => this.eventClose());
this.ws.addEventListener("error", () => this.eventError());
this.ws.addEventListener("message", ({data}) => this.eventMessage(data as string | ArrayBuffer));
}
private eventOpen()
{
this.connected = true;
for (const callback of this.activeConnectionEvent) {
callback(void 0);
}
}
private eventClose()
{
for (const callback of this.passiveConnectionEvent) {
callback(void 0);
}
this.connected = false;
if(this.autoReconnect)
{
this.autoReconnectTimer = setTimeout(() => this.connect(), this.autoReconnectTimeout) as unknown as number;
}
}
private eventError()
{
this.connected = false;
}
private recaivePackEvent : ((data:any) => any)[] = [];
public onRecaivePack(func:(data:any) => any)
{
this.recaivePackEvent.push(func);
}
private activeConnectionEvent : Function[] = [];
public onActive(func:Function)
{
if(this.connected)
{
func()
}else{
this.activeConnectionEvent.push(func);
}
}
private passiveConnectionEvent : Function[] = [];
public onPassive(func:Function)
{
if(!this.connected)
{
func()
}else{
this.passiveConnectionEvent.push(func);
}
}
private eventMessage(data: string | ArrayBuffer)
{
if(typeof data == "string")
{
let $data = JSON.parse(data);
for (const callback of this.recaivePackEvent) {
callback($data);
}
}
}
public tranferToServer(data:any)
{
if(this.connected)
{
this.ws.send(JSON.stringify(data));
}
}
}

View File

@ -1,68 +0,0 @@
import MWSE from "./index";
import { Message } from "./WSTSProtocol";
export default class EventPool
{
public wsts : MWSE;
public events : Map<number, [Function,Function]> = new Map();
public signals : Map<string, Function[]> = new Map();
public requests : Map<number, [Function,Function]> = new Map();
public count = 0;
constructor(wsts:MWSE){
this.wsts = wsts;
}
/**
* request sends a packet that expects a correlated reply and resolves with it.
* Use it ONLY for response-bearing packets. For fire-and-forget (WOM) packets
* use only(): registering a waiter for a packet the server never answers leaves
* a promise pending forever (issue #33).
*/
public request(msg: Message) : Promise<any>
{
return new Promise((ok,rej) => {
let id = ++this.count;
this.events.set(id,[
(data:any) => {
ok(data);
},
(data:any) => {
rej(data);
}
]);
this.wsts.WSTSProtocol.SendRequest(msg, id);
})
}
/**
* only is the WOM (without-me / fire-and-forget) path: it sends the packet and
* leaves NO pending waiter. The engine deliberately does not reply to these
* relays (it returns nil), so there is nothing to await. This is the separation
* issue #33 requires: request() = response-bearing, only() = WOM.
*/
public only(msg: Message)
{
this.wsts.WSTSProtocol.SendOnly(msg);
}
public stream(msg: Message, callback: Function)
{
let id = ++this.count;
this.wsts.WSTSProtocol.StartStream(msg, id);
this.events.set(id,[
(data:any) => {
callback(data);
},
() => { }
]);
}
public signal(event: string, callback: Function)
{
let T = this.signals.get(event);
if(!T)
{
this.signals.set(event, [callback]);
}else{
T.push(callback);
}
}
}

View File

@ -1,32 +0,0 @@
export default class EventTarget
{
private events : {[key:string]:Function[]} = {};
public emit(eventName :string, ...args:any[])
{
if(this.events[eventName])
{
for (const callback of this.events[eventName]) {
callback(...args);
}
}
}
public on(eventName :string, callback:Function)
{
if(this.events[eventName])
{
this.events[eventName].push(callback)
}else{
this.events[eventName] = [callback];
}
}
public activeScope : boolean = false;
scope(f:Function)
{
if(this.activeScope)
{
f()
}else{
this.on('scope', f)
}
}
}

View File

@ -1,198 +0,0 @@
import MWSE from "frontend";
export class IPPressure
{
public mwse : MWSE;
public APNumber? : number;
public APShortCode? : string;
public APIPAddress? : string;
constructor(mwse : MWSE){
this.mwse = mwse;
};
public async allocAPIPAddress()
{
let {status,ip} = await this.mwse.EventPooling.request({
type: 'alloc/APIPAddress'
}) as {
status:"fail"|"success",
ip?:string
};
if(status == 'success')
{
this.APIPAddress = ip;
return ip;
}else{
throw new Error("Error Allocated Access Point IP Address");
}
}
public async allocAPNumber()
{
let {status,number} = await this.mwse.EventPooling.request({
type: 'alloc/APNumber'
}) as {
status:"fail"|"success",
number?:number
};
if(status == 'success')
{
this.APNumber = number;
return number;
}else{
throw new Error("Error Allocated Access Point Number");
}
}
public async allocAPShortCode()
{
let {status,code} = await this.mwse.EventPooling.request({
type: 'alloc/APShortCode'
}) as {
status:"fail"|"success",
code?:string
};
if(status == 'success')
{
this.APShortCode = code;
return code;
}else{
throw new Error("Error Allocated Access Point Short Code");
}
}
public async reallocAPIPAddress()
{
let {status,ip} = await this.mwse.EventPooling.request({
type: 'realloc/APIPAddress'
}) as {
status:"fail"|"success",
ip?:string
};
if(status == 'success')
{
this.APIPAddress = ip;
return ip;
}else{
throw new Error("Error Reallocated Access Point IP Address");
}
}
public async reallocAPNumber()
{
let {status,number} = await this.mwse.EventPooling.request({
type: 'realloc/APNumber'
}) as {
status:"fail"|"success",
number?:number
};
if(status == 'success')
{
this.APNumber = number;
return number;
}else{
throw new Error("Error Reallocated Access Point Number");
}
}
public async reallocAPShortCode()
{
let {status,code} = await this.mwse.EventPooling.request({
type: 'realloc/APShortCode'
}) as {
status:"fail"|"success",
code?:string
};
if(status == 'success')
{
this.APShortCode = code;
return code;
}else{
throw new Error("Error Reallocated Access Point Short Code");
}
}
public async releaseAPIPAddress()
{
let {status} = await this.mwse.EventPooling.request({
type: 'release/APIPAddress'
}) as {
status:"fail"|"success",
};
if(status == 'success')
{
this.APIPAddress = undefined;
}else{
throw new Error("Error release Access Point IP Address");
}
}
public async releaseAPNumber()
{
let {status} = await this.mwse.EventPooling.request({
type: 'release/APNumber'
}) as {
status:"fail"|"success",
};
if(status == 'success')
{
this.APNumber = undefined;
}else{
throw new Error("Error release Access Point Number");
}
}
public async releaseAPShortCode()
{
let {status} = await this.mwse.EventPooling.request({
type: 'release/APShortCode'
}) as {
status:string
};
if(status == 'success')
{
this.APShortCode = undefined;
}else{
throw new Error("Error release Access Point Short Code");
}
}
public async queryAPIPAddress(ip:string)
{
let {status,socket} = await this.mwse.EventPooling.request({
type: 'whois/APIPAddress',
whois: ip
}) as {
status:"fail"|"success",
socket?:string
};
if(status == "success")
{
return socket;
}else{
return null;
}
}
public async queryAPNumber(number:number)
{
let {status,socket} = await this.mwse.EventPooling.request({
type: 'whois/APNumber',
whois: number
}) as {
status:"fail"|"success",
socket?:string
};
if(status == "success")
{
return socket;
}else{
return null;
}
}
public async queryAPShortCode(code:string)
{
let {status,socket} = await this.mwse.EventPooling.request({
type: 'whois/APShortCode',
whois: code
}) as {
status:"fail"|"success",
socket?:string
};
if(status == "success")
{
return socket;
}else{
return null;
}
}
}

View File

@ -1,262 +0,0 @@
import WebRTC from "./WebRTC";
import Peer from "./Peer";
/**
* Deneyseldir kullanılması önerilmez
*/
export default class P2PFileSender
{
public rtc : RTCPeerConnection;
public peer : Peer;
public webrtc : WebRTC;
public totalSize : number = 0;
public isReady : boolean = false;
public isStarted : boolean = false;
public isSending : boolean = false;
public isRecaiving : boolean = false;
public processedSize : number = 0;
public recaivedFile? : File;
public bufferSizePerChannel : number = 10e6;
public bufferSizePerPack : number = 10e3;
public safeBufferSizePerPack : number = 10e3 - 1;
public constructor(webrtc : WebRTC, peer : Peer)
{
this.webrtc = webrtc;
this.rtc = webrtc.rtc;
this.peer = peer;
}
public async RecaiveFile(
_rtc: RTCPeerConnection,
fileMetadata: {name:string, type:string},
channelCount: number,
_totalSize: number,
onEnded: Function
)
{
//let totals = {};
// let index = 0;
/*setChannelStatus(Array.from({length:channelCount}).map((e, index) => {
return {
name: `${index+1}. Kanal`,
current: 0,
currentTotal: 0,
total: 0
}
}));*/
let parts : Blob[] = [];
this.webrtc.on('datachannel',(datachannel:RTCDataChannel) => {
//let channelIndex = index++;
let current = 0;
let totalSize = 0;
let currentPart = 0;
let bufferAmount : ArrayBuffer[] = [];
datachannel.onmessage = function({data}){
if(totalSize == 0)
{
let {
size,
part,
} = JSON.parse(data);
totalSize = size;
currentPart = part;
/*updateChannelStatus(channelIndex, n => {
return {
...n,
total: totalSize,
current: 0
}
});*/
datachannel.send("READY");
}else{
current += data.byteLength;
bufferAmount.push(data);
/*updateChannelStatus(channelIndex, n => {
return {
...n,
current: data.byteLength + n.current,
currentTotal: data.byteLength + n.currentTotal,
}
});
setProcessedSize(n => n + data.byteLength);*/
if(current == totalSize)
{
parts[currentPart] = new Blob(bufferAmount);
bufferAmount = [];
//totals[datachannel.label] += totalSize;
totalSize = 0;
currentPart = 0;
current = 0;
datachannel.send("TOTAL_RECAIVED");
}
}
};
datachannel.onclose = () => {
channelCount--;
if(channelCount == 0)
{
let file = new File(parts, fileMetadata.name, {
type: fileMetadata.type,
lastModified: +new Date
});
onEnded(file);
}
};
})
}
public async SendFile(
file: File,
metadata: object
)
{
this.isSending = true;
this.isStarted = true;
let buffer = await file.arrayBuffer();
let partCount = Math.ceil(buffer.byteLength / 10e6);
let channelCount = Math.min(5, partCount);
if(this.webrtc.iceStatus != "connected")
{
throw new Error("WebRTC is a not ready")
}
this.peer.send({
type: 'file',
name: file.name,
size: file.size,
mimetype: file.type,
partCount,
channelCount,
metadata: metadata
});
let channels : RTCDataChannel[] = [];
for(let channelIndex = 0; channelIndex < channelCount; channelIndex++)
{
let channel = this.rtc.createDataChannel("\\?\\file_" + channelIndex);
channel.binaryType = "arraybuffer";
await new Promise(ok => {
channel.onopen = () => {
ok(void 0);
}
});
channels.push(channel);
};
let currentPart = 0;
let next = () => {
if(currentPart < partCount)
{
let bufferPart = buffer.slice(currentPart * 10e6, currentPart * 10e6 + 10e6)
currentPart++;
return [bufferPart, currentPart - 1];
};
return [false,0];
};
let spyChannelIndex = channels.length;
await new Promise(ok => {
for (let channelIndex = 0; channelIndex < channels.length; channelIndex++)
{
this.sendPartition(
channels[channelIndex],
next,
channelIndex,
() => {
spyChannelIndex--;
if(spyChannelIndex == 0)
{
this.isSending = false;
this.isStarted = false;
ok(undefined)
}
}
);
}
})
}
protected sendPartition(
channel: RTCDataChannel,
nextblob10mb: () => (number | ArrayBuffer)[] | (number | boolean)[],
_channelIndex: number,
onEnded: Function
)
{
let [currentBuffer,currentPartition] = nextblob10mb();
let currentPart = 0;
let next = () => {
if(!(currentBuffer instanceof ArrayBuffer))
{
return;
}
let bufferPart = currentBuffer.slice(currentPart * 16e3, currentPart * 16e3 + 16e3)
currentPart++;
if(bufferPart.byteLength != 0)
{
/*
updateChannelStatus(channelIndex, n => {
return {
...n,
current: bufferPart.byteLength + n.current,
currentTotal: bufferPart.byteLength + n.currentTotal
}
});
setProcessedSize(n => n + bufferPart.byteLength);
*/
return bufferPart
}
};
channel.addEventListener("message",({data}) => {
if(data == "READY")
{
this.sendFileChannel(channel, next)
}
if(data == "TOTAL_RECAIVED")
{
[currentBuffer,currentPartition] = nextblob10mb();
currentPart = 0;
if(currentBuffer != false)
{
/*updateChannelStatus(channelIndex, n => {
return {
...n,
total: currentBuffer.byteLength,
current: 0,
}
});*/
channel.send(JSON.stringify({
size: (currentBuffer as ArrayBuffer).byteLength,
part: currentPartition
}))
}else{
channel.close();
onEnded();
}
}
});
channel.send(JSON.stringify({
size: (currentBuffer as ArrayBuffer).byteLength,
part: currentPartition
}))
}
protected sendFileChannel(
channel: RTCDataChannel,
getNextBlob: () => ArrayBuffer | undefined
)
{
channel.addEventListener("bufferedamountlow",function(){
let buffer = getNextBlob();
if(buffer)
{
channel.send(buffer);
}
});
channel.bufferedAmountLowThreshold = 16e3 - 1;
let c = getNextBlob();
c && channel.send(c);
}
};

View File

@ -1,237 +0,0 @@
import EventTarget from "./EventTarget";
import { PeerInfo } from "./PeerInfo";
import WebRTC from "./WebRTC";
import MWSE from "./index";
interface IPeerOptions{
};
enum IMessageSymbase
{
PayloadMessagePack = -12873.54,
PayloadRTCBasePack = -12884.54
}
export default class Peer extends EventTarget
{
public mwse : MWSE;
public options : IPeerOptions = {};
public socketId? : string;
public selfSocket : boolean = false;
public active : boolean = false;
public info : PeerInfo;
public rtc : WebRTC;
public peerConnection : boolean = false;
public primaryChannel : "websocket" | "datachannel" = "datachannel";
constructor(wsts:MWSE){
super();
this.mwse = wsts;
this.rtc = this.createRTC();
this.info = new PeerInfo(this);
this.on('pack',(data:{type?:string,action?:IMessageSymbase,payload?:any}) => {
if(data.type == ':rtcpack:')
{
return this.rtc.emit("input", data.payload)
};
this.emit("message", data);
});
}
public createRTC(rtcConfig?: RTCConfiguration | undefined, rtcServers?: RTCIceServer[] | undefined) : WebRTC
{
this.rtc = new WebRTC(rtcConfig,rtcServers);
this.rtc.peer = this;
this.rtc.on("connected", () => {
this.peerConnection = true;
});
this.rtc.on('disconnected', () => {
this.peerConnection = false;
})
this.rtc.on("output",(payload:object) => {
this.send({
type: ':rtcpack:',
payload: payload
})
});
this.rtc.on("message",(payload:object) => {
this.emit("pack",payload);
});
return this.rtc;
}
public setPeerOptions(options: string | IPeerOptions){
if(typeof options == "string")
{
this.setSocketId(options)
}else{
this.options = options;
}
}
public setSocketId(uuid: string){
this.socketId = uuid;
}
async metadata() : Promise<any>
{
if(this.socketId == 'me')
{
let result = await this.mwse.EventPooling.request({
type:'my/socketid'
});
this.selfSocket = true;
this.active ||= true;
this.socketId = result;
this.emit('scope');
this.activeScope = true;
return result;
}
};
async request(pack:any){
if(this.active)
{
return await this.mwse.request(this.socketId as string, pack);
}
};
equalTo(peer : Peer | {socketId: string})
{
return this.socketId == peer.socketId;
}
async isReachable()
{
return await this.mwse.EventPooling.request({
type:'is/reachable',
to: this.socketId
});
}
async enablePairAuth(){
await this.mwse.EventPooling.request({
type:'auth/pair-system',
value: 'everybody'
});
}
async disablePairAuth(){
await this.mwse.EventPooling.request({
type:'auth/pair-system',
value: 'disable'
});
}
async enablePairInfo(){
await this.mwse.EventPooling.request({
type: 'connection/pairinfo',
value: true
});
}
async disablePairInfo(){
await this.mwse.EventPooling.request({
type: 'connection/pairinfo',
value: false
});
}
async requestPair()
{
let {message,status} = await this.mwse.EventPooling.request({
type:'request/pair',
to: this.socketId
});
if(
message == "ALREADY-PAIRED" ||
message == "ALREADY-REQUESTED"
)
{
console.warn("Already paired or pair requested")
};
if(status == "fail")
{
console.error("Request Pair Error",status, message);
return false;
}
return true;
}
async endPair()
{
await this.mwse.EventPooling.request({
type:'end/pair',
to: this.socketId
});
this.forget();
}
async acceptPair()
{
let {message,status} = await this.mwse.EventPooling.request({
type:'accept/pair',
to: this.socketId
});
if(status == "fail")
{
console.error("Pair Error",status, message);
return false;
}
return true;
}
async rejectPair()
{
let {message,status} = await this.mwse.EventPooling.request({
type:'reject/pair',
to: this.socketId
});
if(status == "fail")
{
console.error("Pair Error",status, message);
return false;
}
return true;
}
async getPairedList() : Promise<string>
{
let {value} = await this.mwse.EventPooling.request({
type:'pair/list',
to: this.socketId
});
return value;
}
async send(pack: any){
let isOpenedP2P = this.peerConnection && this.rtc?.active;
let isOpenedServer = this.mwse.server.connected;
let sendChannel : "websocket" | "datachannel";
if(isOpenedP2P && isOpenedServer)
{
if(this.primaryChannel == "websocket")
{
sendChannel = "websocket"
}else
{
sendChannel = "datachannel"
}
}else if(isOpenedServer){
sendChannel = "websocket"
}else{
sendChannel = "datachannel"
}
if(sendChannel == "websocket")
{
if(!this.mwse.writable){
return console.warn("Socket is not writable");
}
// WOM (fire-and-forget): a plain peer.send expects no reply, so use the
// only() path and leave no pending waiter. Using request() here would
// register a promise the engine never resolves (issue #33).
this.mwse.EventPooling.only({
type:'pack/to',
pack,
to: this.socketId
});
}else{
if(pack.type != ':rtcpack:')
{
this.rtc?.sendMessage(pack)
}else{
return console.warn("Socket is not writable");
}
}
}
async forget(){
this.mwse.peers.delete(this.socketId as string);
this.mwse.pairs.delete(this.socketId as string);
}
}

View File

@ -1,48 +0,0 @@
import Peer from "./Peer";
export class PeerInfo
{
public peer : Peer;
public info : {[key:string]: any} = {};
constructor(mwse : Peer){
this.peer = mwse;
};
public async fetch(name?:string)
{
if(name)
{
let rinfo = await this.peer.mwse.EventPooling.request(({
type: "peer/info",
peer: this.peer.socketId,
name
}));
if(rinfo.status == "success")
{
this.info = rinfo.info;
}else console.warn(rinfo.message);
}else{
let rinfo = await this.peer.mwse.EventPooling.request(({
type: "peer/info",
peer: this.peer.socketId
}));
if(rinfo.status == "success")
{
this.info = rinfo.info;
}else console.warn(rinfo.message);
};
return this.info;
}
public set(name: string, value: string | number)
{
this.info[name] = value;
this.peer.mwse.WSTSProtocol.SendOnly({
type: "auth/info",
name,
value
});
}
public get(name?:string)
{
return name ? this.info[name] : this.info;
}
}

View File

@ -1,182 +0,0 @@
import EventTarget from "./EventTarget";
import MWSE from "./index";
import Peer from "./Peer";
import { RoomInfo } from "./RoomInfo";
export interface IRoomOptions
{
name: string;
description?:string;
joinType: "free"|"invite"|"password"|"lock";
credential?: string;
ifexistsJoin?: boolean;
accessType?: "public"|"private";
notifyActionInvite?: boolean;
notifyActionJoined?: boolean;
notifyActionEjected?: boolean;
autoFetchInfo?:boolean
}
export default class Room extends EventTarget
{
public mwse : MWSE;
public options! : IRoomOptions;
public config! : IRoomOptions;
public roomId? : string;
public accessType? : "public"|"private";
public description? : string;
public joinType? : "free"|"invite"|"password"|"lock";
public name? : string;
public owner? : string;
public peers : Map<string,Peer> = new Map();
public info : RoomInfo;
constructor(wsts:MWSE){
super();
this.mwse = wsts;
this.info = new RoomInfo(this);
}
public setRoomOptions(options : IRoomOptions | string)
{
if(typeof options == "string")
{
this.roomId = options;
}else{
let defaultOptions = {
joinType: "free",
ifexistsJoin: true,
accessType: "private",
notifyActionInvite: true,
notifyActionJoined: true,
notifyActionEjected: true,
autoFetchInfo: true
};
Object.assign(defaultOptions,options);
this.config = defaultOptions as IRoomOptions;
}
}
setRoomId(uuid: string){
this.roomId = uuid;
}
async createRoom(roomOptions : IRoomOptions){
let config = this.config || roomOptions;
let result = await this.mwse.EventPooling.request({
type:'create-room',
...config
});
if(result.status == 'fail')
{
if(result.message == "ALREADY-EXISTS" && this.config.ifexistsJoin)
{
return this.join();
}
throw new Error(result.message || result.messages);
}else{
this.options = {
...this.config,
...result.room
};
this.roomId = result.room.id;
this.mwse.rooms.set(this.roomId as string, this);
}
}
async join(){
let result = await this.mwse.EventPooling.request({
type:'joinroom',
name: this.config.name,
credential: this.config.credential,
autoFetchInfo: this.config.autoFetchInfo || false
});
if(result.status == 'fail')
{
throw new Error(result.message);
}else{
this.options = {
...this.config,
...result.room
};
if(result.info)
{
this.info.info = result.info;
};
this.roomId = result.room.id;
this.mwse.rooms.set(this.roomId as string, this);
}
}
async eject(){
let {type} = await this.mwse.EventPooling.request({
type:'ejectroom',
roomId: this.roomId
});
this.peers.clear();
if(type == 'success')
{
this.mwse.rooms.delete(this.roomId as string);
}
}
async send(pack: any, wom:boolean = false, handshake = false){
if(!this.mwse.writable){
return console.warn("Socket is not writable");
}
if(handshake)
{
let {type} = await this.mwse.EventPooling.request({
type:'pack/room',
pack,
to: this.roomId,
wom,
handshake
}) as {
type:"success"|"fail"
};
if(type == "fail"){
throw new Error("Cant send message to room")
}
}else{
// WOM broadcast: fire-and-forget, no waiter (issue #33). When the caller
// wants delivery confirmation it passes handshake=true (branch above),
// which the engine answers with a {type} ack.
this.mwse.EventPooling.only({
type:'pack/room',
pack,
to: this.roomId,
wom,
handshake
})
}
}
async fetchPeers(filter?:{[key:string]:any}, onlyNumber:boolean = false) : Promise<Number | Peer[]>
{
if(onlyNumber)
{
let {count} = await this.mwse.EventPooling.request({
type:'room/peer-count',
roomId: this.roomId,
filter: filter || {}
}) as {count:Number};
return count;
}else{
let {status, peers} = await this.mwse.EventPooling.request({
type:'room-peers',
roomId: this.roomId,
filter: filter || {}
}) as {status:"success"|"fail", peers: string[]};
let cup : Peer[] = [];
if(status == 'fail')
{
throw new Error("Cant using peers on room")
}else if(status == 'success'){
for (const peerid of peers) {
let peer = this.mwse.peer(peerid,true);
cup.push(peer);
this.peers.set(peerid, peer);
}
};
return cup;
}
}
}

View File

@ -1,52 +0,0 @@
import Room from "./Room";
export class RoomInfo
{
public room : Room;
public info : {[key:string]: any} = {};
constructor(room : Room){
this.room = room;
this.room.on('updateinfo',(name:string,value:any) => {
this.info[name] = value;
})
};
public async fetch(name?:string)
{
if(name)
{
let rinfo = await this.room.mwse.EventPooling.request(({
type: "room/getinfo",
roomId: this.room.roomId,
name
}));
if(rinfo.status == "success")
{
this.info = rinfo.value;
}else console.warn(rinfo.message);
}else{
let rinfo = await this.room.mwse.EventPooling.request(({
type: "room/info",
roomId: this.room.roomId
}));
if(rinfo.status == "success")
{
this.info = rinfo.value;
}else console.warn(rinfo.message);
};
return this.info;
}
public set(name: string, value: string | number)
{
this.info[name] = value;
this.room.mwse.WSTSProtocol.SendOnly({
type: "room/setinfo",
roomId: this.room.roomId,
name,
value
});
}
public get(name?:string)
{
return name ? this.info[name] : this.info;
}
}

View File

@ -1,66 +0,0 @@
import MWSE from "./index";
export interface Message {
[key:string|number]:any;
}
export default class WSTSProtocol
{
public mwse : MWSE;
constructor(wsts:MWSE){
this.mwse = wsts;
this.addListener();
}
public addListener()
{
this.mwse.server?.onRecaivePack((pack)=>{
this.PackAnalyze(pack)
})
}
public SendRaw(pack: Message)
{
this.mwse.server.tranferToServer(pack);
}
public SendOnly(pack: Message)
{
this.mwse.server.tranferToServer([pack,'R']);
}
public SendRequest(pack: Message, id: number)
{
this.mwse.server.tranferToServer([pack, id, 'R']);
}
public StartStream(pack: Message, id: number)
{
this.mwse.server.tranferToServer([pack, id, 'S']);
}
public PackAnalyze(data:any)
{
let [payload, id, action] = data;
if(typeof id === 'number')
{
let callback = this.mwse.EventPooling.events.get(id);
if(callback)
{
callback[0](payload, action);
switch(action)
{
case 'E':{ // [E]ND flag
this.mwse.EventPooling.events.delete(id);
break;
}
case 'S': // [S]TREAM flag
default:{
break;
}
}
}else console.warn("Missing event sended from server");
}else{
let signals = this.mwse.EventPooling.signals.get(id);
if(signals)
{
for (const callback of signals) {
callback(payload);
}
}else console.warn("Missing event sended from server");
}
}
}

View File

@ -1,522 +0,0 @@
import P2PFileSender from "./P2PFileSender";
import Peer from "./Peer";
interface TransferStreamInfo
{
senders : RTCRtpSender[];
recaivers : RTCRtpReceiver[];
stream:MediaStream | undefined;
id:string;
name:string;
}
export default class WebRTC
{
public static channels : Map<any,any> = new Map();
public static requireGC : boolean = false;
public id : any;
public active : boolean = false;
public connectionStatus : "closed" | "connected" | "connecting" | "disconnected" | "failed" | "new" = "new";
public iceStatus : "checking" | "closed" | "completed" | "connected" | "disconnected" | "failed" | "new" = "new";
public gatheringStatus : "complete" | "gathering" | "new" = "new";
public signalingStatus : "" | "closed" | "have-local-offer" | "have-local-pranswer" | "have-remote-offer" | "have-remote-pranswer" | "stable" = ""
public rtc! : RTCPeerConnection;
public recaivingStream : Map<string, TransferStreamInfo> = new Map();
public sendingStream : Map<string, TransferStreamInfo> = new Map();
public events : { [eventname:string]: Function[] } = {};
public channel : RTCDataChannel | undefined;
public static defaultRTCConfig : RTCConfiguration = {
iceCandidatePoolSize: 0,
iceTransportPolicy:"all",
rtcpMuxPolicy:"require",
};
private isPolite() : boolean
{
let myId = this.peer?.mwse.peer('me').socketId as string;
let peerId = this.peer?.socketId as string;
return myId < peerId;
}
public static defaultICEServers : RTCIceServer[] = [{
urls: "stun:stun.l.google.com:19302"
},{
urls: "stun:stun1.l.google.com:19302"
},{
urls: "stun:stun2.l.google.com:19302"
},{
urls: "stun:stun3.l.google.com:19302"
},{
urls: "stun:stun4.l.google.com:19302"
}];
public peer? : Peer;
public FileTransportChannel? : P2PFileSender;
public makingOffer = false;
public ignoreOffer = false;
public isSettingRemoteAnswerPending = false;
candicatePack : RTCIceCandidate[] = [];
constructor(
rtcConfig?: RTCConfiguration,
rtcServers?: RTCIceServer[]
)
{
let config : any = {};
if(rtcConfig)
{
Object.assign(
config,
WebRTC.defaultRTCConfig,
rtcConfig
)
}else{
Object.assign(
config,
WebRTC.defaultRTCConfig
)
}
config.iceServers = rtcServers || WebRTC.defaultICEServers;
this.rtc = new RTCPeerConnection(config as RTCConfiguration);
this.rtc.addEventListener("connectionstatechange",()=>{
this.eventConnectionState();
})
this.rtc.addEventListener("icecandidate",(...args)=>{
this.eventIcecandidate(...args);
})
this.rtc.addEventListener("iceconnectionstatechange",()=>{
this.eventICEConnectionState();
})
this.rtc.addEventListener("icegatheringstatechange",()=>{
this.eventICEGatherinState();
})
this.rtc.addEventListener("negotiationneeded",()=>{
this.eventNogationNeeded();
})
this.rtc.addEventListener("signalingstatechange",()=>{
this.eventSignalingState();
})
this.rtc.addEventListener("track",(...args)=>{
this.eventTrack(...args);
})
this.rtc.addEventListener("datachannel",(...args)=>{
this.eventDatachannel(...args);
})
this.on('input',async (data:{[key:string]:any})=>{
switch(data.type)
{
case "icecandidate":{
try{
if(this.rtc.remoteDescription){
await this.rtc.addIceCandidate(new RTCIceCandidate(data.value));
}else{
this.candicatePack.push(new RTCIceCandidate(data.value))
}
}catch(error){
debugger;
}finally{
console.log("ICE Canbet")
}
break;
}
case "offer":{
let readyForOffer = !this.makingOffer && (this.rtc.signalingState == "stable" || this.isSettingRemoteAnswerPending);
const offerCollision = !readyForOffer;
this.ignoreOffer = !this.isPolite() && offerCollision;
if(this.ignoreOffer){
return;
}
this.isSettingRemoteAnswerPending = false;
await this.rtc.setRemoteDescription(new RTCSessionDescription(data.value));
this.isSettingRemoteAnswerPending = false;
for (const candidate of this.candicatePack) {
await this.rtc.addIceCandidate(candidate);
}
let answer = await this.rtc.createAnswer({
offerToReceiveAudio: true,
offerToReceiveVideo: true
})
await this.rtc.setLocalDescription(answer);
this.send({
type: 'answer',
value: answer
});
break;
}
case "answer":{
await this.rtc.setRemoteDescription(new RTCSessionDescription(data.value))
for (const candidate of this.candicatePack) {
await this.rtc.addIceCandidate(candidate);
}
break;
}
case "streamInfo":{
let {id,value} = data;
let streamInfo = this.recaivingStream.get(id);
if(!streamInfo)
{
this.recaivingStream.set(id,value as TransferStreamInfo);
}else{
this.recaivingStream.set(id,{
...streamInfo,
...value
} as TransferStreamInfo);
}
this.send({
type:'streamAccept',
id
})
break;
}
case "streamRemoved":{
let {id} = data;
this.emit('stream:stopped', this.recaivingStream.get(id));
this.recaivingStream.delete(id);
break;
}
case "streamAccept":{
let {id} = data;
let sendingStream = this.sendingStream.get(id) as TransferStreamInfo;
let senders = [];
if(sendingStream && sendingStream.stream)
{
for (const track of sendingStream.stream.getTracks()) {
senders.push(this.rtc.addTrack(track, sendingStream.stream));
};
sendingStream.senders = senders;
}
this.emit('stream:accepted', sendingStream);
break;
}
case "message":{
this.emit('message', data.payload);
break;
}
}
})
}
public addEventListener(event:string,callback: Function){
(this.events[event] || (this.events[event]=[])).push(callback);
};
public on(event:string,callback: Function){
this.addEventListener(event, callback)
};
public async dispatch(event:string,...args:any[]) : Promise<any> {
if(this.events[event])
{
for (const callback of this.events[event])
{
await callback(...args)
}
}
}
public async emit(event:string,...args:any[]) : Promise<any> {
await this.dispatch(event, ...args)
}
public connect()
{
if(!this.channel)
{
this.createDefaultDataChannel();
}
}
public sendMessage(data: any)
{
if(data.type == ':rtcpack:')
{
throw "WebRTC Kanalında Sızma";
}
this.send({
type: 'message',
payload: data
});
}
public createDefaultDataChannel()
{
let dt = this.rtc.createDataChannel(':default:',{
ordered: true
});
dt.addEventListener("open",()=>{
this.channel = dt;
WebRTC.channels.set(this.id, this);
this.active = true;
});
dt.addEventListener("message",({data})=>{
let pack = JSON.parse(data);
this.emit('input', pack);
})
dt.addEventListener("close",()=>{
this.channel = undefined;
this.active = false;
})
}
public destroy()
{
this.active = false;
if(this.channel)
{
this.channel.close();
this.channel = undefined;
}
if(this.rtc)
{
this.rtc.close();
//this.rtc = undefined;
};
this.emit('disconnected');
WebRTC.channels.delete(this.id);
}
public eventDatachannel(event: RTCDataChannelEvent)
{
if(event.channel.label == ':default:'){
WebRTC.channels.set(this.id, this);
this.channel = event.channel;
this.active = true;
event.channel.addEventListener("message",({data})=>{
let pack = JSON.parse(data);
this.emit('input', pack);
})
event.channel.addEventListener("close",()=>{
this.channel = undefined;
WebRTC.channels.delete(this.id);
WebRTC.requireGC = true;
this.active = false;
})
}else{
this.emit('datachannel', event.channel);
}
}
public send(data:object)
{
if(this.channel?.readyState == "open")
{
this.channel.send(JSON.stringify(data));
}else{
this.emit('output', data);
}
}
public eventConnectionState()
{
this.connectionStatus = this.rtc.connectionState;
if(this.connectionStatus == 'connected')
{
if(this.active == false)
{
this.emit('connected');
}
};
if(this.connectionStatus == 'failed')
{
this.rtc.restartIce();
};
if(this.connectionStatus == "closed")
{
if(this.active)
{
this.destroy();
}
}
}
public eventIcecandidate(event: RTCPeerConnectionIceEvent)
{
if(event.candidate)
{
this.send({
type:'icecandidate',
value: event.candidate
})
}
}
public eventICEConnectionState()
{
this.iceStatus = this.rtc.iceConnectionState;
}
public eventICEGatherinState()
{
this.gatheringStatus = this.rtc.iceGatheringState;
}
public async eventNogationNeeded()
{
try{
this.makingOffer = true;
let offer = await this.rtc.createOffer({
iceRestart: true,
offerToReceiveAudio: true,
offerToReceiveVideo: true
});
await this.rtc.setLocalDescription(offer);
this.send({
type: 'offer',
value: offer
});
}catch(error){
console.error(`Nogation Error:`, error)
}
finally{
this.makingOffer = false;
}
}
public eventSignalingState()
{
this.signalingStatus = this.rtc.signalingState;
}
public eventTrack(event: RTCTrackEvent)
{
let rtpRecaiver = event.receiver;
if(event.streams.length)
{
for (const stream of event.streams) {
let streamInfo = this.recaivingStream.get(stream.id) as TransferStreamInfo;
(streamInfo.recaivers || (streamInfo.recaivers = [])).push(rtpRecaiver);
if((this.recaivingStream.get(stream.id) as {stream : MediaStream | undefined}).stream == null)
{
streamInfo.stream = stream;
this.emit('stream:added', this.recaivingStream.get(stream.id));
}else{
streamInfo.stream = stream;
}
}
}
}
public sendStream(stream:MediaStream,name:string,info:{[key:string]:any}){
this.send({
type: 'streamInfo',
id: stream.id,
value: {
...info,
name: name
}
});
this.sendingStream.set(stream.id,{
...info,
id:stream.id,
name: name,
stream
} as TransferStreamInfo);
};
public stopStream(_stream:MediaStream){
if(this.connectionStatus != 'connected'){
return
}
if(this.sendingStream.has(_stream.id))
{
let {stream} = this.sendingStream.get(_stream.id) as {stream:MediaStream};
for (const track of stream.getTracks()) {
for (const RTCPSender of this.rtc.getSenders()) {
if(RTCPSender.track?.id == track.id)
{
this.rtc.removeTrack(RTCPSender);
}
}
}
this.send({
type: 'streamRemoved',
id: stream.id
});
this.sendingStream.delete(_stream.id)
}
}
public stopAllStreams()
{
if(this.connectionStatus != 'connected'){
return
}
for (const [, {stream}] of this.sendingStream) {
if(stream == undefined)
{
continue;
}
for (const track of stream.getTracks()) {
for (const RTCPSender of this.rtc.getSenders()) {
if(RTCPSender.track?.id == track.id)
{
this.rtc.removeTrack(RTCPSender);
}
}
}
this.send({
type: 'streamRemoved',
id: stream.id
});
};
this.sendingStream.clear();
}
public async SendFile(file:File, meta: object)
{
if(!this.peer)
{
throw new Error("Peer is not ready");
}
this.FileTransportChannel = new P2PFileSender(this, this.peer);
await this.FileTransportChannel.SendFile(file, meta);
}
public async RecaiveFile(
chnlCount:number,
filemeta: {
name: string;
type: string;
},
totalSize: number
) : Promise<File>
{
if(!this.peer)
{
throw new Error("Peer is not ready");
}
this.FileTransportChannel = new P2PFileSender(this, this.peer);
return await new Promise(recaivedFile => {
if(this.FileTransportChannel)
{
this.FileTransportChannel.RecaiveFile(
this.rtc,
filemeta,
chnlCount,
totalSize,
(file: File) => {
recaivedFile(file)
}
);
}
})
}
}
WebRTC.requireGC = false;
setInterval(()=>{
if(WebRTC.requireGC == false) return;
let img = document.createElement("img");
img.src = window.URL.createObjectURL(new Blob([new ArrayBuffer(5e+7)]));
img.onerror = function() {
window.URL.revokeObjectURL(this.src);
};
WebRTC.requireGC = false;
}, 3000);
declare global {
interface MediaStream {
senders : RTCRtpSender[];
}
}

View File

@ -1,238 +0,0 @@
import {Connection,IConnection} from "./Connection";
import EventPool from "./EventPool";
import EventTarget from "./EventTarget";
import { IPPressure } from "./IPPressure";
import Peer from "./Peer";
import Room, { IRoomOptions } from "./Room";
import WSTSProtocol, { Message } from "./WSTSProtocol";
import WebRTC from "./WebRTC";
//import {Gzip} from "fflate";
export default class MWSE extends EventTarget {
public static rtc : WebRTC;
public server! : Connection;
public WSTSProtocol! : WSTSProtocol;
public EventPooling! : EventPool;
public rooms : Map<string, Room> = new Map();
public pairs : Map<string, Peer> = new Map();
public peers : Map<string, Peer> = new Map();
public virtualPressure : IPPressure;
public me! : Peer;
/*public static compress(message:string, callback:(e:any) => any)
{
let u : any= [];
let C = new Gzip({
level: 9,
mem: 12
},(stream,isLast) => {
u.push(stream);
if(isLast)
{
callback(u);
}
});
C.push(new TextEncoder().encode(message), true);
}*/
constructor(options: IConnection){
super();
MWSE.rtc = MWSE as unknown as WebRTC;
this.server = new Connection(this,options);
this.WSTSProtocol = new WSTSProtocol(this);
this.EventPooling = new EventPool(this);
this.virtualPressure = new IPPressure(this);
this.server.connect();
this.me = new Peer(this);
this.me.scope(()=>{
this.peers.set('me', this.me);
this.peers.set(this.me.socketId as string, this.me);
})
this.server.onActive(async ()=>{
this.me.setSocketId('me');
await this.me.metadata();
this.emit('scope');
this.activeScope = true;
});
this.server.onPassive(async ()=>{
this.emit('close');
});
this.packMessagingSystem();
}
public writable = 1;
public readable = 1;
public destroy()
{
this.server.disconnect();
}
public enableRecaiveData(){
this.WSTSProtocol.SendOnly({ type: 'connection/packrecaive', value: 1 })
this.readable = 1
}
public disableRecaiveData(){
this.WSTSProtocol.SendOnly({ type: 'connection/packrecaive', value: 0 })
this.readable = 0
}
public enableSendData(){
this.WSTSProtocol.SendOnly({ type: 'connection/packsending', value: 1 })
this.writable = 1
}
public disableSendData(){
this.WSTSProtocol.SendOnly({ type: 'connection/packsending', value: 0 })
this.writable = 0
}
public enableNotifyRoomInfo(){
this.WSTSProtocol.SendOnly({ type: 'connection/roominfo', value: 1 })
}
public disableNotifyRoomInfo(){
this.WSTSProtocol.SendOnly({ type: 'connection/roominfo', value: 0 })
}
public async request(peerId: string, pack:Message)
{
let {pack:answer} = await this.EventPooling.request({
type: 'request/to',
to: peerId,
pack
});
return answer;
}
public async response(peerId: string, requestId:number, pack:Message)
{
this.WSTSProtocol.SendOnly({
type: 'response/to',
to: peerId,
pack,
id: requestId
})
}
private packMessagingSystem()
{
this.EventPooling.signal('pack',(payload : {from:string,pack:any}) => {
if(this.readable)
{
let {from,pack} = payload;
this.peer(from, true).emit('pack', pack);
}
})
this.EventPooling.signal('request',(payload : {from:string,pack:any,id:number}) => {
let {from,pack, id} = payload;
let scope = {
body: pack,
response: (pack: Message) => {
this.response(from, id, pack);
},
peer: this.peer(from, true)
};
this.peer(from, true).emit('request', scope);
this.peer('me').emit('request', scope);
})
this.EventPooling.signal('pack/room',(payload : {from:string,pack:any,sender:string}) => {
if(this.readable)
{
let {from,pack,sender} = payload;
this.room(from).emit('message', pack, this.peer(sender));
}
})
this.EventPooling.signal('room/joined',(payload : {id:string,roomid:any,ownerid:string}) => {
let {id,roomid} = payload;
let room = this.room(roomid);
let peer = this.peer(id, true);
room.peers.set(peer.socketId as string, peer);
room.emit('join', peer);
})
this.EventPooling.signal('room/info',(payload : {roomId:string,value:any,name:string}) => {
let {roomId,name,value} = payload;
this.room(roomId).emit('updateinfo', name,value);
})
this.EventPooling.signal('room/ejected',(payload : {id:string,roomid:any,ownerid:string}) => {
let {id,roomid} = payload;
let room = this.room(roomid);
let peer = this.peer(id, true);
room.peers.delete(peer.socketId as string);
room.emit('eject', peer);
})
this.EventPooling.signal('room/closed',(payload : {roomid:any}) => {
let {roomid} = payload;
let room = this.room(roomid);
room.peers.clear();
room.emit('close');
this.rooms.delete(roomid);
})
this.EventPooling.signal("pair/info", (payload : {from : string,name: string, value: string | number | boolean}) => {
let {from, name, value} = payload;
let peer = this.peer(from, true);
peer.info.info[name] = value;
peer.emit("info", name, value);
})
this.EventPooling.signal("request/pair", (payload : {from : string,info: any}) => {
let {from, info} = payload;
let peer = this.peer(from, true);
peer.info.info = info;
peer.emit("request/pair", peer);
this.peer('me').emit('request/pair', peer);
})
this.EventPooling.signal("peer/disconnect", (payload : {id : string}) => {
let {id} = payload;
let peer = this.peer(id, true);
peer.emit("disconnect", peer);
})
this.EventPooling.signal("accepted/pair", (payload : {from : string,info: any}) => {
let {from, info} = payload;
let peer = this.peer(from, true);
peer.info.info = info;
peer.emit("accepted/pair", peer);
this.peer('me').emit('accepted/pair', peer);
})
this.EventPooling.signal("end/pair", (payload : {from : string,info: any}) => {
let {from, info} = payload;
let peer = this.peer(from, true);
peer.emit("end/pair", info);
this.peer('me').emit('end/pair', from, info);
})
}
public room(options: IRoomOptions | string) : Room
{
if(typeof options == "string")
{
if(this.rooms.has(options))
{
return this.rooms.get(options) as Room
}
}
let room = new Room(this);
room.setRoomOptions(options);
this.emit('room');
return room;
}
public peer(options: string | IRoomOptions, isActive = false) : Peer
{
if(typeof options == "string")
{
if(this.peers.has(options))
{
return this.peers.get(options) as Peer
}
if(this.pairs.has(options))
{
return this.pairs.get(options) as Peer
}
}
let peer = new Peer(this);
peer.setPeerOptions(options);
peer.active = isActive;
this.peers.set(peer.socketId as string, peer);
this.emit('peer', peer);
return peer;
}
};
declare global {
interface Window {
MWSE: any;
}
}
window.MWSE = MWSE;

View File

@ -1,14 +0,0 @@
require("./Source/index");
process.on('unhandledRejection',(reason, promise)=>{
console.log("Process unhandledRejection",{reason, promise})
});
process.on('rejectionHandled',(promise)=>{
console.log("Process rejectionHandled",{promise})
});
process.on('multipleResolves',(type, promise, value)=>{
console.log("Process multipleResolves",{type, promise, value})
});
process.on('warning',(err)=>{
console.log("Process warning", err)
});

View File

@ -14,9 +14,8 @@ type Config struct {
Host string // bind address, e.g. "0.0.0.0"
Port int // listen port, default 7707
PublicDir string // static assets directory (default "./public")
ScriptDir string // legacy compiled SDK bundle directory (default "./script")
SDKDir string // ES-module SDK directory (default "./sdk")
PublicDir string // static assets served at /<file> (default "./public")
SDKDir string // ES-module SDK files served at /sdk/ (default "./sdk")
ReadHeaderTimeout time.Duration // HTTP read-header timeout
ShutdownTimeout time.Duration // grace period for in-flight work on shutdown
@ -55,17 +54,15 @@ type ConnConfig struct {
WriteWait time.Duration // deadline for a single socket write
}
// Load reads configuration from the environment, applying defaults that match the
// original server. Recognised variables:
// Load reads configuration from the environment. Recognised variables:
//
// MWSE_HOST, MWSE_PORT, MWSE_PUBLIC_DIR, MWSE_SCRIPT_DIR,
// MWSE_HOST, MWSE_PORT, MWSE_PUBLIC_DIR, MWSE_SDK_DIR,
// MWSE_SHUTDOWN_TIMEOUT (seconds), MWSE_TERM_OUTPUT (1/true)
func Load() Config {
return Config{
Host: env("MWSE_HOST", "0.0.0.0"),
Port: envInt("MWSE_PORT", 7707),
PublicDir: env("MWSE_PUBLIC_DIR", "./public"),
ScriptDir: env("MWSE_SCRIPT_DIR", "./script"),
SDKDir: env("MWSE_SDK_DIR", "./sdk"),
ReadHeaderTimeout: 10 * time.Second,
ShutdownTimeout: time.Duration(envInt("MWSE_SHUTDOWN_TIMEOUT", 10)) * time.Second,

View File

@ -29,7 +29,7 @@ func testEngine(t *testing.T) string {
cfg := config.Load()
cfg.Conn.PingInterval = 80 * time.Millisecond
cfg.ScriptDir = t.TempDir() // static routes are irrelevant here
cfg.SDKDir = t.TempDir() // static routes are irrelevant here
cfg.PublicDir = t.TempDir()
srv := httptest.NewServer(New(hub, cfg).Handler)

View File

@ -1,7 +1,6 @@
// Package httpserver assembles the HTTP surface of the engine: the WebSocket
// upgrade endpoint, the static asset routes (the built SDK and the public files),
// and the /api control plane. It mirrors the routing of the original
// HTTPServer.js while adding timeouts and graceful shutdown (#25).
// upgrade endpoint, the ES-module SDK routes, the public asset directory, and
// the /api control plane.
package httpserver
import (
@ -27,10 +26,8 @@ type ServerOptions struct {
Approver ws.Approver
}
// New builds the *http.Server. WebSocket upgrades are detected on ANY path and
// routed to the engine (the SDK derives its endpoint from wherever the script was
// served, so the upgrade may arrive at "/" or "/script/"). All other requests go
// through the static/API mux.
// New builds the *http.Server. WebSocket upgrades are detected on any path and
// routed to the engine; all other requests go through the static/API mux.
func New(hub *ws.Hub, cfg config.Config, srvOpts ...ServerOptions) *http.Server {
var so ServerOptions
if len(srvOpts) > 0 {
@ -68,32 +65,24 @@ func New(hub *ws.Hub, cfg config.Config, srvOpts ...ServerOptions) *http.Server
// registerStatic wires the asset routes:
//
// - /sdk.js -> redirect to /sdk/index.js (so import.meta.url resolves correctly)
// - /sdk/ -> ES-module SDK files (sdk/EventTarget.js, etc.)
// - /script -> legacy compiled SDK entry (script/index.js)
// - /script/<file> -> files under the script directory
// - / -> the SDK entry (so a bare visit returns the script)
// - /<file> -> a matching file under the public directory
// - anything else -> the status document (status.xml)
// - /sdk.js -> 301 /sdk/index.js (import.meta.url resolves correctly for relative imports)
// - /sdk/ -> ES-module SDK files served from cfg.SDKDir
// - / -> /sdk/index.js redirect (bare URL returns the SDK entry)
// - /<file> -> matching file under cfg.PublicDir
// - anything -> public/status.xml fallback
func registerStatic(mux *http.ServeMux, cfg config.Config) {
scriptIndex := filepath.Join(cfg.ScriptDir, "index.js")
statusDoc := filepath.Join(cfg.ScriptDir, "status.xml")
statusDoc := filepath.Join(cfg.PublicDir, "status.xml")
// ES-module SDK: redirect /sdk.js → /sdk/index.js so that import.meta.url
// resolves to /sdk/index.js and all relative imports go to /sdk/*.
// /sdk.js → /sdk/index.js: keeps import.meta.url = /sdk/index.js so that
// ./EventTarget.js etc. resolve to /sdk/EventTarget.js (same origin).
mux.HandleFunc("/sdk.js", func(w http.ResponseWriter, r *http.Request) {
http.Redirect(w, r, "/sdk/index.js", http.StatusMovedPermanently)
})
mux.Handle("/sdk/", http.StripPrefix("/sdk/", http.FileServer(http.Dir(cfg.SDKDir))))
mux.HandleFunc("/script", func(w http.ResponseWriter, r *http.Request) {
http.ServeFile(w, r, scriptIndex)
})
mux.Handle("/script/", http.StripPrefix("/script/", http.FileServer(http.Dir(cfg.ScriptDir))))
mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
if r.URL.Path == "/" {
http.ServeFile(w, r, scriptIndex)
http.Redirect(w, r, "/sdk/index.js", http.StatusFound)
return
}
if f, ok := safePublicFile(cfg.PublicDir, r.URL.Path); ok {

3824
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -1,61 +0,0 @@
{
"name": "mwse",
"version": "0.1.0",
"description": "Mikro WebSocket Engine",
"scripts": {
"compile": "parcel watch --no-hmr",
"build": "parcel build --no-optimize"
},
"source": "./frontend/index.ts",
"targets": {
"default": {
"distDir": "./script/",
"publicUrl": "./",
"sourceMap": true,
"outputFormat": "global",
"optimize": true,
"context": "browser",
"engines": {
"chrome": "65",
"android": "4.4.3",
"edge": "16",
"firefox": "59",
"ie": "10",
"ios": "10",
"safari": "10"
}
}
},
"repository": {
"type": "git",
"url": "http://git.saqut.com/saqut/MWSE"
},
"keywords": [
"WebSocket",
"server",
"microservice",
"ws"
],
"author": "Abdussamed ULUTAŞ <abdussamedulutas@yandex.com.tr>",
"license": "MIT",
"dependencies": {
"compression": "^1.7.4",
"express": "^4.18.2",
"express-basic-auth": "^1.2.1",
"fflate": "^0.8.1",
"joi": "^17.11.0",
"knex": "^3.0.1",
"sqlite3": "^5.1.6",
"systemjs": "^6.14.2",
"terminal": "^0.1.4",
"terminal-kit": "^3.0.0",
"typescript": "^5.2.2",
"webrtc-adapter": "^8.2.3",
"websocket": "^1.0.34"
},
"devDependencies": {
"@parcel/packager-ts": "^2.7.0",
"@parcel/transformer-typescript-types": "^2.7.0",
"tslib": "^2.4.1"
}
}

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

View File

@ -1,107 +0,0 @@
{
"include": [
"./frontend/**/*.ts",
"./frontend/*.ts"
],
"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": "ES6", /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */
"lib": ["DOM","ES6","ES2015.Promise"], /* 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 TC39 stage 2 draft 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": "ES2015", /* Specify what module code is generated. */
"rootDir": "./frontend", /* 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. */
// "resolveJsonModule": true, /* Enable importing .json files. */
// "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. */
// "outFile": "./script", /* 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": "./script", /* Specify an output folder for all emitted files. */
"removeComments": true, /* Disable emitting comments. */
// "noEmit": true, /* Disable emitting files from a compilation. */
"importHelpers": false, /* 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. */
//"inlineSourceMap": true, /* Include sourcemap files inside the emitted JavaScript. */
//"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": "./script", /* 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. */
"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": false, /* Disable error reporting for unused labels. */
"allowUnreachableCode": false, /* 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. */
}
}