diff --git a/packages/bruno-app/src/components/SaveTransientRequest/index.js b/packages/bruno-app/src/components/SaveTransientRequest/index.js index 3522beca2..06210a289 100644 --- a/packages/bruno-app/src/components/SaveTransientRequest/index.js +++ b/packages/bruno-app/src/components/SaveTransientRequest/index.js @@ -14,6 +14,7 @@ import { closeTabs } from 'providers/ReduxStore/slices/tabs'; import { sanitizeName, validateName, validateNameError } from 'utils/common/regex'; import { resolveRequestFilename } from 'utils/common/platform'; import { transformRequestToSaveToFilesystem, findCollectionByUid, findItemInCollection } from 'utils/collections'; +import { DEFAULT_COLLECTION_FORMAT } from 'utils/common/constants'; import { itemSchema } from '@usebruno/schema'; const SaveTransientRequest = ({ item: itemProp, collection: collectionProp, isOpen = false, onClose }) => { @@ -115,7 +116,7 @@ const SaveTransientRequest = ({ item: itemProp, collection: collectionProp, isOp const transformedItem = transformRequestToSaveToFilesystem(itemToSave); await itemSchema.validate(transformedItem); - const format = collection.format || 'bru'; + const format = collection.format || DEFAULT_COLLECTION_FORMAT; const targetFilename = resolveRequestFilename(sanitizedFilename, format); await ipcRenderer.invoke('renderer:save-transient-request', { diff --git a/packages/bruno-app/src/components/ShareCollection/StyledWrapper.js b/packages/bruno-app/src/components/ShareCollection/StyledWrapper.js index b4613cba5..d8614cc77 100644 --- a/packages/bruno-app/src/components/ShareCollection/StyledWrapper.js +++ b/packages/bruno-app/src/components/ShareCollection/StyledWrapper.js @@ -28,21 +28,6 @@ const StyledWrapper = styled.div` } } - .beta-badge-corner { - position: absolute; - top: 0; - right: 0; - padding: 0.25rem 0.5rem; - font-size: 0.625rem; - font-weight: 600; - text-transform: uppercase; - letter-spacing: 0.025em; - background-color: ${(props) => rgba(props.theme.colors.text.yellow, 0.15)}; - color: ${(props) => props.theme.colors.text.yellow}; - border-top-right-radius: ${(props) => props.theme.border.radius.base}; - border-bottom-left-radius: ${(props) => props.theme.border.radius.base}; - } - .share-button { display: flex; border-radius: ${(props) => props.theme.border.radius.base}; diff --git a/packages/bruno-app/src/components/ShareCollection/index.js b/packages/bruno-app/src/components/ShareCollection/index.js index 11a461000..03863d3d8 100644 --- a/packages/bruno-app/src/components/ShareCollection/index.js +++ b/packages/bruno-app/src/components/ShareCollection/index.js @@ -87,14 +87,13 @@ const ShareCollection = ({ onClose, collectionUid }) => {
- Beta
{isCollectionLoading ? ( diff --git a/packages/bruno-app/src/components/Sidebar/CreateCollection/StyledWrapper.js b/packages/bruno-app/src/components/Sidebar/CreateCollection/StyledWrapper.js index 98fd22b72..9a274e053 100644 --- a/packages/bruno-app/src/components/Sidebar/CreateCollection/StyledWrapper.js +++ b/packages/bruno-app/src/components/Sidebar/CreateCollection/StyledWrapper.js @@ -1,28 +1,10 @@ import styled from 'styled-components'; -import { rgba } from 'polished'; const StyledWrapper = styled.div` - .beta-badge { - margin-left: 0.5rem; - padding: 0.125rem 0.5rem; - font-size: 0.625rem; - font-weight: 500; - text-transform: uppercase; - letter-spacing: 0.025em; - background-color: ${(props) => rgba(props.theme.colors.text.yellow, 0.15)}; - color: ${(props) => props.theme.colors.text.yellow}; - border-radius: ${(props) => props.theme.border.radius.sm}; - } - - .discussion-link { - margin-left: 0.5rem; - font-size: ${(props) => props.theme.font.size.sm}; - color: ${(props) => props.theme.textLink}; - cursor: pointer; - font-weight: 400; - - &:hover { - text-decoration: underline; + .advanced-options { + .caret { + color: ${(props) => props.theme.textLink}; + fill: ${(props) => props.theme.textLink}; } } diff --git a/packages/bruno-app/src/components/Sidebar/CreateCollection/index.js b/packages/bruno-app/src/components/Sidebar/CreateCollection/index.js index d1b8c0e5c..4b0235f80 100644 --- a/packages/bruno-app/src/components/Sidebar/CreateCollection/index.js +++ b/packages/bruno-app/src/components/Sidebar/CreateCollection/index.js @@ -1,4 +1,4 @@ -import React, { useRef, useEffect } from 'react'; +import React, { useRef, useEffect, useState, forwardRef } from 'react'; import { useDispatch, useSelector } from 'react-redux'; import { useFormik } from 'formik'; import * as Yup from 'yup'; @@ -8,11 +8,12 @@ import Portal from 'components/Portal'; import Modal from 'components/Modal'; import { sanitizeName, validateName, validateNameError } from 'utils/common/regex'; import PathDisplay from 'components/PathDisplay/index'; -import { useState } from 'react'; -import { IconArrowBackUp, IconEdit, IconExternalLink } from '@tabler/icons'; +import { IconArrowBackUp, IconEdit, IconCaretDown } from '@tabler/icons'; import Help from 'components/Help'; +import Dropdown from 'components/Dropdown'; import { multiLineMsg } from 'utils/common'; import { formatIpcError } from 'utils/common/error'; +import { DEFAULT_COLLECTION_FORMAT } from 'utils/common/constants'; import StyledWrapper from './StyledWrapper'; import get from 'lodash/get'; import Button from 'ui/Button'; @@ -23,7 +24,11 @@ const CreateCollection = ({ onClose, defaultLocation: propDefaultLocation }) => const workspaces = useSelector((state) => state.workspaces?.workspaces || []); const workspaceUid = useSelector((state) => state.workspaces?.activeWorkspaceUid); const [isEditing, toggleEditing] = useState(false); + const [showFileFormat, setShowFileFormat] = useState(false); const preferences = useSelector((state) => state.app.preferences); + + const dropdownTippyRef = useRef(); + const onDropdownCreate = (ref) => (dropdownTippyRef.current = ref); const activeWorkspace = workspaces.find((w) => w.uid === workspaceUid); const isDefaultWorkspace = activeWorkspace?.type === 'default'; @@ -35,7 +40,7 @@ const CreateCollection = ({ onClose, defaultLocation: propDefaultLocation }) => collectionName: '', collectionFolderName: '', collectionLocation: defaultLocation || '', - format: 'bru' + format: DEFAULT_COLLECTION_FORMAT }, validationSchema: Yup.object({ collectionName: Yup.string() @@ -86,6 +91,20 @@ const CreateCollection = ({ onClose, defaultLocation: propDefaultLocation }) => } }, [inputRef]); + const AdvancedOptions = forwardRef((props, ref) => { + return ( +
+ + +
+ ); + }); + return ( @@ -209,62 +228,61 @@ const CreateCollection = ({ onClose, defaultLocation: propDefaultLocation }) =>
)} -
- - - {formik.touched.format && formik.errors.format ? ( -
{formik.errors.format}
- ) : null} -
+ {showFileFormat && ( +
+ + + {formik.touched.format && formik.errors.format ? ( +
{formik.errors.format}
+ ) : null} +
+ )}
-
- - - - - +
diff --git a/packages/bruno-app/src/providers/ReduxStore/slices/collections/actions.js b/packages/bruno-app/src/providers/ReduxStore/slices/collections/actions.js index 043529020..bef97ed67 100644 --- a/packages/bruno-app/src/providers/ReduxStore/slices/collections/actions.js +++ b/packages/bruno-app/src/providers/ReduxStore/slices/collections/actions.js @@ -1,6 +1,6 @@ import { collectionSchema, environmentSchema, itemSchema } from '@usebruno/schema'; import { parseQueryParams, extractPromptVariables } from '@usebruno/common/utils'; -import { REQUEST_TYPES } from 'utils/common/constants'; +import { REQUEST_TYPES, DEFAULT_COLLECTION_FORMAT } from 'utils/common/constants'; import cloneDeep from 'lodash/cloneDeep'; import filter from 'lodash/filter'; import find from 'lodash/find'; @@ -2642,7 +2642,7 @@ export const importCollection = (collection, collectionLocation, options = {}) = const state = getState(); const activeWorkspace = state.workspaces.workspaces.find((w) => w.uid === state.workspaces.activeWorkspaceUid); - const collectionPath = await ipcRenderer.invoke('renderer:import-collection', collection, collectionLocation, options.format || 'bru'); + const collectionPath = await ipcRenderer.invoke('renderer:import-collection', collection, collectionLocation, options.format || DEFAULT_COLLECTION_FORMAT); if (activeWorkspace && activeWorkspace.pathname && activeWorkspace.type !== 'default') { const workspaceCollection = { diff --git a/packages/bruno-app/src/utils/common/constants.js b/packages/bruno-app/src/utils/common/constants.js index f21b90b83..b52c8dbf2 100644 --- a/packages/bruno-app/src/utils/common/constants.js +++ b/packages/bruno-app/src/utils/common/constants.js @@ -1 +1,3 @@ export const REQUEST_TYPES = ['http-request', 'graphql-request', 'grpc-request', 'ws-request']; + +export const DEFAULT_COLLECTION_FORMAT = 'yml'; diff --git a/packages/bruno-cli/src/commands/run.js b/packages/bruno-cli/src/commands/run.js index adbdf5777..e2205c1f7 100644 --- a/packages/bruno-cli/src/commands/run.js +++ b/packages/bruno-cli/src/commands/run.js @@ -375,7 +375,7 @@ const handler = async function (argv) { result.__name__ = nameOverride || path.basename(filePath, fileExt); } else { const content = fs.readFileSync(filePath, 'utf8').replace(/\r\n/g, '\n'); - const envJson = parseEnvironment(content); + const envJson = parseEnvironment(content, { format: 'bru' }); result = getEnvVars(envJson); result.__name__ = nameOverride || path.basename(filePath, '.bru'); } diff --git a/packages/bruno-electron/src/ipc/collection.js b/packages/bruno-electron/src/ipc/collection.js index 68ce1bbe0..a02118fb8 100644 --- a/packages/bruno-electron/src/ipc/collection.js +++ b/packages/bruno-electron/src/ipc/collection.js @@ -14,7 +14,8 @@ const { parseFolder, stringifyFolder, stringifyEnvironment, - parseEnvironment + parseEnvironment, + DEFAULT_COLLECTION_FORMAT } = require('@usebruno/filestore'); const { dotenvToJson } = require('@usebruno/lang'); const brunoConverters = require('@usebruno/converters'); @@ -133,7 +134,7 @@ const registerRendererEventHandlers = (mainWindow, watcher) => { 'renderer:create-collection', async (event, collectionName, collectionFolderName, collectionLocation, options = {}) => { try { - const format = options.format || 'bru'; + const format = options.format || DEFAULT_COLLECTION_FORMAT; collectionFolderName = sanitizeName(collectionFolderName); const dirPath = path.join(collectionLocation, collectionFolderName); if (fs.existsSync(dirPath)) { @@ -1049,7 +1050,7 @@ const registerRendererEventHandlers = (mainWindow, watcher) => { } }); - ipcMain.handle('renderer:import-collection', async (_, collection, collectionLocation, format = 'bru') => { + ipcMain.handle('renderer:import-collection', async (_, collection, collectionLocation, format = DEFAULT_COLLECTION_FORMAT) => { try { let collectionName = sanitizeName(collection.name); let collectionPath = path.join(collectionLocation, collectionName); diff --git a/packages/bruno-electron/src/utils/collection-import.js b/packages/bruno-electron/src/utils/collection-import.js index 9f3b5f11e..f1b9b5cb8 100644 --- a/packages/bruno-electron/src/utils/collection-import.js +++ b/packages/bruno-electron/src/utils/collection-import.js @@ -3,7 +3,7 @@ const path = require('node:path'); const { ipcMain } = require('electron'); const { sanitizeName, createDirectory, writeFile, safeWriteFileSync, getCollectionStats } = require('./filesystem'); const { generateUidBasedOnHash, stringifyJson } = require('./common'); -const { stringifyRequestViaWorker, stringifyCollection, stringifyEnvironment, stringifyFolder } = require('@usebruno/filestore'); +const { stringifyRequestViaWorker, stringifyCollection, stringifyEnvironment, stringifyFolder, DEFAULT_COLLECTION_FORMAT } = require('@usebruno/filestore'); /** * Recursively find a unique folder name by appending incremental numbers @@ -22,7 +22,7 @@ async function findUniqueFolderName(baseName, collectionLocation, counter = 0) { /** * Import a collection - shared logic used by both IPC handler and onboarding service */ -async function importCollection(collection, collectionLocation, mainWindow, uniqueFolderName = null, format = 'bru') { +async function importCollection(collection, collectionLocation, mainWindow, uniqueFolderName = null, format = DEFAULT_COLLECTION_FORMAT) { // Use provided unique folder name or use collection name let folderName = uniqueFolderName ? sanitizeName(uniqueFolderName) : sanitizeName(collection.name); let collectionPath = path.join(collectionLocation, folderName); diff --git a/packages/bruno-electron/src/utils/collection.js b/packages/bruno-electron/src/utils/collection.js index e0db7bca0..7e579bf10 100644 --- a/packages/bruno-electron/src/utils/collection.js +++ b/packages/bruno-electron/src/utils/collection.js @@ -5,6 +5,7 @@ const { uuid } = require('./common'); const os = require('os'); const { preferencesUtil } = require('../store/preferences'); const path = require('path'); +const { DEFAULT_COLLECTION_FORMAT } = require('@usebruno/filestore'); const mergeHeaders = (collection, request, requestTreePath) => { let headers = new Map(); @@ -393,7 +394,7 @@ const parseYmlFileMeta = (data) => { }; // Format-aware meta parsing function -const parseFileMeta = (data, format = 'bru') => { +const parseFileMeta = (data, format = DEFAULT_COLLECTION_FORMAT) => { if (format === 'yml') { return parseYmlFileMeta(data); } else { diff --git a/packages/bruno-electron/src/utils/parse.js b/packages/bruno-electron/src/utils/parse.js index 415da26a3..09e3f040f 100644 --- a/packages/bruno-electron/src/utils/parse.js +++ b/packages/bruno-electron/src/utils/parse.js @@ -1,4 +1,4 @@ -const { parseRequestAndRedactBody, parseRequestViaWorker } = require('@usebruno/filestore'); +const { parseRequestAndRedactBody, parseRequestViaWorker, DEFAULT_COLLECTION_FORMAT } = require('@usebruno/filestore'); /** * Parses a large BRU request string by redacting body blocks, parsing the remainder, @@ -7,7 +7,7 @@ const { parseRequestAndRedactBody, parseRequestViaWorker } = require('@usebruno/ * @param {string} format - Collection format, defaults to 'bru' * @returns {Promise} parsed request JSON */ -async function parseLargeRequestWithRedaction(bruContent, format = 'bru') { +async function parseLargeRequestWithRedaction(bruContent, format = DEFAULT_COLLECTION_FORMAT) { const { bruFileStringWithRedactedBody, extractedBodyContent } = parseRequestAndRedactBody(bruContent, { format }); const parsedData = await parseRequestViaWorker(bruFileStringWithRedactedBody, { format }); diff --git a/packages/bruno-filestore/src/constants.ts b/packages/bruno-filestore/src/constants.ts new file mode 100644 index 000000000..90ba77879 --- /dev/null +++ b/packages/bruno-filestore/src/constants.ts @@ -0,0 +1,3 @@ +import type { CollectionFormat } from './types'; + +export const DEFAULT_COLLECTION_FORMAT: CollectionFormat = 'yml'; diff --git a/packages/bruno-filestore/src/index.ts b/packages/bruno-filestore/src/index.ts index b76ab102e..d378904fb 100644 --- a/packages/bruno-filestore/src/index.ts +++ b/packages/bruno-filestore/src/index.ts @@ -25,10 +25,11 @@ import { StringifyOptions, CollectionFormat } from './types'; +import { DEFAULT_COLLECTION_FORMAT } from './constants'; import { bruRequestParseAndRedactBodyData } from './formats/bru/utils/request-parse-and-redact-body-data'; // request -export const parseRequest = (content: string, options: ParseOptions = { format: 'bru' }): any => { +export const parseRequest = (content: string, options: ParseOptions = { format: DEFAULT_COLLECTION_FORMAT }): any => { if (options.format === 'bru') { return parseBruRequest(content); } else if (options.format === 'yml') { @@ -44,7 +45,7 @@ export const parseRequestAndRedactBody = (content: string, options: ParseOptions throw new Error(`Unsupported format: ${options.format}`); }; -export const stringifyRequest = (requestObj: BrunoItem, options: StringifyOptions = { format: 'bru' }): string => { +export const stringifyRequest = (requestObj: BrunoItem, options: StringifyOptions = { format: DEFAULT_COLLECTION_FORMAT }): string => { if (options.format === 'bru') { return stringifyBruRequest(requestObj); } else if (options.format === 'yml') { @@ -74,7 +75,7 @@ export const stringifyRequestViaWorker = async (requestObj: any, options: { form }; // collection -export const parseCollection = (content: string, options: ParseOptions = { format: 'bru' }): any => { +export const parseCollection = (content: string, options: ParseOptions = { format: DEFAULT_COLLECTION_FORMAT }): any => { if (options.format === 'bru') { return parseBruCollection(content); } else if (options.format === 'yml') { @@ -83,7 +84,7 @@ export const parseCollection = (content: string, options: ParseOptions = { forma throw new Error(`Unsupported format: ${options.format}`); }; -export const stringifyCollection = (collectionObj: BrunoCollection, brunoConfig: any, options: StringifyOptions = { format: 'bru' }): string => { +export const stringifyCollection = (collectionObj: BrunoCollection, brunoConfig: any, options: StringifyOptions = { format: DEFAULT_COLLECTION_FORMAT }): string => { if (options.format === 'bru') { return stringifyBruCollection(collectionObj, false); } else if (options.format === 'yml') { @@ -93,7 +94,7 @@ export const stringifyCollection = (collectionObj: BrunoCollection, brunoConfig: }; // folder -export const parseFolder = (content: string, options: ParseOptions = { format: 'bru' }): any => { +export const parseFolder = (content: string, options: ParseOptions = { format: DEFAULT_COLLECTION_FORMAT }): any => { if (options.format === 'bru') { return parseBruCollection(content); } else if (options.format === 'yml') { @@ -102,7 +103,7 @@ export const parseFolder = (content: string, options: ParseOptions = { format: ' throw new Error(`Unsupported format: ${options.format}`); }; -export const stringifyFolder = (folderObj: any, options: StringifyOptions = { format: 'bru' }): string => { +export const stringifyFolder = (folderObj: any, options: StringifyOptions = { format: DEFAULT_COLLECTION_FORMAT }): string => { if (options.format === 'bru') { return stringifyBruCollection(folderObj, true); } else if (options.format === 'yml') { @@ -112,7 +113,7 @@ export const stringifyFolder = (folderObj: any, options: StringifyOptions = { fo }; // environment -export const parseEnvironment = (content: string, options: ParseOptions = { format: 'bru' }): any => { +export const parseEnvironment = (content: string, options: ParseOptions = { format: DEFAULT_COLLECTION_FORMAT }): any => { if (options.format === 'bru') { return parseBruEnvironment(content); } else if (options.format === 'yml') { @@ -121,7 +122,7 @@ export const parseEnvironment = (content: string, options: ParseOptions = { form throw new Error(`Unsupported format: ${options.format}`); }; -export const stringifyEnvironment = (envObj: BrunoEnvironment, options: StringifyOptions = { format: 'bru' }): string => { +export const stringifyEnvironment = (envObj: BrunoEnvironment, options: StringifyOptions = { format: DEFAULT_COLLECTION_FORMAT }): string => { if (options.format === 'bru') { return stringifyBruEnvironment(envObj); } else if (options.format === 'yml') { @@ -136,3 +137,4 @@ export const parseDotEnv = (content: string): Record => { export { BruParserWorker }; export * from './types'; +export * from './constants'; diff --git a/packages/bruno-filestore/src/workers/index.ts b/packages/bruno-filestore/src/workers/index.ts index 126039e19..a08886b25 100644 --- a/packages/bruno-filestore/src/workers/index.ts +++ b/packages/bruno-filestore/src/workers/index.ts @@ -1,5 +1,6 @@ import WorkerQueue from './WorkerQueue'; import { Lane, CollectionFormat } from '../types'; +import { DEFAULT_COLLECTION_FORMAT } from '../constants'; import path from 'node:path'; const sizeInMB = (size: number): number => { @@ -54,7 +55,7 @@ class BruParserWorker { return queueForSize?.workerQueue ?? this.workerQueues[this.workerQueues.length - 1].workerQueue; } - private async enqueueTask({ data, taskType, format = 'bru' }: { data: any; taskType: 'parse' | 'stringify'; format?: CollectionFormat }): Promise { + private async enqueueTask({ data, taskType, format = DEFAULT_COLLECTION_FORMAT }: { data: any; taskType: 'parse' | 'stringify'; format?: CollectionFormat }): Promise { const size = getSize(data); const workerQueue = this.getWorkerQueue(size); const workerScriptPath = path.join(__dirname, './workers/worker-script.js'); @@ -67,11 +68,11 @@ class BruParserWorker { }); } - async parseRequest(data: any, format: CollectionFormat = 'bru'): Promise { + async parseRequest(data: any, format: CollectionFormat = DEFAULT_COLLECTION_FORMAT): Promise { return this.enqueueTask({ data, taskType: 'parse', format }); } - async stringifyRequest(data: any, format: CollectionFormat = 'bru'): Promise { + async stringifyRequest(data: any, format: CollectionFormat = DEFAULT_COLLECTION_FORMAT): Promise { return this.enqueueTask({ data, taskType: 'stringify', format }); } diff --git a/packages/bruno-filestore/src/workers/worker-script.ts b/packages/bruno-filestore/src/workers/worker-script.ts index 84c26f604..2e40d5396 100644 --- a/packages/bruno-filestore/src/workers/worker-script.ts +++ b/packages/bruno-filestore/src/workers/worker-script.ts @@ -2,6 +2,7 @@ import { parentPort } from 'node:worker_threads'; import { parseBruRequest, stringifyBruRequest } from '../formats/bru'; import { parseYmlItem, stringifyYmlItem } from '../formats/yml'; import { CollectionFormat } from '../types'; +import { DEFAULT_COLLECTION_FORMAT } from '../constants'; interface WorkerMessage { taskType: 'parse' | 'stringify'; @@ -14,7 +15,7 @@ interface WorkerMessage { parentPort?.on('message', async (message: WorkerMessage) => { try { const { taskType, data: messageData } = message; - const { data, format = 'bru' } = messageData; + const { data, format = DEFAULT_COLLECTION_FORMAT } = messageData; let result: any; if (taskType === 'parse') {