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
2 changes: 0 additions & 2 deletions packages/bruno-app/src/utils/codemirror/autocomplete.js
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,6 @@ const STATIC_API_HINTS = {
'req.headerList.get(name)',
'req.headerList.one(name)',
'req.headerList.all()',
'req.headerList.idx(index)',
'req.headerList.count()',
'req.headerList.has(name)',
'req.headerList.has(name, value)',
Expand Down Expand Up @@ -88,7 +87,6 @@ const STATIC_API_HINTS = {
'res.headerList.get(name)',
'res.headerList.one(name)',
'res.headerList.all()',
'res.headerList.idx(index)',
'res.headerList.count()',
'res.headerList.has(name)',
'res.headerList.has(name, value)',
Expand Down
40 changes: 40 additions & 0 deletions packages/bruno-converters/src/postman/postman-translations.js
Original file line number Diff line number Diff line change
Expand Up @@ -34,8 +34,48 @@ const replacements = {
'pm\\.environment\\.toObject\\(': 'bru.getAllEnvVars(',
'pm\\.environment\\.clear\\(': 'bru.deleteAllEnvVars(',
'pm\\.variables\\.toObject\\(': 'bru.getAllVars(',
// Request header PropertyList methods
'pm\\.request\\.headers\\.remove\\(': 'req.deleteHeader(',
'pm\\.request\\.headers\\.get\\(': 'req.headerList.get(',
'pm\\.request\\.headers\\.has\\(': 'req.headerList.has(',
'pm\\.request\\.headers\\.one\\(': 'req.headerList.one(',
'pm\\.request\\.headers\\.all\\(': 'req.headerList.all(',
'pm\\.request\\.headers\\.count\\(': 'req.headerList.count(',
'pm\\.request\\.headers\\.indexOf\\(': 'req.headerList.indexOf(',
'pm\\.request\\.headers\\.find\\(': 'req.headerList.find(',
'pm\\.request\\.headers\\.filter\\(': 'req.headerList.filter(',
'pm\\.request\\.headers\\.each\\(': 'req.headerList.forEach(',
'pm\\.request\\.headers\\.map\\(': 'req.headerList.map(',
'pm\\.request\\.headers\\.reduce\\(': 'req.headerList.reduce(',
'pm\\.request\\.headers\\.toObject\\(': 'req.headerList.toObject(',
'pm\\.request\\.headers\\.clear\\(': 'req.headerList.clear(',
'pm\\.request\\.headers\\.add\\(': 'req.headerList.append(',
'pm\\.request\\.headers\\.upsert\\(': 'req.headerList.set(',
'pm\\.request\\.headers\\.toString\\(': 'req.headerList.toString(',
'pm\\.request\\.headers\\.toJSON\\(': 'req.headerList.toJSON(',
'pm\\.request\\.headers\\.populate\\(': 'req.headerList.populate(',
'pm\\.request\\.headers\\.repopulate\\(': 'req.headerList.repopulate(',
'pm\\.request\\.headers\\.assimilate\\(': 'req.headerList.assimilate(',
// Lossy: positional inserts map to append (position irrelevant for headers)
// Note: regex can't drop the second arg, so it passes through as-is
'pm\\.request\\.headers\\.prepend\\(': 'req.headerList.append(',
'pm\\.request\\.headers\\.insert\\(': 'req.headerList.append(',
'pm\\.request\\.headers\\.insertAfter\\(': 'req.headerList.append(',
// Response header PropertyList methods
'pm\\.response\\.headers\\.get\\(': 'res.getHeader(',
'pm\\.response\\.headers\\.has\\(': 'res.headerList.has(',
'pm\\.response\\.headers\\.one\\(': 'res.headerList.one(',
'pm\\.response\\.headers\\.all\\(': 'res.headerList.all(',
'pm\\.response\\.headers\\.count\\(': 'res.headerList.count(',
'pm\\.response\\.headers\\.indexOf\\(': 'res.headerList.indexOf(',
'pm\\.response\\.headers\\.find\\(': 'res.headerList.find(',
'pm\\.response\\.headers\\.filter\\(': 'res.headerList.filter(',
'pm\\.response\\.headers\\.each\\(': 'res.headerList.forEach(',
'pm\\.response\\.headers\\.map\\(': 'res.headerList.map(',
'pm\\.response\\.headers\\.reduce\\(': 'res.headerList.reduce(',
'pm\\.response\\.headers\\.toObject\\(': 'res.headerList.toObject(',
'pm\\.response\\.headers\\.toString\\(': 'res.headerList.toString(',
'pm\\.response\\.headers\\.toJSON\\(': 'res.headerList.toJSON(',
'pm\\.response\\.to\\.have\\.jsonSchema\\(': 'expect(res.getBody()).to.have.jsonSchema(',
'pm\\.response\\.to\\.not\\.have\\.jsonSchema\\(': 'expect(res.getBody()).to.not.have.jsonSchema(',
'pm\\.response\\.not\\.to\\.have\\.jsonSchema\\(': 'expect(res.getBody()).not.to.have.jsonSchema(',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,8 @@ const simpleTranslations = {
'req.headerList.map': 'pm.request.headers.map',
'req.headerList.reduce': 'pm.request.headers.reduce',
'req.headerList.toObject': 'pm.request.headers.toObject',
'req.headerList.toString': 'pm.request.headers.toString',
'req.headerList.toJSON': 'pm.request.headers.toJSON',
'req.headerList.append': 'pm.request.headers.add',
'req.headerList.set': 'pm.request.headers.upsert',
'req.headerList.delete': 'pm.request.headers.remove',
Expand Down Expand Up @@ -130,6 +132,8 @@ const simpleTranslations = {
'res.headerList.map': 'pm.response.headers.map',
'res.headerList.reduce': 'pm.response.headers.reduce',
'res.headerList.toObject': 'pm.response.headers.toObject',
'res.headerList.toString': 'pm.response.headers.toString',
'res.headerList.toJSON': 'pm.response.headers.toJSON',

// Cookies jar
'bru.cookies.jar': 'pm.cookies.jar',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,8 @@ const simpleTranslations = {
'pm.request.headers.map': 'req.headerList.map',
'pm.request.headers.reduce': 'req.headerList.reduce',
'pm.request.headers.toObject': 'req.headerList.toObject',
'pm.request.headers.toString': 'req.headerList.toString',
'pm.request.headers.toJSON': 'req.headerList.toJSON',
'pm.request.headers.clear': 'req.headerList.clear',

// Response headers PropertyList methods (read-only)
Expand All @@ -79,6 +81,8 @@ const simpleTranslations = {
'pm.response.headers.map': 'res.headerList.map',
'pm.response.headers.reduce': 'res.headerList.reduce',
'pm.response.headers.toObject': 'res.headerList.toObject',
'pm.response.headers.toString': 'res.headerList.toString',
'pm.response.headers.toJSON': 'res.headerList.toJSON',

// Request properties (pm.request.*)
'pm.request.url.getHost': 'req.getHost',
Expand Down Expand Up @@ -409,6 +413,32 @@ const complexTransformations = [
}
},

// Lossy: positional header inserts → append (only keep the first arg, drop positional ref)
// pm.request.headers.prepend(item) -> req.headerList.append(item)
{
pattern: 'pm.request.headers.prepend',
transform: (path, j) => {
const args = path.parent.value.arguments;
return j.callExpression(j.identifier('req.headerList.append'), args.length > 0 ? [args[0]] : []);
}
},
// pm.request.headers.insert(item, before) -> req.headerList.append(item)
{
pattern: 'pm.request.headers.insert',
transform: (path, j) => {
const args = path.parent.value.arguments;
return j.callExpression(j.identifier('req.headerList.append'), args.length > 0 ? [args[0]] : []);
}
},
// pm.request.headers.insertAfter(item, after) -> req.headerList.append(item)
{
pattern: 'pm.request.headers.insertAfter',
transform: (path, j) => {
const args = path.parent.value.arguments;
return j.callExpression(j.identifier('req.headerList.append'), args.length > 0 ? [args[0]] : []);
}
},

// pm.response.to.be.ok -> expect(res.getStatus()).to.be.within(200, 299)
{
pattern: 'pm.response.to.be.ok',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -292,6 +292,18 @@ console.log("Headers:", JSON.stringify(pm.request.headers));
expect(translatedCode).toBe('const obj = pm.request.headers.toObject();');
});

it('should translate req.headerList.toString to pm.request.headers.toString', () => {
const code = 'const str = req.headerList.toString();';
const translatedCode = translateBruToPostman(code);
expect(translatedCode).toBe('const str = pm.request.headers.toString();');
});

it('should translate req.headerList.toJSON to pm.request.headers.toJSON', () => {
const code = 'const json = req.headerList.toJSON();';
const translatedCode = translateBruToPostman(code);
expect(translatedCode).toBe('const json = pm.request.headers.toJSON();');
});

it('should translate req.headerList.set to pm.request.headers.upsert', () => {
const code = 'req.headerList.set({key: "X-Custom", value: "updated"});';
const translatedCode = translateBruToPostman(code);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -201,9 +201,39 @@ describe('Request Translation', () => {
expect(translatedCode).toBe('req.headerList.clear();');
});

it('should translate pm.request.headers.prepend to req.headerList.append', () => {
const code = 'pm.request.headers.prepend({key: "X-First", value: "1"});';
const translatedCode = translateCode(code);
expect(translatedCode).toBe('req.headerList.append({key: "X-First", value: "1"});');
});

it('should translate pm.request.headers.insert to req.headerList.append (drops positional ref)', () => {
const code = 'pm.request.headers.insert({key: "X-Mid", value: "2"}, "ref");';
const translatedCode = translateCode(code);
expect(translatedCode).toBe('req.headerList.append({key: "X-Mid", value: "2"});');
});

it('should translate pm.request.headers.insertAfter to req.headerList.append (drops positional ref)', () => {
const code = 'pm.request.headers.insertAfter({key: "X-After", value: "3"}, "ref");';
const translatedCode = translateCode(code);
expect(translatedCode).toBe('req.headerList.append({key: "X-After", value: "3"});');
});

it('should translate pm.request.headers.toObject to req.headerList.toObject', () => {
const code = 'const obj = pm.request.headers.toObject();';
const translatedCode = translateCode(code);
expect(translatedCode).toBe('const obj = req.headerList.toObject();');
});

it('should translate pm.request.headers.toString to req.headerList.toString', () => {
const code = 'const str = pm.request.headers.toString();';
const translatedCode = translateCode(code);
expect(translatedCode).toBe('const str = req.headerList.toString();');
});

it('should translate pm.request.headers.toJSON to req.headerList.toJSON', () => {
const code = 'const json = pm.request.headers.toJSON();';
const translatedCode = translateCode(code);
expect(translatedCode).toBe('const json = req.headerList.toJSON();');
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -894,4 +894,16 @@ describe('Response Translation', () => {
const translatedCode = translateCode(code);
expect(translatedCode).toBe('const obj = res.headerList.toObject();');
});

it('should translate pm.response.headers.toString to res.headerList.toString', () => {
const code = 'const str = pm.response.headers.toString();';
const translatedCode = translateCode(code);
expect(translatedCode).toBe('const str = res.headerList.toString();');
});

it('should translate pm.response.headers.toJSON to res.headerList.toJSON', () => {
const code = 'const json = pm.response.headers.toJSON();';
const translatedCode = translateCode(code);
expect(translatedCode).toBe('const json = res.headerList.toJSON();');
});
});
9 changes: 7 additions & 2 deletions packages/bruno-js/src/header-list.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
const PropertyList = require('./property-list');
const ReadOnlyPropertyList = require('./readonly-property-list');

/**
Expand Down Expand Up @@ -78,7 +77,7 @@ const ReadOnlyPropertyList = require('./readonly-property-list');
* | `repopulate(items)` | Clears all, then populates with new items |
* | `assimilate(source, prune?)` | Merges headers; prune removes items not in source |
*/
class HeaderList extends PropertyList {
class HeaderList extends ReadOnlyPropertyList {
#req;
#writable;

Expand Down Expand Up @@ -151,6 +150,12 @@ class HeaderList extends PropertyList {
return { key: str.substring(0, idx).trim(), value: str.substring(idx + 1).trim() };
}

// ── Blocked inherited methods ─────────────────────────────────────────
// These are inherited from ReadOnlyPropertyList but are not part of
// the HeaderList API. Set to undefined so they are not callable.
idx = undefined;
each = undefined;
Comment thread
coderabbitai[bot] marked this conversation as resolved.

// ── Read method overrides (case-insensitive) ──────────────────────────

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ const addBrunoRequestShimToContext = (vm, req) => {
const { evalCode: headersEvalCode } = createPropertyListBridge(vm, req.headerList, headerListObj, {
globalPath: 'globalThis.req.headerList',
syncReadMethods: ['get', 'has', 'count', 'indexOf', 'toObject', 'toString'],
syncReadObjectMethods: ['one', 'all', 'idx', 'toJSON'],
syncReadObjectMethods: ['one', 'all', 'toJSON'],
syncWriteMethods: ['append', 'set', 'delete', 'clear', 'populate', 'repopulate', 'assimilate'],
withIterators: true
});
Expand Down Expand Up @@ -197,7 +197,7 @@ const addBrunoRequestShimToContext = (vm, req) => {
// Wrapped in a block to avoid const redeclaration conflicts with other evalCode blocks
// The bridge generates `each` (shared with CookieList); alias `forEach` for HeaderList's MDN-style API
if (headersEvalCode) {
vm.evalCode(`{ ${headersEvalCode} globalThis.req.headerList.forEach = globalThis.req.headerList.each; }`);
vm.evalCode(`{ ${headersEvalCode} globalThis.req.headerList.forEach = globalThis.req.headerList.each; delete globalThis.req.headerList.each; }`);
}
};

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ const addBrunoResponseShimToContext = (vm, res) => {
const bridge = createPropertyListBridge(vm, res.headerList, headerListObj, {
globalPath: 'globalThis.res.headerList',
syncReadMethods: ['get', 'has', 'count', 'indexOf', 'toObject', 'toString'],
syncReadObjectMethods: ['one', 'all', 'idx', 'toJSON'],
syncReadObjectMethods: ['one', 'all', 'toJSON'],
syncWriteMethods: ['append', 'set', 'delete', 'clear', 'populate', 'repopulate', 'assimilate'],
withIterators: true
});
Expand Down Expand Up @@ -133,7 +133,7 @@ const addBrunoResponseShimToContext = (vm, res) => {
// Wrapped in a block to avoid const redeclaration conflicts with req.headerList's evalCode
// The bridge generates `each` (shared with CookieList); alias `forEach` for HeaderList's MDN-style API
if (resHeadersEvalCode) {
vm.evalCode(`{ ${resHeadersEvalCode} globalThis.res.headerList.forEach = globalThis.res.headerList.each; }`);
vm.evalCode(`{ ${resHeadersEvalCode} globalThis.res.headerList.forEach = globalThis.res.headerList.each; delete globalThis.res.headerList.each; }`);
}
};

Expand Down
20 changes: 1 addition & 19 deletions packages/bruno-js/tests/header-list.spec.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
const HeaderList = require('../src/header-list');
const PropertyList = require('../src/property-list');
const ReadOnlyPropertyList = require('../src/readonly-property-list');
const BrunoRequest = require('../src/bruno-request');
const BrunoResponse = require('../src/bruno-response');
Expand All @@ -19,10 +18,9 @@ describe('HeaderList (req.headerList)', () => {

// ── Inheritance ────────────────────────────────────────────────────────

test('extends PropertyList and ReadOnlyPropertyList', () => {
test('extends ReadOnlyPropertyList', () => {
const { list } = createReqHeaders();
expect(list).toBeInstanceOf(ReadOnlyPropertyList);
expect(list).toBeInstanceOf(PropertyList);
expect(list).toBeInstanceOf(HeaderList);
});

Expand Down Expand Up @@ -73,17 +71,6 @@ describe('HeaderList (req.headerList)', () => {
expect(a1).not.toBe(a2);
});

test('idx() returns header at position', () => {
const { list } = createReqHeaders();
expect(list.idx(0)).toEqual({ key: 'Content-Type', value: 'application/json' });
expect(list.idx(2)).toEqual({ key: 'Accept', value: '*/*' });
});

test('idx() returns undefined for out-of-bounds', () => {
const { list } = createReqHeaders();
expect(list.idx(10)).toBeUndefined();
});

test('count() returns number of headers', () => {
const { list } = createReqHeaders();
expect(list.count()).toBe(3);
Expand Down Expand Up @@ -923,11 +910,6 @@ describe('Response Headers (res.headerList)', () => {
expect(headerList.count()).toBe(3);
});

test('idx() returns header at position', () => {
const { headerList } = createResHeaders();
expect(headerList.idx(1)).toEqual({ key: 'x-request-id', value: 'abc-123' });
});

test('indexOf() finds structurally-equal header', () => {
const { headerList } = createResHeaders();
expect(headerList.indexOf({ key: 'content-type', value: 'application/json' })).toBe(0);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,13 +44,6 @@ tests {
expect(keys).to.include('x-custom');
});

test("req.headerList.idx(index)", function() {
const first = req.headerList.idx(0);
expect(first).to.have.property('key');
expect(first).to.have.property('value');
expect(req.headerList.idx(-1)).to.be.undefined;
});

test("req.headerList.count()", function() {
expect(req.headerList.count()).to.be.at.least(3);
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,13 +40,6 @@ tests {
expect(keys).to.include('x-powered-by');
});

test("res.headerList.idx(index)", function() {
const first = res.headerList.idx(0);
expect(first).to.have.property('key');
expect(first).to.have.property('value');
expect(res.headerList.idx(-1)).to.be.undefined;
});

test("res.headerList.count()", function() {
expect(res.headerList.count()).to.be.at.least(1);
});
Expand Down
Loading