UNPKG

proxy-extend

Version:

Transparently extend a JS object with additional properties (using ES6 Proxy)

1 lines 15.3 kB
{"version":3,"file":"proxyExtend.cjs","names":["_messageTag","_interopRequireDefault","require","obj","__esModule","default","hasOwnProperty","propKey","Object","prototype","call","nullObject","create","TypedArray","getPrototypeOf","Int8Array","nodeInspectCustom","Symbol","for","proxyKey","exports","extend","_value","_extension","arguments","length","undefined","value","extension","TypeError","$msg","unproxied","isString","isNumber","valueType","target","String","Number","usesInternalSlots","Boolean","Date","RegExp","Map","WeakMap","Set","WeakSet","ArrayBuffer","handler","has","Reflect","get","receiver","targetProp","toString","bind","valueOf","Proxy","isProxyable","isProxy","unwrapProxy","proxy","is","unwrap","registerProxyFormatter","util","inspect","replDefaults","showProxy","window","Array","isArray","devtoolsFormatters","push","header","object","_default"],"sources":["../../src/proxyExtend.js"],"sourcesContent":["\nimport $msg from 'message-tag';\n\n\n// Version of `hasOwnProperty` that doesn't conflict\nconst hasOwnProperty = (obj, propKey) => Object.prototype.hasOwnProperty.call(obj, propKey);\n\n// Cache some values\nconst nullObject = Object.create(null);\nconst TypedArray = Object.getPrototypeOf(Int8Array);\nconst nodeInspectCustom = Symbol.for('nodejs.util.inspect.custom');\n\n\nexport const proxyKey = Symbol('proxy-wrapper.proxy');\n\nexport const extend = (_value, _extension = nullObject) => {\n let value = _value;\n let extension = _extension;\n \n if (typeof extension !== 'object' || extension === null) {\n throw new TypeError($msg`Extension must be an object, given ${extension}`);\n }\n \n // Check if the given value is already a proxy with extension. If so, flatten.\n if (typeof value === 'object' && value !== null && proxyKey in value) {\n const unproxied = value[proxyKey];\n value = unproxied.value;\n extension = { ...unproxied.extension, ...extension };\n }\n \n let isString = false;\n let isNumber = false;\n \n // Handle primitive values. Because a Proxy always behaves as an object, we cannot really transparently\n // \"simulate\" a primitive. However, we use sensible equivalents where possible.\n const valueType = typeof value;\n let target = value;\n if (valueType === 'undefined') {\n throw new TypeError($msg`Cannot construct proxy, given \\`undefined\\``);\n } else if (value === null) {\n target = nullObject;\n } else if (valueType === 'string') {\n target = new String(value);\n isString = true;\n } else if (valueType === 'number') {\n target = new Number(value);\n isNumber = true;\n } else if (valueType === 'bigint') {\n // TODO: we could use a boxed `BigInt` through `Object()` (e.g. `Object(42n) instanceof BigInt`):\n // https://2ality.com/2022/02/wrapper-objects.html\n throw new TypeError($msg`Cannot construct proxy from bigint, given ${value}`);\n } else if (valueType === 'boolean') {\n // Note: we could use a boxed `Boolean`, but it would not be very useful because there's not much you can\n // do with it. Boxed booleans (including `new Boolean(false)`) are treated as truthy in logic operations.\n throw new TypeError($msg`Cannot construct proxy from boolean, given ${value}`);\n } else if (valueType === 'symbol') {\n throw new TypeError($msg`Cannot construct proxy from symbol, given ${value}`);\n } else if (valueType !== 'object' && valueType !== 'function') {\n // Note: this shouldn't happen, unless there's a new type of primitive added to JS\n throw new TypeError($msg`Cannot construct proxy, given value of unknown type ${valueType}`);\n }\n \n // Some methods of built-in types cannot be proxied, i.e. they need to bound directly to the\n // target. Because they explicitly check the type of `this` (e.g. `Date`), or because they need\n // to access an internal slot of the target (e.g. `String.toString`).\n // https://stackoverflow.com/questions/36394479/proxies-on-regexps-and-boxed-primitives\n // https://stackoverflow.com/questions/47874488/proxy-on-a-date-object\n // https://stackoverflow.com/questions/43927933/why-is-set-incompatible-with-proxy\n const usesInternalSlots =\n target instanceof String\n || target instanceof Number\n || target instanceof Boolean\n || target instanceof Date\n || target instanceof RegExp\n || target instanceof Map\n || target instanceof WeakMap\n || target instanceof Set\n || target instanceof WeakSet\n || target instanceof ArrayBuffer\n || target instanceof TypedArray;\n \n \n const handler = {\n has(target, propKey) {\n if (hasOwnProperty(extension, propKey)) {\n // Note: use `hasOwnProperty` for the extension, rather than `in`, because we do not want to\n // consider properties in the prototype chain as being part of the extension.\n return true;\n }\n \n // Implement `toJSON` for boxed primitives (otherwise `JSON.stringify` will not work properly).\n if (propKey === 'toJSON' && (isString || isNumber)) { return true; }\n \n if (propKey === nodeInspectCustom) { return true; }\n if (propKey === proxyKey) { return true; }\n \n return Reflect.has(target, propKey);\n },\n \n get(target, propKey, receiver) {\n // Backdoor to get the internal proxied data (value and extension).\n // Note: use `value` here, not `target` (target is just an internal representation).\n if (propKey === proxyKey) { return { value, extension }; }\n \n let targetProp = undefined;\n if (hasOwnProperty(extension, propKey)) {\n targetProp = extension[propKey];\n } else if (propKey in target) {\n targetProp = target[propKey];\n \n // Note: any getter properties will receive the `target`, rather than the proxy as their `this`\n // value. Thus, getters will not have access to the extension. If you really need this behavior,\n // you can use the following. But it's not recommended, due to the impact on performance.\n /*\n if (hasOwnProperty(target, propKey)) {\n const descriptor = Object.getOwnPropertyDescriptor(target, propKey);\n if (typeof descriptor.get === 'function') {\n targetProp = descriptor.get.call(receiver);\n }\n }\n */\n } else {\n // Fallback: property is present in neither the target nor the extension\n \n // Implement `toJSON` for boxed primitives (otherwise `JSON.stringify` will not work properly).\n if (propKey === 'toJSON') {\n if (isString) {\n targetProp = target.toString.bind(target);\n } else if (isNumber) {\n targetProp = target.valueOf.bind(target);\n }\n }\n \n if (propKey === nodeInspectCustom) { targetProp = () => target; }\n }\n \n if (typeof targetProp === 'function') {\n if (usesInternalSlots) {\n // Have `this` bound to the original target\n return targetProp.bind(target);\n } else {\n // Unbound (i.e. `this` can be bound to anything, usually will be the proxy object)\n return targetProp;\n }\n } else {\n return targetProp;\n }\n },\n };\n \n return new Proxy(target, handler);\n};\n\n// Whether the given value can be proxied\nexport const isProxyable = value => {\n if (typeof value === 'object') { // Also covers the case where `value === null`\n return true;\n } else if (typeof value === 'function') {\n return true;\n } else if (typeof value === 'string') {\n return true;\n } else if (typeof value === 'number') {\n return true;\n } else {\n return false;\n }\n};\n\n// Whether the given value is a proxy\nexport const isProxy = value => {\n return typeof value === 'object' && value !== null && proxyKey in value;\n};\n\n// Unwrap the given proxy to access its internal data\nexport const unwrapProxy = proxy => {\n if (!isProxy(proxy)) {\n throw new TypeError($msg`Cannot unwrap input, expected a proxy, received: ${proxy}`);\n }\n \n return proxy[proxyKey];\n};\n\n// Add some properties to `extend` as shorthand\nextend.is = isProxy;\nextend.unwrap = unwrapProxy;\n\n// Make formatting of proxies a little nicer\nexport const registerProxyFormatter = () => {\n if (typeof require === 'function') {\n const util = require('util');\n \n if (util.inspect && util.inspect.replDefaults) {\n util.inspect.replDefaults.showProxy = false;\n }\n }\n \n // https://stackoverflow.com/questions/55733647/chrome-devtools-formatter-for-javascript-proxy\n if (typeof window === 'object' && window !== null) {\n if (!Array.isArray(window.devtoolsFormatters)) {\n window.devtoolsFormatters = [];\n }\n \n window.devtoolsFormatters.push({\n header(value) {\n if (!isProxy(value)) {\n return null;\n }\n \n return ['object', { object: value[proxyKey].value }];\n },\n });\n }\n};\n\nexport default extend;\n"],"mappings":"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"}