فهرست منبع

Initial code for new backend module and job system using TypeScript

Co-authored-by: Owen Diffey <owen@diffey.dev>
Kristian Vos 2 سال پیش
والد
کامیت
c23f955228

+ 13 - 0
backend/package-lock.json

@@ -30,6 +30,7 @@
         "ws": "^8.9.0"
       },
       "devDependencies": {
+        "@types/async": "^3.2.15",
         "@typescript-eslint/eslint-plugin": "^5.40.0",
         "@typescript-eslint/parser": "^5.40.0",
         "eslint": "^8.25.0",
@@ -329,6 +330,12 @@
       "integrity": "sha512-yOlFc+7UtL/89t2ZhjPvvB/DeAr3r+Dq58IgzsFkOAvVC6NMJXmCGjbptdXdR9qsX7pKcTL+s87FtYREi2dEEQ==",
       "dev": true
     },
+    "node_modules/@types/async": {
+      "version": "3.2.15",
+      "resolved": "https://registry.npmjs.org/@types/async/-/async-3.2.15.tgz",
+      "integrity": "sha512-PAmPfzvFA31mRoqZyTVsgJMsvbynR429UTTxhmfsUCrWGh3/fxOrzqBtaTPJsn4UtzTv4Vb0+/O7CARWb69N4g==",
+      "dev": true
+    },
     "node_modules/@types/json-schema": {
       "version": "7.0.11",
       "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.11.tgz",
@@ -4898,6 +4905,12 @@
       "integrity": "sha512-yOlFc+7UtL/89t2ZhjPvvB/DeAr3r+Dq58IgzsFkOAvVC6NMJXmCGjbptdXdR9qsX7pKcTL+s87FtYREi2dEEQ==",
       "dev": true
     },
+    "@types/async": {
+      "version": "3.2.15",
+      "resolved": "https://registry.npmjs.org/@types/async/-/async-3.2.15.tgz",
+      "integrity": "sha512-PAmPfzvFA31mRoqZyTVsgJMsvbynR429UTTxhmfsUCrWGh3/fxOrzqBtaTPJsn4UtzTv4Vb0+/O7CARWb69N4g==",
+      "dev": true
+    },
     "@types/json-schema": {
       "version": "7.0.11",
       "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.11.tgz",

+ 1 - 0
backend/package.json

@@ -37,6 +37,7 @@
     "ws": "^8.9.0"
   },
   "devDependencies": {
+    "@types/async": "^3.2.15",
     "@typescript-eslint/eslint-plugin": "^5.40.0",
     "@typescript-eslint/parser": "^5.40.0",
     "eslint": "^8.25.0",

+ 84 - 0
backend/src/BaseModule.ts

@@ -0,0 +1,84 @@
+import ModuleManager from "./ModuleManager";
+import { ModuleStatus } from "./types/ModuleStatus";
+
+// type ModuleName = keyof Modules;
+
+export default abstract class BaseModule {
+	protected moduleManager: ModuleManager;
+
+	protected name: string;
+
+	protected status: ModuleStatus;
+
+	/**
+	 * Base Module
+	 *
+	 * @param {ModuleManager} moduleManager Module manager class
+	 * @param {string} name Module name
+	 */
+	public constructor(moduleManager: ModuleManager, name: string) {
+		this.moduleManager = moduleManager;
+		this.name = name;
+		this.status = "LOADED";
+		// console.log(`Module (${this.name}) starting`);
+	}
+
+	/**
+	 * getName - Get module name
+	 *
+	 * @returns {string} name
+	 */
+	public getName(): string {
+		return this.name;
+	}
+
+	/**
+	 * getStatus - Get module status
+	 *
+	 * @returns {ModuleStatus} status
+	 */
+	public getStatus(): ModuleStatus {
+		return this.status;
+	}
+
+	/**
+	 * setStatus - Set module status
+	 *
+	 * @param {ModuleStatus} status Module status
+	 */
+	protected setStatus(status: ModuleStatus): void {
+		this.status = status;
+	}
+
+	/**
+	 * startup - Startup module
+	 */
+	public startup(): void {
+		console.log(`Module (${this.name}) starting`);
+		this.setStatus("STARTING");
+	}
+
+	/**
+	 * started - called with the module has started
+	 */
+	protected started(): void {
+		console.log(`Module (${this.name}) started`);
+		this.setStatus("STARTED");
+	}
+
+	/**
+	 * shutdown - Shutdown module
+	 */
+	public shutdown(): void {
+		console.log(`Module (${this.name}) stopping`);
+		this.setStatus("STOPPING");
+	}
+
+	/**
+	 * stopped - called when the module has stopped
+	 */
+	protected stopped(): void {
+		console.log(`Module (${this.name}) stopped`);
+		this.setStatus("STOPPED");
+	}
+}

+ 126 - 0
backend/src/Job.ts

@@ -0,0 +1,126 @@
+import { JobStatus } from "./types/JobStatus";
+
+export default class Job {
+	protected name: string;
+
+	protected module: string;
+
+	protected callback: (resolve: () => void, reject: () => void) => void;
+
+	protected priority: number;
+
+	protected longJob?: {
+		title: string;
+		progress?: {
+			data: unknown;
+			time: Date;
+			timeout?: NodeJS.Timeout;
+		};
+	};
+
+	protected uuid: string;
+
+	protected status: JobStatus;
+
+	protected startedAt: number;
+
+	/**
+	 * Job
+	 *
+	 * @param {string} name Job name
+	 * @param {string} module Job module
+	 * @param {Function} callback Job callback
+	 * @param {object|undefined} options Job options
+	 * @param {number|undefined} options.priority Job priority
+	 * @param {string|undefined} options.longJob Long job title, providing makes job a long job
+	 */
+	public constructor(
+		name: string,
+		module: string,
+		callback: (resolve: () => void, reject: () => void) => void,
+		options?: { priority: number; longJob?: string }
+	) {
+		this.name = name;
+		this.module = module;
+		this.callback = callback;
+		this.priority = 1;
+
+		if (options) {
+			const { priority, longJob } = options;
+			if (priority) this.priority = priority;
+			if (longJob)
+				this.longJob = {
+					title: longJob
+				};
+		}
+
+		/* eslint-disable no-bitwise, eqeqeq */
+		this.uuid = "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(
+			/[xy]/g,
+			c => {
+				const r = (Math.random() * 16) | 0;
+				const v = c == "x" ? r : (r & 0x3) | 0x8;
+				return v.toString(16);
+			}
+		);
+		this.status = "QUEUED";
+		this.startedAt = Date.now();
+	}
+
+	/**
+	 * getName - Get job name
+	 *
+	 * @returns {string} module.name
+	 */
+	public getName(): string {
+		return `${this.module}.${this.name}`;
+	}
+
+	/**
+	 * getPriority - Get job priority
+	 *
+	 * @returns {number} priority
+	 */
+	public getPriority(): number {
+		return this.priority;
+	}
+
+	/**
+	 * getUuid - Get job UUID
+	 *
+	 * @returns {string} UUID
+	 */
+	public getUuid(): string {
+		return this.uuid;
+	}
+
+	/**
+	 * getStatus - Get job status
+	 *
+	 * @returns {JobStatus} status
+	 */
+	public getStatus(): JobStatus {
+		return this.status;
+	}
+
+	/**
+	 * setStatus - Set job status
+	 *
+	 * @param {JobStatus} status Job status
+	 */
+	public setStatus(status: JobStatus): void {
+		this.status = status;
+	}
+
+	/**
+	 * execute - Execute job
+	 *
+	 * @returns {Promise<void>} Promise
+	 */
+	public execute(): Promise<void> {
+		return new Promise((resolve, reject) => {
+			this.setStatus("ACTIVE");
+			this.callback(resolve, reject);
+		});
+	}
+}

+ 188 - 0
backend/src/JobQueue.ts

@@ -0,0 +1,188 @@
+import Job from "./Job";
+import { JobStatus } from "./types/JobStatus";
+
+export default class JobQueue {
+	private concurrency: number;
+
+	private isPaused: boolean;
+
+	private queue: Job[];
+
+	private active: Job[];
+
+	private stats: Record<
+		string,
+		{
+			successful: number;
+			failed: number;
+			total: number;
+			added: number;
+		}
+	>;
+
+	/**
+	 * Job Queue
+	 */
+	public constructor() {
+		this.concurrency = 10;
+		this.isPaused = true;
+		this.queue = [];
+		this.active = [];
+		this.stats = {};
+	}
+
+	/**
+	 * add - Add job to queue
+	 *
+	 * @param {Job} job Job
+	 */
+	public add(job: Job): void {
+		this.queue.push(job);
+		this.updateStats(job.getName(), "added");
+		setTimeout(() => {
+			this.process();
+		}, 0);
+	}
+
+	/**
+	 * getJob - Fetch job
+	 *
+	 * @param {jobId} jobId Job UUID
+	 * @returns {Job|undefined} Job if found
+	 */
+	public getJob(jobId: string): Job | undefined {
+		return (
+			this.queue.find(job => job.getUuid() === jobId) ||
+			this.active.find(job => job.getUuid() === jobId)
+		);
+	}
+
+	/**
+	 * pause - Pause queue
+	 *
+	 * Pause processing of jobs in queue, active jobs will not be paused.
+	 */
+	public pause(): void {
+		this.isPaused = true;
+	}
+
+	/**
+	 * resume - Resume queue
+	 */
+	public resume(): void {
+		this.isPaused = false;
+	}
+
+	/**
+	 * process - Process queue
+	 */
+	private process(): void {
+		if (
+			this.isPaused ||
+			this.active.length >= this.concurrency ||
+			this.queue.length === 0
+		)
+			return;
+
+		const job = this.queue.reduce((a, b) =>
+			a.getPriority() <= b.getPriority() ? a : b
+		);
+		if (job.getPriority() === -1) return;
+
+		this.queue.splice(this.queue.indexOf(job), 1);
+		this.active.push(job);
+
+		job.execute()
+			.then(() => {
+				this.updateStats(job.getName(), "successful");
+			})
+			.catch(() => {
+				this.updateStats(job.getName(), "failed");
+			})
+			.finally(() => {
+				this.updateStats(job.getName(), "total");
+				this.active.splice(this.active.indexOf(job), 1);
+				setTimeout(() => {
+					this.process();
+				}, 0);
+			});
+	}
+
+	/**
+	 * getStatus - Get status of job queue
+	 *
+	 * @returns {object} Job queue status
+	 */
+	public getStatus() {
+		return {
+			isPaused: this.isPaused,
+			queueLength: this.queue.length,
+			activeLength: this.active.length,
+			concurrency: this.concurrency
+		};
+	}
+
+	/**
+	 * getStats - Get statistics of job queue
+	 *
+	 * @returns {object} Job queue statistics
+	 */
+	public getStats() {
+		return {
+			...this.stats,
+			total: Object.values(this.stats).reduce((a, b) => ({
+				successful: a.successful + b.successful,
+				failed: a.failed + b.failed,
+				total: a.total + b.total,
+				added: a.added + b.added
+			}))
+		};
+	}
+
+	/**
+	 * getQueueStatus - Get statistics of queued or active jobs
+	 *
+	 * @param {JobStatus|undefined} type Job type filter
+	 * @returns {object} Job queue statistics
+	 */
+	public getQueueStatus(type?: JobStatus) {
+		const status: Record<
+			string,
+			{
+				uuid: string;
+				priority: number;
+				name: string;
+				status: JobStatus;
+			}[]
+		> = {};
+		const format = (job: Job) => ({
+			uuid: job.getUuid(),
+			priority: job.getPriority(),
+			name: job.getName(),
+			status: job.getStatus()
+		});
+		if (!type || type === "ACTIVE") status.active = this.active.map(format);
+		if (!type || type === "QUEUED") status.queue = this.queue.map(format);
+		return status;
+	}
+
+	/**
+	 * updateStats - Update job statistics
+	 *
+	 * @param {string} jobName Job name
+	 * @param {"successful"|"failed"|"total"} type Stats type
+	 */
+	private updateStats(
+		jobName: string,
+		type: "successful" | "failed" | "total" | "added"
+	) {
+		if (!this.stats[jobName])
+			this.stats[jobName] = {
+				successful: 0,
+				failed: 0,
+				total: 0,
+				added: 0
+			};
+		this.stats[jobName][type] += 1;
+	}
+}

+ 202 - 0
backend/src/ModuleManager.ts

@@ -0,0 +1,202 @@
+import BaseModule from "./BaseModule";
+import Job from "./Job";
+import JobQueue from "./JobQueue";
+import { ModuleStatus } from "./types/ModuleStatus";
+import { Jobs, Modules } from "./types/TestModules";
+
+type ModuleClass<Module extends typeof BaseModule> = {
+	// eslint-disable-next-line
+	new (moduleManager: ModuleManager): Module;
+};
+
+export default class ModuleManager {
+	private modules: Modules | null;
+
+	private jobQueue: JobQueue;
+
+	/**
+	 * Module Manager
+	 *
+	 */
+	public constructor() {
+		this.modules = null;
+		this.jobQueue = new JobQueue();
+	}
+
+	/**
+	 * getStatus - Get status of modules
+	 *
+	 * @returns {object} Module statuses
+	 */
+	public getStatus() {
+		if (!this.modules) return {};
+
+		const status: Record<string, ModuleStatus> = {};
+
+		Object.entries(this.modules).forEach(([name, module]) => {
+			status[name] = module.getStatus();
+		});
+
+		return status;
+	}
+
+	/**
+	 * getJobsStats - Get statistics of job queue
+	 *
+	 * @returns {object} Job queue statistics
+	 */
+	public getJobsStats() {
+		return this.jobQueue.getStats();
+	}
+
+	/**
+	 * getJobsStatus - Get status of job queue
+	 *
+	 * @returns {object} Job queue status
+	 */
+	public getJobsStatus() {
+		return this.jobQueue.getStatus();
+	}
+
+	/**
+	 * getQueueStatus - Get status of queued jobs
+	 *
+	 * @returns {object} Job statuses
+	 */
+	public getQueueStatus() {
+		return this.jobQueue.getQueueStatus();
+	}
+
+	/**
+	 *
+	 * @param {string} moduleName Name of the module
+	 * @returns {typeof BaseModule} Module
+	 */
+	private getModule<T extends keyof Modules>(
+		moduleName: T
+	): Promise<Modules[T]> {
+		return new Promise(resolve => {
+			const mapper = {
+				stations: "StationModule",
+				others: "OtherModule"
+			};
+			import(`./modules/${mapper[moduleName]}`).then(response => {
+				const Module: ModuleClass<Modules[T]> = response.default;
+				const module = new Module(this);
+				resolve(module);
+			});
+		});
+	}
+
+	/**
+	 * loadModules - Load and initialize all modules
+	 *
+	 * @returns {Promise} Promise
+	 */
+	/**
+	 * loadModules - Load and initialize all modules
+	 *
+	 * @returns {Promise} Promise
+	 */
+	private loadModules(): Promise<void> {
+		return new Promise((resolve, reject) => {
+			const fetchModules = async () => ({
+				stations: await this.getModule("stations"),
+				others: await this.getModule("others")
+			});
+			fetchModules()
+				.then(modules => {
+					this.modules = modules;
+					resolve();
+				})
+				.catch(err => {
+					reject(new Error(err));
+				});
+		});
+	}
+
+	/**
+	 * startup - Handle startup
+	 */
+	public startup(): void {
+		this.loadModules()
+			.then(() => {
+				if (!this.modules) return;
+				Object.values(this.modules).forEach(module => module.startup());
+				this.jobQueue.resume();
+			})
+			.catch(() => this.shutdown());
+	}
+
+	/**
+	 * shutdown - Handle shutdown
+	 */
+	public shutdown(): void {
+		if (!this.modules) return;
+		console.log(this.modules);
+		Object.values(this.modules).forEach(module => module.shutdown());
+	}
+
+	/**
+	 * runJob - Run a job
+	 *
+	 * @param {string} moduleName Module name
+	 * @param {string} jobName Job name
+	 * @param {[ any, { priority?: number }? ]} params Params
+	 */
+	public runJob<
+		ModuleName extends keyof Jobs & keyof Modules,
+		JobName extends keyof Jobs[ModuleName] &
+			keyof Omit<Modules[ModuleName], keyof BaseModule>,
+		Payload extends "payload" extends keyof Jobs[ModuleName][JobName]
+			? Jobs[ModuleName][JobName]["payload"]
+			: undefined,
+		ReturnType = "returns" extends keyof Jobs[ModuleName][JobName]
+			? Jobs[ModuleName][JobName]["returns"]
+			: never
+	>(
+		moduleName: ModuleName,
+		jobName: JobName,
+		...params: Payload extends undefined
+			? []
+			: [Payload, { priority?: number }?]
+	): Promise<ReturnType> {
+		const [payload, options] = params;
+		return new Promise<ReturnType>((resolve, reject) => {
+			if (!this.modules) return;
+			const module = this.modules[moduleName];
+			if (!module) reject(new Error("Module not found."));
+			else {
+				const jobFunction = module[jobName];
+				if (!jobFunction || typeof jobFunction !== "function")
+					reject(new Error("Job function not found."));
+				else if (
+					Object.prototype.hasOwnProperty.call(BaseModule, jobName)
+				)
+					reject(new Error("Illegal job function."));
+				else {
+					this.jobQueue.add(
+						new Job(
+							jobName.toString(),
+							moduleName,
+							async (resolveJob, rejectJob) => {
+								jobFunction(payload)
+									.then((response: ReturnType) => {
+										resolveJob();
+										resolve(response);
+									})
+									.catch(() => {
+										rejectJob();
+										reject();
+									});
+							},
+							{
+								priority: (options && options.priority) || 10
+							}
+						)
+					);
+				}
+			}
+		});
+	}
+}

+ 113 - 0
backend/src/main.ts

@@ -0,0 +1,113 @@
+import util from "util";
+import * as readline from "node:readline";
+import ModuleManager from "./ModuleManager";
+
+// Replace console.log with something that replaced certain phrases/words
+const blacklistedConsoleLogs: string[] = [];
+const oldConsole = { log: console.log };
+console.log = (...args) => {
+	const string = util.format.apply(null, args);
+	let blacklisted = false;
+
+	blacklistedConsoleLogs.forEach(blacklistedConsoleLog => {
+		if (string.indexOf(blacklistedConsoleLog) !== -1) blacklisted = true;
+	});
+
+	if (!blacklisted) oldConsole.log.apply(null, args);
+};
+
+const moduleManager = new ModuleManager();
+moduleManager.startup();
+
+const interval = setInterval(() => {
+	moduleManager
+		.runJob("stations", "addToQueue", { songId: "TestId" })
+		.catch(() => {});
+	moduleManager.runJob("stations", "addA").catch(() => {});
+	moduleManager
+		.runJob("others", "doThing", { test: "Test", test2: 123 })
+		.catch(() => {});
+}, 40);
+
+setTimeout(() => {
+	clearTimeout(interval);
+}, 20000);
+
+process.on("uncaughtException", err => {
+	if (err.name === "ECONNREFUSED" || err.name === "UNCERTAIN_STATE") return;
+
+	console.log(`UNCAUGHT EXCEPTION: ${err.stack}`);
+});
+
+const runCommand = (line: string) => {
+	const [command, ...args] = line.split(" ");
+	switch (command) {
+		case "status": {
+			console.log("Module Manager Status:");
+			console.table(moduleManager.getStatus());
+			console.log("Job Queue Status:");
+			console.table(moduleManager.getJobsStatus());
+			break;
+		}
+		case "stats": {
+			console.log("Job Queue Stats:");
+			console.table(moduleManager.getJobsStats());
+			break;
+		}
+		case "queue": {
+			const queueStatus = moduleManager.getQueueStatus().queue;
+			if (queueStatus.length === 0)
+				console.log("There are no jobs in the queue.");
+			else
+				console.log(
+					`There are ${queueStatus.length} jobs in the queue.`
+				);
+			console.table(queueStatus);
+			break;
+		}
+		case "active": {
+			const activeStatus = moduleManager.getQueueStatus().active;
+			if (activeStatus.length === 0)
+				console.log("There are no active jobs.");
+			else console.log(`There are ${activeStatus.length} active jobs.`);
+			console.table(activeStatus);
+			break;
+		}
+		case "eval": {
+			const evalCommand = args.join(" ");
+			console.log(`Running eval command: ${evalCommand}`);
+			// eslint-disable-next-line no-eval
+			const response = eval(evalCommand);
+			console.log(`Eval response: `, response);
+			break;
+		}
+		case "debug": {
+			// eslint-disable-next-line no-debugger
+			debugger;
+			break;
+		}
+		default: {
+			if (!/^\s*$/.test(command))
+				console.log(`Command "${command}" not found`);
+		}
+	}
+};
+
+const rl = readline.createInterface({
+	input: process.stdin,
+	output: process.stdout,
+	completer: (command: string) => {
+		const parts = command.split(" ");
+		const commands = ["eval "];
+
+		if (parts.length === 1) {
+			const hits = commands.filter(c => c.startsWith(parts[0]));
+
+			return [hits.length ? hits : commands, command];
+		}
+
+		return [];
+	}
+});
+
+rl.on("line", runCommand);

+ 56 - 0
backend/src/modules/OtherModule.ts

@@ -0,0 +1,56 @@
+import { UniqueMethods } from "../types/TestModules";
+import BaseModule from "../BaseModule";
+import ModuleManager from "../ModuleManager";
+
+export default class OtherModule extends BaseModule {
+	/**
+	 * Other Module
+	 *
+	 * @param {ModuleManager} moduleManager Module manager class
+	 */
+	public constructor(moduleManager: ModuleManager) {
+		super(moduleManager, "others");
+	}
+
+	/**
+	 * startup - Startup other module
+	 */
+	public override startup(): void {
+		super.startup();
+
+		console.log("Other Startup");
+
+		super.started();
+	}
+
+	/**
+	 * doThing - Do thing
+	 *
+	 * @param {object} payload Payload
+	 * @param {string} payload.test Test1
+	 * @param {number} payload.test2 Test2
+	 * @returns {{ message: string }} Return object
+	 */
+	public doThing(payload: { test: string; test2: number }): Promise<{
+		res: number;
+	}> {
+		return new Promise((resolve, reject) => {
+			const { test, test2 } = payload;
+			// console.log("doThing", test, test2);
+			setTimeout(
+				() =>
+					Math.round(Math.random())
+						? resolve({ res: 123 })
+						: reject(),
+				Math.random() * 1000
+			);
+		});
+	}
+}
+
+export type OtherModuleJobs = {
+	[Property in keyof UniqueMethods<OtherModule>]: {
+		payload: Parameters<UniqueMethods<OtherModule>[Property]>[0];
+		returns: Awaited<ReturnType<UniqueMethods<OtherModule>[Property]>>;
+	};
+};

+ 68 - 0
backend/src/modules/StationModule.ts

@@ -0,0 +1,68 @@
+import { UniqueMethods } from "../types/TestModules";
+import BaseModule from "../BaseModule";
+import ModuleManager from "../ModuleManager";
+
+export default class StationModule extends BaseModule {
+	/**
+	 * Station Module
+	 *
+	 * @param {ModuleManager} moduleManager Module manager class
+	 */
+	public constructor(moduleManager: ModuleManager) {
+		super(moduleManager, "stations");
+	}
+
+	/**
+	 * startup - Startup station module
+	 */
+	public override startup(): void {
+		super.startup();
+
+		console.log("Station Startup");
+
+		super.started();
+	}
+
+	/**
+	 * addToQueue - Add media to queue
+	 *
+	 * @param {object} payload Payload
+	 * @param {string} payload.songId Song ID
+	 */
+	public addToQueue(payload: { songId: string }): Promise<void> {
+		return new Promise((resolve, reject) => {
+			const { songId } = payload;
+			// console.log(`Adding song ${songId} to the queue.`);
+			setTimeout(
+				() => (Math.round(Math.random()) ? resolve() : reject()),
+				Math.random() * 1000
+			);
+		});
+	}
+
+	/**
+	 *
+	 * @returns {{ number: number }} return
+	 */
+	public addA(): Promise<{ number: number }> {
+		return new Promise<{ number: number }>(resolve => {
+			resolve({ number: 123 });
+		});
+	}
+
+	/**
+	 *
+	 */
+	public addB(): Promise<void> {
+		return new Promise<void>(resolve => {
+			resolve();
+		});
+	}
+}
+
+export type StationModuleJobs = {
+	[Property in keyof UniqueMethods<StationModule>]: {
+		payload: Parameters<UniqueMethods<StationModule>[Property]>[0];
+		returns: Awaited<ReturnType<UniqueMethods<StationModule>[Property]>>;
+	};
+};

+ 1 - 0
backend/src/types/JobStatus.ts

@@ -0,0 +1 @@
+export type JobStatus = "QUEUED" | "ACTIVE" | "PAUSED";

+ 7 - 0
backend/src/types/ModuleClass.ts

@@ -0,0 +1,7 @@
+import ModuleManager from "../ModuleManager";
+import { ValueOf } from "./ValueOf";
+import { Modules } from "./TestModules";
+
+export type ModuleClass = {
+	new (moduleManager: ModuleManager): ValueOf<Modules>;
+};

+ 8 - 0
backend/src/types/ModuleStatus.ts

@@ -0,0 +1,8 @@
+export type ModuleStatus =
+	| "LOADED"
+	| "STARTING"
+	| "STARTED"
+	| "STOPPED"
+	| "STOPPING"
+	| "ERROR"
+	| "DISABLED";

+ 23 - 0
backend/src/types/TestModules.ts

@@ -0,0 +1,23 @@
+import BaseModule from "../BaseModule";
+import OtherModule, { OtherModuleJobs } from "../modules/OtherModule";
+import StationModule, { StationModuleJobs } from "../modules/StationModule";
+
+export type Methods<T> = {
+	[P in keyof T as T[P] extends Function ? P : never]: T[P];
+};
+
+export type UniqueMethods<T> = Methods<Omit<T, keyof typeof BaseModule>>;
+
+export type Jobs = {
+	others: {
+		[Property in keyof OtherModuleJobs]: OtherModuleJobs[Property];
+	};
+	stations: {
+		[Property in keyof StationModuleJobs]: StationModuleJobs[Property];
+	};
+};
+
+export type Modules = {
+	others: OtherModule & typeof BaseModule;
+	stations: StationModule & typeof BaseModule;
+};

+ 1 - 0
backend/src/types/ValueOf.ts

@@ -0,0 +1 @@
+export type ValueOf<T> = T[keyof T];