UNPKG

http-simple-proxy

Version:

Simple http/https proxy daemon. Proxying based on the excellent node-http-master.

171 lines (151 loc) 4.28 kB
'use strict'; var extend = require('extend'); var assert = require('assert'); var functionRegex = /function\s*([^()]*)\s*\(([^)]*)/; // get function arguments as array function functionParameters(f) { assert(typeof f === 'function'); var matches = functionRegex.exec(f.toString()); if(!matches || !matches[2].length) { return []; } return matches[2].split(/[,\s]+/).filter(function(str) { return str.length; }); } function functionName(f) { assert(typeof f === 'function'); var matches = functionRegex.exec(f.toString()); if(!matches || !matches[1].length) { return null; } return matches[1]; } // call new operator with array of arguments function construct(constructor, args) { function F() { return constructor.apply(this, args); } F.prototype = constructor.prototype; return new F(); } function DI() { this.mapping = {}; this.parent = null; this.onMissing = function(name) { } } function stringToCamelCase(str) { return str.replace(/^\w/g, function(txt) { return txt.charAt(0).toLowerCase(); }); } function bindTypeGeneric(doCache, name, type) { // implicit name from function name if(typeof name === 'function') { type = name; var constructorName = functionName(type); if(!constructorName) { throw new Error('Unable to resolve name from function'); } // implicitly bind function MyType {} to // MyType - type instance // myType - instance var instanceName = stringToCamelCase(constructorName); if(instanceName !== constructorName) { try { this.bindInstance(constructorName, type); } catch(err) { // ignore error since we are already doing this implicitly } } name = instanceName; } if(this.mapping[name]) { throw new Error('\'' + name + '\' already bound'); } var self = this; var mapping = { // this.cache is saved in mapping // 'this' is mapping resolve: function(overrides) { var result = this.cache; if(!result) { result = construct(type, functionParameters(type).map(function(paramName) { return self.resolve(paramName, overrides); })); } if(doCache) { this.cache = result; } return result; } }; this.mapping[name] = mapping; } DI.prototype.bindInstance = function(name, instance) { if(this.mapping[name]) { throw new Error('\'' + name + '\' already bound'); } this.mapping[name] = { resolve: function() { return instance; } }; }; DI.prototype.bindResolver = function(name, resolver) { if(this.mapping[name]) { throw new Error('\'' + name + '\' already bound'); } this.mapping[name] = { resolve: resolver }; } DI.prototype.bindType = function(name, type) { return bindTypeGeneric.call(this, true, name, type); }; DI.prototype.bindTransientType = function(name, type) { return bindTypeGeneric.call(this, false, name, type); }; DI.prototype.resolve = function(obj, overrides) { var dependencyMap = extend({}, this.mapping, overrides); var args; var resolved; if(typeof obj === 'function') { args = functionParameters(obj); resolved = construct(obj, args.map(this.resolve.bind(this))); } else if(typeof obj === 'string') { if(dependencyMap[obj]) { if(dependencyMap[obj].resolve) { resolved = dependencyMap[obj].resolve(overrides); } else { resolved = dependencyMap[obj]; } } } else { throw new Error('Unknown type to resolve'); } if(typeof resolved ==='undefined' && this.onMissing) { resolved = this.onMissing(obj); } if(typeof resolved === 'undefined' && this.parent) { // search in parent if not found resolved = this.parent.resolve(obj, overrides); } if(typeof resolved === 'undefined' && !dependencyMap[obj]) { throw new Error('No recipe to resolve \'' + obj + '\''); } return resolved; } DI.prototype.clone = function() { var cloned = new DI(); cloned.mapping = extend({}, this.mapping); cloned.parent = this.parent; cloned.onMissing = this.onMissing; return cloned; }; DI.prototype.makeChild = function() { var child = construct(DI, Array.prototype.slice.apply(arguments)); child.parent = this; return child; }; module.exports = DI;