150 lines
3.4 KiB
Markdown
150 lines
3.4 KiB
Markdown
|
|
# One-Port WebSocket Implementation Plan
|
||
|
|
|
||
|
|
> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.
|
||
|
|
|
||
|
|
**Goal:** Serve Express HTTP endpoints, static frontend, and WebSocket traffic on one `WEBSERVER_PORT` using WebSocket path `/ws`.
|
||
|
|
|
||
|
|
**Architecture:** `src/webserver.ts` should create one `http.Server` from the Express app, attach `WebSocketServer` to that same server with `path: "/ws"`, and remove `port + 1`. `public/index.html` should connect to `${location.protocol === "https:" ? "wss" : "ws"}://${location.host}/ws` so dev, production, and reverse proxy setups use the same host and port.
|
||
|
|
|
||
|
|
**Tech Stack:** TypeScript, Express, Node HTTP server, `ws`, Bun scripts, Biome, TypeScript compiler.
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## File Structure
|
||
|
|
|
||
|
|
- Modify `src/webserver.ts`: change WebSocket server construction and logs from separate port to shared HTTP server path `/ws`.
|
||
|
|
- Modify `public/index.html`: change browser WebSocket URL from hardcoded `:3001` to same-origin `/ws`.
|
||
|
|
- No new files required.
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
### Task 1: Attach WebSocket to Existing HTTP Server
|
||
|
|
|
||
|
|
**Files:**
|
||
|
|
- Modify: `src/webserver.ts`
|
||
|
|
|
||
|
|
- [ ] **Step 1: Update WebSocket server creation**
|
||
|
|
|
||
|
|
Replace this code in `src/webserver.ts`:
|
||
|
|
|
||
|
|
```ts
|
||
|
|
const wsPort = port + 1;
|
||
|
|
const wss = new WebSocketServer({ port: wsPort, host: "0.0.0.0" });
|
||
|
|
wsLogger.info({ wsPort }, "WebSocket server listening");
|
||
|
|
```
|
||
|
|
|
||
|
|
With:
|
||
|
|
|
||
|
|
```ts
|
||
|
|
const wsPath = "/ws";
|
||
|
|
const wss = new WebSocketServer({ server, path: wsPath });
|
||
|
|
wsLogger.info({ port, wsPath }, "WebSocket server listening");
|
||
|
|
```
|
||
|
|
|
||
|
|
- [ ] **Step 2: Update connection log**
|
||
|
|
|
||
|
|
Replace this code in `src/webserver.ts`:
|
||
|
|
|
||
|
|
```ts
|
||
|
|
wsLogger.info({ wsPort }, "New WebSocket connection");
|
||
|
|
```
|
||
|
|
|
||
|
|
With:
|
||
|
|
|
||
|
|
```ts
|
||
|
|
wsLogger.info({ port, wsPath }, "New WebSocket connection");
|
||
|
|
```
|
||
|
|
|
||
|
|
- [ ] **Step 3: Run typecheck**
|
||
|
|
|
||
|
|
Run:
|
||
|
|
|
||
|
|
```bash
|
||
|
|
bun run typecheck
|
||
|
|
```
|
||
|
|
|
||
|
|
Expected: command exits `0`.
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
### Task 2: Update Browser WebSocket URL
|
||
|
|
|
||
|
|
**Files:**
|
||
|
|
- Modify: `public/index.html`
|
||
|
|
|
||
|
|
- [ ] **Step 1: Replace hardcoded WebSocket port**
|
||
|
|
|
||
|
|
Replace this code in `public/index.html`:
|
||
|
|
|
||
|
|
```js
|
||
|
|
socket = new WebSocket(`ws://${window.location.hostname}:3001`);
|
||
|
|
```
|
||
|
|
|
||
|
|
With:
|
||
|
|
|
||
|
|
```js
|
||
|
|
const wsProtocol = window.location.protocol === "https:" ? "wss:" : "ws:";
|
||
|
|
socket = new WebSocket(`${wsProtocol}//${window.location.host}/ws`);
|
||
|
|
```
|
||
|
|
|
||
|
|
- [ ] **Step 2: Run lint and build**
|
||
|
|
|
||
|
|
Run:
|
||
|
|
|
||
|
|
```bash
|
||
|
|
bun run lint && bun run build
|
||
|
|
```
|
||
|
|
|
||
|
|
Expected: both commands exit `0`.
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
### Task 3: Verify One-Port Behavior
|
||
|
|
|
||
|
|
**Files:**
|
||
|
|
- Verify: `src/webserver.ts`
|
||
|
|
- Verify: `public/index.html`
|
||
|
|
|
||
|
|
- [ ] **Step 1: Start dev server**
|
||
|
|
|
||
|
|
Run:
|
||
|
|
|
||
|
|
```bash
|
||
|
|
bun run dev
|
||
|
|
```
|
||
|
|
|
||
|
|
Expected logs include Express web interface on configured port and WebSocket server listening with `{ port: 3000, wsPath: "/ws" }`.
|
||
|
|
|
||
|
|
- [ ] **Step 2: Browser smoke test**
|
||
|
|
|
||
|
|
Open:
|
||
|
|
|
||
|
|
```text
|
||
|
|
http://localhost:3000
|
||
|
|
```
|
||
|
|
|
||
|
|
Expected: page loads and browser WebSocket connects to:
|
||
|
|
|
||
|
|
```text
|
||
|
|
ws://localhost:3000/ws
|
||
|
|
```
|
||
|
|
|
||
|
|
- [ ] **Step 3: Endpoint smoke test**
|
||
|
|
|
||
|
|
Run:
|
||
|
|
|
||
|
|
```bash
|
||
|
|
curl http://localhost:3000/health
|
||
|
|
curl http://localhost:3000/metrics
|
||
|
|
```
|
||
|
|
|
||
|
|
Expected: `/health` returns JSON and `/metrics` returns Prometheus text.
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## Self-Review
|
||
|
|
|
||
|
|
- Spec coverage: Covers one-port HTTP/WebSocket server, `/ws` path, frontend URL update, and verification.
|
||
|
|
- Placeholder scan: No TBD/TODO placeholders.
|
||
|
|
- Type consistency: Uses `wsPath` in both server creation and logs; frontend connects to `/ws`.
|