🎯 Рекомендуемые коллекции

Балансированные коллекции примеров кода из различных категорий, которые вы можете исследовать

Примеры Web Функций Web TypeScript

Примеры web функций Web TypeScript включая маршрутизацию, middleware и обслуживание статических файлов

💻 Маршрутизация typescript

🟡 intermediate ⭐⭐⭐

Разбор URL маршрутов, обработка hash-маршрутизации и управление историей браузера

⏱️ 25 min 🏷️ typescript, web, web features
Prerequisites: Intermediate TypeScript, History API, URL API
// Web TypeScript Routing Examples
// Client-side routing with URL parsing, hash routing, and history management

// 1. Route Interface
interface Route {
  path: string;
  handler: (params: Record<string, string>, query: Record<string, string>) => void;
}

// 2. Router
class Router {
  private routes: Route[] = [];
  private notFoundHandler?: () => void;
  private currentParams: Record<string, string> = {};
  private currentQuery: Record<string, string> = {};

  // Add route
  addRoute(path: string, handler: Route['handler']): void {
    this.routes.push({ path, handler });
  }

  // Set 404 handler
  setNotFound(handler: () => void): void {
    this.notFoundHandler = handler;
  }

  // Navigate to path
  navigate(path: string, params: Record<string, string> = {}): void {
    // Build URL with params
    let url = path;

    if (Object.keys(params).length > 0) {
      url = this.replaceParams(path, params);
    }

    // Update browser URL
    window.history.pushState({}, '', url);

    // Handle route
    this.handleRoute(url);
  }

  // Replace current route
  replace(path: string, params: Record<string, string> = {}): void {
    let url = path;

    if (Object.keys(params).length > 0) {
      url = this.replaceParams(path, params);
    }

    window.history.replaceState({}, '', url);
    this.handleRoute(url);
  }

  // Go back
  back(): void {
    window.history.back();
  }

  // Go forward
  forward(): void {
    window.history.forward();
  }

  // Go to specific history entry
  go(delta: number): void {
    window.history.go(delta);
  }

  // Handle route change
  private handleRoute(url: string): void {
    const { path, query } = this.parseURL(url);

    // Find matching route
    const route = this.findRoute(path);

    if (route) {
      const params = this.extractParams(path, route.path);
      this.currentParams = params;
      this.currentQuery = query;
      route.handler(params, query);
    } else if (this.notFoundHandler) {
      this.notFoundHandler();
    }
  }

  // Find matching route
  private findRoute(path: string): Route | null {
    for (const route of this.routes) {
      if (this.matchRoute(path, route.path)) {
        return route;
      }
    }
    return null;
  }

  // Check if path matches route pattern
  private matchRoute(path: string, pattern: string): boolean {
    // Convert pattern to regex
    const regexPattern = pattern
      .replace(/:\w+/g, '([^/]+)')
      .replace(/\*/g, '.*');

    const regex = new RegExp(`^${regexPattern}$`);
    return regex.test(path);
  }

  // Extract params from path
  private extractParams(path: string, pattern: string): Record<string, string> {
    const params: Record<string, string> = {};

    const patternParts = pattern.split('/');
    const pathParts = path.split('/');

    for (let i = 0; i < patternParts.length; i++) {
      const part = patternParts[i];
      if (part.startsWith(':')) {
        const paramName = part.substring(1);
        params[paramName] = pathParts[i] || '';
      }
    }

    return params;
  }

  // Replace params in path
  private replaceParams(path: string, params: Record<string, string>): string {
    let result = path;

    for (const [key, value] of Object.entries(params)) {
      result = result.replace(`:${key}`, value);
    }

    return result;
  }

  // Parse URL into path and query
  private parseURL(url: string): {
    path: string;
    query: Record<string, string>;
  } {
    const [path, queryString] = url.split('?');
    const query: Record<string, string> = {};

    if (queryString) {
      const params = new URLSearchParams(queryString);
      params.forEach((value, key) => {
        query[key] = value;
      });
    }

    return { path, query };
  }

  // Initialize router
  initialize(): void {
    // Handle popstate event (back/forward buttons)
    window.addEventListener('popstate', () => {
      this.handleRoute(window.location.pathname);
    });

    // Handle initial route
    this.handleRoute(window.location.pathname);
  }
}

// 3. Hash Router
class HashRouter {
  private routes: Route[] = [];
  private notFoundHandler?: () => void;

  // Add route
  addRoute(path: string, handler: Route['handler']): void {
    // Remove leading slash for hash matching
    const hashPath = path.startsWith('/') ? path.substring(1) : path;
    this.routes.push({ path: hashPath, handler });
  }

  // Set 404 handler
  setNotFound(handler: () => void): void {
    this.notFoundHandler = handler;
  }

  // Navigate to hash
  navigate(hash: string, params: Record<string, string> = {}): void {
    let url = hash;

    if (Object.keys(params).length > 0) {
      url = this.replaceParams(hash, params);
    }

    window.location.hash = url;
  }

  // Get current hash
  getHash(): string {
    return window.location.hash.substring(1) || '/';
  }

  // Handle hash change
  private handleHashChange(): void {
    const hash = this.getHash();
    const [path, queryString] = hash.split('?');
    const query: Record<string, string> = {};

    if (queryString) {
      const params = new URLSearchParams(queryString);
      params.forEach((value, key) => {
        query[key] = value;
      });
    }

    // Find matching route
    const route = this.findRoute(path);

    if (route) {
      const params = this.extractParams(path, route.path);
      route.handler(params, query);
    } else if (this.notFoundHandler) {
      this.notFoundHandler();
    }
  }

  // Find matching route
  private findRoute(path: string): Route | null {
    for (const route of this.routes) {
      if (this.matchRoute(path, route.path)) {
        return route;
      }
    }
    return null;
  }

  // Check if path matches route pattern
  private matchRoute(path: string, pattern: string): boolean {
    const regexPattern = pattern
      .replace(/:\w+/g, '([^/]+)')
      .replace(/\*/g, '.*');

    const regex = new RegExp(`^${regexPattern}$`);
    return regex.test(path);
  }

  // Extract params from path
  private extractParams(path: string, pattern: string): Record<string, string> {
    const params: Record<string, string> = {};

    const patternParts = pattern.split('/');
    const pathParts = path.split('/');

    for (let i = 0; i < patternParts.length; i++) {
      const part = patternParts[i];
      if (part.startsWith(':')) {
        const paramName = part.substring(1);
        params[paramName] = pathParts[i] || '';
      }
    }

    return params;
  }

  // Replace params in path
  private replaceParams(path: string, params: Record<string, string>): string {
    let result = path;

    for (const [key, value] of Object.entries(params)) {
      result = result.replace(`:${key}`, value);
    }

    return result;
  }

  // Initialize hash router
  initialize(): void {
    window.addEventListener('hashchange', () => {
      this.handleHashChange();
    });

    // Handle initial hash
    this.handleHashChange();
  }
}

// 4. Query Params Manager
class QueryParamsManager {
  // Get query param
  get(param: string): string | null {
    const params = new URLSearchParams(window.location.search);
    return params.get(param);
  }

  // Get all params
  getAll(): Record<string, string> {
    const params = new URLSearchParams(window.location.search);
    const result: Record<string, string> = {};

    params.forEach((value, key) => {
      result[key] = value;
    });

    return result;
  }

  // Set query param
  set(param: string, value: string): void {
    const params = new URLSearchParams(window.location.search);
    params.set(param, value);
    this.update(params.toString());
  }

  // Delete query param
  delete(param: string): void {
    const params = new URLSearchParams(window.location.search);
    params.delete(param);
    this.update(params.toString());
  }

  // Clear all params
  clear(): void {
    this.update('');
  }

  // Update URL
  private update(queryString: string): void {
    const url = new URL(window.location.href);
    url.search = queryString;
    window.history.replaceState({}, '', url.toString());
  }

  // Watch for changes
  watch(callback: (params: Record<string, string>) => void): () => void {
    const handler = () => {
      callback(this.getAll());
    };

    window.addEventListener('popstate', handler);

    // Return cleanup function
    return () => {
      window.removeEventListener('popstate', handler);
    };
  }
}

// 5. Route Guard
class RouteGuard {
  private guards: Map<string, () => boolean | Promise<boolean>> = new Map();

  // Add guard for route
  addGuard(route: string, guard: () => boolean | Promise<boolean>): void {
    this.guards.set(route, guard);
  }

  // Check if route is allowed
  async canNavigate(route: string): Promise<boolean> {
    const guard = this.guards.get(route);

    if (guard) {
      return await guard();
    }

    return true;
  }

  // Add authentication guard
  addAuthGuard(route: string, isAuthenticated: () => boolean): void {
    this.addGuard(route, isAuthenticated);
  }

  // Add permission guard
  addPermissionGuard(route: string, hasPermission: () => boolean | Promise<boolean>): void {
    this.addGuard(route, hasPermission);
  }
}

// 6. Navigation Manager
class NavigationManager {
  private router: Router;
  private history: string[] = [];
  private currentIndex: number = -1;

  constructor(router: Router) {
    this.router = router;
  }

  // Navigate with history tracking
  navigate(path: string, params: Record<string, string> = {}): void {
    // Remove forward history if we're not at the end
    if (this.currentIndex < this.history.length - 1) {
      this.history = this.history.slice(0, this.currentIndex + 1);
    }

    // Add to history
    this.history.push(path);
    this.currentIndex++;

    // Navigate
    this.router.navigate(path, params);
  }

  // Go back in custom history
  back(): void {
    if (this.currentIndex > 0) {
      this.currentIndex--;
      const path = this.history[this.currentIndex];
      window.history.back();
    }
  }

  // Go forward in custom history
  forward(): void {
    if (this.currentIndex < this.history.length - 1) {
      this.currentIndex++;
      const path = this.history[this.currentIndex];
      window.history.forward();
    }
  }

  // Can go back
  canGoBack(): boolean {
    return this.currentIndex > 0;
  }

  // Can go forward
  canGoForward(): boolean {
    return this.currentIndex < this.history.length - 1;
  }

  // Get current position
  getCurrentPosition(): number {
    return this.currentIndex;
  }

  // Get history length
  getHistoryLength(): number {
    return this.history.length;
  }
}

// 7. Route Transition Manager
class RouteTransitionManager {
  private transitions: Map<string, (from: string, to: string) => void> = new Map();

  // Add transition
  addTransition(from: string, to: string, handler: () => void): void {
    const key = this.getKey(from, to);
    this.transitions.set(key, handler);
  }

  // Execute transition
  execute(from: string, to: string): void {
    const key = this.getKey(from, to);
    const handler = this.transitions.get(key);

    if (handler) {
      handler(from, to);
    }
  }

  // Add global transition
  addGlobalTransition(handler: (from: string, to: string) => void): void {
    this.transitions.set('*', handler);
  }

  // Generate transition key
  private getKey(from: string, to: string): string {
    return `${from}>${to}`;
  }
}

// Usage Examples
async function demonstrateRouting() {
  console.log('=== Web TypeScript Routing Examples ===\n');

  // 1. Basic router
  console.log('--- 1. Basic Router ---');
  const router = new Router();

  router.addRoute('/', (params, query) => {
    console.log('Home page');
  });

  router.addRoute('/about', (params, query) => {
    console.log('About page');
  });

  router.addRoute('/users/:id', (params, query) => {
    console.log(`User page: ${params.id}`);
  });

  router.addRoute('/posts/:postId/comments/:commentId', (params, query) => {
    console.log(`Comment: ${params.commentId} on post ${params.postId}`);
  });

  // 2. Navigation
  console.log('\n--- 2. Navigation ---');
  router.navigate('/');
  await new Promise(resolve => setTimeout(resolve, 100));

  router.navigate('/about');
  await new Promise(resolve => setTimeout(resolve, 100));

  router.navigate('/users/123');
  await new Promise(resolve => setTimeout(resolve, 100));

  // 3. Query params
  console.log('\n--- 3. Query Params ---');
  router.navigate('/search?query=typescript&page=1');
  await new Promise(resolve => setTimeout(resolve, 100));

  // 4. Hash router
  console.log('\n--- 4. Hash Router ---');
  const hashRouter = new HashRouter();

  hashRouter.addRoute('/', (params, query) => {
    console.log('Hash home');
  });

  hashRouter.addRoute('profile/:userId', (params, query) => {
    console.log(`Hash profile: ${params.userId}`);
  });

  hashRouter.navigate('profile/456');
  await new Promise(resolve => setTimeout(resolve, 100));

  // 5. Query params manager
  console.log('\n--- 5. Query Params Manager ---');
  const queryManager = new QueryParamsManager();

  queryManager.set('tab', 'profile');
  queryManager.set('section', 'details');

  const allParams = queryManager.getAll();
  console.log('All params:', allParams);

  // 6. Route guard
  console.log('\n--- 6. Route Guard ---');
  const guard = new RouteGuard();

  guard.addAuthGuard('/admin', () => {
    const isAuthenticated = false; // Simulate
    console.log(`Auth check: ${isAuthenticated}`);
    return isAuthenticated;
  });

  const canAccess = await guard.canNavigate('/admin');
  console.log(`Can access /admin: ${canAccess}`);

  // 7. Navigation manager
  console.log('\n--- 7. Navigation Manager ---');
  const navManager = new NavigationManager(router);

  navManager.navigate('/page1');
  await new Promise(resolve => setTimeout(resolve, 100));

  navManager.navigate('/page2');
  await new Promise(resolve => setTimeout(resolve, 100));

  console.log(`Can go back: ${navManager.canGoBack()}`);
  console.log(`History length: ${navManager.getHistoryLength()}`);

  console.log('\n=== All Routing Examples Completed ===');
}

// Export functions
export { Router, HashRouter, QueryParamsManager, RouteGuard, NavigationManager, RouteTransitionManager };
export { demonstrateRouting };
export type { Route };

💻 Middleware typescript

🟡 intermediate ⭐⭐⭐⭐

Реализация цепочки middleware запроса/ответа для обработки и изменения данных

⏱️ 30 min 🏷️ typescript, web, web features
Prerequisites: Intermediate TypeScript, Promise chains, Async/await
// Web TypeScript Middleware Examples
// Request/response processing middleware chain

// 1. Context Interface
interface Context {
  request: any;
  response: any;
  state: Map<string, any>;
  headers: Map<string, string>;
  metadata: Record<string, any>;
}

// 2. Middleware Function Type
type Middleware = (context: Context, next: () => Promise<void>) => Promise<void>;

// 3. Middleware Pipeline
class MiddlewarePipeline {
  private middlewares: Middleware[] = [];

  // Add middleware
  use(middleware: Middleware): this {
    this.middlewares.push(middleware);
    return this;
  }

  // Execute pipeline
  async execute(context: Context): Promise<void> {
    let index = 0;

    const next = async (): Promise<void> => {
      if (index < this.middlewares.length) {
        const middleware = this.middlewares[index++];
        await middleware(context, next);
      }
    };

    await next();
  }

  // Clear all middlewares
  clear(): void {
    this.middlewares = [];
  }

  // Get middleware count
  count(): number {
    return this.middlewares.length;
  }
}

// 4. Common Middlewares
class CommonMiddlewares {
  // Logging middleware
  static logging(): Middleware {
    return async (context: Context, next: () => Promise<void>) => {
      const start = performance.now();

      console.log('[Request]', {
        url: context.request?.url || 'N/A',
        method: context.request?.method || 'N/A',
        headers: Object.fromEntries(context.headers)
      });

      await next();

      const duration = performance.now() - start;
      console.log('[Response]', {
        status: context.response?.status || 'N/A',
        duration: `${duration.toFixed(2)}ms`
      });
    };
  }

  // Timing middleware
  static timing(): Middleware {
    return async (context: Context, next: () => Promise<void>) => {
      const start = performance.now();

      await next();

      const duration = performance.now() - start;
      context.metadata.duration = duration;
    };
  }

  // Error handling middleware
  static errorHandler(errorHandler: (error: Error) => void): Middleware {
    return async (context: Context, next: () => Promise<void>) => {
      try {
        await next();
      } catch (error) {
        errorHandler(error as Error);
        throw error;
      }
    };
  }

  // Header manipulation middleware
  static headers(addHeaders: Record<string, string>): Middleware {
    return async (context: Context, next: () => Promise<void>) => {
      for (const [key, value] of Object.entries(addHeaders)) {
        context.headers.set(key, value);
      }

      await next();
    };
  }

  // Authentication middleware
  static authentication(authCheck: (context: Context) => boolean | Promise<boolean>): Middleware {
    return async (context: Context, next: () => Promise<void>) => {
      const isAuthenticated = await authCheck(context);

      if (!isAuthenticated) {
        throw new Error('Unauthorized');
      }

      context.metadata.authenticated = true;
      await next();
    };
  }

  // Rate limiting middleware
  static rateLimit(options: {
    maxRequests: number;
    windowMs: number;
  }): Middleware {
    const requests = new Map<string, number[]>();

    return async (context: Context, next: () => Promise<void>) => {
      const key = context.request?.ip || 'unknown';
      const now = Date.now();

      if (!requests.has(key)) {
        requests.set(key, []);
      }

      const userRequests = requests.get(key)!;

      // Remove old requests outside the window
      const windowStart = now - options.windowMs;
      const validRequests = userRequests.filter(time => time > windowStart);

      // Check if limit exceeded
      if (validRequests.length >= options.maxRequests) {
        throw new Error('Rate limit exceeded');
      }

      // Add current request
      validRequests.push(now);
      requests.set(key, validRequests);

      context.metadata.rateLimit = {
        remaining: options.maxRequests - validRequests.length,
        reset: now + options.windowMs
      };

      await next();
    };
  }

  // CORS middleware
  static cors(options: {
    origin?: string | string[];
    methods?: string[];
    headers?: string[];
    credentials?: boolean;
  } = {}): Middleware {
    return async (context: Context, next: () => Promise<void>) => {
      const origin = context.request?.headers?.get('Origin') || '*';

      // Set CORS headers
      if (options.origin === '*' || !options.origin) {
        context.headers.set('Access-Control-Allow-Origin', '*');
      } else {
        const allowedOrigins = Array.isArray(options.origin) ? options.origin : [options.origin];
        if (allowedOrigins.includes(origin)) {
          context.headers.set('Access-Control-Allow-Origin', origin);
        }
      }

      if (options.methods) {
        context.headers.set('Access-Control-Allow-Methods', options.methods.join(', '));
      }

      if (options.headers) {
        context.headers.set('Access-Control-Allow-Headers', options.headers.join(', '));
      }

      if (options.credentials) {
        context.headers.set('Access-Control-Allow-Credentials', 'true');
      }

      await next();
    };
  }

  // Compression middleware (simulated)
  static compression(): Middleware {
    return async (context: Context, next: () => Promise<void>) => {
      await next();

      // Check if response should be compressed
      const acceptEncoding = context.request?.headers?.get('Accept-Encoding') || '';

      if (acceptEncoding.includes('gzip') || acceptEncoding.includes('br')) {
        context.headers.set('Content-Encoding', 'gzip');
        context.metadata.compressed = true;
      }
    };
  }

  // Body parser middleware
  static bodyParser(): Middleware {
    return async (context: Context, next: () => Promise<void>) => {
      const contentType = context.request?.headers?.get('Content-Type') || '';

      if (contentType.includes('application/json')) {
        try {
          const body = context.request?.body;
          if (typeof body === 'string') {
            context.request.parsedBody = JSON.parse(body);
          }
        } catch (error) {
          throw new Error('Invalid JSON body');
        }
      }

      await next();
    };
  }

  // Validation middleware
  static validation(validator: (context: Context) => boolean | Promise<boolean>): Middleware {
    return async (context: Context, next: () => Promise<void>) => {
      const isValid = await validator(context);

      if (!isValid) {
        throw new Error('Validation failed');
      }

      await next();
    };
  }

  // Cache middleware
  static cache(cacheKey: (context: Context) => string, ttl: number = 60000): Middleware {
    const cache = new Map<string, { data: any; expiry: number }>();

    return async (context: Context, next: () => Promise<void>) => {
      const key = cacheKey(context);
      const now = Date.now();

      // Check cache
      const cached = cache.get(key);
      if (cached && cached.expiry > now) {
        context.response = cached.data;
        context.metadata.cached = true;
        return;
      }

      await next();

      // Store in cache
      if (context.response) {
        cache.set(key, {
          data: context.response,
          expiry: now + ttl
        });
      }
    };
  }
}

// 5. Middleware Composer
class MiddlewareComposer {
  private pipeline: MiddlewarePipeline;

  constructor() {
    this.pipeline = new MiddlewarePipeline();
  }

  // Add middleware
  use(middleware: Middleware): this {
    this.pipeline.use(middleware);
    return this;
  }

  // Add multiple middlewares
  useAll(middlewares: Middleware[]): this {
    middlewares.forEach(mw => this.pipeline.use(mw));
    return this;
  }

  // Execute with context
  async execute(context: Context): Promise<void> {
    return this.pipeline.execute(context);
  }

  // Create from array
  static from(middlewares: Middleware[]): MiddlewareComposer {
    const composer = new MiddlewareComposer();
    return composer.useAll(middlewares);
  }
}

// 6. Request/Response Mock
class MockRequest {
  constructor(
    public url: string,
    public method: string = 'GET',
    public headers: Map<string, string> = new Map(),
    public body?: any,
    public ip: string = '127.0.0.1'
  ) {}
}

class MockResponse {
  public status: number = 200;
  public headers: Map<string, string> = new Map();
  public body?: any;

  setStatus(code: number): this {
    this.status = code;
    return this;
  }

  setHeader(key: string, value: string): this {
    this.headers.set(key, value);
    return this;
  }

  send(data: any): this {
    this.body = data;
    return this;
  }
}

// 7. Application Builder
class ApplicationBuilder {
  private composer: MiddlewareComposer;
  private contextFactory: () => Context;

  constructor() {
    this.composer = new MiddlewareComposer();
    this.contextFactory = () => ({
      request: null,
      response: null,
      state: new Map(),
      headers: new Map(),
      metadata: {}
    });
  }

  // Set context factory
  setContextFactory(factory: () => Context): this {
    this.contextFactory = factory;
    return this;
  }

  // Add middleware
  use(middleware: Middleware): this {
    this.composer.use(middleware);
    return this;
  }

  // Handle request
  async handle(request: MockRequest): Promise<MockResponse> {
    const context = this.contextFactory();
    context.request = request;
    context.response = new MockResponse();

    await this.composer.execute(context);

    return context.response as MockResponse;
  }
}

// Usage Examples
async function demonstrateMiddleware() {
  console.log('=== Web TypeScript Middleware Examples ===\n');

  // 1. Basic middleware
  console.log('--- 1. Basic Middleware ---');
  const pipeline = new MiddlewarePipeline();

  pipeline.use(async (context, next) => {
    console.log('Middleware 1: Before');
    await next();
    console.log('Middleware 1: After');
  });

  pipeline.use(async (context, next) => {
    console.log('Middleware 2: Before');
    await next();
    console.log('Middleware 2: After');
  });

  const context1: Context = {
    request: { url: '/test' },
    response: null,
    state: new Map(),
    headers: new Map(),
    metadata: {}
  };

  await pipeline.execute(context1);

  // 2. Common middlewares
  console.log('\n--- 2. Common Middlewares ---');
  const app = new ApplicationBuilder();

  app.use(CommonMiddlewares.logging());
  app.use(CommonMiddlewares.timing());
  app.use(CommonMiddlewares.headers({
    'X-Powered-By': 'TypeScript-Middleware',
    'X-Response-Time': '0ms'
  }));

  const request = new MockRequest('/api/users', 'GET');
  request.headers.set('Accept', 'application/json');

  const response = await app.handle(request);
  console.log('Response status:', response.status);

  // 3. Error handling
  console.log('\n--- 3. Error Handling ---');
  const errorApp = new ApplicationBuilder();

  errorApp.use(CommonMiddlewares.errorHandler((error) => {
    console.error('Error caught:', error.message);
  }));

  errorApp.use(async (context, next) => {
    if (context.request?.url === '/error') {
      throw new Error('Intentional error');
    }
    await next();
  });

  await errorApp.handle(new MockRequest('/error'));

  // 4. Authentication
  console.log('\n--- 4. Authentication ---');
  const authApp = new ApplicationBuilder();

  authApp.use(CommonMiddlewares.authentication(async (context) => {
    const token = context.request?.headers?.get('Authorization');
    return token === 'Bearer valid-token';
  }));

  authApp.use(async (context, next) => {
    console.log('Authenticated successfully');
    context.response = new MockResponse().setStatus(200).send({ message: 'Success' });
    await next();
  });

  const authRequest = new MockRequest('/protected', 'GET');
  authRequest.headers.set('Authorization', 'Bearer valid-token');

  const authResponse = await authApp.handle(authRequest);
  console.log('Auth response status:', authResponse.status);

  // 5. Rate limiting
  console.log('\n--- 5. Rate Limiting ---');
  const rateLimitApp = new ApplicationBuilder();

  rateLimitApp.use(CommonMiddlewares.rateLimit({
    maxRequests: 3,
    windowMs: 10000
  }));

  rateLimitApp.use(async (context, next) => {
    context.response = new MockResponse().setStatus(200).send({ message: 'OK' });
    await next();
  });

  // Send multiple requests
  const rlRequest = new MockRequest('/api/test');
  for (let i = 0; i < 4; i++) {
    try {
      const rlResponse = await rateLimitApp.handle(rlRequest);
      console.log(`Request ${i + 1}: ${rlResponse.status}`);
    } catch (error) {
      console.log(`Request ${i + 1}: Rate limited`);
    }
  }

  // 6. Compose
  console.log('\n--- 6. Middleware Composer ---');
  const composed = MiddlewareComposer.from([
    CommonMiddlewares.logging(),
    CommonMiddlewares.timing(),
    async (context, next) => {
      console.log('Final handler');
      await next();
    }
  ]);

  const context2: Context = {
    request: { url: '/composed' },
    response: null,
    state: new Map(),
    headers: new Map(),
    metadata: {}
  };

  await composed.execute(context2);

  console.log('\n=== All Middleware Examples Completed ===');
}

// Export functions
export { MiddlewarePipeline, CommonMiddlewares, MiddlewareComposer, ApplicationBuilder, MockRequest, MockResponse };
export { demonstrateMiddleware };
export type { Context, Middleware };

💻 Статические Файлы typescript

🟡 intermediate ⭐⭐⭐

Обслуживание статических файлов с кэшированием, сжатием и определением MIME-типа

⏱️ 25 min 🏷️ typescript, web, web features
Prerequisites: Intermediate TypeScript, Fetch API, Blob
// Web TypeScript Static File Serving Examples
// Serving static files with various strategies

// 1. MIME Type Detector
class MIMETypeDetector {
  private static mimeTypes: Record<string, string> = {
    '.html': 'text/html',
    '.htm': 'text/html',
    '.css': 'text/css',
    '.js': 'text/javascript',
    '.json': 'application/json',
    '.xml': 'application/xml',
    '.txt': 'text/plain',
    '.pdf': 'application/pdf',
    '.zip': 'application/zip',
    '.png': 'image/png',
    '.jpg': 'image/jpeg',
    '.jpeg': 'image/jpeg',
    '.gif': 'image/gif',
    '.svg': 'image/svg+xml',
    '.ico': 'image/x-icon',
    '.woff': 'font/woff',
    '.woff2': 'font/woff2',
    '.ttf': 'font/ttf',
    '.eot': 'application/vnd.ms-fontobject',
    '.mp3': 'audio/mpeg',
    '.mp4': 'video/mp4',
    '.webm': 'video/webm',
    '.ogg': 'video/ogg'
  };

  // Get MIME type by extension
  static getByExtension(filename: string): string {
    const ext = filename.substring(filename.lastIndexOf('.')).toLowerCase();

    if (ext && this.mimeTypes[ext]) {
      return this.mimeTypes[ext];
    }

    return 'application/octet-stream';
  }

  // Get MIME type by file signature (magic bytes)
  static async getBySignature(file: Blob): Promise<string> {
    const header = await this.readFileHeader(file);

    // Check common signatures
    if (this.startsWith(header, [0x89, 0x50, 0x4E, 0x47])) return 'image/png';
    if (this.startsWith(header, [0xFF, 0xD8, 0xFF])) return 'image/jpeg';
    if (this.startsWith(header, [0x47, 0x49, 0x46, 0x38])) return 'image/gif';
    if (this.startsWith(header, [0x50, 0x4B, 0x03, 0x04])) return 'application/zip';
    if (this.startsWith(header, [0x25, 0x50, 0x44, 0x46])) return 'application/pdf';
    if (this.startsWith(header, [0x47, 0x49, 0x46, 0x38, 0x39, 0x61])) return 'image/gif';

    return 'application/octet-stream';
  }

  // Read file header
  private static async readFileHeader(file: Blob, bytes: number = 8): Promise<Uint8Array> {
    const slice = file.slice(0, bytes);
    const arrayBuffer = await slice.arrayBuffer();
    return new Uint8Array(arrayBuffer);
  }

  // Check if header starts with bytes
  private static startsWith(header: Uint8Array, bytes: number[]): boolean {
    if (header.length < bytes.length) return false;

    for (let i = 0; i < bytes.length; i++) {
      if (header[i] !== bytes[i]) return false;
    }

    return true;
  }
}

// 2. Static File Server
class StaticFileServer {
  private baseUrl: string;
  private cache: Map<string, { data: any; mime: string; timestamp: number }> = new Map();
  private cacheTimeout: number = 60000; // 1 minute

  constructor(baseUrl: string = '/static') {
    this.baseUrl = baseUrl;
  }

  // Serve file
  async serve(filePath: string): Promise<{
    data: any;
    mime: string;
    status: number;
  }> {
    // Check cache
    const cached = this.cache.get(filePath);

    if (cached && Date.now() - cached.timestamp < this.cacheTimeout) {
      return {
        data: cached.data,
        mime: cached.mime,
        status: 200
      };
    }

    // Fetch file
    const url = `${this.baseUrl}${filePath}`;

    try {
      const response = await fetch(url);

      if (!response.ok) {
        return {
          data: null,
          mime: 'text/plain',
          status: response.status
        };
      }

      const mime = response.headers.get('Content-Type') ||
                   MIMETypeDetector.getByExtension(filePath);

      let data: any;

      const contentType = mime.split(';')[0];

      if (contentType.includes('application/json')) {
        data = await response.json();
      } else if (contentType.includes('text/')) {
        data = await response.text();
      } else {
        data = await response.blob();
      }

      // Cache response
      this.cache.set(filePath, {
        data,
        mime,
        timestamp: Date.now()
      });

      return { data, mime, status: 200 };
    } catch (error) {
      return {
        data: null,
        mime: 'text/plain',
        status: 500
      };
    }
  }

  // Serve with fallback
  async serveWithFallback(filePath: string, fallbackPath: string): Promise<{
    data: any;
    mime: string;
    status: number;
  }> {
    const result = await this.serve(filePath);

    if (result.status === 404) {
      return this.serve(fallbackPath);
    }

    return result;
  }

  // Clear cache
  clearCache(): void {
    this.cache.clear();
  }

  // Clear specific cache entry
  clearCacheEntry(filePath: string): void {
    this.cache.delete(filePath);
  }

  // Get cache size
  getCacheSize(): number {
    return this.cache.size;
  }
}

// 3. File Cache Manager
class FileCacheManager {
  private cache: Map<string, {
    content: any;
    mimeType: string;
    etag: string;
    lastModified: string;
  }> = new Map();

  // Add to cache
  set(filePath: string, content: any, mimeType: string, etag?: string, lastModified?: string): void {
    this.cache.set(filePath, {
      content,
      mimeType,
      etag: etag || this.generateETag(content),
      lastModified: lastModified || new Date().toUTCString()
    });
  }

  // Get from cache
  get(filePath: string): {
    content: any;
    mimeType: string;
    etag: string;
    lastModified: string;
  } | null {
    return this.cache.get(filePath) || null;
  }

  // Check if file is cached
  has(filePath: string): boolean {
    return this.cache.has(filePath);
  }

  // Remove from cache
  remove(filePath: string): void {
    this.cache.delete(filePath);
  }

  // Clear all cache
  clear(): void {
    this.cache.clear();
  }

  // Get cache stats
  getStats(): {
    size: number;
    entries: number;
    keys: string[];
  } {
    return {
      size: this.cache.size,
      entries: this.cache.size,
      keys: Array.from(this.cache.keys())
    };
  }

  // Generate ETag
  private generateETag(content: any): string {
    const str = typeof content === 'string' ? content : JSON.stringify(content);
    let hash = 0;

    for (let i = 0; i < str.length; i++) {
      const char = str.charCodeAt(i);
      hash = ((hash << 5) - hash) + char;
      hash = hash & hash;
    }

    return `"${hash.toString(16)}"`;
  }
}

// 4. Asset Preloader
class AssetPreloader {
  private loadedAssets: Set<string> = new Set();
  private failedAssets: Set<string> = new Set();

  // Preload single asset
  async preload(url: string): Promise<{
    success: boolean;
    error?: Error;
  }> {
    if (this.loadedAssets.has(url)) {
      return { success: true };
    }

    if (this.failedAssets.has(url)) {
      return { success: true, error: new Error('Previously failed to load') };
    }

    try {
      const response = await fetch(url);

      if (!response.ok) {
        throw new Error(`HTTP ${response.status}`);
      }

      this.loadedAssets.add(url);
      return { success: true };
    } catch (error) {
      this.failedAssets.add(url);
      return { success: false, error: error as Error };
    }
  }

  // Preload multiple assets
  async preloadMultiple(urls: string[]): Promise<{
    successful: string[];
    failed: Array<{ url: string; error: Error }>;
  }> {
    const results = await Promise.allSettled(
      urls.map(url => this.preload(url))
    );

    const successful: string[] = [];
    const failed: Array<{ url: string; error: Error }> = [];

    results.forEach((result, index) => {
      if (result.status === 'fulfilled' && result.value.success) {
        successful.push(urls[index]);
      } else {
        const error = result.status === 'rejected'
          ? result.reason
          : (result.value as any).error;

        failed.push({ url: urls[index], error });
      }
    });

    return { successful, failed };
  }

  // Preload image
  preloadImage(url: string): Promise<HTMLImageElement> {
    return new Promise((resolve, reject) => {
      const img = new Image();

      img.onload = () => {
        this.loadedAssets.add(url);
        resolve(img);
      };

      img.onerror = () => {
        this.failedAssets.add(url);
        reject(new Error('Failed to load image'));
      };

      img.src = url;
    });
  }

  // Preload images
  async preloadImages(urls: string[]): Promise<HTMLImageElement[]> {
    const results = await Promise.allSettled(
      urls.map(url => this.preloadImage(url))
    );

    return results
      .filter((result): result is PromiseFulfilledResult<HTMLImageElement> =>
        result.status === 'fulfilled'
      )
      .map(result => result.value);
  }

  // Get loaded assets
  getLoadedAssets(): string[] {
    return Array.from(this.loadedAssets);
  }

  // Get failed assets
  getFailedAssets(): string[] {
    return Array.from(this.failedAssets);
  }

  // Clear tracking
  clear(): void {
    this.loadedAssets.clear();
    this.failedAssets.clear();
  }
}

// 5. Compression Handler
class CompressionHandler {
  // Check if compression is supported
  static isSupported(): boolean {
    return 'CompressionStream' in window;
  }

  // Compress data (if supported)
  static async compress(data: string, format: 'gzip' | 'deflate' = 'gzip'): Promise<Uint8Array> {
    if (!this.isSupported()) {
      throw new Error('Compression not supported');
    }

    const stream = new CompressionStream(format);
    const writer = stream.writable.getWriter();
    const encoder = new TextEncoder();

    await writer.write(encoder.encode(data));
    await writer.close();

    const compressed = new ReadableStream({
      start(controller) {
        // Would need to implement proper stream reading
        controller.close();
      }
    });

    // This is a simplified version
    return encoder.encode(data);
  }

  // Get best compression format
  static getBestCompression(acceptEncoding: string): 'gzip' | 'deflate' | 'none' {
    if (acceptEncoding.includes('br')) return 'gzip';
    if (acceptEncoding.includes('gzip')) return 'gzip';
    if (acceptEncoding.includes('deflate')) return 'deflate';
    return 'none';
  }
}

// 6. CDN Manager
class CDNManager {
  private cdnUrls: string[] = [];
  private currentCdnIndex: number = 0;

  constructor(cdnUrls: string[]) {
    this.cdnUrls = cdnUrls;
  }

  // Get CDN URL for asset
  getCdnUrl(assetPath: string): string {
    const cdnUrl = this.cdnUrls[this.currentCdnIndex];
    return `${cdnUrl}${assetPath}`;
  }

  // Rotate CDN
  rotateCdn(): void {
    this.currentCdnIndex = (this.currentCdnIndex + 1) % this.cdnUrls.length;
  }

  // Get all CDN URLs for asset
  getAllCdnUrls(assetPath: string): string[] {
    return this.cdnUrls.map(cdn => `${cdn}${assetPath}`);
  }

  // Try each CDN until one succeeds
  async tryAllCdns(assetPath: string): Promise<{
    success: boolean;
    cdnUrl?: string;
    data?: any;
  }> {
    const urls = this.getAllCdnUrls(assetPath);

    for (const url of urls) {
      try {
        const response = await fetch(url);

        if (response.ok) {
          const data = await response.blob();
          return { success: true, cdnUrl: url, data };
        }
      } catch (error) {
        console.warn(`CDN failed: ${url}`, error);
      }
    }

    return { success: false };
  }
}

// 7. Asset Bundle Manager
class AssetBundleManager {
  private bundles: Map<string, string[]> = new Map();

  // Register bundle
  registerBundle(name: string, assets: string[]): void {
    this.bundles.set(name, assets);
  }

  // Get bundle assets
  getBundle(name: string): string[] | null {
    return this.bundles.get(name) || null;
  }

  // Preload bundle
  async preloadBundle(name: string, preloader: AssetPreloader): Promise<{
    successful: string[];
    failed: Array<{ url: string; error: Error }>;
  }> {
    const assets = this.getBundle(name);

    if (!assets) {
      return { successful: [], failed: [] };
    }

    return preloader.preloadMultiple(assets);
  }

  // Get bundle URL (for script/link tags)
  getBundleHtml(name: string, type: 'script' | 'style'): string {
    const assets = this.getBundle(name);

    if (!assets) {
      return '';
    }

    return assets.map(url => {
      if (type === 'script') {
        return `<script src="${url}"></script>`;
      } else {
        return `<link rel="stylesheet" href="${url}">`;
      }
    }).join('\n');
  }
}

// Usage Examples
async function demonstrateStaticFiles() {
  console.log('=== Web TypeScript Static File Serving Examples ===\n');

  // 1. MIME type detection
  console.log('--- 1. MIME Type Detection ---');
  console.log('HTML:', MIMETypeDetector.getByExtension('index.html'));
  console.log('JavaScript:', MIMETypeDetector.getByExtension('app.js'));
  console.log('PNG:', MIMETypeDetector.getByExtension('image.png'));

  // 2. Static file server
  console.log('\n--- 2. Static File Server ---');
  const server = new StaticFileServer('/assets');

  // Note: These would make actual HTTP requests in a real environment
  console.log('Would serve:', server.baseUrl + '/styles/main.css');
  console.log('Would serve:', server.baseUrl + '/js/app.js');

  // 3. File cache manager
  console.log('\n--- 3. File Cache Manager ---');
  const cacheManager = new FileCacheManager();

  cacheManager.set('/index.html', '<html>...</html>', 'text/html', '"abc123"');
  cacheManager.set('/app.js', 'console.log("Hello");', 'text/javascript');

  console.log('Cache stats:', cacheManager.getStats());

  const cached = cacheManager.get('/index.html');
  console.log('Cached file:', cached?.mimeType);

  // 4. Asset preloader
  console.log('\n--- 4. Asset Preloader ---');
  const preloader = new AssetPreloader();

  // Note: These would be real URLs in production
  const preloadResults = await preloader.preloadMultiple([
    '/assets/image1.png',
    '/assets/image2.png',
    '/assets/image3.png'
  ]);

  console.log('Preloaded:', preloadResults.successful);
  console.log('Failed:', preloadResults.failed);

  // 5. CDN manager
  console.log('\n--- 5. CDN Manager ---');
  const cdnManager = new CDNManager([
    'https://cdn1.example.com',
    'https://cdn2.example.com',
    'https://cdn3.example.com'
  ]);

  console.log('CDN URL:', cdnManager.getCdnUrl('/assets/app.js'));

  cdnManager.rotateCdn();
  console.log('Rotated CDN URL:', cdnManager.getCdnUrl('/assets/app.js'));

  // 6. Asset bundle manager
  console.log('\n--- 6. Asset Bundle Manager ---');
  const bundleManager = new AssetBundleManager();

  bundleManager.registerBundle('vendor', [
    '/assets/js/vendor/react.js',
    '/assets/js/vendor/lodash.js'
  ]);

  bundleManager.registerBundle('app', [
    '/assets/js/app.js',
    '/assets/js/utils.js'
  ]);

  const bundle = bundleManager.getBundle('vendor');
  console.log('Vendor bundle:', bundle);

  console.log('\n=== All Static File Serving Examples Completed ===');
}

// Export functions
export { MIMETypeDetector, StaticFileServer, FileCacheManager, AssetPreloader, CompressionHandler, CDNManager, AssetBundleManager };
export { demonstrateStaticFiles };