diff --git a/src/004-base/000-proxies.js b/src/004-base/000-proxies.js index 69b72acd7d6e9cf89d4301989b3a20a0c8de9df6..9738e68bf063ed42b792f6cf905465fcf22ba7f9 100644 --- a/src/004-base/000-proxies.js +++ b/src/004-base/000-proxies.js @@ -1,43 +1,50 @@ (function() { const readOnlySymbol = Symbol("readonly proxy"); + const dummy = () => true; + + /** + * Creates a readonly proxy for all the target properties except banned methods + * @param {any} target + * @param {string[]} bannedMethodNames The list of method names that are banned and can't be accesses via the proxy + * @param {string} errorMessage message for throwing exceptions + */ + const createProxyWithBannedMethods = (target, bannedMethodNames, errorMessage) => { + return new Proxy(target, { + get: function(o, prop) { + if (prop === readOnlySymbol) { return true; } + const val = o[prop]; + if (typeof val === 'function') { + if (bannedMethodNames.includes(prop)) { + return function() { + throw Error(errorMessage); + }; + } + return val.bind(target); + } + return createReadonlyProxy(val); + }, + set: dummy, + deleteProperty: dummy + }); + }; + globalThis.createReadonlyProxy = function(target) { if (target == null) { return target; } // intentionally == if (target[readOnlySymbol]) { return target; } if (_.isArray(target)) { - return new Proxy(target, { - get: function(o, prop) { - if (prop === readOnlySymbol) { return true; } - const val = o[prop]; - if (typeof val === 'function') { - if (['push', 'unshift', 'pop'].includes(prop)) { - return function() { - throw Error("Cannot modify a readonly array"); - }; - } - return val.bind(target); - } - return createReadonlyProxy(val); - }, - set: function(o, prop, value) { - return true; - }, - deleteProperty: function(o, prop) { - return true; - } - }); - } - if (_.isObject(target)) { + return createProxyWithBannedMethods(target, ['push', 'unshift', 'pop'], "Cannot modify a readonly array"); + } else if (_.isMap(target)) { + return createProxyWithBannedMethods(target, ['clear', 'delete', 'set'], "Cannot modify a readonly Map"); + } else if (_.isSet(target)) { + return createProxyWithBannedMethods(target, ['add', 'clear', 'delete'], "Cannot modify a readonly Set"); + } else if (_.isObject(target)) { return new Proxy(target, { get: function(o, prop) { if (prop === readOnlySymbol) { return true; } return createReadonlyProxy(o[prop]); }, - set: function(o, prop, value) { - return true; - }, - deleteProperty: function(o, prop) { - return true; - } + set: dummy, + deleteProperty: dummy }); } return target;