Node method and accessor delegation utility for creating clean object composition patterns
npx @tessl/cli install tessl/npm-delegates@1.0.0Delegates is a Node.js utility for creating delegation patterns in JavaScript. It allows methods and property accessors to be delegated from one object to a property of that object, providing a fluent API for setting up clean object composition without manually writing wrapper functions.
npm install delegatesconst delegate = require('delegates');const delegate = require('delegates');
// Create a simple object with a target property
const obj = {
request: {
method: 'GET',
url: '/api/users',
headers: { 'Content-Type': 'application/json' }
}
};
// Set up delegation from obj to obj.request
delegate(obj, 'request')
.method('send') // Delegate method calls
.access('method') // Delegate getter and setter
.getter('headers') // Delegate getter only
.setter('timeout') // Delegate setter only
.fluent('url'); // Delegate fluent accessor
// Now you can use delegated methods and properties directly on obj
obj.method = 'POST'; // Sets obj.request.method
console.log(obj.method); // Gets obj.request.method
obj.url('/api/posts'); // Sets obj.request.url and returns obj for chainingDelegates uses the delegation pattern to avoid writing repetitive proxy methods. Instead of manually creating getter/setter functions, it dynamically defines properties and methods on a host object that forward to a target property.
Key components:
The library supports both constructor-style (new Delegator()) and factory-style (delegate()) instantiation, with all configuration methods returning the delegator instance for method chaining.
Creates a delegator instance for configuring method and property delegation.
/**
* Creates a delegator instance used to configure delegation
* @param {Object} proto - The object to add delegated methods/properties to
* @param {string} target - The property name that methods/properties will be delegated to
* @returns {Delegator} Delegator instance for chaining configuration calls
*/
function delegate(proto, target);The constructor can be called with or without new:
delegate(proto, target)new delegate(proto, target)Delegates method calls from the host object to the target property.
/**
* Delegate method calls to the target object
* @param {string} name - The method name to delegate
* @returns {Delegator} Self for method chaining
*/
method(name);Usage Example:
const obj = {
request: {
send: function() { return 'sent'; },
abort: function() { return 'aborted'; }
}
};
delegate(obj, 'request')
.method('send')
.method('abort');
// Now obj.send() calls obj.request.send()
obj.send(); // Returns 'sent'
obj.abort(); // Returns 'aborted'Creates both getter and setter for a property (full accessor).
/**
* Create both getter and setter for a property
* @param {string} name - The property name to create accessor for
* @returns {Delegator} Self for method chaining
*/
access(name);Usage Example:
const obj = {
config: {
timeout: 5000,
retries: 3
}
};
delegate(obj, 'config')
.access('timeout')
.access('retries');
// Both getting and setting work
obj.timeout = 10000; // Sets obj.config.timeout
console.log(obj.timeout); // Gets obj.config.timeout (10000)Creates a getter property that delegates to the target object.
/**
* Create a getter property that delegates to the target object
* @param {string} name - The property name to create getter for
* @returns {Delegator} Self for method chaining
*/
getter(name);Usage Example:
const obj = {
response: {
get status() { return 200; },
get headers() { return { 'content-type': 'application/json' }; }
}
};
delegate(obj, 'response')
.getter('status')
.getter('headers');
// Read-only access to response properties
console.log(obj.status); // 200
console.log(obj.headers); // { 'content-type': 'application/json' }Creates a setter property that delegates to the target object.
/**
* Create a setter property that delegates to the target object
* @param {string} name - The property name to create setter for
* @returns {Delegator} Self for method chaining
*/
setter(name);Usage Example:
const obj = {
config: {
_timeout: 5000,
set timeout(value) { this._timeout = value; }
}
};
delegate(obj, 'config')
.setter('timeout');
// Write-only access to config property
obj.timeout = 10000; // Sets obj.config.timeoutCreates a fluent accessor that works as both getter and chainable setter.
/**
* Create a fluent accessor that works as both getter and chainable setter
* @param {string} name - The property name to create fluent accessor for
* @returns {Delegator} Self for method chaining
*/
fluent(name);Usage Example:
const obj = {
request: {
method: 'GET',
url: '/'
}
};
delegate(obj, 'request')
.fluent('method')
.fluent('url');
// Getter usage (no arguments)
console.log(obj.method()); // 'GET'
console.log(obj.url()); // '/'
// Setter usage (with argument) - returns obj for chaining
obj.method('POST').url('/api/users');
console.log(obj.request.method); // 'POST'
console.log(obj.request.url); // '/api/users'/**
* Delegator constructor function
* @constructor
* @param {Object} proto - The object to add delegated methods/properties to
* @param {string} target - The property name that delegation targets
*/
function Delegator(proto, target);
/**
* Delegator instance with chainable methods
*/
interface Delegator {
/** The object being extended with delegated functionality */
proto: Object;
/** The property name on proto that delegation targets */
target: string;
/** Array of delegated method names */
methods: string[];
/** Array of delegated getter names */
getters: string[];
/** Array of delegated setter names */
setters: string[];
/** Array of delegated fluent accessor names */
fluents: string[];
/** Delegate a method call */
method(name: string): Delegator;
/** Create both getter and setter */
access(name: string): Delegator;
/** Create a getter only */
getter(name: string): Delegator;
/** Create a setter only */
setter(name: string): Delegator;
/** Create a fluent accessor (getter/chainable setter) */
fluent(name: string): Delegator;
}const delegate = require('delegates');
// HTTP request-like object
function Request(options) {
this.options = options || {};
this.headers = {};
}
// Delegate multiple aspects to different properties
delegate(Request.prototype, 'options')
.access('method')
.access('url')
.access('timeout')
.fluent('json');
delegate(Request.prototype, 'headers')
.fluent('contentType')
.fluent('authorization');
// Usage
const req = new Request({ method: 'GET', url: '/api' });
req.method = 'POST';
req.contentType('application/json')
.authorization('Bearer token123');// Common pattern used in frameworks like Koa.js
function Context(req, res) {
this.request = req;
this.response = res;
}
// Delegate request properties and methods
delegate(Context.prototype, 'request')
.method('acceptsLanguages')
.method('acceptsCharsets')
.access('querystring')
.access('search')
.getter('protocol')
.getter('secure');
// Delegate response properties and methods
delegate(Context.prototype, 'response')
.method('redirect')
.access('status')
.access('body')
.setter('type');