@freeword/meta
Version:
Meta package for Freeword: exports all core types, constants, and utilities from the src/ directory.
276 lines • 10.4 kB
JavaScript
import _ /**/ from 'lodash';
import * as NodeUtil from 'node:util';
// import { nextTick } from 'node:process'
import { AtoZlos } from "../lexicon/LexiconConsts.js";
import * as JSPrintf from 'sprintf-js';
export * from "./Streaming.js";
export * from "./Random.js";
export * from "../UtilityConsts.js";
export * from "./Rot13.js";
export { badOutcome, throwable } from "./Outcome.js";
export const { sprintf, vsprintf } = JSPrintf;
/** @returns a bag with keys a, b, ... z and values of type VT */
export function alphabetLookupBag(seed) {
if (_.isFunction(seed)) {
return objectify(AtoZlos, (ltr) => seed(ltr));
}
return objectify(AtoZlos, () => (seed));
}
/** Assign a **non-enumerable**, writable, configurable property to an object
* See also {@link setNormalProp} and {@link decorate}
* @param obj - The object to decorate
* @param key - The key to decorate the object with
* @param value - The value to decorate the object with
* @returns The value
*/
export function adorn(obj, key, value) {
Object.defineProperty(obj, key, { value, enumerable: false, writable: false, configurable: true });
return value;
}
/** Assign an enumerable, writable, configurable property to an object
* See also {@link adorn} and {@link decorate}
* @param obj - The object to decorate
* @param key - The key to decorate the object with
* @param value - The value to decorate the object with
* @returns The value
*/
export function setNormalProp(obj, key, value) {
Object.defineProperty(obj, key, { value, enumerable: true, writable: true, configurable: true });
return value;
}
export function setNormalProps(obj, vals) {
Object.entries(vals).forEach(([key, value]) => {
Object.defineProperty(obj, key, { value, enumerable: true, writable: true, configurable: true });
});
return obj;
}
export function setHiddenProps(obj, vals) {
Object.entries(vals).forEach(([key, value]) => {
Object.defineProperty(obj, key, { value, enumerable: false, writable: true, configurable: true });
});
return obj;
}
/** Assign non-enumerable, writable, configurable properties to an object
* See also {@link adorn} and {@link decorate}
* @param obj - The object to decorate
* @param vals - The values to decorate the object with
* @returns The decorated object
*/
export function decorate(obj, vals) {
Object.entries(vals).forEach(([key, value]) => {
Object.defineProperty(obj, key, { value, enumerable: false, writable: false, configurable: true });
});
return obj;
}
/** Get the own properties of an object
*
* @param obj - The object to get the own properties of
* @returns The own properties of the object; empty object if nil
*/
export function ownProps(obj) {
if (_.isNil(obj)) {
return {};
}
return Object.getOwnPropertyDescriptors(obj);
}
/** Get the own property names of an object
*
* @param obj - The object to get the own property names of
* @returns The own property names of the object; empty array if nil
*/
export function ownPropnames(obj) {
if (_.isNil(obj)) {
return [];
}
return Object.getOwnPropertyNames(obj);
}
/** Get the property names of the **first parent prototype** of an object
*
* @param obj - The object to get the prototype property names of
* @returns The prototype property names of the object; empty array if nil
*/
export function protoPropnames(obj) {
if (_.isNil(obj)) {
return [];
}
const proto = Object.getPrototypeOf(obj);
return ownPropnames(proto);
}
/** Get the property descriptor of a property of the **first parent prototype** of an object
*
* @param obj - The object to get the property descriptor of
* @param propname - The name of the property to get the descriptor of
* @returns The property descriptor of the property; undefined if not found
*/
export function protoProp(obj, propname) {
return Object.getOwnPropertyDescriptor(Object.getPrototypeOf(obj), propname);
}
/** Get the property descriptor of a property of an object
*
* @param obj - The object to get the property descriptor of
* @param propname - The name of the property to get the descriptor of
* @returns The property descriptor of the property; undefined if not found
*/
export function ownProp(obj, propname) {
return Object.getOwnPropertyDescriptor(obj, propname);
}
/** Get the first property descriptor found ascending the prototype chain
* for a given property name
*
* @param obj - The object to get the property descriptor of
* @param propname - The name of the property to get the descriptor of
* @param depth - The depth of the prototype chain to search
* @returns The property descriptor of the property; undefined if not found
*/
export function getProp(obj, propname, depth = 0) {
if (depth < 0) {
return undefined;
}
const val = Object.getOwnPropertyDescriptor(obj, propname);
if (val) {
return val;
}
const proto = Object.getPrototypeOf(obj);
if (!proto) {
return undefined;
}
return getProp(proto, propname, depth - 1);
}
export function bagsize(bag) {
if (_.isArray(bag)) {
return bag.length;
}
return _.keys(bag).length;
}
function indentPaddingFor(indent) {
if (!indent) {
return '';
}
if (_.isString(indent)) {
return indent;
}
return _.repeat(' ', indent);
}
/**
* Pretty-print an array of strings in chunks
*
* @param arr - The array to pretty-print
* @param opts - The options for pretty-printing
* @returns The pretty-printed array
*/
export function prettifyInChunks(arr, { chunkSize = 20, colwd = 12, key, indent = 2 } = {}) {
const indentPadding = indentPaddingFor(indent);
const lines = _.chunk(arr, chunkSize).map((chunk) => chunk.map((val) => _.padEnd(inspectify(val) + ',', colwd)).join(' '));
const body = lines.join('\n' + indentPadding);
return (key ? kfy(key) : '') + '[\n ' + body + '\n]';
}
export function prettifyArray(arr, opts = {}) {
const inspected = inspectify(arr);
if ((inspected.length < 180) && (!opts.chunkArrays)) {
return opts.naked ? inspected.slice(1, -1).trim() : inspected;
}
return prettifyInChunks(arr, opts);
}
export function prettifySet(set, { key, ...opts } = {}) {
const elements = prettifyArray(Array.from(set), opts);
return (key ? kfy(key) : '') + 'new Set(' + elements + ')';
}
export function inspectify(val, _opts = {}) {
return NodeUtil.inspect(val, { depth: 10, colors: false, breakLength: Infinity, maxArrayLength: Infinity, maxStringLength: Infinity, compact: true, numericSeparator: true });
}
const FieldnameRE = /^[a-zA-Z_]\w*$/;
export function kfy(key, opts = {}) {
const str = FieldnameRE.test(key) ? key : inspectify(key);
return _.padEnd(str + ':', opts.keywd);
}
export function prettifyField(val, opts = {}) {
if (_.isArray(val)) {
return prettifyArray(val, opts);
}
if (_.isSet(val)) {
return prettifySet(val, opts);
}
return inspectify(val);
}
export function prettify(obj, { key, naked = false, ...opts } = {}) {
const firstkey = _.isNil(key) ? '' : (kfy(key) + ' ');
const startBracket = naked ? '' : '{';
const endBracket = naked ? '' : '}';
const lines = [];
const indentPadding = indentPaddingFor(opts.indent);
_.each(obj, (val, key) => {
const keypart = kfy(key, { keywd: 12, ...opts });
const valpart = prettifyField(val, opts);
lines.push(indentPadding + keypart + ' ' + valpart);
});
return firstkey + startBracket + '\n' + lines.join(',\n') + ',\n' + endBracket;
}
export function rebag(clxn, func) {
let seq = 0;
return Object.fromEntries(_.map(clxn, (val, key) => func(val, key, seq++, clxn)));
}
export function objectify(clxn, func) {
return rebag(clxn, (val, key, seq, ...args) => [val, func(val, key, seq, ...args)]);
}
export function bagslice(bag, start, end) {
const keys = _.keys(bag).slice(start, end);
return _.pick(bag, keys);
}
export function sortOnKeys([key, _val]) { return key; }
export function sortOnNumkeys([key, _val]) { return Number(key); }
/** Sort a bag by a funtion -- by default, its keys.
* @note **by default, keys that parse as positive integers (1, 2, 3, ...) will appear first in retrieval order**.
* This is part of the spec for Object.
* @example { '1': 1, '2': 2, '-1': -1, '0.9': 0.9, '1.1': 1.1 }
* If you use the magic function `sortOnNumkeys`,
* keys that stringify as integers will be reinserted as `x.0`:
* @example { '-1.0': -1, '0.9': 0.9, '1.0': 1, '1.1': 1.1, '2.0': 2 }
*/
export function bagsort(bag, sortfn = sortOnKeys, { mungeNumKeys = true } = {}) {
const result = {};
const sorted = _.orderBy(_.entries(bag), sortfn);
if (!mungeNumKeys) {
const pairs = _.orderBy(_.entries(bag), sortfn);
return _.fromPairs(pairs);
}
for (const [key, val] of sorted) {
const mungedKey = (Number.isInteger(Number(key))) ? (String(key) + '.0') : key;
result[mungedKey] = val;
}
return result;
}
const nextTicker = globalThis.nextTick ?? ((func) => (func()));
/**
* Sleep for one tick (i.e. let everyone else have a turn)
* @returns true
*/
export async function sleepNextTick() {
return new Promise((yay) => { nextTicker(() => yay(true)); });
}
/**
* Sleep for a given number of milliseconds
* @param ms - The number of milliseconds to sleep
* @param nextTick - Whether to sleep for one tick before starting the sleep
* @note IMPORTANT: if nextTick is true, the sleep will be delayed by
* the duration given PLUS an unknowable amount of time
* @returns true
*/
export async function sleep(ms, awakeNextTick = false) {
if (awakeNextTick) {
await sleepNextTick();
}
return new Promise((resolve) => setTimeout(resolve, ms)).then(() => true);
}
export async function* catiters(...iters) {
for (const iter of iters) {
yield* iter;
}
}
export function isNode() {
return (typeof process !== 'undefined' && process.release && process.release.name === 'node');
}
export function isBrowser() {
// if (isNode()) { return false }
return (!!import.meta.client);
}
//# sourceMappingURL=UF.js.map