mirror of
https://github.com/usebruno/bruno.git
synced 2026-07-09 06:55:03 +00:00
* 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.
228 lines
6.8 KiB
JavaScript
228 lines
6.8 KiB
JavaScript
/**
|
||
* 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;
|