All files / src/utils middleware.ts

42.59% Statements 23/54
100% Branches 7/7
75% Functions 3/4
42.59% Lines 23/54

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119  1x                                                       1x         1x 13x 13x                                                 1x                                                                               1x 5x 5x 5x 10x 7x 5x 5x 7x 7x 10x 3x 3x 10x 5x 5x 5x  
import type { IncomingMessage, ServerResponse } from 'node:http';
import send from 'send';
import type { Esmx } from '../core';
 
/**
 * Middleware function type definition
 *
 * @description
 * Middleware is a function used to handle HTTP requests. It receives the request object, response object, and the next middleware function as parameters.
 * Middleware can perform the following operations:
 * - Modify request and response objects
 * - End the request-response cycle
 * - Call the next middleware
 *
 * @example
 * ```ts
 * // Create a simple logging middleware
 * const loggerMiddleware: Middleware = (req, res, next) => {
 *   console.log(`${req.method} ${req.url}`);
 *   next();
 * };
 * ```
 */
export type Middleware = (
    req: IncomingMessage,
    res: ServerResponse,
    next: Function
) => void;
 
const reFinal = /\.final\.[a-zA-Z0-9]+$/;
/**
 * Determine if a file path is an immutable file that complies with esmx specifications
 * @param path File path
 */
export function isImmutableFile(filename: string) {
    return reFinal.test(filename);
}
 
/**
 * Create middleware for Esmx application
 *
 * @param esmx - Esmx instance
 * @returns Returns a middleware that handles static resources
 *
 * @description
 * This function creates a middleware to handle static resource requests for modules. It will:
 * - Create corresponding static resource middleware based on module configuration
 * - Handle cache control for resources
 * - Support long-term caching for immutable files
 *
 * @example
 * ```ts
 * import { Esmx, createMiddleware } from '@esmx/core';
 *
 * const esmx = new Esmx();
 * const middleware = createMiddleware(esmx);
 *
 * // Use in HTTP server
 * server.use(middleware);
 * ```
 */
export function createMiddleware(esmx: Esmx): Middleware {
    const middlewares = Object.values(esmx.moduleConfig.links).map(
        (item): Middleware => {
            const base = `/${item.name}/`;
            const baseUrl = new URL(`file:`);
            const root = item.client;
            return (req, res, next) => {
                const url = req.url ?? '/';
                const { pathname } = new URL(req.url ?? '/', baseUrl);
 
                if (!url.startsWith(base) || req.method !== 'GET') {
                    next();
                    return;
                }
 
                send(req, pathname.substring(base.length - 1), {
                    root
                })
                    .on('headers', () => {
                        if (isImmutableFile(pathname)) {
                            res.setHeader(
                                'cache-control',
                                'public, max-age=31536000, immutable'
                            );
                        } else {
                            res.setHeader('cache-control', 'no-cache');
                        }
                    })
                    .pipe(res);
            };
        }
    );
    return mergeMiddlewares(middlewares);
}
 
/**
 * Merge multiple middlewares into one middleware execution
 * @param middlewares List of middlewares
 * @returns
 */
export function mergeMiddlewares(middlewares: Middleware[]): Middleware {
    return (req, res, next) => {
        let index = 0;
        function dispatch() {
            if (index < middlewares.length) {
                middlewares[index](req, res, () => {
                    index++;
                    dispatch();
                });
                return;
            } else {
                next();
            }
        }
        dispatch();
    };
}