CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/npm-h3

Minimal H(TTP) framework built for high performance and portability across multiple JavaScript runtimes.

Pending
Quality

Pending

Does it follow best practices?

Impact

Pending

No eval scenarios have been run

SecuritybySnyk

Pending

The risk profile of this skill

Overview
Eval results
Files

advanced-features.mddocs/

Advanced Features

Advanced H3 features including proxy functionality, Server-Sent Events (SSE), WebSocket support, static file serving, and caching utilities.

Capabilities

Proxy Support

Forward requests to other servers with comprehensive proxy options.

/**
 * Proxy request to target server
 * @param event - H3 event
 * @param target - Target URL or server
 * @param opts - Proxy configuration options
 * @returns Promise resolving to response body
 */
function proxy(
  event: H3Event,
  target: string,
  opts?: ProxyOptions
): Promise<BodyInit | undefined | null>;

/**
 * Proxy request with full control (alias for proxy)
 * @param event - H3 event
 * @param target - Target URL or server
 * @param opts - Proxy configuration options
 * @returns Promise resolving to response body
 */
function proxyRequest(
  event: H3Event,
  target: string,
  opts?: ProxyOptions
): Promise<BodyInit | undefined | null>;

/**
 * Get headers for proxy request
 * @param event - H3 event
 * @param options - Header processing options
 * @returns Headers object for proxy request
 */
function getProxyRequestHeaders(
  event: H3Event,
  options?: {
    ignoredHeaders?: string[];
    allowedHeaders?: string[];
  }
): Headers;

/**
 * Fetch with H3 event context
 * @param event - H3 event
 * @param req - Request, URL, or string
 * @param init - Request initialization options
 * @returns Promise resolving to Response
 */
function fetchWithEvent(
  event: H3Event,
  req: ServerRequest | URL | string,
  init?: RequestInit
): Promise<Response>;

Usage Examples:

import { proxy, getProxyRequestHeaders, fetchWithEvent } from "h3";

// Basic proxy
const proxyHandler = defineHandler(async (event) => {
  return await proxy(event, "https://api.external-service.com");
});

// Proxy with options
const advancedProxyHandler = defineHandler(async (event) => {
  return await proxy(event, "https://backend.example.com", {
    headers: {
      "X-Forwarded-For": getRequestIP(event),
      "X-Original-Host": getRequestHost(event)
    },
    onProxyReq: (proxyReq, req) => {
      console.log(`Proxying ${req.method} ${req.url}`);
    },
    onProxyRes: (proxyRes, req, res) => {
      console.log(`Proxy response: ${proxyRes.status}`);
    }
  });
});

// Custom proxy headers
const customHeadersProxyHandler = defineHandler(async (event) => {
  const proxyHeaders = getProxyRequestHeaders(event, {
    ignoredHeaders: ["authorization", "cookie"],
    allowedHeaders: ["content-type", "accept", "user-agent"]
  });
  
  return await proxy(event, "https://public-api.com", {
    headers: {
      ...Object.fromEntries(proxyHeaders.entries()),
      "X-API-Key": process.env.EXTERNAL_API_KEY
    }
  });
});

// Fetch with event context
const fetchHandler = defineHandler(async (event) => {
  const response = await fetchWithEvent(
    event,
    "https://api.example.com/data",
    {
      method: "POST",
      headers: { "Content-Type": "application/json" },
      body: JSON.stringify({ query: getQuery(event) })
    }
  );
  
  const data = await response.json();
  return { data };
});

// Load balancing proxy
const loadBalancerHandler = defineHandler(async (event) => {
  const servers = [
    "https://server1.example.com",
    "https://server2.example.com",
    "https://server3.example.com"
  ];
  
  const serverIndex = Math.floor(Math.random() * servers.length);
  const targetServer = servers[serverIndex];
  
  return await proxy(event, targetServer, {
    headers: {
      "X-Load-Balancer": "h3-proxy",
      "X-Server-Index": serverIndex.toString()
    }
  });
});

Server-Sent Events (SSE)

Real-time server-to-client communication using Server-Sent Events.

/**
 * Create Server-Sent Events stream
 * @param event - H3 event
 * @param options - SSE configuration options
 * @returns Event stream helper object
 */
function createEventStream(
  event: H3Event,
  options?: EventStreamOptions
): EventStreamHelper;

interface EventStreamHelper {
  /**
   * Send a data event
   * @param data - Data to send (string or object)
   */
  push(data: string | any): void;
  
  /**
   * Send a named event with data
   * @param event - Event name
   * @param data - Event data
   */
  pushEvent(event: string, data: string | any): void;
  
  /**
   * Send an event with full control
   * @param message - Complete event message
   */
  pushMessage(message: EventStreamMessage): void;
  
  /**
   * Close the event stream
   */
  close(): void;
  
  /**
   * Send keep-alive ping
   */
  ping(): void;
}

Usage Examples:

import { createEventStream } from "h3";

// Basic SSE stream
const sseHandler = defineHandler((event) => {
  const eventStream = createEventStream(event);
  
  // Send initial data
  eventStream.push("Connected to stream");
  
  // Send periodic updates
  const interval = setInterval(() => {
    eventStream.push({
      timestamp: Date.now(),
      message: "Periodic update"
    });
  }, 1000);
  
  // Cleanup on client disconnect
  event.waitUntil(new Promise((resolve) => {
    const checkConnection = setInterval(() => {
      if (event.aborted) {
        clearInterval(interval);
        clearInterval(checkConnection);
        eventStream.close();
        resolve(undefined);
      }
    }, 5000);
  }));
  
  return eventStream;
});

// Named events SSE
const namedEventsHandler = defineHandler((event) => {
  const eventStream = createEventStream(event, {
    retry: 3000, // Client retry interval
    headers: {
      "Cache-Control": "no-cache",
      "Connection": "keep-alive"
    }
  });
  
  // Send different event types
  eventStream.pushEvent("user-connected", { userId: "123" });
  eventStream.pushEvent("notification", { 
    type: "info", 
    message: "Welcome to the stream" 
  });
  
  // Send with custom message format
  eventStream.pushMessage({
    event: "custom-event",
    data: JSON.stringify({ custom: "data" }),
    id: "msg-001",
    retry: 5000
  });
  
  return eventStream;
});

// Real-time data stream
const realtimeDataHandler = defineHandler(async (event) => {
  const eventStream = createEventStream(event);
  
  // Subscribe to real-time data source
  const unsubscribe = dataService.subscribe((data) => {
    eventStream.push(data);
  });
  
  // Handle client disconnect
  event.waitUntil(new Promise((resolve) => {
    const checkConnection = () => {
      if (event.aborted) {
        unsubscribe();
        eventStream.close();
        resolve(undefined);
      } else {
        setTimeout(checkConnection, 1000);
      }
    };
    checkConnection();
  }));
  
  return eventStream;
});

// Chat room SSE
const chatStreamHandler = defineHandler((event) => {
  const { roomId } = getRouterParams(event);
  const eventStream = createEventStream(event);
  
  // Join chat room
  chatService.joinRoom(roomId, (message) => {
    eventStream.pushEvent("message", {
      id: message.id,
      user: message.user,
      text: message.text,
      timestamp: message.timestamp
    });
  });
  
  // Send room history
  chatService.getHistory(roomId).then(messages => {
    eventStream.pushEvent("history", messages);
  });
  
  // Handle user leaving
  event.waitUntil(new Promise((resolve) => {
    const cleanup = () => {
      chatService.leaveRoom(roomId);
      eventStream.close();
      resolve(undefined);
    };
    
    if (event.aborted) {
      cleanup();
    } else {
      setTimeout(() => {
        if (event.aborted) cleanup();
      }, 1000);
    }
  }));
  
  return eventStream;
});

WebSocket Support

WebSocket handler definitions for real-time bidirectional communication.

/**
 * Define WebSocket event hooks
 * @param hooks - WebSocket lifecycle hooks
 * @returns WebSocket hooks object
 */
function defineWebSocket(hooks: Partial<WSHooks>): Partial<WSHooks>;

/**
 * Define WebSocket handler
 * @param hooks - WebSocket lifecycle hooks
 * @returns Event handler for WebSocket upgrade
 */
function defineWebSocketHandler(hooks: Partial<WSHooks>): EventHandler;

interface WSHooks {
  /**
   * Called when WebSocket connection is opened
   * @param peer - WebSocket peer connection
   */
  open(peer: WSPeer): void | Promise<void>;
  
  /**
   * Called when message is received
   * @param peer - WebSocket peer connection
   * @param message - Received message
   */
  message(peer: WSPeer, message: WSMessage): void | Promise<void>;
  
  /**
   * Called when WebSocket connection is closed
   * @param peer - WebSocket peer connection
   * @param details - Close details
   */
  close(peer: WSPeer, details: WSCloseDetails): void | Promise<void>;
  
  /**
   * Called when WebSocket error occurs
   * @param peer - WebSocket peer connection
   * @param error - Error object
   */
  error(peer: WSPeer, error: Error): void | Promise<void>;
  
  /**
   * Called during WebSocket upgrade
   * @param event - H3 event
   */
  upgrade(event: H3Event): void | Promise<void>;
}

Usage Examples:

import { defineWebSocketHandler, defineWebSocket } from "h3";

// Basic WebSocket handler
const wsHandler = defineWebSocketHandler({
  open(peer) {
    console.log("WebSocket connection opened");
    peer.send("Welcome to WebSocket server");
  },
  
  message(peer, message) {
    console.log("Received:", message.text());
    
    // Echo message back
    peer.send(`Echo: ${message.text()}`);
  },
  
  close(peer, details) {
    console.log("WebSocket connection closed:", details.code);
  },
  
  error(peer, error) {
    console.error("WebSocket error:", error);
  }
});

// Chat WebSocket
const chatWsHandler = defineWebSocketHandler({
  async open(peer) {
    // Store connection in chat room
    const roomId = peer.url.searchParams.get("room") || "general";
    await chatService.addPeer(roomId, peer);
    
    // Send room info
    peer.send(JSON.stringify({
      type: "room-info",
      roomId,
      userCount: await chatService.getUserCount(roomId)
    }));
  },
  
  async message(peer, message) {
    const data = JSON.parse(message.text());
    const roomId = peer.url.searchParams.get("room") || "general";
    
    switch (data.type) {
      case "chat-message":
        // Broadcast to all peers in room
        await chatService.broadcast(roomId, {
          type: "message",
          user: data.user,
          text: data.text,
          timestamp: Date.now()
        });
        break;
        
      case "typing":
        // Broadcast typing indicator
        await chatService.broadcastToOthers(roomId, peer, {
          type: "user-typing",
          user: data.user
        });
        break;
    }
  },
  
  async close(peer) {
    const roomId = peer.url.searchParams.get("room") || "general";
    await chatService.removePeer(roomId, peer);
  }
});

// Game WebSocket
const gameWsHandler = defineWebSocketHandler({
  async upgrade(event) {
    // Validate game token
    const token = getQuery(event).token;
    const gameSession = await validateGameToken(token);
    
    if (!gameSession) {
      throw HTTPError.status(401, "Invalid game token");
    }
    
    // Store game info in context
    event.context.gameId = gameSession.gameId;
    event.context.playerId = gameSession.playerId;
  },
  
  async open(peer) {
    const { gameId, playerId } = peer.context;
    
    // Add player to game
    await gameService.addPlayer(gameId, playerId, peer);
    
    // Send game state
    const gameState = await gameService.getState(gameId);
    peer.send(JSON.stringify({
      type: "game-state",
      state: gameState
    }));
  },
  
  async message(peer, message) {
    const data = JSON.parse(message.text());
    const { gameId, playerId } = peer.context;
    
    // Process game action
    const result = await gameService.processAction(gameId, playerId, data);
    
    if (result.valid) {
      // Broadcast update to all players
      await gameService.broadcastUpdate(gameId, result.update);
    } else {
      // Send error to player
      peer.send(JSON.stringify({
        type: "error",
        message: result.error
      }));
    }
  }
});

// WebSocket with authentication
const authWsHandler = defineWebSocketHandler({
  async upgrade(event) {
    const token = getHeader(event, "authorization")?.replace("Bearer ", "");
    
    if (!token) {
      throw HTTPError.status(401, "Authentication required");
    }
    
    const user = await verifyJWTToken(token);
    if (!user) {
      throw HTTPError.status(401, "Invalid token");
    }
    
    event.context.user = user;
  },
  
  open(peer) {
    const user = peer.context.user;
    console.log(`User ${user.id} connected via WebSocket`);
    
    peer.send(JSON.stringify({
      type: "authenticated",
      user: { id: user.id, name: user.name }
    }));
  },
  
  async message(peer, message) {
    const user = peer.context.user;
    const data = JSON.parse(message.text());
    
    // Process authenticated user action
    await processUserAction(user, data);
  }
});

Static File Serving

Serve static files with caching, compression, and security features.

/**
 * Serve static files from filesystem
 * @param event - H3 event
 * @param options - Static serving options
 * @returns Promise resolving to Response or undefined
 */
function serveStatic(
  event: H3Event,
  options: ServeStaticOptions
): Promise<Response | undefined>;

Usage Examples:

import { serveStatic } from "h3";

// Basic static serving
const staticHandler = defineHandler(async (event) => {
  return await serveStatic(event, {
    root: "./public",
    index: ["index.html"],
    dotFiles: "deny"
  });
});

// Advanced static serving
const advancedStaticHandler = defineHandler(async (event) => {
  return await serveStatic(event, {
    root: "./assets",
    index: ["index.html", "index.htm"],
    dotFiles: "deny",
    etag: true,
    lastModified: true,
    maxAge: 86400, // 24 hours
    immutable: event.url.pathname.includes("/static/"),
    fallthrough: false,
    redirect: true,
    extensions: ["html", "htm"]
  });
});

// SPA serving with fallback
const spaHandler = defineHandler(async (event) => {
  const response = await serveStatic(event, {
    root: "./dist",
    index: ["index.html"],
    fallthrough: true
  });
  
  // Fallback to index.html for SPA routes
  if (!response && !event.url.pathname.startsWith("/api/")) {
    return await serveStatic(event, {
      root: "./dist",
      index: ["index.html"]
    });
  }
  
  return response;
});

// Multiple static directories
const multiStaticHandler = defineHandler(async (event) => {
  // Try serving from uploads first
  if (event.url.pathname.startsWith("/uploads/")) {
    const response = await serveStatic(event, {
      root: "./uploads",
      maxAge: 3600
    });
    if (response) return response;
  }
  
  // Fall back to public directory
  return await serveStatic(event, {
    root: "./public",
    maxAge: 86400
  });
});

Type Definitions

Proxy Types

/**
 * Proxy configuration options
 */
interface ProxyOptions {
  /**
   * Additional headers to send
   */
  headers?: Record<string, string>;
  
  /**
   * Transform request before sending
   */
  onProxyReq?: (proxyReq: Request, req: Request) => void;
  
  /**
   * Transform response before returning
   */
  onProxyRes?: (proxyRes: Response, req: Request, res: Response) => void;
  
  /**
   * Handle proxy errors
   */
  onError?: (error: Error, req: Request, res: Response) => void;
  
  /**
   * Custom agent for requests
   */
  agent?: any;
  
  /**
   * Timeout in milliseconds
   */
  timeout?: number;
  
  /**
   * Follow redirects
   */
  followRedirects?: boolean;
}

SSE Types

/**
 * Server-Sent Events options
 */
interface EventStreamOptions {
  /**
   * Client retry interval in milliseconds
   */
  retry?: number;
  
  /**
   * Additional response headers
   */
  headers?: Record<string, string>;
  
  /**
   * Auto-close timeout
   */
  autoClose?: number;
}

/**
 * SSE message format
 */
interface EventStreamMessage {
  /**
   * Event name
   */
  event?: string;
  
  /**
   * Event data
   */
  data: string;
  
  /**
   * Event ID
   */
  id?: string;
  
  /**
   * Retry interval
   */
  retry?: number;
}

WebSocket Types

/**
 * WebSocket peer connection
 */
interface WSPeer {
  /**
   * Send message to peer
   */
  send(message: string | ArrayBuffer): void;
  
  /**
   * Close connection
   */
  close(code?: number, reason?: string): void;
  
  /**
   * Connection URL
   */
  url: URL;
  
  /**
   * Connection context
   */
  context: any;
  
  /**
   * Ready state
   */
  readyState: number;
}

/**
 * WebSocket message
 */
interface WSMessage {
  /**
   * Get message as text
   */
  text(): string;
  
  /**
   * Get message as ArrayBuffer
   */
  arrayBuffer(): ArrayBuffer;
  
  /**
   * Message type
   */
  type: "text" | "binary";
}

/**
 * WebSocket close details
 */
interface WSCloseDetails {
  /**
   * Close code
   */
  code: number;
  
  /**
   * Close reason
   */
  reason: string;
}

Static Serving Types

/**
 * Static serving options
 */
interface ServeStaticOptions {
  /**
   * Root directory to serve from
   */
  root: string;
  
  /**
   * Index file names
   */
  index?: string[];
  
  /**
   * How to handle dotfiles
   */
  dotFiles?: "allow" | "deny" | "ignore";
  
  /**
   * Enable ETag generation
   */
  etag?: boolean;
  
  /**
   * Enable Last-Modified header
   */
  lastModified?: boolean;
  
  /**
   * Cache max age in seconds
   */
  maxAge?: number;
  
  /**
   * Mark as immutable
   */
  immutable?: boolean;
  
  /**
   * Allow fallthrough for missing files
   */
  fallthrough?: boolean;
  
  /**
   * Redirect directories without trailing slash
   */
  redirect?: boolean;
  
  /**
   * File extensions to try
   */
  extensions?: string[];
}

/**
 * Static asset metadata
 */
interface StaticAssetMeta {
  /**
   * File path
   */
  path: string;
  
  /**
   * File size
   */
  size: number;
  
  /**
   * MIME type
   */
  type: string;
  
  /**
   * Last modified date
   */
  mtime: Date;
  
  /**
   * ETag value
   */
  etag?: string;
}

docs

advanced-features.md

core-framework.md

error-handling.md

event-handling.md

handlers-middleware.md

index.md

request-processing.md

response-handling.md

runtime-adapters.md

web-utilities.md

tile.json