Giant refactoring (or at least the start)

In short:
- cvmts is now bundled/built via parcel and inside of a npm/yarn workspace with multiple nodejs projects
- cvmts now uses the crusttest QEMU management and RFB library (or a fork, if you so prefer).
- cvmts does NOT use node-canvas anymore, instead we opt for the same route crusttest took and just encode jpegs ourselves from the RFB provoded framebuffer via jpeg-turbo. this means funnily enough sharp is back for more for thumbnails, but actually seems to WORK this time
- IPData is now managed in a very similar way to the original cvm 1.2 implementation where a central manager and reference count exist. tbh it wouldn't be that hard to implement multinode either, but for now, I'm not going to take much time on doing that.

this refactor is still incomplete. please do not treat it as generally available while it's not on the default branch. if you want to use it (and report bugs or send fixes) feel free to, but while it may "just work" in certain situations it may be very broken in others.

(yes, I know windows support is partially totaled by this; it's something that can and will be fixed)
This commit is contained in:
modeco80
2024-04-23 09:57:02 -04:00
parent 28dddfc363
commit cb297e15c4
46 changed files with 5661 additions and 1011 deletions

28
shared/package.json Normal file
View File

@@ -0,0 +1,28 @@
{
"name": "@cvmts/shared",
"version": "1.0.0",
"description": "cvmts shared util bits",
"type": "module",
"main": "dist/index.js",
"types": "dist/index.d.ts",
"files": [
"dist"
],
"targets": {
"types": {},
"shared": {
"context": "browser",
"isLibrary": true,
"outputFormat": "esmodule"
}
},
"devDependencies": {
"@protobuf-ts/plugin": "^2.9.4",
"parcel": "^2.12.0"
},
"scripts": {
"build": "parcel build src/index.ts --target shared --target types"
},
"author": "",
"license": "ISC"
}

50
shared/src/Logger.ts Normal file
View File

@@ -0,0 +1,50 @@
import { Format } from "./format";
import { StringLike } from "./StringLike";
export enum LogLevel {
VERBOSE = 0,
INFO,
WARNING,
ERROR
};
let gLogLevel = LogLevel.INFO;
export function SetLogLevel(level: LogLevel) {
gLogLevel = level;
}
export class Logger {
private _component: string;
constructor(component: string) {
this._component = component;
}
// TODO: use js argments stuff.
Verbose(pattern: string, ...args: Array<StringLike>) {
if(gLogLevel <= LogLevel.VERBOSE)
console.log(`[${this._component}] [VERBOSE] ${Format(pattern, ...args)}`);
}
Info(pattern: string, ...args: Array<StringLike>) {
if(gLogLevel <= LogLevel.INFO)
console.log(`[${this._component}] [INFO] ${Format(pattern, ...args)}`);
}
Warning(pattern: string, ...args: Array<StringLike>) {
if(gLogLevel <= LogLevel.WARNING)
console.warn(`[${this._component}] [WARNING] ${Format(pattern, ...args)}`);
}
Error(pattern: string, ...args: Array<StringLike>) {
if(gLogLevel <= LogLevel.ERROR)
console.error(`[${this._component}] [ERROR] ${Format(pattern, ...args)}`);
}
}

9
shared/src/StringLike.ts Normal file
View File

@@ -0,0 +1,9 @@
// TODO: `Object` has a toString(), but we should probably gate that off
/// Interface for things that can be turned into strings
export interface ToStringable {
toString(): string;
}
/// A type for strings, or things that can (in a valid manner) be turned into strings
export type StringLike = string | ToStringable;

77
shared/src/format.ts Normal file
View File

@@ -0,0 +1,77 @@
import { StringLike } from './StringLike';
function isalpha(char: number) {
return RegExp(/^\p{L}/, 'u').test(String.fromCharCode(char));
}
/// A simple function for formatting strings in a more expressive manner.
/// While JavaScript *does* have string interpolation, it's not a total replacement
/// for just formatting strings, and a method like this is better for data independent formatting.
///
/// ## Example usage
///
/// ```typescript
/// let hello = Format("Hello, {0}!", "World");
/// ```
export function Format(pattern: string, ...args: Array<StringLike>) {
let argumentsAsStrings: Array<string> = [...args].map((el) => {
// This catches cases where the thing already is a string
if (typeof el == 'string') return el as string;
return el.toString();
});
let pat = pattern;
// Handle pattern ("{0} {1} {2} {3} {4} {5}") syntax if found
for (let i = 0; i < pat.length; ++i) {
if (pat[i] == '{') {
let replacementStart = i;
let foundSpecifierEnd = false;
// Make sure the specifier is not cut off (the last character of the string)
if (i + 3 > pat.length) {
throw new Error(`Error in format pattern "${pat}": Cutoff/invalid format specifier`);
}
// Try and find the specifier end ('}').
// Whitespace and a '{' are considered errors.
for (let j = i + 1; j < pat.length; ++j) {
switch (pat[j]) {
case '}':
foundSpecifierEnd = true;
i = j;
break;
case '{':
throw new Error(`Error in format pattern "${pat}": Cannot start a format specifier in an existing replacement`);
case ' ':
throw new Error(`Error in format pattern "${pat}": Whitespace inside format specifier`);
case '-':
throw new Error(`Error in format pattern "${pat}": Malformed format specifier`);
default:
if (isalpha(pat.charCodeAt(j))) throw new Error(`Error in format pattern "${pat}": Malformed format specifier`);
break;
}
if (foundSpecifierEnd) break;
}
if (!foundSpecifierEnd) throw new Error(`Error in format pattern "${pat}": No terminating "}" character found`);
// Get the beginning and trailer
let beginning = pat.substring(0, replacementStart);
let trailer = pat.substring(replacementStart + 3);
let argumentIndex = parseInt(pat.substring(replacementStart + 1, i));
if (Number.isNaN(argumentIndex) || argumentIndex > argumentsAsStrings.length) throw new Error(`Error in format pattern "${pat}": Argument index out of bounds`);
// This is seriously the only decent way to do this in javascript
// thanks brendan eich (replace this thanking with more choice words in your head)
pat = beginning + argumentsAsStrings[argumentIndex] + trailer;
}
}
return pat;
}

24
shared/src/index.ts Normal file
View File

@@ -0,0 +1,24 @@
// public modules
export * from './StringLike.js';
export * from './Logger.js';
export * from './format.js';
export function Clamp(input: number, min: number, max: number) {
return Math.min(Math.max(input, min), max);
}
export async function Sleep(ms: number) {
return new Promise((resolve) => setTimeout(resolve, ms));
}
export type Size = {
width: number;
height: number;
};
export type Rect = {
x: number,
y: number,
width: number,
height: number
};

1
shared/tsconfig.json Symbolic link
View File

@@ -0,0 +1 @@
../tsconfig.json