{"version":3,"sources":["../../../../src/lib/A-Request/A-Request.helper.ts"],"names":[],"mappings":"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","file":"A-Request.helper.mjs","sourcesContent":["import { A_HttpServerRequestContext } from \"./A-HttpServerRequest.context\";\nimport { A_RequestError } from \"./A-Request.error\";\nimport { A_Request_BodyType, A_Request_FileUpload } from \"./A-Request.types\";\n\n/**\n * A_RequestHelper - Stateless utility class for HTTP request processing\n * \n * This helper class provides static methods for parsing and processing HTTP request data:\n * - URL parameter extraction from route patterns\n * - Query string parsing\n * - Request body parsing (JSON, form-data, multipart, raw)\n * - URL-encoded form data parsing\n * - Multipart form data parsing (including file uploads)\n * \n * All methods are stateless and only depend on input parameters.\n * \n * @example\n * ```typescript\n * // Extract URL parameters\n * const params = A_RequestHelper.extractParams('/users/123', '/users/:id');\n * // { id: '123' }\n * \n * // Parse query string\n * const query = A_RequestHelper.extractQuery('/api/users?page=1&limit=10');\n * // { page: '1', limit: '10' }\n * \n * // Parse request body\n * const result = await A_RequestHelper.parseRequestBody(req, { maxBodySize: 1024 * 1024 });\n * // { data: {...}, type: 'json' }\n * ```\n */\nexport class A_RequestHelper {\n\n    /**\n     * Extract URL parameters from a URL using a route pattern\n     * @param url - The actual URL to extract parameters from\n     * @param routePattern - The route pattern with parameters (e.g., \"/users/:id/posts/:postId\")\n     * @returns Object containing extracted parameters\n     */\n    static extractParams(url: string, routePattern: string): Record<string, string> {\n        // Remove query string (anything after ?)\n        const cleanUrl = url.split('?')[0];\n        const cleanPattern = routePattern.split('?')[0];\n\n        const urlSegments = cleanUrl.split('/').filter(Boolean);\n        const patternSegments = cleanPattern.split('/').filter(Boolean);\n\n        const params: Record<string, string> = {};\n\n        for (let i = 0; i < patternSegments.length; i++) {\n            const patternSegment = patternSegments[i];\n            const urlSegment = urlSegments[i];\n\n            if (patternSegment.startsWith(':')) {\n                const paramName = patternSegment.slice(1); // Remove ':' from pattern\n                if (urlSegment) {\n                    params[paramName] = decodeURIComponent(urlSegment);\n                }\n            } else if (patternSegment !== urlSegment) {\n                // If static segments don't match → fail\n                return {};\n            }\n        }\n\n        return params;\n    }\n\n\n\n    /**\n     * Extract query parameters from URL\n     * @param url - The URL to extract query parameters from\n     * @returns Object containing query parameters\n     */\n    static extractQuery<T = Record<string, string>>(url: string): T {\n        const query: Record<string, string> = {};\n\n        // Take only the part after \"?\"\n        const queryString = url.split('?')[1];\n        if (!queryString) return query as T;\n\n        // Remove fragment (#...) if present\n        const cleanQuery = queryString.split('#')[0];\n\n        // Split into key=value pairs\n        for (const pair of cleanQuery.split('&')) {\n            if (!pair) continue;\n            const [key, value = ''] = pair.split('=');\n            query[decodeURIComponent(key)] = decodeURIComponent(value);\n        }\n\n        return query as T;\n    }\n\n\n    /**\n     * Parse cookies from Cookie header\n     */\n    static parseCookies(\n        cookieHeader?: string\n    ): Record<string, string> {\n        const cookies: Record<string, string> = {};\n\n        if (!cookieHeader) return {};\n\n        cookieHeader.split(';').forEach(cookie => {\n            const [name, ...rest] = cookie.trim().split('=');\n            const value = rest.join('=');\n            if (name && value) {\n                cookies[name] = decodeURIComponent(value);\n            }\n        });\n\n        return cookies;\n    }\n\n    /**\n     * Parse request body based on Content-Type\n     * \n     * \n     * @param context - The HTTP server request context\n     * @returns Parsed body data and detected body type\n     */\n    static parseRequestBody<T extends any = any>(\n        context: A_HttpServerRequestContext\n    ): {\n        data: T;\n        type: A_Request_BodyType;\n    } {\n        let parsedBody: any;\n        let bodyType: A_Request_BodyType;\n\n        switch (true) {\n            case !!context.contentType && context.contentType.includes('application/json'):\n                parsedBody = JSON.parse(context.data || '{}');\n                bodyType = 'json';\n                break;\n            case !!context.contentType && context.contentType.includes('application/x-www-form-urlencoded'):\n                parsedBody = A_RequestHelper.parseFormUrlEncoded(context.data) as T;\n                bodyType = 'form';\n                break;\n            case !!context.contentType && context.contentType.includes('multipart/form-data'):\n                const multipartResult = A_RequestHelper.parseMultipartData(Buffer.concat(context.buffers), context.contentType);\n                // Return the entire multipart result (fields + files) as T\n                parsedBody = {\n                    ...multipartResult.fields,\n                    _files: multipartResult.files\n                } as T;\n                bodyType = 'multipart';\n                break;\n            case !!context.contentType && context.contentType.includes('text/'):\n                parsedBody = context.data as T;\n                bodyType = 'text';\n                break;\n            default:\n                parsedBody = Buffer.concat(context.buffers) as T;\n                bodyType = 'raw';\n                break;\n        }\n\n\n        return {\n            data: parsedBody,\n            type: bodyType\n        };\n    }\n\n    /**\n     * Parse URL-encoded form data (application/x-www-form-urlencoded)\n     * @param body - The URL-encoded body string\n     * @returns Object containing form data\n     */\n    static parseFormUrlEncoded(\n        /**\n         * The URL-encoded body string\n         */\n        body: string\n    ): Record<string, string | string[]> {\n        const result: Record<string, string | string[]> = {};\n\n        if (!body) return result;\n\n        const pairs = body.split('&');\n\n        for (const pair of pairs) {\n            if (!pair) continue;\n\n            const [key, value = ''] = pair.split('=');\n            const decodedKey = decodeURIComponent(key.replace(/\\+/g, ' '));\n            const decodedValue = decodeURIComponent(value.replace(/\\+/g, ' '));\n\n            // Handle multiple values for the same key (arrays)\n            if (decodedKey in result) {\n                const existing = result[decodedKey];\n                if (Array.isArray(existing)) {\n                    existing.push(decodedValue);\n                } else {\n                    result[decodedKey] = [existing, decodedValue];\n                }\n            } else {\n                result[decodedKey] = decodedValue;\n            }\n        }\n\n        return result;\n    }\n\n    /**\n     * Parse multipart form data (for file uploads and form data)\n     * @param buffer - The raw buffer containing multipart data\n     * @param contentType - The content type header\n     * @returns Object containing fields and files\n     */\n    static parseMultipartData(\n        /**\n         * The raw buffer containing multipart data\n         */\n        buffer: Buffer,\n        /**\n         * The content type header\n         */\n        contentType: string\n    ): {\n        fields: Record<string, string>;\n        files: A_Request_FileUpload[];\n    } {\n        // Extract boundary\n        const boundaryMatch = contentType.match(/boundary=(.+)/);\n        if (!boundaryMatch) {\n            throw new A_RequestError(\n                A_RequestError.RequestBodyParsingError,\n                'Missing boundary in multipart/form-data content type'\n            )\n        }\n\n        const boundary = '--' + boundaryMatch[1];\n        const textData = buffer.toString();\n        const parts = textData.split(boundary).filter(part => part.trim() && !part.includes('--'));\n\n        const fields: Record<string, string> = {};\n        const files: A_Request_FileUpload[] = [];\n\n        parts.forEach(part => {\n            const [headers, content] = part.split('\\r\\n\\r\\n');\n            if (!headers || content === undefined) return;\n\n            const nameMatch = headers.match(/name=\"([^\"]+)\"/);\n            const filenameMatch = headers.match(/filename=\"([^\"]+)\"/);\n            const contentTypeMatch = headers.match(/Content-Type: ([^\\r\\n]+)/);\n\n            if (nameMatch) {\n                const fieldName = nameMatch[1];\n                const cleanContent = content.replace(/\\r\\n$/, '');\n\n                if (filenameMatch) {\n                    // It's a file upload\n                    const file: A_Request_FileUpload = {\n                        fieldName,\n                        filename: filenameMatch[1],\n                        encoding: 'binary',\n                        mimetype: contentTypeMatch?.[1] || 'application/octet-stream',\n                        size: Buffer.byteLength(cleanContent),\n                        buffer: Buffer.from(cleanContent, 'binary')\n                    };\n                    files.push(file);\n                } else {\n                    // It's a regular field\n                    fields[fieldName] = cleanContent;\n                }\n            }\n        });\n\n        return { fields, files };\n    }\n\n}"]}