Repository URL to install this package:
|
Version:
1.2.19 ▾
|
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
var _exportNames = {
composition: true,
coerceResponseWithSchema: true,
autofixTypes: true,
coerceAndAutofixResponseWithSchema: true
};
exports.composition = composition;
exports.coerceResponseWithSchema = coerceResponseWithSchema;
exports.autofixTypes = autofixTypes;
exports.default = exports.coerceAndAutofixResponseWithSchema = void 0;
var _chainAbleBoost = require("chain-able-boost");
var _exotic = require("exotic");
var _ramda = require("ramda");
var _lodash = require("lodash");
var _deps = require("../deps");
var _get = require("./get");
Object.keys(_get).forEach(function (key) {
if (key === "default" || key === "__esModule") return;
if (Object.prototype.hasOwnProperty.call(_exportNames, key)) return;
Object.defineProperty(exports, key, {
enumerable: true,
get: function get() {
return _get[key];
}
});
});
// @NOTE named - by ref
// exported.cast = {cast, is}
// make Ramda's names better
// R.allButLast = R.init
// R.removePathInObj = R.dissocPath
// @TODO use `Nil` type
// just adapt this to use types in composition and lotsa fun
// const {
// isObj,
// isArray,
// isNumber,
// isBoolean,
// isString,
// toBoolean,
// toString,
// toNumber,
// toArray,
// toObject,
// isFunction,
// } = Typed.spread(Object, Array, Function, Number, String, Boolean)
const primitives = [(0, _exotic.Typed)(Boolean), (0, _exotic.Typed)(Number), (0, _exotic.Typed)(String)];
const find = (value, path) => {
return primitives.find(Type => Type.is(value, path)) || {
coerce: _chainAbleBoost.identity
};
};
/**
* @NOTE this is the only piece being used in this
* @NOTE there is an opinionated decision here that can be improved if required
* where ONLY schema is traversed, and *applied* to the data
* and it doesn't double traverse
* (mainly because it allows defaulting)
*
* 1. recurse [schema/x] (call self)
* 2. .get from response
* 3. use types in `x` -> schema -> nested types -> coerce response
* 4. how to handle response data best that is not in there, could just `pluck` style, or throw
*
* @alias evolveResponseWithSchema
* @param {Object} schema types
* @param {*} response data
* @return {*} based on response + schema coersion
*/
function coerceResponseWithSchema(x, response) {
if ((0, _exotic.isNil)(x)) {
return response;
}
if ((0, _exotic.isString)(x)) {
return (0, _exotic.toString)(response);
}
if ((0, _exotic.isNumber)(x)) {
return (0, _exotic.toNumber)(response);
}
if ((0, _exotic.isBoolean)(x)) {
return (0, _exotic.toBoolean)(response);
}
if ((0, _exotic.isObj)(x)) {
let coerced = (0, _exotic.toEmpty)(x);
(0, _chainAbleBoost.forOwn)(x, (value, key) => {
// @NOTE could use R.prop whatever
coerced[key] = coerceResponseWithSchema(value, (0, _lodash.get)(response, key));
});
if ((0, _exotic.isArray)(x)) {
return (0, _exotic.toArray)(coerced);
} else {
return (0, _exotic.toObject)(coerced);
}
}
console.log(x);
throw new TypeError('missing some data!');
}
/**
* @NOTE this is the same as coercePrimitives, but does not `flatten`
* @TODO @JAMES eventually this gets integrated with json parsing in api call middleware @PERF
* @TODO split this elsewhere just temp autofixing coersion in defaulting data
* @see coercePrimitives
* @param {*} response
* @return {*} response with fixed data, or better-than-null
*/
function autofixTypes(response) {
if ((0, _exotic.isNil)(response)) {
return _exotic.EMPTY_STRING;
}
if ((0, _exotic.isString)(response)) {
return (0, _exotic.toString)(response);
}
if ((0, _exotic.isNumber)(response)) {
return (0, _exotic.toNumber)(response);
}
if ((0, _exotic.isBoolean)(response)) {
return (0, _exotic.toBoolean)(response);
}
if ((0, _exotic.isObj)(response)) {
let coerced = (0, _exotic.toEmpty)(response);
(0, _chainAbleBoost.forOwn)(response, (value, key) => {
// @NOTE could use R.prop whatever
coerced[key] = autofixTypes(value, (0, _lodash.get)(response, key));
});
if ((0, _exotic.isArray)(response)) {
return (0, _exotic.toArray)(coerced);
} else {
return (0, _exotic.toObject)(coerced);
}
}
throw new TypeError('missing some data! ' + JSON.stringify(response));
}
const coerceAndAutofixResponseWithSchema = (schema, response) => coerceResponseWithSchema(schema, autofixTypes(response));
/**
* @closure
* @return {*} whatever (object)
* @param {*} original data
*
* could also use all schemas or whatever here,
*
* could say split back out as 1 line fns
* and use as constructor in here with curries
*
* much stuff to do
*/
exports.coerceAndAutofixResponseWithSchema = coerceAndAutofixResponseWithSchema;
function composition(original) {
let target = (0, _ramda.clone)(original);
/**
* @desc closure javascript fp true style epicness
* @constructor
*/
function constructor() {
// waaaaat
const fp = Object.create(null);
fp.liftRecursive = liftRecursive;
fp.target = target;
fp.reshapeWhere = reshapeWhere;
fp.transformWhere = transformWhere;
fp.coercePrimitives = coercePrimitives;
fp.liftProp = liftProp;
fp.liftPaths = liftPaths;
fp.coerceResponseWithSchema = coerceResponseWithSchema;
fp.autofixTypes = autofixTypes;
return fp;
}
const fp = constructor();
/**
* @TODO property, value
* @TODO can use http://ramdajs.com/docs/#applySpec - for some of the lifting
* @see http://funkyjavascript.com/lenses/
* @curried 2
*
* @alias evolveWhere
* @param {Object} schema to match & reschema
* @param {Object} obj object to transform
* @return {Object} transformed object
*/
function transformWhere(schema, obj) {
let transformed = obj; // recurse the schema
(0, _chainAbleBoost.recurse)(obj).forEach((x, key, traverser) => {
if (traverser.isRoot) {
return;
} // @TODO !!!!!!!! THIS PART I LOST IT !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! (and api changed)
const evolution = schemaEvolver(x, key); // when we have an evolution, use it
if (evolution !== false) {
const setForPath = (0, _ramda.lens)((0, _ramda.path)(traverser.path), (0, _ramda.assocPath)(traverser.path)); // apply the evolution to this path setting immutably
transformed = (0, _ramda.set)(setForPath, evolution, transformed);
}
});
return transformed;
}
/**
* @param {Array<string> | string} property to lift deeply into top-level
* @TODO @MICHAEL requires logic for duplication on multi-levels
*/
function liftRecursive(property) {
let lifted = (0, _exotic.toEmpty)(target);
(0, _chainAbleBoost.recurse)(target).forEach((x, key, traverser) => {
// only exact property matches
if (key !== property) {
return;
} // dot-prop-path
const path = traverser.path.slice();
const pathUp = (0, _ramda.init)(path); // no parent
if (pathUp.length === 0) {
return;
} // STUPID THING
const setForPath = R.lens(R.path(pathUp), R.assocPath(pathUp));
const sliced = R.set(setForPath, x, lifted);
lifted = (0, _chainAbleBoost.merge)(lifted, sliced); // @NOTE shallow copy means 1 level deep sheesh
// lifted = Object.assign({}, lifted, sliced)
lifted = (0, _ramda.dissocPath)(path, lifted);
}); // immutable with `lifted` can merge(target, lifted, {clone: true})
// for however you want to have a history of transformations :-)
}
/**
* @desc changes all primitives that are not their correct type, to the correct version
* @param {Object} data
* @return {Object} coerced data
* @example "false" -> false
*/
function coercePrimitives(data) {
const flat = (0, _chainAbleBoost.toUniverseView)(data, true);
(0, _chainAbleBoost.forOwn)(flat, (value, path) => {
// which primitive wants this one
const Type = find(value, path); // upgrade it
flat[path] = Type.coerce(value, path);
});
return flat;
}
/**
* @desc traverses the OBJECT (response) to apply DEFAULTS based on TYPE or AUTOFIX data
*
* @alias defaulto
* @param {Object} schema
* @param {*} obj
* @return {*}
*/
function coerceOrDefault(schema, obj) {
let transformed = obj;
(0, _chainAbleBoost.recurse)(obj).forEach((x, key, traverser) => {
// ignore first
if (!key) {
return;
}
const path = traverser.path;
let evolution = (0, _lodash.get)(schema, path); // when we have an evolution, use it
if (evolution !== undefined) {
// get type
const EvolutionType = _exotic.Typed.findAll(evolution)[0];
if ((0, _exotic.isNative)(evolution)) {
// if no default value, coerce to default
evolution = EvolutionType.coerce;
} else {
// with a default,
// check type,
// if not that type,
// default it (would've autocoerced before this step)
const defaulValue = evolution;
evolution = x => EvolutionType.is(x) ? x : defaulValue;
}
(0, _lodash.set)(transformed, path, evolution(x));
}
});
return transformed;
}
/**
* @param {Object} data data to reshape
* @param {string} property single property to lift up
* @return {Object} lifted
*/
function liftProp(data, property) {
const value = (0, _lodash.get)(data, property);
if (!value) {
return data;
}
let merged = (0, _chainAbleBoost.merge)(data, value, {
clone: true
});
let lifted = (0, _deps.withoutProp)(property, merged);
return lifted;
}
/**
* @param {Object} data data to reshape
* @param {Array<string>} paths list of paths to lift up
* @return {Object} lifted
*/
function liftPaths(data, paths) {
let lifted = data;
(0, _chainAbleBoost.forOwn)(paths, path => {
lifted = liftProp(data, path);
});
return lifted;
}
/**
* @see http://funkyjavascript.com/lenses/
* @tutorial https://github.com/ramda/ramda-lens/tree/master/src
* @param {Object} data data to reshape
* @param {Object} shape shape to reshape when it fits
* @param {Function} evolution make it fit - playdoe
* @return {Object} reshaped
*/
function reshapeWhere(data, shape, evolution) {
let reshaped = data;
const matchShape = (x, key) => {
if ((0, _exotic.isFunction)(shape)) {
return shape(x, key);
}
const doesMatch = (0, _chainAbleBoost.where)(shape, x, key);
return doesMatch;
};
(0, _chainAbleBoost.recurse)(this.dump).forEach((x, key, traverser) => {
const isFound = matchShape(x, key);
if (!isFound) {
return;
}
const setForPath = (0, _ramda.lens)((0, _ramda.path)(traverser.path), (0, _ramda.assocPath)(traverser.path));
const setted = evolution(x);
reshaped = (0, _ramda.set)(setForPath, setted, reshaped);
});
return reshaped;
}
return fp;
}
var _default = composition;
exports.default = _default;