Files
smartserve/readme.hints.md

7.2 KiB

SmartServe Development Hints

Architecture Overview

SmartServe is a cross-platform HTTP server for Node.js, Deno, and Bun using:

  • Web Standards API (Request/Response) for cross-platform compatibility
  • TC39 Stage 3 decorators (TypeScript 5.0+) for route definition
  • Adapter pattern for runtime-specific implementations

Key Files

Core

  • ts/core/smartserve.classes.smartserve.ts - Main server class
  • ts/core/smartserve.interfaces.ts - All type definitions
  • ts/core/smartserve.errors.ts - HTTP error classes

Adapters

  • ts/adapters/adapter.factory.ts - Runtime detection via @push.rocks/smartenv
  • ts/adapters/adapter.node.ts - Node.js with Request/Response conversion
  • ts/adapters/adapter.deno.ts - Deno (zero overhead, native)
  • ts/adapters/adapter.bun.ts - Bun (zero overhead, native)

Decorators

  • ts/decorators/decorators.route.ts - @Route class decorator
  • ts/decorators/decorators.methods.ts - @Get, @Post, etc.
  • ts/decorators/decorators.interceptors.ts - @Guard, @Transform, @Intercept
  • ts/decorators/decorators.registry.ts - Controller registration and route matching

Static Files

  • ts/files/file.server.ts - Static file serving with streaming, ETags, directory listing
  • ts/utils/utils.mime.ts - MIME type detection
  • ts/utils/utils.etag.ts - ETag generation

Compression

  • ts/compression/compression.runtime.ts - Cross-runtime compression (Node.js zlib, Web CompressionStream)
  • ts/compression/compression.middleware.ts - Compression config and helpers
  • ts/utils/utils.encoding.ts - Accept-Encoding parsing
  • ts/decorators/decorators.compress.ts - @Compress and @NoCompress decorators

Protocols

  • ts/protocols/webdav/webdav.handler.ts - WebDAV RFC 4918 handler
  • ts/protocols/webdav/webdav.xml.ts - XML generation (multistatus, lock responses)
  • ts/protocols/webdav/webdav.types.ts - WebDAV type definitions

Decorator System

Uses TC39 decorators (NOT experimental decorators). Metadata stored via Symbol property on classes.

Interceptor Execution Order (Onion Model)

Request → Class Guards → Method Guards → Handler → Method Transforms → Class Transforms → Response

Guard vs Transform vs Intercept

  • @Guard(fn) = @Intercept({ request: fn }) - returns boolean (true=allow, false=403)
  • @Transform(fn) = @Intercept({ response: fn }) - modifies response
  • @Intercept({ request?, response? }) - full control

Dependencies

Required:

  • @push.rocks/smartenv - Runtime detection
  • @push.rocks/smartpath - Path utilities
  • @push.rocks/smartlog - Logging
  • @push.rocks/lik - Collections

Optional:

  • ws - WebSocket support for Node.js (Deno/Bun have native)

WebDAV Usage

import { SmartServe } from '@push.rocks/smartserve';

const server = new SmartServe({
  port: 8080,
  webdav: {
    root: '/path/to/files',
    auth: (ctx) => {
      // Optional auth - return true to allow, false to reject
      const auth = ctx.headers.get('Authorization');
      return auth === 'Basic dXNlcjpwYXNz';
    },
    locking: true, // Enable file locking (RFC 4918)
  }
});

await server.start();
// Mount at http://localhost:8080 as network drive

Supported WebDAV Methods

  • OPTIONS - Capability discovery
  • PROPFIND - Directory listing and file metadata
  • PROPPATCH - Property modification (returns 403)
  • MKCOL - Create directory
  • COPY - Copy files/directories
  • MOVE - Move/rename files/directories
  • LOCK/UNLOCK - Exclusive write locking
  • GET/HEAD/PUT/DELETE - Standard file operations

TypedRouter WebSocket Integration

SmartServe supports first-class TypedRouter integration for type-safe RPC over WebSockets.

Usage

import { SmartServe } from '@push.rocks/smartserve';
import { TypedRouter, TypedHandler } from '@api.global/typedrequest';

const router = new TypedRouter();
router.addTypedHandler(new TypedHandler('echo', async (data) => {
  return { echoed: data.message };
}));

const server = new SmartServe({
  port: 3000,
  websocket: {
    typedRouter: router,
    onConnectionOpen: (peer) => {
      peer.tags.add('authenticated');
      console.log(`Client connected: ${peer.id}`);
    },
    onConnectionClose: (peer) => {
      console.log(`Client disconnected: ${peer.id}`);
    },
  },
});

await server.start();

// Broadcast to all connections
server.broadcastWebSocket({ type: 'notification', message: 'Hello!' });

// Broadcast to specific tag
server.broadcastWebSocketByTag('authenticated', { type: 'secure-message' });

// Get all connections
const connections = server.getWebSocketConnections();

Key Features

  • TypedRouter mode: Set typedRouter for automatic JSON-RPC routing (mutually exclusive with onMessage)
  • Connection registry: Active only when typedRouter is set
  • Peer tags: Use peer.tags.add/has/delete for connection filtering
  • Broadcast methods: broadcastWebSocket() and broadcastWebSocketByTag()
  • Lifecycle hooks: onConnectionOpen and onConnectionClose (alongside existing onOpen/onClose)

Architecture Notes

  • typedRouter and onMessage are mutually exclusive (throws WebSocketConfigError if both set)
  • Connection registry only active when typedRouter is configured
  • Bun adapter stores peer ID/tags in ws.data for persistence across events
  • Internal _connectionCallbacks passed to adapters for registry communication

Compression

SmartServe supports automatic response compression with Brotli and gzip.

Configuration

const server = new SmartServe({
  port: 3000,

  // Simple: enable with defaults (compression is ON by default)
  compression: true,

  // Detailed configuration
  compression: {
    enabled: true,
    algorithms: ['br', 'gzip'],  // Brotli preferred, gzip fallback
    threshold: 1024,              // Don't compress < 1KB
    level: 4,                     // Compression level
    compressibleTypes: [          // Custom MIME types
      'text/',
      'application/json',
      'application/javascript',
    ],
    exclude: ['/api/stream/*'],   // Skip certain paths
  },

  // Pre-compressed static files
  static: {
    root: './public',
    precompressed: true,  // Serve index.html.br, index.html.gz if available
  },
});

Per-Route Control

@Controller('/api')
class ApiController {
  @Get('/data')
  getData() {
    return { large: 'json' };  // Compressed by default
  }

  @Get('/stream')
  @NoCompress()  // Skip compression for SSE/streaming
  getStream() {
    return new Response(eventStream, {
      headers: { 'Content-Type': 'text/event-stream' }
    });
  }

  @Get('/heavy')
  @Compress({ level: 11 })  // Force max brotli compression
  getHeavy() {
    return massiveData;
  }
}

Cross-Runtime Support

Runtime Brotli gzip Implementation
Node.js Native zlib module
Deno ⚠️ CompressionStream API
Bun ⚠️ CompressionStream API

Note: Brotli in Deno/Bun falls back to gzip if CompressionStream doesn't support it.

TODO

  • WebDAV protocol support (PROPFIND, MKCOL, COPY, MOVE, LOCK, UNLOCK)
  • TypedRouter WebSocket integration
  • Brotli/gzip compression with per-route control
  • HTTP/2 support investigation
  • Performance benchmarks