/** * Builds a URL-encoded payload from various data formats * * This function handles multiple input formats: * - Array of objects with 'name' and 'value' properties (preserves order) * * @param data The request body data * @returns URL-encoded string suitable for application/x-www-form-urlencoded content type * * @example * // Array format (preserves order) * buildFormUrlEncodedPayload([{name: 'a', value: '1'}, {name: 'b', value: '2'}]) * // Returns: 'a=1&b=2' */ export const buildFormUrlEncodedPayload = (params: Array<{ name: string; value: string | number | boolean | undefined }>): string => { // Ensure params is iterable (array) if (!Array.isArray(params)) { return ''; } const resultParams = new URLSearchParams(); for (const param of params) { // Invalid items are ignored if (typeof param !== 'object' || param === null) continue; if (!('name' in param)) continue; // Append parameter with value (default to empty string if undefined/null) resultParams.append(param.name, String(param.value ?? '')); } return resultParams.toString(); }; /** * Determines if the given object is a FormData instance. * Supports native FormData (Node 18+, browser) and the 'form-data' npm package. * @param obj - Object to check. * @returns True if obj is a FormData instance, false otherwise. */ export const isFormData = (obj: unknown): boolean => { // Check constructor name (works for both native FormData and form-data npm package) // todo: checking constructor.name can produce false positives for objects that have a constructor.name property set to 'FormData', but this is rare. return obj?.constructor?.name === 'FormData'; }; /** * Extracts boundary parameter from a Content-Type header value. * @param contentType - The Content-Type header value (e.g., "multipart/mixed; boundary=my-boundary") * @returns The boundary value if found, or null if not present */ export const extractBoundaryFromContentType = (contentType: unknown): string | null => { if (typeof contentType !== 'string') { return null; } const match = contentType.match(/boundary="([^"]+)"|boundary=([^;\s]+)/i); return match ? (match[1] || match[2]) : null; };