Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import { humanizeRequestBodyMode } from 'utils/collections';
import StyledWrapper from './StyledWrapper';
import { updateRequestBody } from 'providers/ReduxStore/slices/collections/index';
import { toastError } from 'utils/common/error';
import fastJsonFormat from 'fast-json-format';
import { prettifyJsonString } from 'utils/common/index';
import xmlFormat from 'xml-formatter';

const RequestBodyMode = ({ item, collection }) => {
Expand Down Expand Up @@ -39,7 +39,7 @@ const RequestBodyMode = ({ item, collection }) => {
const onPrettify = () => {
if (body?.json && bodyMode === 'json') {
try {
const prettyBodyJson = fastJsonFormat(body.json);
const prettyBodyJson = prettifyJsonString(body.json);
dispatch(
updateRequestBody({
content: prettyBodyJson,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import React, { useState } from 'react';
import { useDispatch, useSelector } from 'react-redux';
import { autoDetectLang } from 'utils/codemirror/lang-detect';
import { toastError } from 'utils/common/error';
import fastJsonFormat from 'fast-json-format';
import { prettifyJsonString } from 'utils/common/index';
import xmlFormat from 'xml-formatter';
import WSRequestBodyMode from '../BodyMode/index';

Expand Down Expand Up @@ -105,7 +105,7 @@ export const SingleWSMessage = ({
const onPrettify = () => {
if (codeType === 'json') {
try {
const prettyBodyJson = fastJsonFormat(content);
const prettyBodyJson = prettifyJsonString(content);
const currentMessages = [...(body.ws || [])];
currentMessages[index] = {
...currentMessages[index],
Expand Down
17 changes: 16 additions & 1 deletion packages/bruno-app/src/utils/common/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { customAlphabet } from 'nanoid';
import xmlFormat from 'xml-formatter';
import { JSONPath } from 'jsonpath-plus';
import fastJsonFormat from 'fast-json-format';
import { patternHasher } from '@usebruno/common/utils';

// a customized version of nanoid without using _ and -
export const uuid = () => {
Expand Down Expand Up @@ -293,7 +294,7 @@ export const formatResponse = (data, dataBufferString, mode, filter, bufferThres
}

try {
return fastJsonFormat(rawData);
return prettifyJsonString(rawData);
} catch (error) {}

if (typeof data === 'string') {
Expand Down Expand Up @@ -322,3 +323,17 @@ export const formatResponse = (data, dataBufferString, mode, filter, bufferThres

return safeStringifyJSON(data, !isVeryLargeResponse);
};

export const prettifyJsonString = (jsonDataString) => {
if (typeof jsonDataString !== 'string') return jsonDataString;
try {
const { hashed, restore } = patternHasher(jsonDataString);
const formattedJsonDataStringHashed = fastJsonFormat(hashed);
const formattedJsonDataString = restore(formattedJsonDataStringHashed);
return formattedJsonDataString;
} catch (error) {
console.log('error formatting json data!');
console.error(error);
}
return jsonDataString;
};
96 changes: 95 additions & 1 deletion packages/bruno-app/src/utils/common/index.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,8 @@ import {
humanizeDate,
relativeDate,
getContentType,
formatSize
formatSize,
prettifyJsonString
} from './index';

describe('common utils', () => {
Expand Down Expand Up @@ -191,4 +192,97 @@ describe('common utils', () => {
expect(formatSize(NaN)).toBe('0B');
});
});

describe('prettifyJsonString', () => {
test('should return non-string inputs unchanged', () => {
expect(prettifyJsonString(null)).toBe(null);
expect(prettifyJsonString(undefined)).toBe(undefined);
expect(prettifyJsonString(123)).toBe(123);
expect(prettifyJsonString([])).toEqual([]);
expect(prettifyJsonString({})).toEqual({});
expect(prettifyJsonString(true)).toBe(true);
});

test('should format valid JSON without Bruno variables', () => {
const input = '{"name":"John","age":30}';
const expected = `{\n "name": "John",\n "age": 30\n}`;
console.log(prettifyJsonString(input));
expect(prettifyJsonString(input)).toBe(expected);
});

test('should format valid JSON with Bruno variables', () => {
const input = '{"name": {{userName}}}';
const expected = `{\n "name": {{userName}}\n}`;
console.log(prettifyJsonString(input));
expect(prettifyJsonString(input)).toBe(expected);
});

test('should format complex json string', () => {
const input = `{"id": 123456789123456789123456789,"name": "Test 'JSON' Data with "quotes" — Pretty Print ","active": true,"price": 199.9999999,"decimals": 1.00,"nullValue": null,"unicodeText": "こんにちは世界 ","escapedCharacters": "Line1\nLine2\tTabbed\"Quoted\" and 'single quoted' with 'code' style","nestedObject": { "level1": { "level2": { "emptyArray": [], "specialChars": "@#$%^&*()_+-=[]{}|;':,./<>?~", "booleanValues": [ true, false, true ], "numbers": [ 0, -1, 1.23e10, 3.1415926535 ] } }},"mixedArray": [ "string with 'apostrophe'", 42, false, null, { "innerObj": { "keyWithQuotes": "value containing \`backticks\` and 'single quotes'", "nestedArray": [ { "a": "O'Reilly" }{ "b": "'inline code'" }, [ "deep", "array", { "c": "contains 'quotes'" } ] ] } }],"nonStringVariable": {{nonStringVar}},"withBrunoVariable": "{{string}} '{{with}}' "{{variety}}" of '{{variables}}'","dateExample": "2025-11-07T12:34:56Z","regexExample": "^([a-z0-9_\.-]+)@([\da-z\.-]+)\.([a-z\.]{2,6})$","urls": { "website": "https://example.com?param='value'&flag='true'", "escapedURL": "https:\/\/escaped-url.com\/path\?q='search'\&debug='on'"},"multiLineString": "This is a long text\nthat spans multiple\nlines with \`backticks\` 'quotes' and 'code' snippets "}`;
const expectedOutput = `{
"id": 123456789123456789123456789,
"name": "Test 'JSON' Data with "quotes" — Pretty Print ",
"active": true,
"price": 199.9999999,
"decimals": 1.00,
"nullValue": null,
"unicodeText": "こんにちは世界 ",
"escapedCharacters": "Line1\nLine2\tTabbed\"Quoted\" and 'single quoted' with 'code' style",
"nestedObject": {
"level1": {
"level2": {
"emptyArray": [],
"specialChars": "@#$%^&*()_+-=[]{}|;':,./<>?~",
"booleanValues": [
true,
false,
true
],
"numbers": [
0,
-1,
1.23e10,
3.1415926535
]
}
}
},
"mixedArray": [
"string with 'apostrophe'",
42,
false,
null,
{
"innerObj": {
"keyWithQuotes": "value containing \`backticks\` and 'single quotes'",
"nestedArray": [
{
"a": "O'Reilly"
}{
"b": "'inline code'"
},
[
"deep",
"array",
{
"c": "contains 'quotes'"
}
]
]
}
}
],
"nonStringVariable": {{nonStringVar}},
"withBrunoVariable": "{{string}} '{{with}}' "{{variety}}" of '{{variables}}'",
"dateExample": "2025-11-07T12:34:56Z",
"regexExample": "^([a-z0-9_\.-]+)@([\da-z\.-]+)\.([a-z\.]{2,6})$",
"urls": {
"website": "https://example.com?param='value'&flag='true'",
"escapedURL": "https:\/\/escaped-url.com\/path\?q='search'\&debug='on'"
},
"multiLineString": "This is a long text\nthat spans multiple\nlines with \`backticks\` 'quotes' and 'code' snippets "
}`;
expect(prettifyJsonString(input)).toBe(expectedOutput);
});
});
});
4 changes: 2 additions & 2 deletions packages/bruno-app/src/utils/curl/index.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { forOwn } from 'lodash';
import curlToJson from './curl-to-json';
import fastJsonFormat from 'fast-json-format';
import { prettifyJsonString } from 'utils/common/index';

export const getRequestFromCurlCommand = (curlCommand, requestType = 'http-request') => {
const parseFormData = (parsedBody) => {
Expand Down Expand Up @@ -67,7 +67,7 @@ export const getRequestFromCurlCommand = (curlCommand, requestType = 'http-reque
body.file = parsedBody;
}else if (contentType.includes('application/json')) {
body.mode = 'json';
body.json = fastJsonFormat(parsedBody);
body.json = prettifyJsonString(parsedBody);
} else if (contentType.includes('xml')) {
body.mode = 'xml';
body.xml = parsedBody;
Expand Down
4 changes: 4 additions & 0 deletions packages/bruno-common/src/utils/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,7 @@ export {
export {
buildFormUrlEncodedPayload
} from './form-data';

export {
patternHasher
} from './template-hasher';
Loading