# Server TypeScript Conversion Plan

## Overview
Convert the Node.js server from JavaScript to TypeScript to improve type safety, developer experience, and enable proper imports of TypeScript modules (database, schemas, etc.) from within the server code.

## Current State Analysis

### Files to Convert
1. **`server.js`** (57 lines) - Main server entry point
   - Handles Vite dev server setup
   - Production build serving
   - Socket.IO initialization
   - Express middleware

2. **`server/socket.js`** (89 lines) - Socket.IO server
   - Connection handling
   - Draft room management
   - Global instance storage

### Existing TypeScript Configuration
- **`tsconfig.node.json`** already includes `server/**/*.ts`
- Module resolution is set to "bundler" with ES2022 target
- Path aliases configured: `~/` for app, `~/database/` for database

### Dependencies Already Installed
- TypeScript is already in devDependencies
- `@types/node` is configured in tsconfig.node.json
- Vite handles TypeScript compilation in development

## Implementation Steps

### Step 1: Install Required Dependencies
```bash
npm install --save-dev tsx @types/express @types/morgan @types/compression
```

**Why tsx?**
- Fastest TypeScript runner for Node.js
- No compilation step needed
- Works in both development and production
- Supports ESM modules and path aliases
- Better than ts-node for performance

### Step 2: Convert server.js to server.ts

**File: `server.ts`**

```typescript
import compression from "compression";
import express, { Express } from "express";
import morgan from "morgan";
import { createServer } from "http";
import { ViteDevServer } from "vite";
import path from "path";
import { fileURLToPath } from "url";

// ESM module resolution helpers
const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);

// Configuration
const BUILD_PATH = "./build/server/index.js";
const DEVELOPMENT = process.env.NODE_ENV === "development";
const PORT = Number.parseInt(process.env.PORT || "3000", 10);

async function createAppServer(): Promise<void> {
  const app: Express = express();
  
  app.use(compression());
  app.disable("x-powered-by");

  if (DEVELOPMENT) {
    console.log("Starting development server");
    
    // Dynamic import to avoid bundling Vite in production
    const { createServer: createViteServer } = await import("vite");
    
    const viteDevServer: ViteDevServer = await createViteServer({
      server: { middlewareMode: true },
    });
    
    app.use(viteDevServer.middlewares);
    
    app.use(async (req, res, next) => {
      try {
        const source = await viteDevServer.ssrLoadModule("./server/app.ts");
        return await source.app(req, res, next);
      } catch (error) {
        if (error instanceof Error) {
          viteDevServer.ssrFixStacktrace(error);
        }
        next(error);
      }
    });
  } else {
    console.log("Starting production server");
    
    app.use(
      "/assets",
      express.static("build/client/assets", { 
        immutable: true, 
        maxAge: "1y" 
      })
    );
    
    app.use(morgan("tiny"));
    app.use(express.static("build/client", { maxAge: "1h" }));
    
    // Dynamic import for production build
    const { app: productionApp } = await import(BUILD_PATH);
    app.use(productionApp);
  }

  // Create HTTP server
  const httpServer = createServer(app);

  // Initialize Socket.IO
  const { initializeSocketIO } = await import("./server/socket.js");
  initializeSocketIO(httpServer);

  // Start server
  httpServer.listen(PORT, () => {
    console.log(`Server is running on http://localhost:${PORT}`);
  });
}

// Start the server
createAppServer().catch((error) => {
  console.error("Failed to start server:", error);
  process.exit(1);
});
```

### Step 3: Convert server/socket.js to server/socket.ts

**File: `server/socket.ts`**

```typescript
import { Server as SocketIOServer, Socket } from "socket.io";
import { Server as HTTPServer } from "http";

// Global type augmentation for Socket.IO instance
declare global {
  var __socketIO: SocketIOServer | undefined;
}

let io: SocketIOServer | null = null;

/**
 * Initialize Socket.IO server
 */
export function initializeSocketIO(httpServer: HTTPServer): SocketIOServer {
  if (io) {
    console.log("Socket.IO already initialized");
    return io;
  }

  // Create Socket.IO server with proper typing
  io = new SocketIOServer(httpServer, {
    cors: process.env.NODE_ENV === "production" && process.env.APP_URL
      ? {
          origin: process.env.APP_URL,
          credentials: true,
        }
      : undefined,
  });

  // Connection handling with typed socket
  io.on("connection", (socket: Socket) => {
    console.log("Client connected:", socket.id);

    // Join draft room
    socket.on("join-draft", (seasonId: string) => {
      if (!seasonId) {
        console.error("No seasonId provided for join-draft");
        return;
      }
      socket.join(`draft-${seasonId}`);
      console.log(`Socket ${socket.id} joined draft-${seasonId}`);
    });

    // Leave draft room
    socket.on("leave-draft", (seasonId: string) => {
      if (!seasonId) return;
      socket.leave(`draft-${seasonId}`);
      console.log(`Socket ${socket.id} left draft-${seasonId}`);
    });

    // Test event handler
    socket.on("test-event", (data: any) => {
      console.log("📨 Received test-event from client:", socket.id, data);
      
      socket.emit("test-message", {
        originalMessage: data,
        serverResponse: "Hello from server!",
        serverTimestamp: new Date().toISOString(),
        socketId: socket.id,
      });
      
      console.log("✅ Sent test-message response to client:", socket.id);
    });

    socket.on("disconnect", () => {
      console.log("Client disconnected:", socket.id);
    });
  });

  // Store globally for access in route handlers
  global.__socketIO = io;

  console.log("Socket.IO initialized");
  return io;
}

/**
 * Get the Socket.IO server instance
 */
export function getSocketIO(): SocketIOServer {
  const instance = io || global.__socketIO;
  if (!instance) {
    throw new Error("Socket.IO not initialized. Call initializeSocketIO first.");
  }
  return instance;
}
```

### Step 4: Create Type Definitions for Socket Events (Optional but Recommended)

**File: `server/socket.types.ts`**

```typescript
// Define your socket event types for better type safety
export interface ServerToClientEvents {
  "test-message": (data: {
    originalMessage: any;
    serverResponse: string;
    serverTimestamp: string;
    socketId: string;
  }) => void;
  "pick-made": (data: any) => void;
  "draft-started": (data: { seasonId: string; currentPickNumber: number }) => void;
  "draft-completed": () => void;
  "timer-update": (data: {
    seasonId: string;
    teamId: string;
    timeRemaining: number;
    currentPickNumber: number;
  }) => void;
}

export interface ClientToServerEvents {
  "join-draft": (seasonId: string) => void;
  "leave-draft": (seasonId: string) => void;
  "test-event": (data: any) => void;
}

export interface InterServerEvents {
  ping: () => void;
}

export interface SocketData {
  userId?: string;
  teamId?: string;
}
```

Then update `server/socket.ts` to use these types:

```typescript
import { Server as SocketIOServer } from "socket.io";
import type { 
  ServerToClientEvents, 
  ClientToServerEvents, 
  InterServerEvents, 
  SocketData 
} from "./socket.types";

// Create typed Socket.IO server
io = new SocketIOServer<
  ClientToServerEvents,
  ServerToClientEvents,
  InterServerEvents,
  SocketData
>(httpServer, { /* ... */ });
```

### Step 5: Update package.json Scripts

```json
{
  "scripts": {
    "build": "react-router build",
    "db:generate": "dotenv -- drizzle-kit generate",
    "db:migrate": "dotenv -- drizzle-kit migrate",
    "dev": "dotenv -- tsx watch server.ts",
    "start": "tsx server.ts",
    "start:production": "drizzle-kit migrate && tsx server.ts",
    "typecheck": "react-router typegen && tsc -b"
  }
}
```

**Key Changes:**
- `dev`: Uses `tsx watch` for auto-restart on file changes
- `start`: Uses `tsx` to run TypeScript directly
- `start:production`: Same as start but with migration

### Step 6: Update Dockerfile

```dockerfile
FROM node:20-alpine AS development-dependencies-env
COPY . /app
WORKDIR /app
RUN npm ci

FROM node:20-alpine AS production-dependencies-env
COPY ./package.json package-lock.json /app/
WORKDIR /app
RUN npm ci --omit=dev

FROM node:20-alpine AS build-env
COPY . /app/
COPY --from=development-dependencies-env /app/node_modules /app/node_modules
WORKDIR /app
RUN npm run build

FROM node:20-alpine
# Copy TypeScript files instead of JavaScript
COPY ./package.json package-lock.json server.ts /app/
COPY --from=production-dependencies-env /app/node_modules /app/node_modules
COPY --from=build-env /app/build /app/build
COPY ./drizzle /app/drizzle
COPY ./drizzle.config.ts /app/drizzle.config.ts
COPY ./server /app/server
COPY ./database /app/database
COPY ./app /app/app
# Copy TypeScript configs for tsx runtime
COPY ./tsconfig*.json /app/
WORKDIR /app
# Install tsx in production for running TypeScript
RUN npm install tsx
CMD ["npm", "run", "start:production"]
```

### Step 7: Update server.ts Import Path

After converting socket.js to socket.ts, update the import in server.ts:

```typescript
// Change from:
const { initializeSocketIO } = await import("./server/socket.js");

// To:
const { initializeSocketIO } = await import("./server/socket");
// Or explicitly:
const { initializeSocketIO } = await import("./server/socket.ts");
```

### Step 8: Add Timer Functionality (Now Possible!)

With TypeScript server, you can now properly import database modules:

**File: `server/timer.ts`**

```typescript
import { database } from "~/database/context";
import * as schema from "~/database/schema";
import { eq, and } from "drizzle-orm";
import { getSocketIO } from "./socket";

export async function startDraftTimerSystem(): Promise<void> {
  setInterval(async () => {
    try {
      await updateDraftTimers();
    } catch (error) {
      console.error("[Timer] Error updating draft timers:", error);
    }
  }, 1000);
  
  console.log("[Timer] Draft timer system started");
}

async function updateDraftTimers(): Promise<void> {
  const db = database();
  const io = getSocketIO();
  
  // Get all active drafts
  const activeDrafts = await db.query.seasons.findMany({
    where: eq(schema.seasons.status, "draft"),
  });
  
  // ... rest of timer logic
}
```

Then in `server/socket.ts`, add:

```typescript
import { startDraftTimerSystem } from "./timer";

export function initializeSocketIO(httpServer: HTTPServer): SocketIOServer {
  // ... existing code ...
  
  // Start timer system after Socket.IO is initialized
  startDraftTimerSystem();
  
  return io;
}
```

## Testing Plan

### 1. Development Mode Testing
```bash
# Start dev server with TypeScript
npm run dev

# Verify:
- Server starts without errors
- Vite dev server works
- Socket.IO connections work
- Hot reload works with tsx watch
- Can import database modules
```

### 2. Production Build Testing
```bash
# Build the app
npm run build

# Start production server
NODE_ENV=production npm start

# Verify:
- Server starts without errors
- Static assets are served
- Socket.IO works in production
- No TypeScript compilation errors
```

### 3. Docker Testing
```bash
# Build Docker image
docker build -t brackt-test .

# Run container
docker run -p 3000:3000 brackt-test

# Verify:
- Container starts successfully
- Application works in containerized environment
```

## Benefits of This Approach

1. **Type Safety**: Full TypeScript throughout the server
2. **Better Imports**: Can import TypeScript modules directly
3. **No Build Step**: tsx runs TypeScript directly
4. **Fast Development**: tsx watch provides fast restarts
5. **Production Ready**: tsx works in production without compilation
6. **Maintainable**: Cleaner code with proper types
7. **Debugging**: Better stack traces and error messages

## Migration Checklist

- [ ] Install tsx and type packages
- [ ] Convert server.js to server.ts
- [ ] Convert server/socket.js to server/socket.ts
- [ ] Create socket.types.ts for event types
- [ ] Update package.json scripts
- [ ] Update Dockerfile
- [ ] Test development mode
- [ ] Test production build
- [ ] Test Docker build
- [ ] Implement timer system with database imports
- [ ] Remove old .js files

## Rollback Plan

If issues arise:
1. Keep original .js files until testing complete
2. Can revert package.json scripts to use .js files
3. Git commit before starting conversion
4. Test thoroughly in development before deploying

## Notes for Implementation

- tsx handles TypeScript path aliases (`~/`) automatically
- No need for separate compilation step
- Works with existing Vite setup
- Compatible with React Router v7 SSR
- Maintains existing Socket.IO functionality
- Enables proper database module imports
