Comparing version 0.0.2 to 1.52.0
106
package.json
{ | ||
"name": "futil", | ||
"version": "0.0.2", | ||
"description": "Javascript Functional Utilities", | ||
"main": "index.js", | ||
"version": "1.52.0", | ||
"description": "F(unctional) util(ities). Resistance is futile.", | ||
"main": "lib/futil-js.js", | ||
"scripts": { | ||
"test": "mocha" | ||
"build": "webpack", | ||
"danger": "duti", | ||
"test": "babel-node ./node_modules/mocha/bin/_mocha --require ./test/init.js", | ||
"test:watch": "chokidar 'src/*.js' 'test/*.js' -c 'npm t'", | ||
"browser": "TEST_ENV=browser karma start karma.conf.js", | ||
"browser:local": "karma start karma.conf.js", | ||
"coverage": "nyc --require babel-core/register --require babel-polyfill --require ./test/init.js mocha test", | ||
"cicoveralls": "nyc report --reporter=text-lcov --require babel-core/register --require babel-polyfill --require ./test/init.js mocha test | coveralls", | ||
"cicoverage": "nyc --reporter=lcov --require babel-core/register --require babel-polyfill --require ./test/init.js mocha test --reporter json > test-results.json", | ||
"lint": "eslint dangerfile.js src/*.js test/*.js", | ||
"lint:ci": "npm run lint -- -o lint-results.json -f json", | ||
"lint-fix": "eslint dangerfile.js src/*.js test/*.js --fix", | ||
"fmt": "prettier --write *.js; prettier --write '{src,test}/**/*.js'", | ||
"duti:fix": "npm run fmt && npm run lint-fix && npm run fmt", | ||
"prepublish": "webpack", | ||
"rename": "node renamePackage" | ||
}, | ||
"repository": { | ||
"type": "git", | ||
"url": "https://github.com/joaonuno/futil-js.git" | ||
"author": { | ||
"name": "Samuel Greene", | ||
"email": "sgreene@smartprocure.us" | ||
}, | ||
"keywords": [ | ||
"functional", | ||
"utilities", | ||
"apply", | ||
"call" | ||
], | ||
"author": "João Nuno Silva <jnss81@gmail.com> (http://jnuno.com)", | ||
"license": "MIT", | ||
"dependencies": { | ||
"babel-polyfill": "^6.23.0", | ||
"lodash": "^4.17.4" | ||
}, | ||
"babel": { | ||
"presets": [ | ||
"env" | ||
] | ||
}, | ||
"prettier": { | ||
"singleQuote": true, | ||
"semi": false, | ||
"trailingComma": "es5" | ||
}, | ||
"devDependencies": { | ||
"mocha": "1.14.0", | ||
"sinon": "1.7.3", | ||
"chai": "1.8.1" | ||
"babel": "^6.5.2", | ||
"babel-cli": "^6.22.2", | ||
"babel-core": "^6.22.1", | ||
"babel-eslint": "^8.0.0", | ||
"babel-loader": "^7.0.0", | ||
"babel-plugin-transform-object-rest-spread": "^6.26.0", | ||
"babel-preset-env": "^1.6.1", | ||
"bluebird": "^3.5.0", | ||
"chai": "^4.1.0", | ||
"chai-as-promised": "^7.1.1", | ||
"chokidar": "^2.0.0", | ||
"chokidar-cli": "^1.2.0", | ||
"codacy-coverage": "^3.0.0", | ||
"coveralls": "^3.0.0", | ||
"duti": "latest", | ||
"eslint": "4.19.1", | ||
"eslint-config-smartprocure": "^1.0.2", | ||
"karma": "^2.0.0", | ||
"karma-chai": "^0.1.0", | ||
"karma-chai-as-promised": "^0.1.2", | ||
"karma-chrome-launcher": "^2.2.0", | ||
"karma-json-reporter": "^1.2.1", | ||
"karma-mocha": "^1.3.0", | ||
"karma-sauce-launcher": "^1.2.0", | ||
"karma-sourcemap-loader": "^0.3.7", | ||
"karma-webpack": "^3.0.0", | ||
"mocha": "^5.0.0", | ||
"mocha-lcov-reporter": "^1.2.0", | ||
"nyc": "^12.0.1", | ||
"prettier": "^1.7.4", | ||
"read-pkg": "^4.0.1", | ||
"sinon": "^5.0.0", | ||
"sinon-chai": "^3.0.0", | ||
"webpack": "^4.1.0", | ||
"webpack-cli": "^3.0.0", | ||
"write-pkg": "^3.2.0" | ||
}, | ||
"dependencies": { | ||
"underscore": "1.x.x", | ||
"funop": "0.0.1" | ||
} | ||
"directories": { | ||
"test": "test" | ||
}, | ||
"repository": { | ||
"type": "git", | ||
"url": "git+ssh://git@github.com/smartprocure/futil-js.git" | ||
}, | ||
"bugs": { | ||
"url": "https://github.com/smartprocure/futil-js/issues" | ||
}, | ||
"homepage": "https://github.com/smartprocure/futil-js#readme", | ||
"runkitExampleFilename": "runkit.example.js", | ||
"readme": "README.md" | ||
} |
619
README.md
@@ -1,6 +0,617 @@ | ||
futil | ||
===== | ||
<a href='https://smartprocure.github.io/futil-js/'><img src='https://user-images.githubusercontent.com/8062245/28718527-796382ac-7374-11e7-98a3-9791223042a4.png' width='200' alt='futil-js'></a> | ||
Javascript Functional Utilities | ||
--- | ||
[![Build Status](https://travis-ci.org/joaonuno/futil-js.png)](https://travis-ci.org/joaonuno/futil-js) | ||
[![CircleCI](https://circleci.com/gh/smartprocure/futil-js.svg?style=svg)](https://circleci.com/gh/smartprocure/futil-js) | ||
[![Greenkeeper badge](https://badges.greenkeeper.io/smartprocure/futil-js.svg)](https://greenkeeper.io/) | ||
[![npm version](https://badge.fury.io/js/futil-js.svg)](https://badge.fury.io/js/futil-js) | ||
![dependencies](https://david-dm.org/smartprocure/futil-js.svg) | ||
[![Code Climate](https://codeclimate.com/github/smartprocure/futil-js/badges/gpa.svg)](https://codeclimate.com/github/smartprocure/futil-js) | ||
[![Codacy Badge](https://api.codacy.com/project/badge/Grade/1302fe4c3f0447be9d5dbd00f9baa12f)](https://www.codacy.com/app/daedalus28/futil-js?utm_source=github.com&utm_medium=referral&utm_content=smartprocure/futil-js&utm_campaign=Badge_Grade) | ||
[![Coverage Status](https://coveralls.io/repos/github/smartprocure/futil-js/badge.svg?branch=master)](https://coveralls.io/github/smartprocure/futil-js?branch=master) | ||
[![code style: prettier](https://img.shields.io/badge/code_style-prettier-ff69b4.svg?style=flat-square)](https://github.com/prettier/prettier) | ||
[![Try futil-js on RunKit](https://badge.runkitcdn.com/futil-js.svg)](https://npm.runkit.com/futil-js) | ||
[![Sauce Test Status](https://saucelabs.com/browser-matrix/futil.svg)](https://saucelabs.com/u/futil) | ||
A collection of F(unctional) Util(ities). Resistance is futile. | ||
Mostly, these are generic utilities that could conceivably be part of a library like lodash/fp, but for some reason or other are not. | ||
# Docs | ||
https://smartprocure.github.io/futil-js/ | ||
# Version History/Changelog | ||
See our [changelog](https://github.com/smartprocure/futil-js/blob/master/CHANGELOG.md) | ||
# Installing | ||
`npm i -S futil` | ||
or | ||
`npm i -S futil-js` | ||
This package requires `lodash/fp`, so make sure that's available in your app. | ||
# Usage | ||
`import * as F from futil` | ||
or | ||
`import F from futil` | ||
or | ||
`import {x,y,z} from futil` | ||
# API | ||
## Function | ||
### maybeCall | ||
`(fn, a, b) -> fn(a, b)` If `fn` is a function, call the function with the passed-in arguments. Otherwise, return `false`. | ||
### callOrReturn | ||
`(fn, a, b) -> fn(a, b)` If `fn` is a function, call the function with the passed-in arguments. Otherwise, return `fn`. | ||
### boundMethod | ||
`(a, Monoid f) -> f[a] :: f a` Binds a function of an object to it's object. | ||
### converge | ||
`(f, [g1, g2, ...gn]) -> a -> f([g1(a), g2(a), ...])` | ||
http://ramdajs.com/docs/#converge. Note that `f` is called on the array of the return values of `[g1, g2, ...gn]` rather than applied to it. | ||
### comply (alias: composeApply) | ||
`(f, g) -> x -> f(g(x))(x)` | ||
A combinator that combines compose and apply. `f` should be a 2 place curried function. Useful for applying comparisons to pairs defined by some one place function, e.g. `var isShorterThanFather = F.comply(isTallerThan, fatherOf)` | ||
### defer | ||
Implement `defer`, ported from bluebird docs and used by debounceAsync | ||
### debounceAsync | ||
A `_.debounce` for async functions that ensure the returned promise is resolved with the result of the execution of the actual call. Using `_.debounce` with `await` or `.then` would result in the earlier calls never returning because they're not executed - the unit tests demonstate it failing with `_.debounce`. | ||
### flurry | ||
`(f1, f2, ...fn) -> f1Arg1 -> f1Arg2 -> ...f1ArgN -> fn(f2(f1))` | ||
Flurry is combo of flow + curry, preserving the arity of the initial function. See https://github.com/lodash/lodash/issues/3612. | ||
## Logic | ||
### overNone | ||
`([f1, f2, ...fn]) -> !f1(x) && !f2(x) && ...!fn(x)` Creates a function that checks if none of the array of predicates passed in returns truthy for `x` | ||
### ifElse | ||
`(condition, onTrue, onFalse) -> x -> (T(condition)(x) ? onTrue(x) : onFalse(x))` | ||
http://ramdajs.com/docs/#ifElse. The transform function T supports passing a boolean for `condition` as well as any valid argument of `_.iteratee`, e.g. `myBool = applyTest(x); F.ifElse(myBool, doSomething, doSomethingElse);` | ||
### when | ||
`(condition, onTrue) -> x -> (T(condition)(x) ? onTrue(x) : _.identity(x))` | ||
http://ramdajs.com/docs/#when. `T` extends `_.iteratee` as above. | ||
### unless | ||
`(condition, onFalse) -> x -> (T(condition)(x) ? _.identity(x) : onFalse(x))` | ||
http://ramdajs.com/docs/#unless. `T` extends `_.iteratee` as above. | ||
### whenTruthy | ||
`when` curried with `Boolean` | ||
### whenExists | ||
`when` curried with `exists` | ||
## Collection | ||
### flowMap | ||
`[f1, f2, ...fn] -> _.map(_.flow(fn))` Maps a flow of `f1, f2, ...fn` over a collection. | ||
### findApply | ||
`f -> x -> f(find(f, x))` | ||
A version of `find` that also applies the predicate function to the result. Useful when you have an existing function that you want to apply to a member of a collection that you can best find by applying the same function. | ||
## Collection Algebras or composable/recursive data types | ||
### map | ||
`(a -> b) -> [a] -> [b]` | ||
Maps a function over an iterable. Works by default for Arrays and Plain Objects. | ||
### deepMap | ||
`(a -> b) -> [a] -> [b]` | ||
Maps a function over a recursive iterable. Works by default for nested Arrays, nested Plain Objects and mixed nested Arrays and Plain Objects. Also works for any other iterable data type as long as two other values are sent: a mapping function, and a type checker (See the unit tests for deepMap). | ||
## Lodash Conversions | ||
These are conversions of lodash fp methods. | ||
### `In`s (Rearg False) | ||
`getIn`, `hasIn`, `includesIn`, `pickIn` | ||
lodash/fp is great, but sometimes the curry order isn't exactly what you want. | ||
These methods provide alternative orderings that are sometimes more convenient. | ||
The idea of `In` methods is to name them by convention, so when ever you need a method that actually takes the collection first (e.g. a `get` where the data is static but the field is dynamic), you can just add `In` to the end (such as `getIn` which takes the object first) | ||
### `On`s (Immutable False) | ||
`extendOn`, `defaultsOn`, `mergeOn`, `setOn`, `unsetOn`, `pullOn` | ||
lodash/fp likes to keep things pure, but sometimes JS can get pretty dirty. | ||
These methods are alternatives for working with data that--for whatever the use case is--needs to be mutable | ||
Any methods that interact with mutable data will use the `On` convention (as it is some action occuring `On` some data) | ||
### `Indexed` (Cap False) | ||
`mapIndexed`, `eachIndexed`, `reduceIndexed`, `mapValuesIndexed` | ||
lodash/fp caps iteratees to one argument by default, but sometimes you need the index. | ||
These methods are uncapped versions of lodash's methods. | ||
Any method with uncapped iteratee arguments will use the `Indexed` convention. | ||
## Array | ||
### compactJoin | ||
`joinString -> [string1, string2, ...stringN] -> string1 + joinString + string2 + joinString ... + stringN` Joins an array after compacting. Note that due to the underlying behavior of `_.curry` no default `join` value is supported -- you must pass in some string with which to perform the join. | ||
### dotJoin | ||
`[string1, string2, ...stringN] -> string1 + '.' + string2 + '.' ... + stringN` Compacts and joins an array with '.' | ||
### dotJoinWith | ||
`filterFunction -> [string1, string2, ...stringN] -> string1 + '.' + string2 + '.' ... + stringN` Compacts an array by the provided function, then joins it with '.' | ||
### repeated | ||
`[a] -> [a]` Returns an array of elements that are repeated in the array. | ||
### mergeRanges | ||
`([[], [], []]) -> [[], []]` Takes any number of ranges and return the result of merging them all. | ||
Example: `[[0,7], [3,9], [11,15]] -> [[0,9], [11,15]]` | ||
### insertAtIndex | ||
`insertAtIndex -> (index, val, string) -> string` Insert a string at a specific index. | ||
Example: `(1, '123', 'hi') -> 'h123i'` | ||
### push | ||
`(val, array) -> array` Return `array` with `val` pushed. | ||
### cycle | ||
`[a, b...] -> a -> b` Creates a function that takes an element of the original array as argument and returns the next element in the array (with wrapping). Note that (1) This will return the first element of the array for any argument not in the array and (2) due to the behavior of `_.curry` the created function will return a function equivalent to itself if called with no argument. | ||
### arrayToObject | ||
`(k, v, [a]) -> { k(a): v(a) }` Creates an object from an array by generating a key/value pair for each element in the array using the key and value mapper functions. | ||
### zipObjectDeepWith | ||
A version of `_.zipObjectDeep` that supports passing a function to determine values intead of an array, which will be invoked for each key. | ||
### flags | ||
`[a, b] -> {a:true, b:true}` Converts an array of strings into an object mapping to true. Useful for optimizing `includes`. | ||
### prefixes | ||
`['a', 'b', 'c'] -> [['a'], ['a', 'b'], ['a', 'b', 'c']]` Returns a list of all prefixes. Works on strings, too. Implementations must guarantee that the orginal argument has a length property. | ||
### encoder | ||
`string -> {encode: array -> string, decode: string -> array}` Creates an object with encode and decode functions for encoding arrays as strings. The input string is used as input for join/split. | ||
#### dotEncoder | ||
`{ encode: ['a', 'b'] -> 'a.b', decode: 'a.b' -> ['a', 'b'] }` An encoder using `.` as the separator | ||
#### slashEncoder | ||
`{ encode: ['a', 'b'] -> 'a/b', decode: 'a/b' -> ['a', 'b'] }` An encoder using `/` as the separator | ||
### chunkBy | ||
`((a, a) -> Boolean) -> [a] -> [[a]]` Returns an array of | ||
arrays, where each one of the arrays has one or more elements of the | ||
original array, grouped by the first function received. Similar to | ||
Haskell's [groupBy](http://zvon.org/other/haskell/Outputlist/groupBy_f.html). | ||
## Object | ||
### singleObject | ||
`(k, v) -> {k: v}` Creates an object with a key and value. | ||
### singleObjectE | ||
`(v, k) -> {k: v}` Flipped version of `singleObject`. | ||
### chunkObject | ||
`({a, b}) -> [{a}, {b}]` Breaks an object into an array of objects with one key each. | ||
### compactObject | ||
Remove properties with falsey values. | ||
Example: `({ a: 1, b: null, c: false }) -> {a:1}` | ||
### isEmptyObject: | ||
Check if the variable is an empty object (`{}`). | ||
### isNotEmptyObject: | ||
Check if the variable is **not** an empty object (`{}`). | ||
### stripEmptyObjects | ||
Omit properties whose values are empty objects. | ||
Example: `{ a:1, b:{}, c:2 } -> {a:1, c:2}` | ||
(*TODO* remame to `omitEmptyObjects`) | ||
### compareDeep | ||
Checks if an object's property is equal to a value. | ||
### matchesSignature | ||
Returns true if object keys are only elements from signature list. (but does not require all signature keys to be present) | ||
### matchesSome | ||
Similar to `_.matches`, except it returns true if 1 or more object properties match instead of all of them. See https://github.com/lodash/lodash/issues/3713. | ||
### pickInto | ||
*TODO* | ||
### renameProperty | ||
`sourcePropertyName -> targetPropertyName -> sourceObject -> sourceObject` | ||
Rename a property on an object. | ||
Example: `renameProperty('a', 'b', { a: 1 }) -> { b: 1 }` | ||
### unwind | ||
`'b' -> { a: true, b: [1, 2] } -> { a: true, b: 1 }, { a: true, b: 2}` | ||
Just like mongo's `$unwind`: produces an array of objects from an object and one of its array-valued properties. Each object is constructed from the original object with the array value replaced by its elements. Unwinding on a nonexistent property returns an empty array. | ||
### flattenObject | ||
Flatten an object with the paths for keys. | ||
Example: `{ a: { b: { c: 1 } } } => { 'a.b.c' : 1 }`. | ||
### unflattenObject | ||
Unlatten an object with the paths for keys. | ||
Example: `{ 'a.b.c' : 1 } => { a: { b: { c: 1 } } }`. | ||
### mapProp | ||
_Deprecated in favor of lodash `update`_ Applies a map function at a specific path | ||
Example: `mapProp(double, 'a', {a: 2, b: 1}) -> {a: 4, b: 1}`. | ||
### getOrReturn | ||
`_.get` that returns the target object if lookup fails | ||
### alias | ||
`_.get` that returns the prop if lookup fails | ||
### aliasIn | ||
Flipped `alias` | ||
### cascade | ||
A `_.get` that takes an array of paths (or functions to return values) and returns the value at the first path that matches. Similar to `_.overSome`, but returns the first result that matches instead of just truthy (and supports a default value) | ||
### cascadeIn | ||
Flipped cascade | ||
### cascadeKey | ||
A `_.get` that takes an array of paths and returns the first path that matched | ||
### cascadeProp | ||
A `_.get` that takes an array of paths and returns the first value that has an existing path | ||
### cascadePropKey | ||
A `_.get` that takes an array of paths and returns the first path that exists | ||
### unkeyBy | ||
`newKey -> {a:x, b:y} -> [{...x, newKey: a}, {...y, newKey: b}]` Opposite of `_.keyBy`. Creates an array from an object where the key is merged into the values keyed by `newKey`. Example: `F.unkeyBy('_key')({ a: { status: true}, b: { status: false }) -> [{ status: true, _key: 'a' }, { status: false, _key: 'b' }]`. Passing a falsy value other than `undefined` for `newKay` will result in each object key being pushed into its corresponding return array member with itself as value, e.g. `F.unkeyBy('')({ a: { status: true}, b: { status: false }) -> [{ status: true, a: 'a' }, { status: false, b: 'b' }]`. Passing `undefined` will return another instance of F.unkeyBy. | ||
### simpleDiff | ||
`(from, to) -> simpleDiff` Produces a simple flattened (see `flattenObject`) diff between two objects. For each (flattened) key, it produced a `from` and a `to` value. Note that this will omit any values that aren't present in the deltas object. | ||
### simpleDiffArray | ||
`(from, to) -> [simpleDiffChanges]` Same as `simpleDiff`, but produces an array of `{ field, from, to }` objects instead of `{ field: { from, to } }` | ||
### diff | ||
`(from, to) -> diff` Same as `simpleDiff`, but also takes in count deleted properties. | ||
**Note:** We're considering not maintaining this in the long term, so you might probably have more success with any existing library for this purpose. | ||
### diffArray | ||
`(from, to) -> [diffChanges]` Same as `simpleDiffArray`, but also takes in count deleted properties. | ||
**Note:** We're considering not maintaining this in the long term, so you might probably have more success with any existing library for this purpose. | ||
### pickOn | ||
A `_.pick` that mutates the object | ||
### mergeAllArrays | ||
Like `_.mergeAll`, but concats arrays instead of replacing. This is basically the example from the lodash `mergeAllWith` docs. | ||
### invertByArray | ||
`{ a: [x, y, z], b: [x] } -> { x: [a, b], y: [a], z: [a] }` Similar to `_.invert`, but expands arrays instead of converting them to strings before making them keys. | ||
### stampKey | ||
`key -> { a: { x: 1 }, b: { y: 2 } } -> { a: { x: 1, key: 'a' }, b: { y: 2, key: 'b' } }` Iterates over object properties and stamps their keys on the values in the field name provided. | ||
## String | ||
### parens | ||
`'asdf' -> '(asdf)'` Wraps a string in parenthesis. | ||
### trimStrings | ||
Maps `_.trim` through all the strings of a given object or array. | ||
### autoLabel | ||
`string -> string` Converts strings like variable names to labels (generally) suitable for GUIs, including support for acronyms and numbers. It's basically `_.startCase` with acronym and number support. | ||
### autoLabelOption | ||
`string -> {value:string, label:string}` Creates a `{value, label}` which applies `autoLabel` the string parameter on puts it on the label property, with the original on the value property. You can also pass in an object with value or with both value and label. | ||
### autoLabelOptions | ||
`[string] -> [{value:string, label:string}]` Applies `autoLabelOption` to a collection. Useful for working with option lists like generating select tag options from an array of strings. | ||
## Regex | ||
### testRegex | ||
`regex -> string -> bool` Just like ramda test, creates a function to test a regex on a string. | ||
### makeRegex | ||
`options:string -> string -> regex` A curried implementation of `RegExp` construction. | ||
### makeAndTest | ||
`options:string -> string -> (string -> bool)` Makes and tests a RegExp with makeRegex and testRegex. | ||
### matchAnyWord | ||
`string -> string -> bool` Returns true if the second string matches any of the words in the first string. | ||
### matchAllWords | ||
`string -> string -> bool` Returns true if the second string matches all of the words in the first string. | ||
### postings | ||
`regex -> string -> [[number, number]]` Returns an array of postings (position ranges) for a regex and string to test, e.g. `F.postings(/a/g, 'vuhfaof') -> [[4, 5]]` | ||
### highlight | ||
`start -> end -> regex -> input -> highlightedInput` Wraps the matches for `regex` found in `input` with the strings `start` and `end`. | ||
Example: `('<b>', '</b>', /h/, 'hi') -> '<b>h</b>i'` | ||
### allMatches | ||
`regex -> string -> [{text: string, start: number, end: number}]` Returns an array of matches with start/end data, e.g. `F.allMatches(/a/g, 'vuhfaof') -> [ { text: 'a', start: 4, end: 5 } ]` | ||
## Math | ||
### greaterThanOne | ||
`number -> bool` Returns true if number is greater than one. | ||
## Lang | ||
Language level utilities | ||
### throws | ||
Just throws whatever it is passed. | ||
### tapError | ||
Tap error will run the provided function and then throw the first argument. It's like `_.tap` for rethrowing errors. | ||
### exists (alias: isNotNil) | ||
Negated `_.isNil` | ||
### isMultiple | ||
Returns true if the input has a `length` property > 1, such as arrays, strings, or custom objects with a lenth property | ||
### append | ||
A curried, flipped `_.add`. The flipping matters for strings, e.g. `F.append('a')('b') -> 'ba'` | ||
### isBlank | ||
`x -> bool` | ||
Designed to determine if something has a meaningful value, like a ux version of truthiness. It's true for everything except null, undefined, '', [], and {}. Another way of describing it is that it's the same as falsiness except 0 and false are truthy and {} is falsey. Useful for implementing "required" validation rules. | ||
### isNotBlank | ||
`x -> bool` | ||
Opposite of `isBlank` | ||
### isBlankDeep | ||
`f -> x -> bool` | ||
Recurses through an object's leaf properties and passes an array of booleans to the combinator, such as `_.some`, `_.every`, and `F.none` | ||
## Lens | ||
A lens is a getter and setter pair, which can be used to interface to some part of an object graph. | ||
Methods that operate on lenses can encapsulate common operations independent of knowledge of their surrounding context. | ||
Unlike some traditional functional lenses (like Ramda's), the set methods here are generally mutable. | ||
An object lens is simply an object that has a `get` and `set` function. | ||
An example of this is a mobx boxed observable. | ||
A function lens is a lens expressed as a single function that takes the value to set or returns the current value if nothing is passed. | ||
Examples of this in the wild are knockout observables and jquery plugin api style methods. | ||
The utilities in this library expect can accept either kind of lens, and utilities are provided to seamlessly convert between the two. | ||
### Stubs | ||
Lens stubs are primarily a reference implementation, but are useful for testing and mocking purposes | ||
#### functionLens | ||
Takes a value and returns a function lens for that value | ||
#### objectLens | ||
Takes a value and returns a object lens for that value | ||
### Lens Conversions | ||
Methods to convert between lens types | ||
#### fnToObj | ||
Converts a function lens an object lens | ||
#### objToFn | ||
Converts an object lens to a function lens | ||
### Lens Construction | ||
This the main way you'll generally interact with the lens API | ||
#### lensProp | ||
`propertyName -> object -> { get: () -> object.propertyName, set: propertyValue -> object.propertyName }` | ||
Creates an object lens for a given property on an object. `.get` returns the value at that path and `set` places a new value at that path. Supports deep paths like lodash get/set. | ||
#### lensOf | ||
Takes an object and returns an object with lenses at the values of each path. Basically `mapValues(lensProp)`. | ||
### Lens Manipulation | ||
*Note*: As of version 1.37, any manipulation function that takes a lens can also drop in a key and target object for an implicit lensProp conversion (e.g. you can do `view(key, obj)` instead of just `view(lens)`) | ||
#### view | ||
`Lens -> object.propertyName` | ||
Gets the value of the lens, regardless of if it's a function or object lens | ||
#### views | ||
`Lens -> (() -> object.propertyName)` | ||
Returns a function that gets the value of the lens, regardless of if it's a function or object lens | ||
#### set | ||
`propertyValue -> Lens -> object.propertyName` | ||
Sets the value of the lens, regardless of if it's a function or object lens | ||
#### sets | ||
Creates a function that will set a lens with the provided value | ||
#### flip | ||
Takes a lens and negates its value | ||
#### on | ||
Returns a function that will set a lens to `true` | ||
#### off | ||
Returns a function that will set a lens to `false` | ||
## Aspect | ||
Aspects provide a functional oriented implementation of Aspect Oriented Programming. | ||
An aspect wraps a function and allows you run code at various points like before and after execution. | ||
Notably, aspects in this library allow you to have a shared state object between aspects and are very useful for automating things like status indicators, etc on functions. | ||
There is a _lot_ of prior art in the javascript world, but most of it assumes a vaguely object oriented context. | ||
The implementation in `futil-js` is done in just 20 lines of code and seems to capture all of the use cases of AOP. | ||
> Note: To do OO style AOP with this these aspects, just use lodash's `_.update` method and optionally `boundMethod` from `futil` if `this` matters | ||
> Caveat: While you can and should compose (or `_.flow`) aspects together, don't put non aspects in the middle of the composition. Aspects rely on a `.state` property on the wrapped function that they propagate through, but the chain will break if a non-aspect is mixed in between. Additionally, if you need external access to the state, make sure the aspects are the outer most part of the composition so the `.state` property will be available on the result of the composition. | ||
### aspect | ||
`{options} -> f -> aspectWrapped(f)` | ||
The aspect api takes an options object and returns a function which takes a function to wrap. | ||
The wrapped function will be decorated with a `state` object and is equivalent to the original function for all arguments. | ||
Options supports the following parameters: | ||
| Name | Description | | ||
| --- | --- | | ||
| `init: (state) -> ()` | A function for setting any inital state requirements. Should mutate the shared state object. | | ||
| `after: (result, state, params) -> ()` | Runs after the wrapped function executes and recieves the shared state and the result of the function. Can be async. | | ||
| `before: (params, state) -> ()` | Runs before the wrapped function executes and receves the shared state and the params passed to the wrapped function. Can be async. | | ||
| `onError: (error, state, params) -> ()` | Runs if the wrapped function throws an error. If you don't throw inside this, it will swallow any errors that happen. | | ||
| `always: (state, params) -> ()` | Runs after the wrapped function whether it throws an error or not, similar to a `Promise.catch` | | ||
Example Usage: | ||
```js | ||
let exampleAspect = aspect({ | ||
before: () => console.log('pre run'), | ||
after: () => console.log('post run') | ||
}) | ||
let f = () => console.log('run') | ||
let wrapped = exampleAspect(f) | ||
wrapped() | ||
// Logs to the console: | ||
// pre run | ||
// run | ||
// post run | ||
``` | ||
### aspectSync | ||
This is a synchronous version of `aspect`, for situations when it's not desirable to `await` a method you're adding aspects to. The API is the same, but things like `onError` won't work if you pass an async function to the aspect. | ||
### aspects | ||
There are a few basic aspects included on `F.aspects` (E.g. `var loggedFunc = F.aspect(F.aspects.logs)(func)`) because they seem to be universally useful. | ||
All of the provided aspects take an `extend` function to allow customizing the state mutation method (e.g. in mobx, you'd use `extendObservable`). | ||
If null, they default to `defaultsOn` from `futil-js` - check the unit tests for example usage. | ||
#### logs | ||
Logs adds a `logs` array to the function state and just pushes in results on each run | ||
#### error | ||
Captures any exceptions thrown and set it on an `error` error it puts on state | ||
#### errors | ||
Captures any exceptions thrown and pushes them sequentially into an `errors` array it puts on state | ||
#### status | ||
Adds a `status` property that is set to `processing` before the wrapped function runs and `succeeded` when it's done or `failed` if it threw an exception. Also adds shortcuts on state for `processing`, `succeeded`, and `failed`, which are booleans which are based on the value of `status`. Also adds a `setStatus` method which is used internally to update these properties. | ||
#### clearStatus | ||
Sets `status` to null after provided timeout (default is 500ms) elapses. If a null timeout is passed, it will never set status to null. | ||
#### concurrency | ||
Prevents a function from running if it's state has `processing` set to true at the time of invocation | ||
#### command | ||
Flows together `status`, `clearStatus`, `concurrency`, and `error`, taking `extend` and `timeout` as optional parameters to construct the aspect | ||
#### deprecate | ||
Utility for marking functions as deprecated - it's just a `before` with a console.warn. Takes the name of thing being deprecated, optionally deprecation version, and optionally an alternative and returns a higher order function which you can wrap deprecated methods in. This is what's used internally to mark deprecations. Includes a partial stack trace as part of the deprecation warning. | ||
## Trees | ||
All tree functions take a traversal function so that you can customize how to traverse arbitrary nested structures. | ||
*Note*: Be careful about cyclic structures that can result in infinite loops, such as objects with references to itself. There are cases where you'd intentionally want to visit the same node multiple times, such as traversing a directed acyclic graph (which would work just fine and eventually terminate, but would visit a node once for each parent it has connected to it) - but it's up to the user to be sure you don't create infinite loops. | ||
### isTraversable | ||
A default check if something can be traversed - currently it is arrays and plain objects. | ||
### traverse | ||
The default traversal function used in other tree methods if you don't supply one. It returns false if it's not traversable or empty, and returns the object if it is. | ||
### walk | ||
`traverse -> (pre, post=_.noop) -> tree -> x` | ||
A depth first search which visits every node returned by `traverse` recursively. Both `pre-order` and `post-order` traversals are supported (and can be mixed freely). `walk` also supports exiting iteration early by returning a truthy value from either the `pre` or `post` functions. The returned value is also the return value of `walk`. The pre, post, and traversal functions are passed the current node as well as the parent stack (where parents[0] is the direct parent). | ||
### transformTree | ||
`traverse -> _iteratee -> tree -> newTree` | ||
Structure preserving pre-order depth first traversal which clones, mutates, and then returns a tree. Basically `walk` with a `_.cloneDeep` first (similar to a tree map because it preserves structure). `_iteratee` can be any suitable argument to `_.iteratee` https://lodash.com/docs/4.17.5#iteratee | ||
### reduceTree | ||
`traverse -> (accumulator, initialValue, tree) -> x` | ||
Just like `_.reduce`, but traverses over the tree with the traversal function in `pre-order`. | ||
### treeToArray | ||
`traverse -> tree -> [treeNode, treeNode, ...]` | ||
Flattens the tree nodes into an array, simply recording the node values in pre-order traversal. | ||
### treeToArrayBy | ||
`traverse -> f -> tree -> [f(treeNode), f(treeNode), ...]` | ||
Like `treeToArray`, but accepts a customizer to process the tree nodes before putting them in an array. It's `_.map` for trees - but it's not called treeMap because it does not preserve the structure as you might expect `map` to do. | ||
### leaves | ||
`traverse -> tree -> [treeNodes]` | ||
Returns an array of the tree nodes that can't be traversed into in `pre-order`. | ||
### treeLookup | ||
`(traverse, buildIteratee) -> ([_iteratee], tree) -> treeNode` | ||
Looks up a node matching a path, which defaults to lodash `iteratee` but can be customized with buildIteratee. The `_iteratee` members of the array can be any suitable arguments for `_.iteratee` https://lodash.com/docs/4.17.5#iteratee | ||
### keyByWith | ||
`traverse -> transformer -> _iteratee -> tree -> result` | ||
Similar to a keyBy (aka groupBy) for trees, but also transforms the grouped values (instead of filtering out tree nodes). The transformer takes three args, the current node, a boolean of if the node matches the current group, and what group is being evaluated for this iteratee. The transformer is called on each node for each grouping. `_iteratee` is any suitable argument to `_.iteratee`, as above. | ||
### flattenTree | ||
`traverse -> buildPath -> tree -> result` | ||
Creates a flat object with a property for each node, using `buildPath` to determine the keys. `buildPath` takes the same arguments as a tree walking iteratee. It will default to a dot tree path. | ||
### treePath | ||
`(build, encoder) -> treePathBuilderFunction` | ||
Creates a path builder for use in `flattenTree`. By default, the builder will look use child indexes and a dotEncoder. Encoder can be an encoding function or a futil `encoder` (an object with encode and decode functions) | ||
### propTreePath | ||
`prop -> treePathBuilderFunction` | ||
Creates a path builder for use in `flattenTree`, using a slashEncoder and using the specified prop function as an iteratee on each node to determine the keys. | ||
### treeKeys | ||
A utility tree iteratee that returns the stack of node indexes | ||
### treeValues | ||
A utility tree iteratee that returns the stack of node values | ||
### tree | ||
`(traverse, buildIteratee) -> {walk, reduce, transform, toArray, toArrayBy, leaves, lookup, keyByWith, traverse, flatten, flatLeaves }` | ||
Takes a traversal function and returns an object with all of the tree methods pre-applied with the traversal. This is useful if you want to use a few of the tree methods with a custom traversal and can provides a slightly nicer api. | ||
Exposes provided `traverse` function as `traverse` |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
New author
Supply chain riskA new npm collaborator published a version of the package for the first time. New collaborators are usually benign additions to a project, but do indicate a change to the security surface area of a package.
Found 1 instance in 1 package
Environment variable access
Supply chain riskPackage accesses environment variables, which may be a sign of credential stuffing or data theft.
Found 1 instance in 1 package
Minified code
QualityThis package contains minified code. This may be harmless in some cases where minified code is included in packaged libraries, however packages on npm should not minify code.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
No bug tracker
MaintenancePackage does not have a linked bug tracker in package.json.
Found 1 instance in 1 package
No v1
QualityPackage is not semver >=1. This means it is not stable and does not support ^ ranges.
Found 1 instance in 1 package
No website
QualityPackage does not have a website.
Found 1 instance in 1 package
353855
48
5499
0
618
36
1
2
+ Addedbabel-polyfill@^6.23.0
+ Addedlodash@^4.17.4
+ Addedbabel-polyfill@6.26.0(transitive)
+ Addedbabel-runtime@6.26.0(transitive)
+ Addedcore-js@2.6.12(transitive)
+ Addedlodash@4.17.21(transitive)
+ Addedregenerator-runtime@0.10.50.11.1(transitive)
- Removedfunop@0.0.1
- Removedunderscore@1.x.x
- Removedfunop@0.0.1(transitive)
- Removedunderscore@1.13.7(transitive)