Skip to content

feat: add PropertyList-based variable scoping APIs (bru.variables, bru.environment, bru.globals)#7887

Open
sanish-bruno wants to merge 4 commits intousebruno:mainfrom
sanish-bruno:feat/variables-scripting-apis
Open

feat: add PropertyList-based variable scoping APIs (bru.variables, bru.environment, bru.globals)#7887
sanish-bruno wants to merge 4 commits intousebruno:mainfrom
sanish-bruno:feat/variables-scripting-apis

Conversation

@sanish-bruno
Copy link
Copy Markdown
Collaborator

@sanish-bruno sanish-bruno commented Apr 29, 2026

JIRA

Summary

Adds PropertyList-based variable scoping APIs to Bruno's scripting engine, providing a Postman-compatible interface for variable management.

New APIs

Three new namespaced objects on the bru scripting context:

  • bru.variables — runtime variables (equivalent to pm.variables)
  • bru.environment — active collection environment (equivalent to pm.environment)
  • bru.globals — active global environment (equivalent to pm.globals)

Each exposes a consistent PropertyList interface:

Method variables environment globals
get(key)
set(key, value) ✅ (supports { persist: true })
has(key)
unset(key) ❌ (not implemented — UI sync)
clear() ❌ (not implemented — UI sync)
toObject()
name ✅ (getter) ✅ (getter)
Iterators (each, map, filter, find, reduce)
Collection methods (one, all, idx, count, indexOf, toJSON, toString)

Postman mapping

Postman Bruno
pm.variables.get/set/has/unset/clear/toObject bru.variables.get/set/has/unset/clear/toObject
pm.environment.get/set/has/unset/clear/toObject/name bru.environment.get/set/has/unset/clear/toObject/name
pm.globals.get/set/has/toObject bru.globals.get/set/has/toObject
pm.*.replaceIn() bru.interpolate() (shared, not scoped)

Implementation

  • VariableList class (packages/bruno-js/src/variable-list.js) — extends PropertyList in dynamic mode, wraps the existing plain { key: value } objects and mutates them in place
  • Backward compatible — all existing bru.getVar(), bru.setEnvVar(), etc. methods remain unchanged and operate on the same underlying objects
  • QuickJS sandbox — added syncWriteMethods support to createPropertyListBridge, wired all three VariableList instances with full bridge support. get() and toObject() routed through syncReadObjectMethods to safely handle object values across the VM boundary
  • globals.namegetGlobalEnvironmentVariables() in bruno-app now injects __name__ (mirroring how collection environments work), exposed as bru.globals.name getter
  • Postman translator — updated both AST-based and regex-based translators to produce new PropertyList form. Removed complex pm.environment.has / pm.globals.has transformations (now simple 1:1 translations). Filled in previously missing translations (unset, clear, has for globals/variables)

Files changed

File Change
packages/bruno-js/src/variable-list.js New — VariableList class
packages/bruno-js/src/bru.js Add variables, environment, globals instances + getGlobalEnvName()
packages/bruno-js/src/sandbox/quickjs/utils/property-list-bridge.js Add syncWriteMethods support
packages/bruno-js/src/sandbox/quickjs/shims/bru.js Wire three PropertyList bridges + name getters
packages/bruno-app/src/utils/collections/index.js Inject __name__ into global environment variables
packages/bruno-converters/src/utils/postman-to-bruno-translator.js Update translations to PropertyList form
packages/bruno-converters/src/postman/postman-translations.js Update regex translations
packages/bruno-js/tests/variable-list.spec.js New — unit tests
packages/bruno-converters/tests/** Updated assertions to match new translator output

Contribution Checklist:

  • I've used AI significantly to create this pull request
  • The pull request only addresses one issue or adds one feature.
  • The pull request does not introduce any breaking changes
  • I have read the contribution guidelines.

Summary by CodeRabbit

  • New Features

    • Introduced structured variable scope APIs with expanded method coverage: environment variables, runtime variables, and global variables now support get, set, has, unset, clear, and metadata operations.
  • Tests

    • Updated converter tests to validate proper translation of variable access patterns.

…and bru.variables methods

This commit modifies the translation logic to replace deprecated Postman API calls with updated bru methods for environment and variable management. The changes include updating method names in both the translation files and the corresponding test cases to ensure consistency and correctness in the translation process.
…slations and implementation

This commit comments out the globals.unset and globals.clear methods in the Postman translations and the Bru class implementation, marking them as TODOs to be re-enabled once the UI sync issue is resolved. This change ensures that the code remains functional while addressing the current limitations in the UI.
This commit updates the order of sync read methods in the bru.js shims for variables, environment, and globals to ensure a consistent structure across the code. The changes enhance readability and maintainability without altering functionality.
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Apr 29, 2026

Walkthrough

Replaces flat Postman variable helpers with namespaced Bruno APIs (bru.environment.*, bru.variables.*, bru.globals.*), adds a new VariableList abstraction, updates QuickJS bridging to expose namespaced property-list APIs, and updates translator mappings and tests to use the new surface; removes bespoke AST expansions for has() checks.

Changes

Cohort / File(s) Summary
Postman translation mappings
packages/bruno-converters/src/postman/postman-translations.js
Switched pm.environment/pm.variables/pm.globals mappings from legacy flat helpers to namespaced bru.environment.*, bru.variables.*, bru.globals.*; added has/unset/replaceIn/toObject/clear/name coverage; removed several legacy mappings.
Translator logic
packages/bruno-converters/src/utils/postman-to-bruno-translator.js
Updated name mappings to namespace-style Bruno calls; removed bespoke AST expansions for pm.environment.has/pm.globals.has, relying on direct has(...) mappings.
Bruno runtime
packages/bruno-js/src/bru.js
Introduced VariableList instances for this.variables, this.environment, this.globals; added environment name exposure, persistence handling, key validation hooks, and getGlobalEnvName(); globals.unset/clear produce not-implemented errors.
QuickJS shim & bridge
packages/bruno-js/src/sandbox/quickjs/shims/bru.js, .../utils/property-list-bridge.js
Exposed bridged bru.environment, bru.variables, bru.globals to VM; added syncWriteMethods support to property-list bridge; wired enumerable name getters.
VariableList implementation
packages/bruno-js/src/variable-list.js
New VariableList class (extends PropertyList) with get/set/unset/clear, interpolation support, key validation, filterKeys, and onSet callback; exported as module.
Tests updated / added
packages/bruno-converters/tests/... (13 files), packages/bruno-js/tests/variable-list.spec.js
Updated 13 translator tests to expect namespaced Bruno APIs; added comprehensive VariableList unit tests validating CRUD, interpolation, filtering, validation, callbacks, and inherited read APIs.
App collector tweak
packages/bruno-app/src/utils/collections/index.js
Global environment variables object now includes __name__ metadata sourced from active global environment.

Sequence Diagram(s)

sequenceDiagram
    participant PM as Postman Script
    participant Translator as Postman→Bruno Translator
    participant VM as QuickJS VM (bru shim)
    participant Runtime as Bru Runtime (VariableList / backing store)

    PM->>Translator: Parse & translate pm.* calls
    Translator->>VM: Emit namespaced calls (bru.environment.get/set/has, bru.variables.*, bru.globals.*) 
    VM->>Runtime: Bridge call marshalled (sync write/read via property-list bridge)
    Runtime->>Runtime: VariableList.get/set/unset/clear (interpolate/validate/persist)
    Runtime-->>VM: Return values (or undefined for sync writes)
    VM-->>Translator: (runtime results used in emitted code)
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

Suggested labels

size/XXL

Suggested reviewers

  • helloanoop
  • lohit-bruno
  • naman-bruno
  • bijin-bruno
  • sid-bruno

Poem

🌱 Namespaced variables take the stage anew,
bru.environment, bru.variables, bru.globals in view.
Bridges hum, VariableList keeps order tight,
Translations shifted, tests now right,
A tidier sandbox, and code that feels true ✨

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately describes the main change: introducing PropertyList-based variable scoping APIs (bru.variables, bru.environment, bru.globals) to the scripting engine.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@sanish-bruno sanish-bruno changed the title Feat/variables scripting apis feat: add PropertyList-based variable scoping APIs (bru.variables, bru.environment, bru.globals) Apr 29, 2026
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🧹 Nitpick comments (1)
packages/bruno-js/tests/variable-list.spec.js (1)

53-60: Add regression coverage for reserved keys (__proto__, etc.).

Given VariableList#set now centralizes writes, this suite should include a blocking-case test for dangerous prototype keys to prevent future regressions.

Suggested test addition
     test('set() allows valid key characters', () => {
       list.set('my-var_name.v2', 'ok');
       expect(vars['my-var_name.v2']).toBe('ok');
     });
+
+    test('set() rejects reserved prototype keys', () => {
+      expect(() => list.set('__proto__', 'x')).toThrow('not allowed');
+      expect(() => list.set('prototype', 'x')).toThrow('not allowed');
+      expect(() => list.set('constructor', 'x')).toThrow('not allowed');
+    });
As per coding guidelines: `Add tests for any new functionality or meaningful changes. If code is added, removed, or significantly modified, corresponding tests should be updated or created.` and `Cover both the "happy path" and the realistically problematic paths.`
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/bruno-js/tests/variable-list.spec.js` around lines 53 - 60, The test
suite is missing regression coverage for dangerous reserved prototype keys; add
tests in packages/bruno-js/tests/variable-list.spec.js that exercise
VariableList#set to ensure it rejects reserved keys such as "__proto__",
"constructor" (and similar prototype-polluting names) by throwing the same
validation error used for invalid characters; follow the existing test style
(see tests 'set() validates key format' and 'set() allows valid key characters')
and assert that calling list.set('__proto__', 'x') and list.set('constructor',
'x') throws the expected error to prevent prototype pollution regressions.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@packages/bruno-js/src/variable-list.js`:
- Line 3: The variableNameRegex and the set() logic currently allow
meta-reserved keys (e.g., "__proto__", "prototype", "constructor") which enables
prototype-pollution; update validation so the regex or the set() validator
explicitly rejects those reserved property names before assigning into the
internal object. Concretely, add a guard in the set method(s) referenced (around
the blocks at the locations matching variableNameRegex and the set
implementations) that returns or throws when key matches
/^__proto__$|^prototype$|^constructor$/ or when key is not matched by the
tightened variableNameRegex, and apply the same guard to the other
set/assignment sites noted (the blocks corresponding to lines 71-76 and 103-113)
to ensure all writes block prototype-related keys.

In `@packages/bruno-js/tests/variable-list.spec.js`:
- Around line 127-130: Rename the test titled 'has() still checks filtered keys
via dataSource' to reflect that filtered keys are not visible to has(); update
the test title string (the argument to the test() call) for the test that calls
list.has('__name__') and list.has('host') to something like "has() respects
filtered keys and returns false for filtered entries" so the description matches
the assertions in the test.

---

Nitpick comments:
In `@packages/bruno-js/tests/variable-list.spec.js`:
- Around line 53-60: The test suite is missing regression coverage for dangerous
reserved prototype keys; add tests in
packages/bruno-js/tests/variable-list.spec.js that exercise VariableList#set to
ensure it rejects reserved keys such as "__proto__", "constructor" (and similar
prototype-polluting names) by throwing the same validation error used for
invalid characters; follow the existing test style (see tests 'set() validates
key format' and 'set() allows valid key characters') and assert that calling
list.set('__proto__', 'x') and list.set('constructor', 'x') throws the expected
error to prevent prototype pollution regressions.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: f2e37527-b140-46e2-a75e-82235836b8ae

📥 Commits

Reviewing files that changed from the base of the PR and between 13a9f9b and 4622ad1.

📒 Files selected for processing (20)
  • packages/bruno-converters/src/postman/postman-translations.js
  • packages/bruno-converters/src/utils/postman-to-bruno-translator.js
  • packages/bruno-converters/tests/postman/postman-translations/postman-comments.spec.js
  • packages/bruno-converters/tests/postman/postman-translations/postman-edge-cases.spec.js
  • packages/bruno-converters/tests/postman/postman-translations/postman-variables.spec.js
  • packages/bruno-converters/tests/postman/postman-translations/transpiler-tests/combined.test.js
  • packages/bruno-converters/tests/postman/postman-translations/transpiler-tests/environment.test.js
  • packages/bruno-converters/tests/postman/postman-translations/transpiler-tests/legacy-tests-syntax.test.js
  • packages/bruno-converters/tests/postman/postman-translations/transpiler-tests/multiline-syntax.test.js
  • packages/bruno-converters/tests/postman/postman-translations/transpiler-tests/postman-references.test.js
  • packages/bruno-converters/tests/postman/postman-translations/transpiler-tests/request.test.js
  • packages/bruno-converters/tests/postman/postman-translations/transpiler-tests/response.test.js
  • packages/bruno-converters/tests/postman/postman-translations/transpiler-tests/testing-framework.test.js
  • packages/bruno-converters/tests/postman/postman-translations/transpiler-tests/variable-chaining.test.js
  • packages/bruno-converters/tests/postman/postman-translations/transpiler-tests/variables.test.js
  • packages/bruno-js/src/bru.js
  • packages/bruno-js/src/sandbox/quickjs/shims/bru.js
  • packages/bruno-js/src/sandbox/quickjs/utils/property-list-bridge.js
  • packages/bruno-js/src/variable-list.js
  • packages/bruno-js/tests/variable-list.spec.js

@@ -0,0 +1,117 @@
const PropertyList = require('./property-list');

const variableNameRegex = /^[\w-.]*$/;
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Block reserved keys to prevent prototype-pollution behavior.

set() currently permits keys like __proto__ (and related meta-keys), which can mutate object prototype behavior when writing into plain objects. This is a security/correctness risk in scripting inputs.

Proposed fix
 const PropertyList = require('./property-list');
 
 const variableNameRegex = /^[\w-.]*$/;
+const reservedVariableKeys = new Set(['__proto__', 'prototype', 'constructor']);
@@
   set(key, value, options) {
-    if (!key) {
+    if (typeof key !== 'string' || key.length === 0) {
       throw new Error('Creating a variable without specifying a name is not allowed.');
     }
     this.#validateKey(key);
     this._variablesObj[key] = value;
@@
   `#validateKey`(key) {
+    if (reservedVariableKeys.has(key)) {
+      throw new Error(`Variable name: "${key}" is not allowed.`);
+    }
     if (this._validateKeyFn) {
       this._validateKeyFn(key);
       return;
     }

Also applies to: 71-76, 103-113

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/bruno-js/src/variable-list.js` at line 3, The variableNameRegex and
the set() logic currently allow meta-reserved keys (e.g., "__proto__",
"prototype", "constructor") which enables prototype-pollution; update validation
so the regex or the set() validator explicitly rejects those reserved property
names before assigning into the internal object. Concretely, add a guard in the
set method(s) referenced (around the blocks at the locations matching
variableNameRegex and the set implementations) that returns or throws when key
matches /^__proto__$|^prototype$|^constructor$/ or when key is not matched by
the tightened variableNameRegex, and apply the same guard to the other
set/assignment sites noted (the blocks corresponding to lines 71-76 and 103-113)
to ensure all writes block prototype-related keys.

Comment on lines +127 to +130
test('has() still checks filtered keys via dataSource', () => {
// __name__ is filtered from reads, so has() won't find it
expect(list.has('__name__')).toBe(false);
expect(list.has('host')).toBe(true);
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Test name is misleading relative to the assertion.

The title says has() “still checks filtered keys”, but the assertion expects false for __name__. Please rename the test to reflect the actual expected behavior.

Suggested rename
-    test('has() still checks filtered keys via dataSource', () => {
+    test('has() returns false for filtered keys', () => {
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/bruno-js/tests/variable-list.spec.js` around lines 127 - 130, Rename
the test titled 'has() still checks filtered keys via dataSource' to reflect
that filtered keys are not visible to has(); update the test title string (the
argument to the test() call) for the test that calls list.has('__name__') and
list.has('host') to something like "has() respects filtered keys and returns
false for filtered entries" so the description matches the assertions in the
test.

This commit introduces a new method `getGlobalEnvName` in the Bru class to retrieve the global environment name. Additionally, it updates the Bru shims to expose this method and define a property for the global environment name, enhancing the accessibility of environment information within the application.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant