@cch137/format-utils
Version:
A collection of utility modules for formatting and processing data
180 lines (179 loc) • 5.79 kB
JavaScript
const findObj = (objs, p) => objs.find((o) => p in o) || objs[0];
const Merged = Symbol("Merged");
export function merge(...objs) {
objs.unshift({
[Symbol.for("nodejs.util.inspect.custom")]() {
const obj = {};
for (const o of objs)
for (const k in o)
obj[k] = o[k];
return obj;
},
});
objs = Object.freeze(objs
.map((i) => (Merged in i ? i[Merged].slice(1) : i))
.flat());
return new Proxy(objs[0], {
has: (t, p) => objs.some((o) => p in o),
get: (t, p) => (p === Merged ? objs : findObj(objs, p)[p]),
set: (t, p, v) => Reflect.set(findObj(objs, p), p, v),
deleteProperty: (t, p) => Reflect.deleteProperty(findObj(objs, p), p),
ownKeys: (t) => objs.reduce((p, c) => p.concat(Reflect.ownKeys(c).filter((i) => !p.includes(i))), Reflect.ownKeys(t)),
defineProperty: (t, p, a) => Reflect.defineProperty(findObj(objs, p), p, a),
getOwnPropertyDescriptor: (t, p) => Reflect.getOwnPropertyDescriptor(findObj(objs, p), p),
});
}
export const nullPrototype = Reflect.getPrototypeOf({});
export function getPrototypesOf(obj) {
const chain = [obj];
while (true) {
const prototype = Reflect.getPrototypeOf(obj);
if (prototype === nullPrototype)
return chain;
chain.push(prototype);
obj = prototype;
}
}
export function getAllKeys(obj) {
const prototypes = getPrototypesOf(obj);
if (prototypes.length === 1)
return Reflect.ownKeys(obj);
return prototypes.reduce((prev, curr) => {
Reflect.ownKeys(curr).forEach((k) => {
if (!prev.includes(k) && k !== "constructor")
prev.push(k);
});
return prev;
}, []);
}
export function getAllDescriptors(obj) {
const prototypes = getPrototypesOf(obj);
const descriptors = prototypes
.reverse()
.reduceRight((prev, curr) => Object.assign(prev, Object.getOwnPropertyDescriptors(curr)), {});
if (prototypes.length > 1)
Reflect.deleteProperty(descriptors, "constructor");
return descriptors;
}
export const isObject = (value) => typeof value === "object" && value !== null;
export function decircular(obj, level = 0, objectLevels = [], objectIds = new Map(), encodedObjects = new Map()) {
if (objectIds.has(obj))
return objectIds.get(obj);
const objectId = { o: -1 };
const encodedObject = {};
(objectLevels[level] || (objectLevels[level] = [])).push(obj);
objectIds.set(obj, objectId);
encodedObjects.set(obj, encodedObject);
const descriptors = getAllDescriptors(obj);
for (const key in descriptors) {
const descriptor = descriptors[key];
if (descriptor.get)
continue;
const { value } = descriptor;
encodedObject[key] = isObject(value)
? decircular(value, level + 1, objectLevels, objectIds, encodedObjects)
: value;
}
if (level === 0) {
const sortedObjects = [];
let index = 0;
objectLevels.forEach((level) => {
level.forEach((o) => {
sortedObjects.push(o);
objectIds.get(o).o = `${index++}`;
});
});
return sortedObjects.map((o) => encodedObjects.get(o));
}
return objectId;
}
export function encircular(series) {
const root = series[0];
for (const obj of series) {
for (const k in obj) {
const v = obj[k];
if (isObject(v))
obj[k] = series[v.o];
}
}
return root;
}
export function pick(obj, keys) {
return new Proxy(obj, {
get(t, p) {
if (keys.includes(p))
return Reflect.get(t, p);
return undefined;
},
set(t, p, v) {
if (keys.includes(p))
return Reflect.set(t, p, v);
return false;
},
has(t, p) {
if (keys.includes(p))
return Reflect.has(t, p);
return false;
},
ownKeys(t) {
return Reflect.ownKeys(t).filter((i) => keys.includes(i));
},
deleteProperty(t, p) {
if (keys.includes(p))
return Reflect.deleteProperty(t, p);
return false;
},
});
}
export function omit(obj, keys) {
return new Proxy(obj, {
get(t, p) {
if (keys.includes(p))
return undefined;
return Reflect.get(t, p);
},
set(t, p, v) {
if (keys.includes(p))
return false;
return Reflect.set(t, p, v);
},
has(t, p) {
if (keys.includes(p))
return false;
return Reflect.has(t, p);
},
ownKeys(t) {
return Reflect.ownKeys(t).filter((i) => !keys.includes(i));
},
deleteProperty(t, p) {
if (keys.includes(p))
return false;
return Reflect.deleteProperty(t, p);
},
});
}
export function readOnly(obj, _keys) {
const keys = _keys || null;
return new Proxy(obj, {
set(t, p, v) {
if (!keys || keys.includes(p))
return false;
return Reflect.set(t, p, v);
},
deleteProperty(t, p) {
if (!keys || keys.includes(p))
return false;
return Reflect.deleteProperty(t, p);
},
});
}
export function writeOnly(obj, _keys) {
const keys = _keys || null;
return new Proxy(obj, {
get(t, p) {
if (!keys || keys.includes(p))
return undefined;
return Reflect.get(t, p);
},
});
}