From 15c2373fb02319bc8fb178825652168e64acaeda Mon Sep 17 00:00:00 2001 From: James Ha Date: Sat, 11 Oct 2025 15:30:17 -0700 Subject: [PATCH] add first attempt of adding set / map logic --- .../src/components/Devtools/Console/index.js | 43 +++++- .../bruno-js/src/sandbox/quickjs/index.js | 49 +++++++ .../src/sandbox/quickjs/shims/console.js | 131 +++++++++++++++++- 3 files changed, 217 insertions(+), 6 deletions(-) diff --git a/packages/bruno-app/src/components/Devtools/Console/index.js b/packages/bruno-app/src/components/Devtools/Console/index.js index 0e1de515a..b4c2aa574 100644 --- a/packages/bruno-app/src/components/Devtools/Console/index.js +++ b/packages/bruno-app/src/components/Devtools/Console/index.js @@ -67,14 +67,55 @@ const LogTimestamp = ({ timestamp }) => { const LogMessage = ({ message, args }) => { const { displayedTheme } = useTheme(); + // Helper function to transform Bruno special types back to readable format + const transformBrunoTypes = obj => { + if (typeof obj !== 'object' || obj === null) { + return obj; + } + + // Handle Bruno special types + if (obj.__brunoType) { + switch (obj.__brunoType) { + case 'Set': + return { + '[[Set]]': obj.__brunoValue, + 'size': obj.__brunoValue.length, + }; + case 'Map': + return { + '[[Map]]': obj.__brunoValue, + 'size': obj.__brunoValue.length, + }; + case 'Function': + return `[Function: ${obj.__brunoValue.split('\n')[0].substring(0, 50)}...]`; + case 'undefined': + return 'undefined'; + default: + return obj; + } + } + + // Recursively transform nested objects + if (Array.isArray(obj)) { + return obj.map(transformBrunoTypes); + } + + const transformed = {}; + for (const [key, value] of Object.entries(obj)) { + transformed[key] = transformBrunoTypes(value); + } + return transformed; + }; + const formatMessage = (msg, originalArgs) => { if (originalArgs && originalArgs.length > 0) { return originalArgs.map((arg, index) => { if (typeof arg === 'object' && arg !== null) { + const transformedArg = transformBrunoTypes(arg); return (
{ + if (arg instanceof Set) { + return { + __brunoType: 'Set', + __brunoValue: Array.from(arg), + size: arg.size + }; + } + if (arg instanceof Map) { + return { + __brunoType: 'Map', + __brunoValue: Array.from(arg.entries()), + size: arg.size + }; + } + return arg; + }); + return originalConsoleLog.apply(this, processedArgs); + }; + + // Also override other console methods + ['debug', 'info', 'warn', 'error'].forEach(method => { + const originalMethod = console[method]; + console[method] = function(...args) { + const processedArgs = args.map(arg => { + if (arg instanceof Set) { + return { + __brunoType: 'Set', + __brunoValue: Array.from(arg), + size: arg.size + }; + } + if (arg instanceof Map) { + return { + __brunoType: 'Map', + __brunoValue: Array.from(arg.entries()), + size: arg.size + }; + } + return arg; + }); + return originalMethod.apply(this, processedArgs); + }; + }); + await bru.sleep(0); try { ${externalScript} diff --git a/packages/bruno-js/src/sandbox/quickjs/shims/console.js b/packages/bruno-js/src/sandbox/quickjs/shims/console.js index 984422893..8a7101347 100644 --- a/packages/bruno-js/src/sandbox/quickjs/shims/console.js +++ b/packages/bruno-js/src/sandbox/quickjs/shims/console.js @@ -1,30 +1,151 @@ const addConsoleShimToContext = (vm, console) => { if (!console) return; + // Helper function to convert QuickJS values to native values with Set/Map support + const dumpWithSetMapSupport = arg => { + try { + // First try to dump normally + const dumped = vm.dump(arg); + + // Check if it's a Set by trying to access Set-specific properties + if (vm.typeof(arg) === 'object' && arg !== vm.null && arg !== vm.undefined) { + // Try to get the constructor name + const constructorProp = vm.getProp(arg, 'constructor'); + if (constructorProp) { + const constructorNameProp = vm.getProp(constructorProp, 'name'); + if (constructorNameProp) { + const constructorName = vm.dump(constructorNameProp); + constructorNameProp.dispose(); + + if (constructorName === 'Set') { + // It's a Set, convert to our special format + const sizeProp = vm.getProp(arg, 'size'); + const size = sizeProp ? vm.dump(sizeProp) : 0; + sizeProp?.dispose(); + + // Get values by calling values() method + const valuesFn = vm.getProp(arg, 'values'); + if (valuesFn) { + const valuesIterator = vm.callFunction(valuesFn, arg); + const values = []; + + // Try to extract values (this is a simplified approach) + try { + // For now, we'll use a different approach - convert via Array.from + const arrayFromFn = vm.getProp(vm.global, 'Array'); + if (arrayFromFn) { + const fromFn = vm.getProp(arrayFromFn, 'from'); + if (fromFn) { + const arrayResult = vm.callFunction(fromFn, arrayFromFn, arg); + const arrayValues = vm.dump(arrayResult); + arrayResult.dispose(); + fromFn.dispose(); + + constructorProp.dispose(); + valuesFn.dispose(); + valuesIterator?.dispose(); + arrayFromFn.dispose(); + + return { + __brunoType: 'Set', + __brunoValue: arrayValues, + size: size, + }; + } + fromFn?.dispose(); + } + arrayFromFn?.dispose(); + } catch (e) { + // Fallback to empty array + } + + valuesFn.dispose(); + valuesIterator?.dispose(); + + return { + __brunoType: 'Set', + __brunoValue: values, + size: size, + }; + } + } else if (constructorName === 'Map') { + // It's a Map, convert to our special format + const sizeProp = vm.getProp(arg, 'size'); + const size = sizeProp ? vm.dump(sizeProp) : 0; + sizeProp?.dispose(); + + // Try to convert via Array.from + try { + const arrayFromFn = vm.getProp(vm.global, 'Array'); + if (arrayFromFn) { + const fromFn = vm.getProp(arrayFromFn, 'from'); + if (fromFn) { + const arrayResult = vm.callFunction(fromFn, arrayFromFn, arg); + const arrayValues = vm.dump(arrayResult); + arrayResult.dispose(); + fromFn.dispose(); + arrayFromFn.dispose(); + + constructorProp.dispose(); + + return { + __brunoType: 'Map', + __brunoValue: arrayValues, + size: size, + }; + } + fromFn?.dispose(); + } + arrayFromFn?.dispose(); + } catch (e) { + // Fallback + } + + constructorProp.dispose(); + + return { + __brunoType: 'Map', + __brunoValue: [], + size: size, + }; + } + } + constructorNameProp?.dispose(); + } + constructorProp?.dispose(); + } + + return dumped; + } catch (e) { + // Fallback to normal dump + return vm.dump(arg); + } + }; + const consoleHandle = vm.newObject(); const logHandle = vm.newFunction('log', (...args) => { - const nativeArgs = args.map(vm.dump); + const nativeArgs = args.map(dumpWithSetMapSupport); console?.log?.(...nativeArgs); }); const debugHandle = vm.newFunction('debug', (...args) => { - const nativeArgs = args.map(vm.dump); + const nativeArgs = args.map(dumpWithSetMapSupport); console?.debug?.(...nativeArgs); }); const infoHandle = vm.newFunction('info', (...args) => { - const nativeArgs = args.map(vm.dump); + const nativeArgs = args.map(dumpWithSetMapSupport); console?.info?.(...nativeArgs); }); const warnHandle = vm.newFunction('warn', (...args) => { - const nativeArgs = args.map(vm.dump); + const nativeArgs = args.map(dumpWithSetMapSupport); console?.warn?.(...nativeArgs); }); const errorHandle = vm.newFunction('error', (...args) => { - const nativeArgs = args.map(vm.dump); + const nativeArgs = args.map(dumpWithSetMapSupport); console?.error?.(...nativeArgs); });