UNPKG

@freeword/meta

Version:

Meta package for Freeword: exports all core types, constants, and utilities from the src/ directory.

276 lines 10.4 kB
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