Files
bruno/packages/bruno-js/src/readonly-property-list.js
sanish chirayath bef4b6bbee feat(cookies): add direct cookie access methods and update translations (#7073)
* feat(cookies): add direct cookie access methods and update translations

- Introduced new methods for direct cookie access: `bru.cookies.get`, `bru.cookies.has`, and `bru.cookies.toObject`.
- Updated translation mappings in `bruno-to-postman-translator` and `postman-to-bruno-translator` to support these new methods.
- Enhanced tests to verify correct translation between `bru` and `pm` cookie methods, including mixed usage scenarios.
- Updated `Bru` class to handle cookie access based on the current request URL.

* feat(cookies): enhance cookie management with new methods and refactor

- Added new cookie methods: `toString`, `clear`, `delete`, `one`, `all`, `idx`, `count`, `indexOf`, `find`, `filter`, `each`, `map`, and `reduce` to `bru.cookies`.
- Refactored `Bru` class to utilize a new `CookieList` for cookie management, improving structure and readability.
- Updated translation mappings in `bruno-to-postman-translator` and `postman-to-bruno-translator` to include new cookie methods.
- Introduced `PropertyList` and `ReadOnlyPropertyList` classes for better data structure management.
- Enhanced tests for comprehensive coverage of new cookie functionalities and their interactions.

* docs(readonly-property-list): clarify array usage in constructor comments

* feat(cookies): add direct cookie manipulation tests and methods

* feat(cookies): add hasCookie method for checking cookie existence

* fix

* refactor(cookies): simplify cookie method translations

* feat(cookies): expand cookie API with new methods and tests

- Added new cookie methods: `get`, `has`, `toString`, `clear`, `upsert`, `remove`, `idx`, and `indexOf` to enhance cookie management.
- Updated translation mappings for `bru.cookies` to include new methods in `bruno-to-postman-translator` and `postman-to-bruno-translator`.
- Introduced tests for new methods and their interactions, ensuring comprehensive coverage of cookie functionalities.
- Enhanced existing tests to validate correct behavior of cookie methods across different scenarios.

* refactor(cookies): update CookieList to extend PropertyList and improve error handling

* test(cookies): add regression tests for jar and direct cookie patterns

- Introduced regression tests to ensure that jar patterns are correctly prioritized over direct cookie access patterns in translations.
- Updated `CookieList` to extend `ReadOnlyPropertyList` instead of `PropertyList`, clarifying its functionality.
- Refactored cookie method handling in the `bru` shim to utilize a new asynchronous bridge for improved error handling and consistency.

* refactor(cookies): update translations and remove PropertyList

- Enhanced comments in `postman-translations.js` to clarify the order of cookie jar translations.
- Updated `cookie-list.js` comments to better describe the factory function for the cookie jar.
- Removed the `PropertyList` class and its associated tests, streamlining the codebase and focusing on `ReadOnlyPropertyList` and `CookieList` for cookie management.

* fix(cookies): normalize tough-cookie objects and improve remove method comments

- Updated `CookieList` to normalize tough-cookie instances to plain objects, preventing circular references and exposing internal structures.
- Enhanced comments in the `remove` method to clarify behavior when removing non-existent or empty-named cookies.

* test(cookies): update tests to use async/await for consistency

* test(cookies): use async/await in cookie tests for consistency

* refactor(readonly-property-list): update get and reduce methods for improved behavior

* fix(cookies): update cookie method signature in autocomplete hints and enhance translation comments

- Modified the autocomplete hint for `bru.cookies.has` to include the new signature with an optional value parameter.
- Improved comments in `postman-translations.js` to clarify the order of cookie jar translation patterns for better understanding.

* refactor(cookies): introduce PropertyList for enhanced cookie management

* refactor(property-list): simplify repopulate method and enhance item handling logic

* feat(cookies): implement PropertyList bridge for enhanced cookie management

- Introduced a new `createPropertyListBridge` utility to streamline the integration of cookie methods into the QuickJS VM.
- Replaced the previous async cookie bridge with a more flexible approach, allowing for both synchronous and asynchronous cookie operations.
- Added comprehensive tests to validate the functionality of the new cookie methods in both developer and safe modes.
- Updated existing cookie tests to ensure compatibility with the new PropertyList structure.

* fix(tests): correct expected passed requests in cookie tests

- Updated the expected number of passed requests in the cookie tests from 34 to 6 to reflect the correct validation results.
- Ensured consistency in test assertions across multiple test cases for the PropertyList API.

* fix(cookies): update cookie URLs to use localhost for testing

- Changed all cookie-related test scripts to use `{{localhost}}` instead of `{{host}}` for the ping URL, ensuring consistency in local testing environments.
- Updated the cookie test suite to reflect the new URL structure, enhancing the reliability of the tests.
- Removed outdated cookie test files to streamline the test suite.

* refactor(cookies): standardize cookie handling with localhost variable

- Updated cookie test scripts to utilize the `{{localhost}}` variable for setting and retrieving cookies, ensuring consistency across tests.
- Enhanced clarity in comments regarding cookie behavior for different domains.
- Improved test assertions to validate cookie management functionality more effectively.

* refactor(property-list, readonly-property-list): update methods to use private class fields

* feat(cookies): enhance CookieList API with detailed documentation and method improvements

- Updated the `CookieList` class to provide comprehensive documentation on cookie management methods, including `add`, `upsert`, `remove`, and `delete`.
- Improved method signatures to support both callback and Promise-based usage for asynchronous operations.
- Added detailed descriptions for read and write methods, including examples and expected behavior.
- Enhanced the integration of the `CookieList` with the QuickJS VM by updating the property list bridge to include `toJSON` in sync read object methods.

* feat(cookies): add detailed examples and improve async bridge documentation

- Enhanced the `createPropertyListBridge` function documentation with comprehensive examples for setting up cookie methods in QuickJS.
- Clarified the two-phase setup process for async write methods, detailing the registration of bridge functions and the generation of JavaScript code for method wrappers.
- Added a new test case for the `toJSON()` method to ensure it returns a cloned array of all cookies, validating the expected structure and properties.

* fix(assert-runtime): correct syntax error in response parser assignment

- Added a semicolon at the end of the response parser assignment to ensure proper syntax in the AssertRuntime class.
2026-03-27 19:42:23 +05:30

228 lines
6.8 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/**
* ReadOnlyPropertyList - A read-only collection data structure.
*
* Two modes:
* - Static mode: items stored internally in an array (for headers, query params, etc.)
* - Dynamic mode: a dataSource function returns fresh items on every read (for cookies)
*
* Items are plain objects with a configurable key property (keyProperty) and value property (valueProperty).
*
* This base class provides only read/search/iteration/transform methods.
* See PropertyList for static-mode mutation methods.
* See CookieList for async cookie-jar write methods.
*
* Convention:
* #field / #method truly private, inaccessible to subclasses
* _field / _method protected, intended for subclass access only
*/
class ReadOnlyPropertyList {
// ── Private fields (not accessible by subclasses) ────────────────────
#valueProperty;
#dataSource;
/**
* @param {object} options
* @param {string} [options.keyProperty='key'] - The property name used as the unique key
* @param {string} [options.valueProperty='value'] - The property name used as the value
* @param {Function} [options.dataSource] - Dynamic data source function (returns array of items)
* @param {Array} [options.items] - Initial items for static mode
*/
// Items are stored in an array (not a Map) to support positional access (idx, indexOf),
// ordered insertion (insert, insertAfter, prepend in PropertyList), and duplicate keys.
// At typical list sizes (cookies, headers) the O(n) key lookup is negligible.
constructor({ keyProperty = 'key', valueProperty = 'value', dataSource, items } = {}) {
this._keyProperty = keyProperty;
this.#valueProperty = valueProperty;
this._dynamic = typeof dataSource === 'function';
if (this._dynamic) {
this.#dataSource = dataSource;
} else {
this._items = Array.isArray(items) ? [...items] : [];
}
}
/**
* Returns the current list of items.
* In dynamic mode, calls the dataSource function.
* In static mode, returns the internal array.
*/
#getItems() {
return this._dynamic ? this.#dataSource() : this._items;
}
// ── Retrieval ──────────────────────────────────────────────────────────
/**
* Get the value of an item by its key.
* @param {string} name
* @returns {*} The value property of the matching item, or undefined
*/
get(name) {
const items = this.#getItems();
// Use findLast so that duplicate keys resolve to the last entry,
// consistent with toObject() which also gives last-wins semantics.
const item = items.findLast((i) => i[this._keyProperty] === name);
return item ? item[this.#valueProperty] : undefined;
}
/**
* Get the full item object by its key.
* @param {string} name
* @returns {object|undefined}
*/
one(name) {
const items = this.#getItems();
// Use findLast so that duplicate keys resolve to the last entry,
// consistent with get() and toObject() which also give last-wins semantics.
return items.findLast((i) => i[this._keyProperty] === name);
}
/**
* Get a cloned array of all items.
* @returns {Array}
*/
all() {
return [...this.#getItems()];
}
/**
* Get an item by its positional index.
* @param {number} index
* @returns {object|undefined}
*/
idx(index) {
return this.#getItems()[index];
}
/**
* Get the number of items.
* @returns {number}
*/
count() {
return this.#getItems().length;
}
/**
* Get the index of an item.
* Uses structural equality (matching by key and value) so it works
* even when the item is a copy rather than the same reference.
* @param {object} item
* @returns {number} -1 if not found
*/
indexOf(item) {
if (!item || typeof item !== 'object') return -1;
const items = this.#getItems();
const keyProp = this._keyProperty;
return items.findIndex(
(i) => i[keyProp] === item[keyProp] && i[this.#valueProperty] === item[this.#valueProperty]
);
}
// ── Search ─────────────────────────────────────────────────────────────
/**
* Check if an item with the given key exists.
* If value is provided, also checks that the item's value matches.
* @param {string} name
* @param {*} [value]
* @returns {boolean}
*/
has(name, value) {
const items = this.#getItems();
if (value !== undefined) {
return items.some((i) => i[this._keyProperty] === name && i[this.#valueProperty] === value);
}
return items.some((i) => i[this._keyProperty] === name);
}
/**
* Find the first item matching a predicate.
* @param {Function} predicate
* @returns {object|undefined}
*/
find(predicate) {
return this.#getItems().find(predicate);
}
/**
* Filter items by a predicate.
* @param {Function} predicate
* @returns {Array}
*/
filter(predicate) {
return this.#getItems().filter(predicate);
}
// ── Iteration ──────────────────────────────────────────────────────────
/**
* Iterate over each item.
* @param {Function} fn - Called with (item, index)
*/
each(fn) {
this.#getItems().forEach(fn);
}
/**
* Map over items.
* @param {Function} fn
* @returns {Array}
*/
map(fn) {
return this.#getItems().map(fn);
}
/**
* Reduce items.
* @param {Function} fn
* @param {*} [initialValue] - Optional initial accumulator value
* @returns {*}
*/
reduce(fn, ...rest) {
return rest.length ? this.#getItems().reduce(fn, rest[0]) : this.#getItems().reduce(fn);
}
// ── Transformation ─────────────────────────────────────────────────────
/**
* Convert to a plain object { key: value }.
* @returns {object}
*/
toObject() {
const result = {};
for (const item of this.#getItems()) {
result[item[this._keyProperty]] = item[this.#valueProperty];
}
return result;
}
/**
* Convert to a string "key=value; key2=value2".
* @returns {string}
*/
toString() {
return this.#getItems()
.map((i) => `${i[this._keyProperty]}=${i[this.#valueProperty]}`)
.join('; ');
}
/**
* Convert to JSON (returns the same as all()).
* @returns {Array}
*/
toJSON() {
return this.all();
}
/**
* Check if an object is an instance of ReadOnlyPropertyList.
* @param {*} obj
* @returns {boolean}
*/
static isPropertyList(obj) {
return obj instanceof ReadOnlyPropertyList;
}
}
module.exports = ReadOnlyPropertyList;