Compare commits

..
9 Commits
Author SHA1 Message Date
Schluffe d57c58a3bd Setup example worker 2026-09-27 18:09:53 +02:00
Schluffe a647cd5184 WIP 2026-09-27 14:09:50 +02:00
Schluffe 9758ae6dad WIP 2026-09-26 23:54:03 +02:00
Schluffe 8b2de757ac fix deno exclude 2026-09-26 18:08:55 +02:00
Schluffe 4fdace24b7 Setup deno properly 2026-09-26 17:23:12 +02:00
Schluffe b8c110b53b try agian 2026-09-26 15:23:41 +02:00
Schluffe c8df253758 try agian 2026-09-26 15:22:43 +02:00
Schluffe d24997dea9 CI WIP 2026-09-26 15:17:08 +02:00
Schluffe 9525548253 CI stuff 2026-09-26 15:15:12 +02:00
13 changed files with 1379 additions and 52 deletions
+11 -1
View File
@@ -23,7 +23,17 @@ jobs:
key: bun-${{ hashFiles('bun.lockb') }} key: bun-${{ hashFiles('bun.lockb') }}
- run: bun install --frozen-lockfile - run: bun install --frozen-lockfile
- run: bun run test - 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 - uses: actions/upload-artifact@v3
if: ${{ !cancelled() }} if: ${{ !cancelled() }}
with: with:
+2 -1
View File
@@ -17,7 +17,8 @@
"suspicious": { "suspicious": {
"noTsIgnore": "off", "noTsIgnore": "off",
"noAssignInExpressions": "off", "noAssignInExpressions": "off",
"noFallthroughSwitchClause": "warn" "noFallthroughSwitchClause": "warn",
"noGlobalAssign": "warn"
}, },
"correctness": { "correctness": {
"useParseIntRadix": "off", "useParseIntRadix": "off",
+5 -5
View File
@@ -6,17 +6,17 @@
"name": "nage", "name": "nage",
"devDependencies": { "devDependencies": {
"@biomejs/biome": "2.5.14", "@biomejs/biome": "2.5.14",
"@playwright/test": "^1.63.0", "@playwright/test": "1.63.0",
"@types/bun": "1.4.2", "@types/bun": "1.4.2",
"@types/node": "^26.6.3", "@types/node": "26.6.3",
"jsdom": "^30.1.1", "jsdom": "30.1.1",
"lz-string": "1.5.0", "lz-string": "1.5.0",
"playwright": "^1.63.0", "playwright": "1.63.0",
"solid-js": "1.9.15", "solid-js": "1.9.15",
"typescript": "7.0.2", "typescript": "7.0.2",
"vite": "8.3.1", "vite": "8.3.1",
"vite-plugin-solid": "2.11.14", "vite-plugin-solid": "2.11.14",
"vitest": "^5.0.2", "vitest": "5.0.2",
}, },
}, },
}, },
+25
View File
@@ -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"]
}
Generated
+1208 -8
View File
File diff suppressed because it is too large Load Diff
+9
View File
@@ -85,3 +85,12 @@ function animate(timestamp: number): void {
animate(delta); 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
View File
@@ -25,6 +25,6 @@
"vitest": "5.0.2" "vitest": "5.0.2"
}, },
"imports": { "imports": {
"#/": "./*" "#/": "./"
} }
} }
+48 -5
View File
@@ -1,15 +1,58 @@
/// <reference lib="deno.ns" />
import { assertEquals } from "jsr:@std/assert"; import { assertEquals } from "jsr:@std/assert";
import { createSession } from "#/server/handler.ts";
import handler from "./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( Deno.test(
"test", "needs upgrade header",
setup(async () => { setup(async () => {
const t1 = "123-abc"; const res = await fetch(`http://${hostname}:${port}`);
const [s1, m1] = await connect(`${href}?ticket=${t1}`); 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(); await flush();
assertEquals(m1, []); assertEquals(m1, []);
await close(s1); await close(s1);
}, handler), }, handler),
); );
+57 -27
View File
@@ -1,15 +1,11 @@
import { STATUS_CODE, STATUS_TEXT, type StatusCode } from "jsr:@std/http"; import { STATUS_CODE, STATUS_TEXT, type StatusCode } from "jsr:@std/http";
const ALLOWED_ORIGIN = "http://0.0.0.0:8042"; type Session = {
id: string;
const sessions = new Map<string, WebSocket>(); connId: number;
createdAt: number;
function createSession(socket: WebSocket): string { lastSeenAt: number;
const session = crypto.randomUUID(); };
sessions.set(session, socket);
return session;
}
function createResponse( function createResponse(
status: StatusCode, status: StatusCode,
@@ -18,32 +14,66 @@ function createResponse(
return new Response(body, { status, statusText: STATUS_TEXT[status] }); 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 { export default function (request: Request): Response {
if (request.headers.get("upgrade")?.toLowerCase() !== "websocket") { if (request.headers.get("upgrade")?.toLowerCase() !== "websocket") {
return createResponse(STATUS_CODE.UpgradeRequired); return createResponse(STATUS_CODE.UpgradeRequired);
} }
const url = new URL(request.url); 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); return createResponse(STATUS_CODE.BadRequest);
} }
const sessionId = url.searchParams.get("sessionId"); const session = sessions.get(sessionId);
if (session === undefined) {
if (sessionId === null) { return createResponse(STATUS_CODE.Unauthorized);
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
} }
return createResponse(STATUS_CODE.InternalServerError); return connectSession(session, request);
} }
-2
View File
@@ -1,5 +1,3 @@
/// <reference lib="deno.ns" />
export function startServer( export function startServer(
handler: Deno.ServeHandler, handler: Deno.ServeHandler,
hostname: string = "localhost", hostname: string = "localhost",
-1
View File
@@ -2,7 +2,6 @@ import { startServer } from "./server.ts";
export const hostname = "0.0.0.0"; export const hostname = "0.0.0.0";
export const port = 8042; export const port = 8042;
export const href = `ws://${hostname}:${port}`;
export function setup( export function setup(
fn: (ctx: Deno.TestContext) => Promise<void>, fn: (ctx: Deno.TestContext) => Promise<void>,
+7 -1
View File
@@ -18,7 +18,13 @@ function devTools(): Plugin {
export default defineConfig({ export default defineConfig({
server: { host, port, hmr: false }, server: { host, port, hmr: false },
build: { target: "esnext" }, build: {
target: "esnext",
main: `${root}index.html`,
lib: {
entry: `${root}worker.ts`,
},
},
test: { test: {
include: ["./engine/**/*.test.ts"], include: ["./engine/**/*.test.ts"],
environment: "jsdom", environment: "jsdom",
+6
View File
@@ -0,0 +1,6 @@
// biome-ignore lint: web worker
onmessage = (event) => {
if (event.data === "foo") {
postMessage("bar");
}
};