Compare commits
9
Commits
97a71ba6c2
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d57c58a3bd | ||
|
|
a647cd5184 | ||
|
|
9758ae6dad | ||
|
|
8b2de757ac | ||
|
|
4fdace24b7 | ||
|
|
b8c110b53b | ||
|
|
c8df253758 | ||
|
|
d24997dea9 | ||
|
|
9525548253 |
@@ -23,7 +23,17 @@ jobs:
|
||||
key: bun-${{ hashFiles('bun.lockb') }}
|
||||
- run: bun install --frozen-lockfile
|
||||
- run: bun run test
|
||||
- run: bun run test:e2e
|
||||
e2e:
|
||||
runs-on: ubuntu-bun-deno
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/cache@v4
|
||||
with:
|
||||
path: ~/.bun/install/cache
|
||||
key: bun-${{ hashFiles('bun.lockb') }}
|
||||
- run: bun install --frozen-lockfile
|
||||
- run: bunx playwright install --with-deps
|
||||
- run: bunx playwright test
|
||||
- uses: actions/upload-artifact@v3
|
||||
if: ${{ !cancelled() }}
|
||||
with:
|
||||
|
||||
+2
-1
@@ -17,7 +17,8 @@
|
||||
"suspicious": {
|
||||
"noTsIgnore": "off",
|
||||
"noAssignInExpressions": "off",
|
||||
"noFallthroughSwitchClause": "warn"
|
||||
"noFallthroughSwitchClause": "warn",
|
||||
"noGlobalAssign": "warn"
|
||||
},
|
||||
"correctness": {
|
||||
"useParseIntRadix": "off",
|
||||
|
||||
@@ -6,17 +6,17 @@
|
||||
"name": "nage",
|
||||
"devDependencies": {
|
||||
"@biomejs/biome": "2.5.14",
|
||||
"@playwright/test": "^1.63.0",
|
||||
"@playwright/test": "1.63.0",
|
||||
"@types/bun": "1.4.2",
|
||||
"@types/node": "^26.6.3",
|
||||
"jsdom": "^30.1.1",
|
||||
"@types/node": "26.6.3",
|
||||
"jsdom": "30.1.1",
|
||||
"lz-string": "1.5.0",
|
||||
"playwright": "^1.63.0",
|
||||
"playwright": "1.63.0",
|
||||
"solid-js": "1.9.15",
|
||||
"typescript": "7.0.2",
|
||||
"vite": "8.3.1",
|
||||
"vite-plugin-solid": "2.11.14",
|
||||
"vitest": "^5.0.2",
|
||||
"vitest": "5.0.2",
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
{
|
||||
"tasks": {
|
||||
"dev": "deno run -A npm:vite",
|
||||
"build": "deno run -A npm:vite build",
|
||||
"preview": "deno run -A npm:vite build && deno run -A npm:vite preview",
|
||||
"test": "deno run -A npm:vitest",
|
||||
"test:e2e": "deno run -A npm:playwright test",
|
||||
"check": "deno run -A npm:@biomejs/biome check --fix",
|
||||
"serve": {
|
||||
"command": "tbd",
|
||||
"description": "tbd",
|
||||
"dependencies": ["tbd"]
|
||||
}
|
||||
},
|
||||
"imports": {
|
||||
"#/": "./"
|
||||
},
|
||||
"nodeModulesDir": "auto",
|
||||
"compilerOptions": {
|
||||
"jsx": "preserve",
|
||||
"jsxImportSource": "solid-js",
|
||||
"lib": ["DOM", "DOM.Iterable", "ESNext", "deno.ns"]
|
||||
},
|
||||
"exclude": ["/", "!server"]
|
||||
}
|
||||
@@ -85,3 +85,12 @@ function animate(timestamp: number): void {
|
||||
|
||||
animate(delta);
|
||||
})();
|
||||
|
||||
const worker = new Worker(new URL("./worker.ts", import.meta.url), {
|
||||
type: "module",
|
||||
});
|
||||
|
||||
worker.onmessage = (event) => {
|
||||
console.log(event.data);
|
||||
};
|
||||
worker.postMessage("foo");
|
||||
|
||||
+1
-1
@@ -25,6 +25,6 @@
|
||||
"vitest": "5.0.2"
|
||||
},
|
||||
"imports": {
|
||||
"#/": "./*"
|
||||
"#/": "./"
|
||||
}
|
||||
}
|
||||
|
||||
+48
-5
@@ -1,15 +1,58 @@
|
||||
/// <reference lib="deno.ns" />
|
||||
import { assertEquals } from "jsr:@std/assert";
|
||||
import { createSession } from "#/server/handler.ts";
|
||||
import handler from "./handler.ts";
|
||||
import { close, connect, flush, href, setup } from "./testSetup.ts";
|
||||
import { close, connect, flush, hostname, port, setup } from "./testSetup.ts";
|
||||
|
||||
const href = `ws://${hostname}:${port}`;
|
||||
|
||||
Deno.test(
|
||||
"test",
|
||||
"needs upgrade header",
|
||||
setup(async () => {
|
||||
const t1 = "123-abc";
|
||||
const [s1, m1] = await connect(`${href}?ticket=${t1}`);
|
||||
const res = await fetch(`http://${hostname}:${port}`);
|
||||
assertEquals(res.status, 426);
|
||||
assertEquals(res.statusText, "Upgrade Required");
|
||||
}, handler),
|
||||
);
|
||||
|
||||
Deno.test(
|
||||
"needs a session parameter",
|
||||
setup(async () => {
|
||||
const res = await connect(href).catch((reason) => {
|
||||
return reason;
|
||||
});
|
||||
|
||||
assertEquals(
|
||||
res.message,
|
||||
"NetworkError: failed to connect to WebSocket: Invalid status code: 400",
|
||||
);
|
||||
}, handler),
|
||||
);
|
||||
|
||||
Deno.test(
|
||||
"needs an existing session",
|
||||
setup(async () => {
|
||||
const res = await connect(`${href}?sessionId=DOES_NOT_EXISTS`).catch(
|
||||
(reason) => {
|
||||
return reason;
|
||||
},
|
||||
);
|
||||
|
||||
assertEquals(
|
||||
res.message,
|
||||
"NetworkError: failed to connect to WebSocket: Invalid status code: 401",
|
||||
);
|
||||
}, handler),
|
||||
);
|
||||
|
||||
Deno.test(
|
||||
"connects to an existing session",
|
||||
setup(async () => {
|
||||
const sessionId = createSession();
|
||||
const [s1, m1] = await connect(`${href}?sessionId=${sessionId}`);
|
||||
|
||||
await flush();
|
||||
assertEquals(m1, []);
|
||||
|
||||
await close(s1);
|
||||
}, handler),
|
||||
);
|
||||
|
||||
+57
-27
@@ -1,15 +1,11 @@
|
||||
import { STATUS_CODE, STATUS_TEXT, type StatusCode } from "jsr:@std/http";
|
||||
|
||||
const ALLOWED_ORIGIN = "http://0.0.0.0:8042";
|
||||
|
||||
const sessions = new Map<string, WebSocket>();
|
||||
|
||||
function createSession(socket: WebSocket): string {
|
||||
const session = crypto.randomUUID();
|
||||
sessions.set(session, socket);
|
||||
|
||||
return session;
|
||||
}
|
||||
type Session = {
|
||||
id: string;
|
||||
connId: number;
|
||||
createdAt: number;
|
||||
lastSeenAt: number;
|
||||
};
|
||||
|
||||
function createResponse(
|
||||
status: StatusCode,
|
||||
@@ -18,32 +14,66 @@ function createResponse(
|
||||
return new Response(body, { status, statusText: STATUS_TEXT[status] });
|
||||
}
|
||||
|
||||
const sessions = new Map<string, Session>();
|
||||
const connections = new Map<number, WebSocket>();
|
||||
|
||||
export function createSession(): string {
|
||||
const id = crypto.randomUUID();
|
||||
const now = Date.now();
|
||||
sessions.set(id, {
|
||||
id,
|
||||
connId: 0,
|
||||
createdAt: now,
|
||||
lastSeenAt: 0,
|
||||
});
|
||||
|
||||
return id;
|
||||
}
|
||||
|
||||
let currentId = 1;
|
||||
const nextId = (): number => {
|
||||
return currentId++;
|
||||
};
|
||||
|
||||
const connectSession = (session: Session, request: Request): Response => {
|
||||
const { response, socket } = Deno.upgradeWebSocket(request);
|
||||
const connId = nextId();
|
||||
|
||||
socket.onopen = () => {
|
||||
session.lastSeenAt = Date.now();
|
||||
session.connId = connId;
|
||||
connections.set(connId, socket);
|
||||
};
|
||||
socket.onclose = () => {
|
||||
session.lastSeenAt = Date.now();
|
||||
connections.delete(connId);
|
||||
};
|
||||
socket.onmessage = () => {
|
||||
session.lastSeenAt = Date.now();
|
||||
};
|
||||
socket.onerror = (e) => {
|
||||
session.lastSeenAt = Date.now();
|
||||
console.error(e);
|
||||
};
|
||||
|
||||
return response;
|
||||
};
|
||||
|
||||
export default function (request: Request): Response {
|
||||
if (request.headers.get("upgrade")?.toLowerCase() !== "websocket") {
|
||||
return createResponse(STATUS_CODE.UpgradeRequired);
|
||||
}
|
||||
|
||||
const url = new URL(request.url);
|
||||
if (url.origin !== ALLOWED_ORIGIN) {
|
||||
const sessionId = url.searchParams.get("sessionId");
|
||||
if (sessionId === null) {
|
||||
return createResponse(STATUS_CODE.BadRequest);
|
||||
}
|
||||
|
||||
const sessionId = url.searchParams.get("sessionId");
|
||||
|
||||
if (sessionId === null) {
|
||||
const upgrade = Deno.upgradeWebSocket(request);
|
||||
createSession(upgrade.socket);
|
||||
|
||||
return upgrade.response;
|
||||
} else {
|
||||
const session = sessions.get(sessionId);
|
||||
if (session === null) {
|
||||
return createResponse(STATUS_CODE.Unauthorized);
|
||||
}
|
||||
|
||||
// TODO
|
||||
// I need a new socket every time
|
||||
const session = sessions.get(sessionId);
|
||||
if (session === undefined) {
|
||||
return createResponse(STATUS_CODE.Unauthorized);
|
||||
}
|
||||
|
||||
return createResponse(STATUS_CODE.InternalServerError);
|
||||
return connectSession(session, request);
|
||||
}
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
/// <reference lib="deno.ns" />
|
||||
|
||||
export function startServer(
|
||||
handler: Deno.ServeHandler,
|
||||
hostname: string = "localhost",
|
||||
|
||||
@@ -2,7 +2,6 @@ import { startServer } from "./server.ts";
|
||||
|
||||
export const hostname = "0.0.0.0";
|
||||
export const port = 8042;
|
||||
export const href = `ws://${hostname}:${port}`;
|
||||
|
||||
export function setup(
|
||||
fn: (ctx: Deno.TestContext) => Promise<void>,
|
||||
|
||||
+7
-1
@@ -18,7 +18,13 @@ function devTools(): Plugin {
|
||||
|
||||
export default defineConfig({
|
||||
server: { host, port, hmr: false },
|
||||
build: { target: "esnext" },
|
||||
build: {
|
||||
target: "esnext",
|
||||
main: `${root}index.html`,
|
||||
lib: {
|
||||
entry: `${root}worker.ts`,
|
||||
},
|
||||
},
|
||||
test: {
|
||||
include: ["./engine/**/*.test.ts"],
|
||||
environment: "jsdom",
|
||||
|
||||
Reference in New Issue
Block a user