
Security News
Feross on TBPN: How North Korea Hijacked Axios
Socket CEO Feross Aboukhadijeh breaks down how North Korea hijacked Axios and what it means for the future of software supply chain security.
yarn add point-less
or
npm i point-less
Lenses are a very deep, abstract concept, but in practice it can still be useful
to use only the two most basic lens operations, view and over, on the most
basic type of lens, which essentially is just a path.
view(path, object) reads the value of object at the path path.
over(path, updater, object) updates the value of object at the path path,
to be whatever the function updater returns. updater receives the current
value as an argument. So for example,
const path = ['info', 'count'];
const updater = x => x + 1;
const object = {
info: {
name: 'fran',
count: 7
}
};
over(path, updater, object); // { info: { name: 'fran', count: 8 } }
The third basic lens method set is a special case of over, where the
updater is a constant function. set(path, value, object) will set the
value of object at the path path to be value (which is equivalent to
over(path, () => value, object)).
A path is an array of values corresponding to chained property lookups in an
object. For example, the path ['name', 'first'] in an object person would
correspond to person.name.first.
A pathMap is an object where every value is either a path or a pathMap.
The keys of a pathMap are called pathAliases.
For example:
const personMap = {
firstName: ['name', 'first'],
lastName: ['name', 'last'],
age: ['age']
};
const familyMap = {
mom: personMap,
dad: personMap,
name: ['name']
};
Here, familyMap would (partially) describe the following object:
const family = {
mom: {
name: {
first: 'Shirley',
last: 'Schmidt'
},
age: 70
},
dad: {
name: {
first: 'Denny',
last: 'Crane'
},
age: 86
},
name: 'Crane'
}
Given a pathMap and lens methods { view, set, over }, this library aims
to automate some of the boilerplate inherent in reading and updating state.
pointless(pathMap, lensInterface = vanilla)Takes a pathMap as input and returns a stateInterface instance. It accepts
an optional second argument lensInterface, which describes how data is read
and updated. The default lensInterface, vanilla, works on plain JS objects.
enhanceLensInterface(lensPath, view, over)Generates a lensInterface instance based on the three atomic functions
provided. These functions should act in the same way as their ramda namesakes
(to generate the vanilla interface, the ramda functions themselves are used).
A lensInterface instance used for working with regular JS objects.
A lensInterface instance used for working with immutable objects (caveat:
the way this is implemented is really lazy, but it's enough to cover all
functionality provided by the stateInterface API, as of now).
stateInterface apiview, set, overEach of the three basic lens operations have properties corresponding to the
properties of the given pathMap. For example:
const { pointless } = require('point-less');
const pathMap = { firstName: ['name', 'first'] };
const { view, over } = pointless(pathMap);
const brahms = { name: { first: 'johannes'} };
view.firstName(brahms); // 'johannes'
const capitalize = name => name[0].toUpper() + name.slice(1);
over.firstName(capitalize, brahms); // { name: { first: 'Johannes' } }
view[pathAlias](state)Reads the value of state at the path associated to pathAlias.
set[pathAlias](value, state)Sets the value of state to value, at the path associated to pathAlias.
over[pathAlias](updater, state, ...extraArgs)Sets the value of state at the path associated to pathAlias. The new
value is computed as updater(state, ...extraArgs).
at(...subpathArgs)at returns a relativeStateInterface at a path computed from subpathArgs.
That is, it uses the same pathMap given to the original state interface,
but all paths are implicitly prepended with the extra path given to at. As a
result, you can read and update an object even if the pathMap only applies to
a subobject. For example:
const pathMap = {
lastName: ['name', 'last']
};
const data = {
users: [
{ name: { first: 'debra', last: 'messing' } },
{ name: { first: 'debra', last: 'missing' } }
]
};
const substate = pointless(pathMap).at(['users', 1]);
substate.view.lastName(data); // 'missing'
// updates the last name in the context of the full object; `newData` is still
// the same shape as `data`.
const newData = substate.set.lastName('amassing', data);
newData.users[1]; // { name: { first: 'debra', last: 'amassing' } }
A relativeStateInterface is also a stateInterface, so the API of the latter
also applies to the former. In addition, a relativeStateInterface also has an
all property. This gives a full { view, set, over } interface on the
entire object at the subpath. Continuing the above:
const newUser = { name: { first: 'deer', last: 'hunter' } };
const newData = substate.all.set(newUser, data);
newData.users[1] === newUser; // true
The subpath is computed from ...subpathArgs by fully flattening the array
subpathArgs. That is, at(['a', 'b', 'c']) is equivalent to
at('a', 'b', 'c') and at([[[['a']]], ['b']], 'c').
In addition, any function in the flattened subpath array can be used to
dynamically select a path after the state has been received. Any function
found in subpath will receive as arguments both state and any other
arguments after state. Continuing the above:
const substate = pointless(pathMap).at('users', (_, action) => action.index);
const updateFirst = substate.firstName.over((_, action) => action.firstName);
const newData = updateFirst(data, { index: 1, firstName: 'barbara' });
newData.users[1]; // { name: { first: 'barbara', last: 'massing' } };
overWith(selectors, updater, state)overWith is a shortcut for a special case of an over call, to facilitate
using multiple pieces of the current state to update multiple pieces.
selectors is an array of functions which take the state as input.
Whatever they return is passed into the arguments of updater,
another user-provided function. Note that the selectors don't need to be
simple property reads from a stateInterface; any function that takes the
state as input, such as a memoized selector, would work.
The return value of updater must be an object updates, whose keys are
pathAliases in pathMap. Each pathAlias will have its corresponding value
in state set to updates[pathAlias]. As a somewhat contrived example:
const pathMap = {
birthDay: ['dob', 'day'],
birthMonth: ['dob', 'month'],
birthYear: ['dob', 'year'],
birthStr: ['birthStr']
};
const { view, overWith } = pointless(pathMap);
const computeBirthStr = overWith(
[view.birthDay, view.birthMonth, view.birthYear],
(day, month, year) => ({ birthStr: [month, day, year].join('/') })
);
const data = { dob: { day: 15, month: 5, year: 1992 } };
computeBirthStr(data); // { dob: { ... }, birthStr: '5/15/1992' }
pathsA function can be used as an element of a path array, when the path should depend on the current state or otherwise won't be known until a lens operation is called. The motivating case for this was to pick out an index in an array, for example:
const actionUser = ['users', (state, action) => action.index];
const pathMap = {
actionUser,
actionUsername: [...actionUser, 'name']
};
const data = {
users: [{ name: 'momoney' }, { name: 'moproblems' }]
};
const state = pointless(pathMap);
state.view.actionUser(data, { index: 1 }); // { name: 'moproblems' };
const newData = state.over.actionUsername(
(state, action) => action.newName,
data,
{ index: 1, newName: 'moped' }
);
newData; // { users: [{ name: 'momoney' }, { name: 'moped' }] }
As stated above, a pathMap can have another pathMap as one of its values.
In that case, the pathAlias follows the same chain of properties as in the
pathMap:
const userGroupPathMap = {
count: ['count'],
};
const nestedPathMap = {
online: userGroupPathMap,
offline: userGroupPathMap
};
const data = {
online: { count: 7 },
offline: { count: 100 }
};
const state = pointless(nestedPathMap);
state.view.online.size(data); // 7
state.view.offline.size(data); // 100
FAQs
pointless state interface
We found that point-less demonstrated a not healthy version release cadence and project activity because the last version was released a year ago. It has 1 open source maintainer collaborating on the project.
Did you know?

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Security News
Socket CEO Feross Aboukhadijeh breaks down how North Korea hijacked Axios and what it means for the future of software supply chain security.

Security News
OpenSSF has issued a high-severity advisory warning open source developers of an active Slack-based campaign using impersonation to deliver malware.

Research
/Security News
Malicious packages published to npm, PyPI, Go Modules, crates.io, and Packagist impersonate developer tooling to fetch staged malware, steal credentials and wallets, and enable remote access.