[d24f17c] | 1 | "use strict";
|
---|
| 2 | Object.defineProperty(exports, "__esModule", { value: true });
|
---|
| 3 | exports.softMixProtos = exports.proxyMix = exports.getIngredientWithProp = void 0;
|
---|
| 4 | const util_1 = require("./util");
|
---|
| 5 | /**
|
---|
| 6 | * Finds the ingredient with the given prop, searching in reverse order and breadth-first if searching ingredient
|
---|
| 7 | * prototypes is required.
|
---|
| 8 | */
|
---|
| 9 | const getIngredientWithProp = (prop, ingredients) => {
|
---|
| 10 | const protoChains = ingredients.map(ingredient => (0, util_1.protoChain)(ingredient));
|
---|
| 11 | // since we search breadth-first, we need to keep track of our depth in the prototype chains
|
---|
| 12 | let protoDepth = 0;
|
---|
| 13 | // not all prototype chains are the same depth, so this remains true as long as at least one of the ingredients'
|
---|
| 14 | // prototype chains has an object at this depth
|
---|
| 15 | let protosAreLeftToSearch = true;
|
---|
| 16 | while (protosAreLeftToSearch) {
|
---|
| 17 | // with the start of each horizontal slice, we assume this is the one that's deeper than any of the proto chains
|
---|
| 18 | protosAreLeftToSearch = false;
|
---|
| 19 | // scan through the ingredients right to left
|
---|
| 20 | for (let i = ingredients.length - 1; i >= 0; i--) {
|
---|
| 21 | const searchTarget = protoChains[i][protoDepth];
|
---|
| 22 | if (searchTarget !== undefined && searchTarget !== null) {
|
---|
| 23 | // if we find something, this is proof that this horizontal slice potentially more objects to search
|
---|
| 24 | protosAreLeftToSearch = true;
|
---|
| 25 | // eureka, we found it
|
---|
| 26 | if (Object.getOwnPropertyDescriptor(searchTarget, prop) != undefined) {
|
---|
| 27 | return protoChains[i][0];
|
---|
| 28 | }
|
---|
| 29 | }
|
---|
| 30 | }
|
---|
| 31 | protoDepth++;
|
---|
| 32 | }
|
---|
| 33 | return undefined;
|
---|
| 34 | };
|
---|
| 35 | exports.getIngredientWithProp = getIngredientWithProp;
|
---|
| 36 | /**
|
---|
| 37 | * "Mixes" ingredients by wrapping them in a Proxy. The optional prototype argument allows the mixed object to sit
|
---|
| 38 | * downstream of an existing prototype chain. Note that "properties" cannot be added, deleted, or modified.
|
---|
| 39 | */
|
---|
| 40 | const proxyMix = (ingredients, prototype = Object.prototype) => new Proxy({}, {
|
---|
| 41 | getPrototypeOf() {
|
---|
| 42 | return prototype;
|
---|
| 43 | },
|
---|
| 44 | setPrototypeOf() {
|
---|
| 45 | throw Error('Cannot set prototype of Proxies created by ts-mixer');
|
---|
| 46 | },
|
---|
| 47 | getOwnPropertyDescriptor(_, prop) {
|
---|
| 48 | return Object.getOwnPropertyDescriptor((0, exports.getIngredientWithProp)(prop, ingredients) || {}, prop);
|
---|
| 49 | },
|
---|
| 50 | defineProperty() {
|
---|
| 51 | throw new Error('Cannot define new properties on Proxies created by ts-mixer');
|
---|
| 52 | },
|
---|
| 53 | has(_, prop) {
|
---|
| 54 | return (0, exports.getIngredientWithProp)(prop, ingredients) !== undefined || prototype[prop] !== undefined;
|
---|
| 55 | },
|
---|
| 56 | get(_, prop) {
|
---|
| 57 | return ((0, exports.getIngredientWithProp)(prop, ingredients) || prototype)[prop];
|
---|
| 58 | },
|
---|
| 59 | set(_, prop, val) {
|
---|
| 60 | const ingredientWithProp = (0, exports.getIngredientWithProp)(prop, ingredients);
|
---|
| 61 | if (ingredientWithProp === undefined)
|
---|
| 62 | throw new Error('Cannot set new properties on Proxies created by ts-mixer');
|
---|
| 63 | ingredientWithProp[prop] = val;
|
---|
| 64 | return true;
|
---|
| 65 | },
|
---|
| 66 | deleteProperty() {
|
---|
| 67 | throw new Error('Cannot delete properties on Proxies created by ts-mixer');
|
---|
| 68 | },
|
---|
| 69 | ownKeys() {
|
---|
| 70 | return ingredients
|
---|
| 71 | .map(Object.getOwnPropertyNames)
|
---|
| 72 | .reduce((prev, curr) => curr.concat(prev.filter(key => curr.indexOf(key) < 0)));
|
---|
| 73 | },
|
---|
| 74 | });
|
---|
| 75 | exports.proxyMix = proxyMix;
|
---|
| 76 | /**
|
---|
| 77 | * Creates a new proxy-prototype object that is a "soft" mixture of the given prototypes. The mixing is achieved by
|
---|
| 78 | * proxying all property access to the ingredients. This is not ES5 compatible and less performant. However, any
|
---|
| 79 | * changes made to the source prototypes will be reflected in the proxy-prototype, which may be desirable.
|
---|
| 80 | */
|
---|
| 81 | const softMixProtos = (ingredients, constructor) => (0, exports.proxyMix)([...ingredients, { constructor }]);
|
---|
| 82 | exports.softMixProtos = softMixProtos;
|
---|