README
title: "@ashnazg/utils" sidebar_label: "utils"
This is a grab-bag of the most idiosyncratic functions that I keep writing in each project, so I'm MIT/NPM'ing them just so I can use yarn instead of cut/paste.
Usage
better typeof
var u = require('@ashnazg/utils'); // ~/projects/ashnazg-npm/utils/utils.js
var poor_answer1 = typeof null; // "object"
var poor_answer2 = typeof []; // "object"
var better1 = u.type(null); // "null"
var better2 = u.type([]); // "array"
structured stack tracing
function haslogicbug() {
u.die("the momwraths aren't gyring correctly");
}
result:
FAIL: @ashnazg/utils/test.js:23:haslogicbug() the momwraths aren't gyring correctly
misc oneliners
u.iso.today() // => 2020-01-01
u.iso.now() // => 2020-01-01T22:14:04.390Z
var {lo} = require('@ashnazg/utils');
lo(...args) // logs args after pretty-printing each as JSON, with tabs,
lo exists because I'm sick of writing console.log + JSON a hundred times per day. So I reduced it to two letters to save on carpal.
if you thought lo didn't need to exist, wait til you see literalizer!
Release 0.3.N
Dusty Refactor, avoid using -- I'm polishing a refactor where all browser-friendly utilities are available as const u = require('@ashnazg/utils/browser') -- I've done local
yarn link testing, but to get the coverage I need to be sure, I need to publish a build or two that may have borked dependencies between internal chunks...
Release 0.3.0
- breaking change: moved utils.q to new package: quantum-promise
- Added processing hooks to u.boolify: each hook takes the original val as a param:
u.boolify(val, {onTruth, onFalse, onNullish, onInvalidType, onInvalidString}); // defaults: true false false throw throw- An 'on' config can be a non-function value; in that case, it's just returned.
- wrote u.prettyPrintError for excessively colorized error writing to console.
Release 0.2.7
Created a mocha-specific tool for turning on extra verbosity (or whatever you want) on tests that failed on the previous run of this specific describe() group.
USAGE:
const autoVerbosity = require('@ashnazg/utils/auto-verbose');
describe('group', function() {
autoVerbosity({
on(task_title) {
modify state to increase verbosity -- is only called on jobs who were in last run's fail list.
}, off(task_title, task_status) {
modify state to reset verbosity -- runs after _every_ test, regardless of prior failure-ness.
},
cache: '/UNIQUE-path-for-this-describe' // for example, '/tmp/mocha-GROUPTITLE'
});
it('tests stuff', ...);
});
Release 0.2.6
u.readdir(path, {opts}) returns an async iterator over all the files (or other entries).
- I made this because I couldn't find a lib that definitely handled backpressure, the lack of which was a deal breaker.
- opts allow you to control what entities, where to recurse, and whether you want the type or just the name:
files = true, symlinks = true, recurse = true, recurse_symlinks = false, // NOT implemented yet: I want to put in a cycle-detector first. types = false, // return {name, type} instead of just 'name' dirs = false, pipes = false, sockets = false, blocks = false, character_devices = false,
u.merge(base, ...srcs) is like Object.assign, except it smart-merges arrays and maps all the way through the tree.
tweaks to u.class(foo) when foo is an object -- iterable types are preferred over returning plain old object, but a constructor name trumps both of those.
Release 0.2.5
Removed the "level=FAIL" param from u.complain(why, level) in favor of making all of the complain-based tools accept varargs:
u.complain('server error', network_request)
Release 0.2.4
Overhauled the fail/warn logs.
- removed stdout entirely, so I can pipe with no noise in the stream, and all noise into error logs:
emits_errors | emits_other_errors | process | save - u.fatalUserError is deprecated in favor of u.quit(...printables) or the less exity u.fail(...)
- want a stack?
- u.complain(err, level=FAIL) either prints err's stack, or if err is a string, uses its own stack to figure out what file/line to drop into stderr.
- u.die(err) is complain+exit() (if process.exitCode is not an error yet, make it one)
- don't want a stack?
- u.warn(...printables)
- u.fail(...printables)
- u.quit(...printables) is fail+exit() (if process.exitCode is not an error yet, make it one)
- want a named info stream that measures time between calls to it?
const logger = u.info('NETWORK'); logger("Do I even have a network?"); logger("I finished my tests."); $ASHNAZG_LOG_NETWORK=1 node ./myapp.js INFO[NETWORK]: Do I even have a network? INFO[NETWORK]: I finished my tests. (0.001s)
Also, u.curl() gained a flag that allows small response bodies to be loaded into RAM -- just syntactic sugar so I don't have to write parse(readFile()) so much.
const rec = await u.curl({src: 'https...', include_body_max_mb: 42})- rec.dst always exists, but rec.response.body will be set if the file length is not over the limit
- rec.response.body is a json object if the body parses without errors, and a raw utf8 otherwise.
Release 0.2.3
u.curl now has a ttl_days_fail mode so you can have a different cache duration on http errors. It also tracks the duration of the curl call in the metadata now.
Release 0.2.2
- u.curl now has the option of saving errors to the metadata cache, and if reusing a cache entry, throwing/returning as if this was a live call
- u.class now distinguishes (async) iterable from general objects.
Release 0.2.1
- Bugfixing curl/shell tools
- added my tiny shim of promise tools inspired by Kriskowal's Q
Release 0.2.0
- u.exists(fn) is like fs.stat() except it returns false on NOENT instead of throwing
- u.chowngrp(fn {owner: -1, group: -1}) supports both uid/gid as well as 'user' and 'group'
- u.shell("cmd foo", opts) => {cmd, code: 0, signal: 'SIGINT', killed: false, stdout: '', stderr: ''}
- opts.max_code (default 0) determines what counts as a reject vs resolve. (magic value -1 means never throw.)
- u.symlink(existing, new) is here just for symmetry; I find it habit-disruptive for a few tools to be f(dst, src) when everything else is f(src, dst)
- u.hardlink(src, dst, opts) if (opts.or_cp), falls back on u.cp if the link operation fails due to crossing a filesystem boundary.
- it, u.cp, and u.mv now use chmod(opts) to ensure consistent final attributes in all implementation paths.
- bugfix: u.mv() would fall apart if the initial rename() did work.
- {base: 'page-'} option added to u.mktemp({}) mode.
- small but backwards-compatibility breaking change:
u.mktemp('foo')is now interpreted asu.mktemp({base: 'foo'})and notu.mktemp({fn:'foo'})- manually switching your invocation to {fn: 'foo'} will retain old behavior.
- this is going to be logged to stderr for one or more releases while things transition before the new behavior goes back to "not acting deprecated"
old way:
const fn1 = u.mktemp('foo');
const fn2 = u.mktemp('foo');
assert(fn1 === fn2);
new way:
const fn1 = u.mktemp('foo');
const fn2 = u.mktemp('foo');
assert(fn1 !== fn2);
Release 0.1.10
- u.class: it's like u.type, but for objects, it returns classy names if possible, and for functions, returns 'generator' and 'async_generator' where the function's metadata is one. (Caveat: has nothing to do with whether the function returns an iterable, since any function could do so.)
- split u.mkdtemp out from the implementation of u.mktemp for use cases that just want the directory.
- u.mv and u.cp both take (src, dst, optional_stream_write_opts)
- do not use this release see 0.2.1
Release 0.1.9
Bugfix: u.assert.throw({run() {throw "string"}}) now works as well as run() {throw new Error("string");}
Release 0.1.8
Bugfix: u.assert.throw({run() {...}}) wasn't awaiting on run(), so it was only working for sync throws.
Release 0.1.7
- getEnv() extracts secrets from the environment into a convenient map
- cache() uses https module to make a local disk cache without touching content details like encodings
- undent and removeDeadLines
- parsePickyDate is picky and supports a subset of ISO: it only allows an explicit 'Z' timezone, and it only allows fractionals on seconds.
- why not Date()? because I want to know when my upstreams' formatting strays.
- quit() is like die() but avoids printing stack traces; die is for code bugs; quit is for external problems like network down.
- md5 now supports explicitly setting encoding.
- md5.file takes a filename instead of data value
- +u.cheapHash a simple hasher for no-risk use cases. (shoutout to Mike McShaffry)
assert(){run: () => throw 'thing', expected: 'thing', cleanup: () => 'released OS handles'})is like assert.throws, except this handles promises.
Release 0.1.6
Added more oneliners like md5; mucked about greatly with 1.5's error/stack output. (now with async promise stacks not being ignored!)
- md5
- now(optional date) returns an ISO date string in prod and a human-friendly one in dev.
- now.asFilename(...) is the same but only using ascii characters that are trivial in HTTP/windows/linux filename usage (colons are right out.)
- divertConsole(...) returns a console equivalent for ease of switching between dev-screen and prod-syslog
- u.complain / u.die / etc now shows a 'FAIL:' on stdout and any trace on stderr
Release 0.1.5
Added:
|Func|What
|-|-
|chopHOME(path) | which turns /home/$USER/foo into ~/foo or /home/otherperson/foo into ~otherperson/foo
|chopMain(path) | if path is under your node main's path, make it relative to that.
|chopPWD(path) | if path is under your PWD, make it relative to that.
|findGoodLookingStackFrame(key) | finds a stackframe that has your search_key in the file path and
isn't "log()". (key defaults to your main module's folder name)
Release 0.1.4
- Added u.getFrame(offset=1) which returns {file, func, line} of your parent frame. (or grandparent for offset>1)
- Added options to u.u.getStack(shift,keep,raw) to support that.
- shift: skip these early frames
- keep: return only this many.
- raw: return the v8 objects, not string tuples
Release 0.1.3
Added a good enough for now template tag called undent for making pretty heredocs in JS. It doesn't handle interpolations yet, as I didn't need any for today's use case.
Release 0.1.2
Added a centralized copy of my estlint rules that I like to plug into each FE or BE project's config.
Release 0.1.1
u.fatalUserError previously accepted only an err-like {message:''}; it now also accepts a string or printable that doesn't have a message field.
Release 0.1.0
Fixed a typo in u.type that meant the test for null was broken.