{"_id":"o-method-proxy","_rev":"1-4708a413cb4604496bf70c9b8ad581e1","name":"o-method-proxy","dist-tags":{"latest":"1.0.0"},"versions":{"1.0.0":{"name":"o-method-proxy","version":"1.0.0","description":"Utility to create configurable Proxies","main":"index.js","scripts":{"test":"npx mocha --config ./utilities-config/.mocharc.json"},"keywords":["utilities","helpers","proxy","metaprogramming","introspection","reflection"],"author":{"name":"Martin Rubi"},"license":"ISC","homepage":"https://haijin-development@bitbucket.org/haijin-development/method-proxy.git","devDependencies":{"chai":"^4.2.0","eslint":"^7.0.0","eslint-config-standard":"^14.1.1","eslint-plugin-import":"^2.20.2","eslint-plugin-node":"^11.1.0","eslint-plugin-promise":"^4.2.1","eslint-plugin-standard":"^4.0.1","mocha":"^7.1.2","nyc":"^15.0.1"},"gitHead":"b76f5cf8d5e26b6e9f68a535474ad519872a5728","_id":"o-method-proxy@1.0.0","_nodeVersion":"12.16.2","_npmVersion":"6.14.4","dist":{"integrity":"sha512-RtYs6NKYYVB7aZIXtthOiQdh65uHV8M6FEsHzz+OLFlXZuFmtC7Hgs4L05kvFKni2gojcmN8qjsr8NRbbafL3g==","shasum":"8447375dcf23aeef7893b6165301e81a937fff9f","tarball":"https://registry.npmjs.org/o-method-proxy/-/o-method-proxy-1.0.0.tgz","fileCount":5,"unpackedSize":31090,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJe0m/2CRA9TVsSAnZWagAAP50P/29keb0n9vYcolQhQrAK\nyiW6MO4j0foQIgAg2NTXSIAMopR1Dz+zdURASMA28zlbsLpRz7bCa2Mx02Iu\nJip9wjlFCJr7s2qkIYQP1ieoBdgQHhw2elVLSZEiOiinMCbWG95qbaJ9pFk2\nikZhvyW7hBq34e4WRzhXjpYnYRgXkm29VfYyTKNA7P7AbyNcs27EaookWQBF\np+iwKXi2rhDx2Z9vyMVLxflYc64TtnFlYUe3dzKb5S0T7fEGw+X9cxvR8xqR\nacimkwFK1rvIlgz64tIhrlXIjQf2W0RdmYXE2D0BHgvqo7dk3jCqjH3lCmPM\nxkHKXBsU0hVreNWNg6POTf9B3wAKm1aze9QDi6EjaWZZUgCJctAmCs1h24Uk\nN3Ls1p44HohnIh8cej5UjeD4XWMvRkhI2alfcNowQ//tmVnpLFZam3dOQRzz\n96sC1OGLNz/VaJHXeqBRfA7dq9jb0oDG3UdWap1Nf63aRipyeko9Yk1uOuiD\nwhNtxDu1jKWJMg19QGQ/4fUIcogHB3e8Jg5cPj4CM38xSTqCYsULAbEcZ1s4\nmtn2FBa6CVQFDmLwP2EnMAOYJD5XHOhpeyXE2MkQRIM0B7GB+pRp7QC3+Z84\nW2bIdw4LyvB+9E7aaV+ABt1gcLyQLXBfv320l6fBgHED4+tD9IZKsZipNm1E\nZTe6\r\n=1K+Y\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIGDB/LpcOBCGkA8zMN4DFHG+biPoNSBeN1Fqy/vv+KBBAiAr7Xs7GkaXt+6GYBC1qNbB0MYcKIoAfdXTYJokJOYHBg=="}]},"maintainers":[{"name":"haijindev","email":"haijin.development@gmail.com"}],"_npmUser":{"name":"haijindev","email":"haijin.development@gmail.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/o-method-proxy_1.0.0_1590849526007_0.19642729510439327"},"_hasShrinkwrap":false}},"time":{"created":"2020-05-30T14:38:46.007Z","1.0.0":"2020-05-30T14:38:46.160Z","modified":"2022-05-12T00:02:33.439Z"},"maintainers":[{"name":"haijindev","email":"haijin.development@gmail.com"}],"description":"Utility to create configurable Proxies","homepage":"https://haijin-development@bitbucket.org/haijin-development/method-proxy.git","keywords":["utilities","helpers","proxy","metaprogramming","introspection","reflection"],"author":{"name":"Martin Rubi"},"license":"ISC","readme":"# MethodProxy\n\nHelper to create configurable Proxies.\n\n# What can I use a Proxy for?\n\nPretty much for anything you can possibly think of. Just try your best not to.\n\nHere's a short list of topics where MethodProxy can be used:\n\n- [Transparent remote procedure calls](examples/remoteProcedureCall.js)\n- Transparent lazy loading from a database, file or remote server\n- Revertible object freezing\n- Transparent persistency of objects to a database\n- [Transparent logging of an object method call](examples/logging.js)\n- [Mock and stub objects](examples/stubDouble.js)\n- [Emulate traits and dynamically add behaviour to existing objects](examples/encryptingInMemory.js)\n- [Implement dynamic public/private access to methods and properties](examples/privateMethods.js)\n- Benchmark and trace method calls\n- Transparent dynamic compilation or optimization of a method on its first call\n- Implement syncronous Promises and Futures\n- Transparent validation of methods parameters and object invariants\n\nTake a look at the [examples](/examples) for some interesting uses of the MethodProxy.\n\nOn the other hand the use of Proxies may have a cost in performance, it difficults debugging quite a lot and care needs to be taken in aspects like the use of `instaceof`, `typeof`, chaining proxies, `getOwnProperties` , etc.\n\n\n## Installation\n\n```\nnpm install o-method-proxy\n```\n\n## Usage\n\n### Overriding an object method by the method name\n\nOverride a single method by its name.\n\n```javascript\nconst MethodProxy = require('o-method-proxy')\nconst util = require('util')\n\nconst proxyDefinition = (proxy) => {\n  proxy.on({\n    method: 'setDescription', // override only the method 'setDescription'\n    evaluate: function (proxy, target, methodName, args) {\n      console.info(`setDescription was called on the object '${util.inspect(target)}' with arguments '${args}'`) // do something like logging the method call\n      const targetMethod = targetObject[methodName]\n      return Reflect.apply(targetMethod, proxy, args) // call the underlaying target method\n    }\n  })\n}\n\n// Get or create the target object\nconst targetObject = new Product()\n// Wrap it with the proxy\nconst product = MethodProxy.on(targetObject, proxyDefinition)\n\n// Then use the proxy as it if was the regular object\nproduct.setDescription('A product')\nproduct.getDescription()\n```\n\n### Overriding many methods by name\n\nOverride many methods by name.\n\n```javascript\nconst MethodProxy = require('o-method-proxy')\nconst util = require('util')\n\nconst proxyDefinition = (proxy) => {\n  proxy.on({\n    method: 'setDescription',\n    evaluate: function (proxy, target, methodName, args) {\n      console.info(`setDescription was called on the object '${util.inspect(target)}' with arguments '${args}'`)\n      const targetMethod = targetObject[methodName]\n      return Reflect.apply(targetMethod, proxy, args)\n    }\n  })\n  proxy.on({\n    method: 'setName',\n    evaluate: function (proxy, target, methodName, args) {\n      console.info(`setName was called on the object '${util.inspect(target)}' with arguments '${args}'`)\n      const targetMethod = targetObject[methodName]\n      return Reflect.apply(targetMethod, proxy, args)\n    }\n  })\n}\n\n// Get or create the target object\nconst targetObject = new Product()\nconst product = MethodProxy.on(targetObject, proxyDefinition)\n\n// Then use the proxy as it if was the regular object\nproduct.setDescription('A product')\nproduct.getDescription()\n```\n\n### Overriding many methods by name at once\n\nOverride many method by its name at once sharing the same override function.\n\n```javascript\nconst MethodProxy = require('o-method-proxy')\nconst util = require('util')\n\nconst proxyDefinition = (proxy) => {\n  proxy.on({\n    methods: ['setDescription', 'setName']\n    evaluate: function (proxy, target, methodName, args) {\n      console.info(`${methodName} was called on the object '${util.inspect(target)}' with arguments '${args}'`)\n      const targetMethod = targetObject[methodName]\n      return Reflect.apply(targetMethod, proxy, args)\n    }\n  })\n}\n\n// Get or create the target object\nconst targetObject = new Product()\nconst product = MethodProxy.on(targetObject, proxyDefinition)\n\n// Then use the proxy as it if was the regular object\nproduct.setDescription('A product')\nproduct.getDescription()\n```\n\n### Overriding many methods matching a criteria\n\nOverride all the methods matching a criteria.\n\nThe matching block has the form\n\n```javascript\nfunction(methodName, proxy, target) {\n  return true|false\n},\n```\n\nand it's expected to return true if the method should be overriden or false if not.\n\n```javascript\nconst MethodProxy = require('o-method-proxy')\nconst util = require('util')\n\nconst proxyDefinition = (proxy) => {\n  proxy.on({\n    methodMatching: (methodName, proxy, target) => { return methodName.startsWith('set') },\n    evaluate: function (proxy, target, methodName, args) {\n      console.info(`${methodName} was called on the object '${util.inspect(target)}' with arguments '${args}'`)\n      const targetMethod = targetObject[methodName]\n      return Reflect.apply(targetMethod, proxy, args)\n    }\n  })\n}\n\n// Get or create the target object\nconst targetObject = new Product()\nconst product = MethodProxy.on(targetObject, proxyDefinition)\n\n// Then use the proxy as it if was the regular object\nproduct.setDescription('A product')\nproduct.getDescription()\n```\n\n### Overriding absent methods\n\nOverride only the methods that are not defined in the target object.\n\n```javascript\nconst MethodProxy = require('o-method-proxy')\nconst util = require('util')\n\nconst proxyDefinition = (proxy) => {\n  proxy.on({\n    absentMethod: function (proxy, target, methodName, args) {\n      throw new Error(`Target object '${util.inspect(target)}' does not implement the method ${methodName}`)\n    }\n  })\n}\n\n// Get or create the target object\nconst targetObject = new Product()\nconst product = MethodProxy.on(targetObject, proxyDefinition)\n\n// Then use the proxy as it if was the regular object\nproduct.setdescription('A product')\nproduct.getDescription()\n```\n\n### Overriding all methods\n\nOverride all the messages received by the target object, including both its absent and defined\nmethods.\n\n```javascript\nconst MethodProxy = require('o-method-proxy')\nconst util = require('util')\n\nconst proxyDefinition = (proxy) => {\n  proxy.on({\n    allMethods: function (proxy, target, methodName, args) {\n      console.info(`${methodName} was called on the object '${util.inspect(target)}' with arguments '${args}'`)\n      const targetMethod = targetObject[methodName]\n      return Reflect.apply(targetMethod, proxy, args)\n    }\n  })\n}\n\n// Get or create the target object\nconst targetObject = new Product()\nconst product = MethodProxy.on(targetObject, proxyDefinition)\n\n// Then use the proxy as it if was the regular object\nproduct.setdescription('A product')\nproduct.getDescription()\n```\n\n### Combining all the previous overrides\n\nAll the previous overrides can be combined in the same proxy.\n\nThe priority of the overrides is as follows:\n\n1. `method:` and `methods:` methods has the higher priority\n2. `methodMatching:`\n3. `absentMethod:`\n4. `allMethods:`\n\nTake a look at the [examples](/examples) for some interesting uses of the MethodProxy.\n\n### Properties overrides\n\nProxyMethod can also override gets and sets of an object properties and/or the rest of the js Proxy handlers defined in the [ECMAScript standard](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Proxy/handler)\n\nThe complete protocol of ProxyMethod is as follows\n\n\n```javascript\nconst MethodProxy = require('o-method-proxy')\nconst util = require('util')\n\nconst proxyDefinition = (proxy) => {\n  /// Methods overrides\n\n  proxy.on({\n    method: 'setDescription',\n    evaluate: function (proxy, target, methodName, args) {\n      // ...\n    }\n  })\n\n  proxy.on({\n    methodMatching: (methodName, proxy, target) => { return methodName.startsWith('set') },\n    evaluate: function (proxy, target, methodName, args) {\n      // ...\n    }\n  })\n\n  proxy.on({\n    methodMatching: (methodName, proxy, target) => { return methodName.startsWith('set') },\n    evaluate: function (proxy, target, methodName, args) {\n      // ...\n    }\n  })\n\n  proxy.on({\n    absentMethod: function (proxy, target, methodName, args) {\n      // ...\n    }\n  })\n\n  proxy.on({\n    allMethods: function (proxy, target, methodName, args) {\n      // ...\n    }\n  })\n\n  /// Properties overrides\n\n  proxy.on({\n    propertyGet: 'n',\n    evaluate: function (proxy, target, propertyName) {\n      // ...\n    }\n  })\n\n  proxy.on({\n    propertiesGet: ['n', 'm'],\n    evaluate: function (proxy, target, propertyName) {\n      // ...\n    }\n  })\n\n  proxy.on({\n    propertyGetMatching: function (propertyName) { return propertyName.startsWith('___') },\n    evaluate: function (proxy, target, propertyName) {\n      // ...\n    }\n  })\n\n  proxy.on({\n    absentPropertyGet: function (proxy, target, propertyName) {\n      // ...\n    }\n  })\n\n  proxy.on({\n    allPropertiesGet: function (proxy, target, propertyName) {\n      // ...\n    }\n  })\n\n  proxy.on({\n    propertySet: 'n',\n    evaluate: function (proxy, target, propertyName) {\n      // ...\n    }\n  })\n\n  proxy.on({\n    allPropertiesSet: function (proxy, target, propertyName, value) {\n      // ...\n    }\n  })\n\n  proxy.on({\n    propertySet: 'n',\n    evaluate: function (proxy, target, propertyName) {\n      // ...\n    }\n  })\n\n  proxy.on({\n    absentPropertySet: function (proxy, target, propertyName, value) {\n      // ...\n    }\n  })\n\n  proxy.on({\n    allPropertiesSet: function (proxy, target, propertyName, value) {\n      // ...\n    }\n  })\n\n  /// Handlers overrides\n\n  proxy.on({\n    getPrototypeOf: function (target) {\n      // ...\n    }\n  })\n\n  proxy.on({\n    setPrototypeOf: function (target, objectPrototype) {\n      // ...\n    }\n  })\n\n  proxy.on({\n    isExtensible: function (target) {\n      // ...\n    }\n  })\n\n  proxy.on({\n    preventExtensions: function (target) {\n      // ...\n    }\n  })\n\n  proxy.on({\n    getOwnPropertyDescriptor: function (target, property) {\n      // ...\n    }\n  })\n\n  proxy.on({\n    defineProperty: function (target, property, descriptor) {\n      // ...\n    }\n  })\n\n  proxy.on({\n    has: function (target, property) {\n      // ...\n    }\n  })\n\n  proxy.on({\n    get: function (target, property, receiver) {\n      // ...\n    }\n  })\n\n  proxy.on({\n    set: function (target, property, value) {\n      // ...\n    }\n  })\n\n  proxy.on({\n    deleteProperty: function (target, property) {\n      // ...\n    }\n  })\n\n  proxy.on({\n    ownKeys: function (target) {\n      // ...\n    }\n  })\n\n  proxy.on({\n    apply: function (target, proxy, argumentsList) {\n      // ...\n    }\n  })\n\n  proxy.on({\n    construct: function (target, argumentsList) {\n      // ...\n    }\n  })\n}\n```\n\nIt is not necessary (or even advisable) to define each override. Define just the ones you need.\n\n\n### Proxy handler custom configuration\n\nIf for some reason you need the Proxy handler as its given to the `Proxy` constructor instead of calling `MethodProxy.on()` create the handler with `MethodProxy.handler()` and customize it or pass it along in your program\n\n```javascript\n// Instead of creating the proxy object at once create the Proxy handler ...\nconst proxyHandler = MethodProxy.handler( (config) => {\n  config.on({\n    allMethods: function(proxy, target, methodName, args) {\n      /// ...\n    }\n  })\n})\n\n// customize the handler to fit your needs ...\nhandler.set = function(...) { .... }\nhandler.getPrototypeOf = function(...) { .... }\n\n// and create the proxy object with the customized handler\nconst proxy = new Proxy(target, handler)\n```\n\n### Overriding `apply`\n\n`apply` override only works on functions therefore the target object of the MethodProxy must be a function:\n\n```javascript\nconst proxyDefinition = (proxy) => {\n  proxy.on({\n    apply: function (target, proxy, argumentsList) {\n      // ...\n    }\n  })\n}\nconst targetObject = function () {} // <-- Function\nconst object = MethodProxy.on(targetObject, proxyDefinition)\n```\n\n### Overriding both properties and methods\n\nOverriding both properties and methods with a combination of `allMethods|absentMethod` and `allPropertiesGet|allPropertiesSet|absentPropertyGet|absentPropertySet` can be tricky because in js a method is a property of type `function`.\n\nFor example if you override both `allMethods` **and** `allPropertiesGet` it will only hook the `allPropertiesGet` because to call the method js first gets the object property and then it calls `apply` on it.\n\nTo make it work make sure that `allPropertiesGet` does not handle the methods you want to hook with `allMethods`, for example filtering the property by its name.\n\n### Recomendation\n\nIn this documentation and examples the behaviour of each proxy is inlined in the proxy definition.\n\nThat is to make the example more simple and concise but it's not a good practice.\n\nIt would be better to extract each MethodProxy definition to a class and I encourage you to do so.\n\nYou can use this [example](examples/wrappingItUp.js) as a guide.","readmeFilename":"README.md"}