const { mapObject } = require('underscore');
const { at_maybe, cloneImpl, isLensClass } = require('./src-cjs/constants');
const Errors = require('./cjs/errors');
const fusion = require('./cjs/fusion').default;
const Lens = require('./cjs/lens').default;
const { eachFound, maybeDo } = require('./cjs/utils');
let fuse = null;
/**
* @module natural-lenses
* @summary Construct a Lens from the given indexing steps
*
* @param {...*} key A name or index to use in successive subscripting (i.e. square bracket) operations
* @returns {Lens} The constructed lens
*
* @property {symbol} at_maybe Key for method implementing retrieval from a container
* @property {symbol} clone Key for method implementing cloning of a container with modifications
* @property {Function} eachFound [Documentation]{@link module:natural-lenses#eachFound}
* @property {Function} Factory [Class]{@link Factory} for customized lens creation
* @property {Function} fuse [Documentation]{@link module:natural-lenses#fuse}
* @property {symbol} isLens Key for testing objects for "lens-ness"
* @property {Function} JsContainerFactory [Class]{@link JsContainerFactory} for customized container creation
* @property {Object} jsContainers {@link JsContainerFactory} for standard JavaScript containers (Map and Array)
* @property {Function} maybeDo [Documentation]{@link module:natural-lenses#maybeDo}
* @property {Function} nfocal [Construct]{@link module:natural-lenses#nfocal} a multifocal lens
* @property {Function} polyfillImmutable [Documentation]{@link module:natural-lenses#polyfillImmutable}
* @property {Function} Step [Class]{@link Step} for customized Lens steps
*
* @description
* This module is (when `require`d) or exports as default (when `import`ed) a
* Function accepting an arbitrary number of keys and returning a {@link Lens}.
*
* A Lens is a way to safely apply the indexing (i.e. square-bracket) operator
* repeatedly, and to clone-with-changes a complex value to assist programming
* in an immutable-data style.
*
* In addition to the properties enumerated here, all error classes are also
* exposed as properties or named exports.
*/
function makeLens(...keys) {
return new Lens(...keys);
}
Object.defineProperties(makeLens, {
at_maybe: {enumerable: true, value: at_maybe},
clone: {enumerable: true, value: cloneImpl},
eachFound: {enumerable: true, value: eachFound},
isLens: {enumerable: true, value: isLensClass},
maybeDo: {enumerable: true, value: maybeDo},
...mapObject(Errors, (cls) => ({enumerable: true, value: cls})),
/**
* @function module:natural-lenses#fuse
* @summary Fuse multiple optics into a single, sequential application
* @param {...Optic} optic Optic object to fuse
* @returns {Lens|OpticArray} A single {@link Optic} joining the *optics*
*
* @description
* To understand the slot reference of the returned optic, consider the
* *getting* model: `optics[0]` will be applied to the input data, and
* for all other optics (i > 0), `optics[i]` will be applied to the
* result from `optics[i - 1]`. In other words, each optic *gets* something
* within the optic to its left, with the leftmost *getting* from the
* input data. Modifications affect the same slot.
*
* If all *optics* are [Lenses]{@link Lens}, the result will be a Lens. This
* does not apply for Lens-derived objects (e.g. from [Factories]{@link Factory}) —
* if such are passed, the result will always be an OpticArray.
*/
fuse: {enumerable: true, get: () => {
const OpticArray = require('./cjs/optic_array.js').default;
fuse = fuse || fusion({ Lens, OpticArray });
return fuse;
}},
/**
* @function module:natural-lenses#nfocal
* @summary Construct a multifocal lens
* @param {Array.<Optic> | Object.<string,Optic>} lenses Collection of [Lenses]{@link Lens} to combine
* @returns {ArrayNFocal|ObjectNFocal}
*
* @description
* Where a standard lens looks at a single *slot* within a JSONic object, a
* multifocal lens consists of multiple lenses (standard or multifocal) whose
* results are aggregated into a single value, which can be a dictionary-like
* Object or an Array.
*
* Pass an Array of lenses to create a multifocal lens that outputs an Array,
* where the position of each lens in the input Array corresponds to the
* output position of the data (when *getting* with the multifocal lens). When
* *getting* in a Maybe context (`#get_maybe`), the result will always be an
* Object with a `just` property, though some elements of the Array may be
* empty.
*
* Pass an Object with lens values to create a multifocal lens outputting an
* Object, which will bear the same properties as the input Object and whose
* values are the values in the data referenced by the corresponding lens (when
* *getting* using this multifocal lens).
*
* Be aware that the `xformInClone` method on these kinds of multifocal lenses
* works differently from a basic Lens, since multifocals can have a
* "stereoscopic" view of data. Instead of a single transformation function,
* `xformInClone` (and it's `_maybe` variant) accept an iterable of key/transform
* pairs (where the key is an integer index in the case of an Array multifocal).
* This allows full control over the order in which transformations are applied
* to the input data, resolving the issue of "stereoscopic conflict".
*/
nfocal: {enumerable: true, get: () => (lenses) => {
return require('./cjs/nfocal').makeNFocal(lenses);
}},
Factory: {enumerable: true, get: () => require('./cjs/lens_factory').default},
JsContainerFactory: {enumerable: true, get: () => require('./cjs/js_container_factory').default},
jsContainers: {enumerable: true, get: () => require('./cjs/js_container_factory').DEFAULT_FACTORY},
polyfillImmutable: {enumerable: true, get: () => require('./cjs/immutable_support').polyfillImmutable},
Step: {enumerable: true, get: () => require('./cjs/custom_step').default},
});
/**
* @constant
* @name module:natural-lenses#at_maybe
* @type {symbol}
*
* @description
* This constant is a key used for querying a method from container objects of
* type `function(*): {@link Maybe}.<*>`. The value passed to this method will be
* a *key* from a Lens -- the kind of value that should be passed for a
* conceptual "indexing" of the container. For Object, this is a string property
* name. For Array, this is an integer index, where negative values count
* backward from the end of the Array. For Map, this uses `Map.prototype.has`
* and `Map.prototype.get`.
*/
/**
* @constant
* @name module:natural-lenses#clone
* @type {symbol}
*
* @description
* This constant is a key used for querying a method from container objects of
* type `function({set: {0: *, 1: *}?, spliceOut: *?}): *`. The intent of the
* method is to clone the container with some kind of alteration -- either
* a key/index set to the given value in the clone, or a key/index deleted
* from the clone.
*
* If the operation description passed contains a `set` property,
* the value of that property should be an Array where element 0 is a key or
* index into the container and element 1 is the value to set (cf. arguments
* to `Map.prototype.set`).
*
* If the operation description passed contains a `spliceOut` property,
* the value of that property should be a key or index to delete from the
* container. Where possible, the result should be to leave the container
* in a state where `container[at_maybe](key)` returns `{}` (a *Nothing*).
* This is specifically a problem for [immutable.List]{@link external:immutable.List},
* which offers only a dense presentation of elements: every non-negative index
* less than `size` is a valid and "contained" entry. The implementation provided
* by this library for implementing this method on
* [immutable.List]{@link external:immutable.List} sets the value of the entry
* in the clone to `undefined`.
*
* In the provided implemenation for Array, negative indexes are interpreted
* counting backward from the end of the Array, as with `Array.prototype.slice`.
*
* `Symbol.species` is honored for determining the constructor used for the
* clone; Object is a special case that defaults to Object if `Symbol.species`
* is not present.
*/
/**
* @constant
* @name module:natural-lenses#isLens
* @type {symbol}
*
* @description
* This property is set on every kind of object to be recognized by this
* library as implementing lens-like behavior. Setting this property to
* any truthy value on your own objects will cause this library to treat it
* in many ways like a {@link Lens}.
*/
module.exports = makeLens;