Files
bruno/packages/bruno-electron/src/utils/common.js
lohit 97c700beba fix: update logic for checking formdata instances (#6643)
* fix: update logic for checking formdata instance

* fix: isFormData logic update

* fix: review comment fix, add isFormData to @usebruno/common package

* fix: review comment fix
2026-01-04 21:27:07 +05:30

165 lines
4.6 KiB
JavaScript

const { customAlphabet } = require('nanoid');
const iconv = require('iconv-lite');
const { cloneDeep } = require('lodash');
const { formatMultipartData } = require('./form-data');
const { isFormData } = require('@usebruno/common').utils;
// a customized version of nanoid without using _ and -
const uuid = () => {
// https://github.com/ai/nanoid/blob/main/url-alphabet/index.js
const urlAlphabet = 'useandom26T198340PX75pxJACKVERYMINDBUSHWOLFGQZbfghjklqvwyzrict';
const customNanoId = customAlphabet(urlAlphabet, 21);
return customNanoId();
};
const stringifyJson = async (str) => {
try {
return JSON.stringify(str, null, 2);
} catch (err) {
return Promise.reject(err);
}
};
const parseJson = async (obj) => {
try {
return JSON.parse(obj);
} catch (err) {
return Promise.reject(err);
}
};
const getCircularReplacer = () => {
const seen = new WeakSet();
return (key, value) => {
if (typeof value === 'object' && value !== null) {
if (seen.has(value)) return '[Circular]';
seen.add(value);
}
return value;
};
};
const safeStringifyJSON = (data, indent = null) => {
if (data === undefined) return undefined;
try {
// getCircularReplacer - removes circular references that cause an error when stringifying
return JSON.stringify(data, getCircularReplacer(), indent);
} catch (e) {
console.warn('Failed to stringify data:', e.message);
return data;
}
};
const safeParseJSON = (data) => {
try {
return JSON.parse(data);
} catch (e) {
return data;
}
};
const simpleHash = (str) => {
let hash = 0;
for (let i = 0; i < str.length; i++) {
const char = str.charCodeAt(i);
hash = (hash << 5) - hash + char;
hash &= hash; // Convert to 32bit integer
}
return new Uint32Array([hash])[0].toString(36);
};
const generateUidBasedOnHash = (str) => {
const hash = simpleHash(str);
return `${hash}`.padEnd(21, '0');
};
const flattenDataForDotNotation = (data) => {
var result = {};
function recurse(current, prop) {
if (Object(current) !== current) {
result[prop] = current;
} else if (Array.isArray(current)) {
for (var i = 0, l = current.length; i < l; i++) {
recurse(current[i], prop + '[' + i + ']');
}
if (l == 0) {
result[prop] = [];
}
} else {
var isEmpty = true;
for (var p in current) {
isEmpty = false;
recurse(current[p], prop ? prop + '.' + p : p);
}
if (isEmpty && prop) {
result[prop] = {};
}
}
}
recurse(data, '');
return result;
};
const parseDataFromResponse = (response, disableParsingResponseJson = false) => {
// Parse the charset from content type: https://stackoverflow.com/a/33192813
const charsetMatch = /charset=([^()<>@,;:"/[\]?.=\s]*)/i.exec(response.headers['content-type'] || '');
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp/exec#using_exec_with_regexp_literals
const charsetValue = charsetMatch?.[1];
const dataBuffer = Buffer.from(response.data);
// Overwrite the original data for backwards compatibility
let data;
if (iconv.encodingExists(charsetValue)) {
data = iconv.decode(dataBuffer, charsetValue);
} else {
data = iconv.decode(dataBuffer, 'utf-8');
}
// Try to parse response to JSON, this can quietly fail
try {
// Filter out ZWNBSP character
// https://gist.github.com/antic183/619f42b559b78028d1fe9e7ae8a1352d
data = data.replace(/^\uFEFF/, '');
if (!disableParsingResponseJson) {
data = JSON.parse(data);
}
} catch { }
return { data, dataBuffer };
};
const parseDataFromRequest = (request) => {
let requestDataString;
// File uploads are redacted, multipart FormData is formatted from original data for readability, and other types are stringified as-is.
if (request.mode === 'file') {
requestDataString = '<request body redacted>';
} else if (isFormData(request?.data) && Array.isArray(request._originalMultipartData)) {
const boundary = request.data._boundary || 'boundary';
requestDataString = formatMultipartData(request._originalMultipartData, boundary);
} else {
requestDataString = typeof request?.data === 'string' ? request?.data : safeStringifyJSON(request?.data);
}
const requestCopy = cloneDeep(request);
if (!requestCopy.data) {
return { data: null, dataBuffer: null };
}
requestCopy.data = requestDataString;
return parseDataFromResponse(requestCopy);
};
module.exports = {
uuid,
stringifyJson,
parseJson,
safeStringifyJSON,
safeParseJSON,
simpleHash,
generateUidBasedOnHash,
flattenDataForDotNotation,
parseDataFromResponse,
parseDataFromRequest
};