Testing websocket client

This commit is contained in:
2026-02-15 23:55:54 +01:00
parent be2c27093c
commit 4520eaf861
3 changed files with 120 additions and 45 deletions
+33 -1
View File
@@ -11,10 +11,12 @@
"@types/cors": "^2.8.19",
"@types/diff-match-patch": "^1.0.36",
"@types/express": "^5.0.6",
"@types/ws": "^8.18.1",
"axios": "^1.13.5",
"cors": "^2.8.6",
"diff-match-patch": "^1.0.5",
"express": "^5.2.1"
"express": "^5.2.1",
"ws": "^8.19.0"
},
"devDependencies": {
"@types/mocha": "^10.0.6",
@@ -407,6 +409,15 @@
"dev": true,
"license": "MIT"
},
"node_modules/@types/ws": {
"version": "8.18.1",
"resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.18.1.tgz",
"integrity": "sha512-ThVF6DCVhA8kUGy+aazFQ4kXQ7E1Ty7A3ypFOe0IcJV8O/M511G99AW24irKrW56Wt44yG9+ij8FaqoBGkuBXg==",
"license": "MIT",
"dependencies": {
"@types/node": "*"
}
},
"node_modules/@typescript-eslint/eslint-plugin": {
"version": "6.21.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-6.21.0.tgz",
@@ -4140,6 +4151,27 @@
"integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==",
"license": "ISC"
},
"node_modules/ws": {
"version": "8.19.0",
"resolved": "https://registry.npmjs.org/ws/-/ws-8.19.0.tgz",
"integrity": "sha512-blAT2mjOEIi0ZzruJfIhb3nps74PRWTCz1IjglWEEpQl5XS/UNama6u2/rjFkDDouqr4L67ry+1aGIALViWjDg==",
"license": "MIT",
"engines": {
"node": ">=10.0.0"
},
"peerDependencies": {
"bufferutil": "^4.0.1",
"utf-8-validate": ">=5.0.2"
},
"peerDependenciesMeta": {
"bufferutil": {
"optional": true
},
"utf-8-validate": {
"optional": true
}
}
},
"node_modules/y18n": {
"version": "5.0.8",
"resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz",
+3 -1
View File
@@ -91,9 +91,11 @@
"@types/cors": "^2.8.19",
"@types/diff-match-patch": "^1.0.36",
"@types/express": "^5.0.6",
"@types/ws": "^8.18.1",
"axios": "^1.13.5",
"cors": "^2.8.6",
"diff-match-patch": "^1.0.5",
"express": "^5.2.1"
"express": "^5.2.1",
"ws": "^8.19.0"
}
}
+78 -37
View File
@@ -1,16 +1,78 @@
import axios from "axios";
import { WebSocket } from "ws";
import { LiveRunMove } from "./types";
import { randomUUID } from "crypto";
var cookies: string | undefined;
let socket: WebSocket | null = null;
let cookies: string | undefined;
type PendingResolver = {
resolve: (value: any) => void;
reject: (err: any) => void;
};
const pending = new Map<string, PendingResolver>();
export function getCookies(): string | undefined {
return cookies;
}
export function setCookies(c: string) {
cookies = c;
}
function getOrigin(useLocalhost: boolean) {
return useLocalhost ? "http://localhost:3000" : "https://dev-run.selimaj.dev";
function getWsOrigin(useLocalhost: boolean) {
return useLocalhost
? "ws://localhost:3000/api/run"
: "wss://dev-run.selimaj.dev/api/run";
}
function ensureSocket(useLocalhost: boolean): WebSocket {
if (socket && socket.readyState === WebSocket.OPEN) {
return socket;
}
socket = new WebSocket(getWsOrigin(useLocalhost));
socket.onmessage = (e) => {
const msg = JSON.parse(e.data.toString());
const { requestId, ok, data, error } = msg;
if (!requestId) return;
const pendingReq = pending.get(requestId);
if (!pendingReq) return;
pending.delete(requestId);
ok ? pendingReq.resolve(data) : pendingReq.reject(error);
};
socket.onclose = () => {
socket = null;
};
return socket;
}
function send<T>(
useLocalhost: boolean,
payload: Record<string, any>,
): Promise<T> {
return new Promise((resolve, reject) => {
const ws = ensureSocket(useLocalhost);
const requestId = randomUUID();
pending.set(requestId, { resolve, reject });
ws.send(
JSON.stringify({
...payload,
requestId,
cookies, // optional: only needed if you authenticate via cookies manually
}),
);
});
}
export async function addRun(
@@ -18,34 +80,20 @@ export async function addRun(
problem: string,
mode: string,
): Promise<string> {
return (
await axios.put(
getOrigin(useLocalhost) + "/api/run",
{
const data = await send<{ runId: string }>(useLocalhost, {
type: "create",
problem,
category: mode,
},
{
headers: {
Cookie: cookies,
},
},
)
).data.runId;
});
return data.runId;
}
export async function submitRun(useLocalhost: boolean, runId: string) {
await axios.post(
getOrigin(useLocalhost) + "/api/run",
{
await send(useLocalhost, {
type: "submit",
runId,
},
{
headers: {
Cookie: cookies,
},
},
);
});
}
export async function addRunMoves(
@@ -55,18 +103,11 @@ export async function addRunMoves(
language: string | null,
moves: LiveRunMove[],
) {
await axios.post(
getOrigin(useLocalhost) + "/api/run/move",
{
await send(useLocalhost, {
type: "move",
runId,
file,
moves,
language,
},
{
headers: {
Cookie: cookies,
},
},
);
moves,
});
}