Spaces:
Runtime error
Runtime error
; | |
Object.defineProperty(exports, "__esModule", { value: true }); | |
exports.softMixProtos = exports.proxyMix = exports.getIngredientWithProp = void 0; | |
const util_1 = require("./util"); | |
/** | |
* Finds the ingredient with the given prop, searching in reverse order and breadth-first if searching ingredient | |
* prototypes is required. | |
*/ | |
const getIngredientWithProp = (prop, ingredients) => { | |
const protoChains = ingredients.map(ingredient => (0, util_1.protoChain)(ingredient)); | |
// since we search breadth-first, we need to keep track of our depth in the prototype chains | |
let protoDepth = 0; | |
// not all prototype chains are the same depth, so this remains true as long as at least one of the ingredients' | |
// prototype chains has an object at this depth | |
let protosAreLeftToSearch = true; | |
while (protosAreLeftToSearch) { | |
// with the start of each horizontal slice, we assume this is the one that's deeper than any of the proto chains | |
protosAreLeftToSearch = false; | |
// scan through the ingredients right to left | |
for (let i = ingredients.length - 1; i >= 0; i--) { | |
const searchTarget = protoChains[i][protoDepth]; | |
if (searchTarget !== undefined && searchTarget !== null) { | |
// if we find something, this is proof that this horizontal slice potentially more objects to search | |
protosAreLeftToSearch = true; | |
// eureka, we found it | |
if (Object.getOwnPropertyDescriptor(searchTarget, prop) != undefined) { | |
return protoChains[i][0]; | |
} | |
} | |
} | |
protoDepth++; | |
} | |
return undefined; | |
}; | |
exports.getIngredientWithProp = getIngredientWithProp; | |
/** | |
* "Mixes" ingredients by wrapping them in a Proxy. The optional prototype argument allows the mixed object to sit | |
* downstream of an existing prototype chain. Note that "properties" cannot be added, deleted, or modified. | |
*/ | |
const proxyMix = (ingredients, prototype = Object.prototype) => new Proxy({}, { | |
getPrototypeOf() { | |
return prototype; | |
}, | |
setPrototypeOf() { | |
throw Error('Cannot set prototype of Proxies created by ts-mixer'); | |
}, | |
getOwnPropertyDescriptor(_, prop) { | |
return Object.getOwnPropertyDescriptor((0, exports.getIngredientWithProp)(prop, ingredients) || {}, prop); | |
}, | |
defineProperty() { | |
throw new Error('Cannot define new properties on Proxies created by ts-mixer'); | |
}, | |
has(_, prop) { | |
return (0, exports.getIngredientWithProp)(prop, ingredients) !== undefined || prototype[prop] !== undefined; | |
}, | |
get(_, prop) { | |
return ((0, exports.getIngredientWithProp)(prop, ingredients) || prototype)[prop]; | |
}, | |
set(_, prop, val) { | |
const ingredientWithProp = (0, exports.getIngredientWithProp)(prop, ingredients); | |
if (ingredientWithProp === undefined) | |
throw new Error('Cannot set new properties on Proxies created by ts-mixer'); | |
ingredientWithProp[prop] = val; | |
return true; | |
}, | |
deleteProperty() { | |
throw new Error('Cannot delete properties on Proxies created by ts-mixer'); | |
}, | |
ownKeys() { | |
return ingredients | |
.map(Object.getOwnPropertyNames) | |
.reduce((prev, curr) => curr.concat(prev.filter(key => curr.indexOf(key) < 0))); | |
}, | |
}); | |
exports.proxyMix = proxyMix; | |
/** | |
* Creates a new proxy-prototype object that is a "soft" mixture of the given prototypes. The mixing is achieved by | |
* proxying all property access to the ingredients. This is not ES5 compatible and less performant. However, any | |
* changes made to the source prototypes will be reflected in the proxy-prototype, which may be desirable. | |
*/ | |
const softMixProtos = (ingredients, constructor) => (0, exports.proxyMix)([...ingredients, { constructor }]); | |
exports.softMixProtos = softMixProtos; | |