@politie/sherlock-utils
Version:
Utility functions that are designed to work with Sherlock. His toolbelt.
281 lines (268 loc) • 9.43 kB
JavaScript
import { isDerivable, lens, _internal, isSettableDerivable, atom, PullDataSource, ErrorWrapper, unresolved, derive, unwrap, utils } from '@politie/sherlock';
import { __extends } from 'tslib';
function defaultMapFactory() { return new Map; }
var CACHED_PROXY = '__cachedProxy';
function derivableCache(opts) {
var cache = (opts.mapFactory || defaultMapFactory)();
var delayedEviction = opts.delayedEviction, derivableFactory = opts.derivableFactory;
var descriptor = {
get: function (key) {
var cachedDerivable = cache.get(key);
// If the cache has a hit for the current key, we know it is already connected (through another proxy).
if (cachedDerivable) {
return cachedDerivable.getState();
}
// A cache miss means no other proxy is currently connected.
var newDerivable = _internal.independentTracking(function () { return derivableFactory(key); });
// We don't want final-value-optimalization, because that defeats the purpose of the cache. A final value
// is not registered as an observed value, which means we cannot track the usage of our newly created derivable.
// Therefore introduce a non-final atom (`atom(0)`) in the derivation:
var derivable = isSettableDerivable(newDerivable)
? lens({ get: function () { return newDerivable.get(); }, set: function (v) { return newDerivable.set(v); } }, atom(0))
: atom(0).derive(function () { return newDerivable.get(); });
if (delayedEviction) {
derivable.autoCache();
}
// Get the state of our derivable early so it connects when needed.
var state = derivable.getState();
if (derivable.connected) {
derivable[CACHED_PROXY] = this;
cache.set(key, derivable);
derivable.connected$.react(function () { return cache.delete(key); }, { skipFirst: true, once: true });
}
return state;
},
set: function (newValue, key) {
var derivable = cache.get(key) || derivableFactory(key);
if (!isSettableDerivable(derivable)) {
throw _internal.augmentStack(new Error('Cached derivable is not settable'), derivable);
}
derivable.set(newValue);
},
};
return function (key) {
if (!isDerivable(key)) {
var cacheItem = cache.get(key);
if (cacheItem) {
return cacheItem[CACHED_PROXY];
}
}
return lens(descriptor, key);
};
}
/**
* Lazy PullDataSource that is based on a plain javascript function that has to supply the value when someone subscribes to this
* datasource.
*/
var FunctionDataSource = /** @class */ (function (_super) {
__extends(FunctionDataSource, _super);
/* istanbul ignore next: transpiled code for constructor cannot be fully covered */
/* More info: https://github.com/Microsoft/TypeScript/issues/13029 */
function FunctionDataSource(fn) {
var _this = _super.call(this) || this;
_this.fn = fn;
return _this;
}
/**
* Required function that calculates the current value for this datasource. Will be called once everytime
* `get()` is called when not connected. When connected, it will be called once and then only whenever `checkForChanges()`
* was called.
*/
FunctionDataSource.prototype.calculateCurrentValue = function () {
return this.fn();
};
/**
* Update the currently cached value of this datasource (only when connected) and notify observers when neccessary.
*/
FunctionDataSource.prototype.changed = function () {
_super.prototype.checkForChanges.call(this);
};
return FunctionDataSource;
}(PullDataSource));
function fromPromise(prom) {
var atom$ = atom.unresolved();
prom.then(function (v) { return atom$.setFinal(v); }, function (e) { return atom$.setFinal(new ErrorWrapper(e)); });
return atom$;
}
function lift(f) {
return function () {
var ps = [];
for (var _i = 0; _i < arguments.length; _i++) {
ps[_i] = arguments[_i];
}
return new _internal.Derivation(f, ps);
};
}
function pairwise(f, init) {
var oldValue = init;
return function wrapped(newValue) {
var result = f.call(this, newValue, oldValue);
oldValue = newValue;
return result;
};
}
/**
* Returns the current state of the provided Derivable without registering any dependencies while in a derivation. Comparable to #getState().
*/
function peekState(d) {
return _internal.independentTracking(function () { return d.getState(); });
}
/**
* Returns the current value of the provided Derivable without registering any dependencies while in a derivation. Comparable to #get().
*/
function peek(d) {
return _internal.independentTracking(function () { return d.get(); });
}
/**
* Returns the current value of the provided Derivable without registering any dependencies while in a derivation. Comparable to #value.
*/
function peekValue(d) {
return _internal.independentTracking(function () { return d.value; });
}
function scan(f, seed) {
var acc = seed;
return function wrapped(value) {
return acc = f.call(this, acc, value);
};
}
function getStateObject(from) {
return toStateObject(from.getState());
}
function toStateObject(state) {
if (state === unresolved) {
return { errored: false, resolved: false };
}
if (state instanceof ErrorWrapper) {
var error = state.error;
return { error: error, errored: true, resolved: true };
}
return { value: state, errored: false, resolved: true };
}
function fromStateObject(state) {
if (state.errored) {
return new ErrorWrapper(state.error);
}
if (state.resolved) {
return state.value;
}
return unresolved;
}
function materialize(derivable) {
return derivable.mapState(toStateObject);
}
function dematerialize(derivable) {
return derivable.map(fromStateObject);
}
function setStateObject(to, state) {
if (!state.resolved) {
to.unset();
}
else if (state.errored) {
to.setError(state.error);
}
else {
to.set(state.value);
}
}
function syncState(from, to, opts) {
return materialize(from).react(function (state) { return setStateObject(to, state); }, opts);
}
function copyState(from, to) {
setStateObject(to, getStateObject(from));
}
/**
* Performs JavaScript `&&` operation on the provided arguments after unwrapping.
*
* @method
*/
var and = andOrImpl(function (v) { return !v; });
/**
* Performs JavaScript `||` operation on the provided arguments after unwrapping.
*
* @method
*/
var or = andOrImpl(function (v) { return !!v; });
/**
* Returns the first operand that is not `null` or `undefined` after unwrapping.
*
* @method
*/
var firstNotNull = andOrImpl(function (v) { return v != null; });
function andOrImpl(breakOn) {
return function () {
var args = [];
for (var _i = 0; _i < arguments.length; _i++) {
args[_i] = arguments[_i];
}
return derive(function () {
var value;
for (var _i = 0, args_1 = args; _i < args_1.length; _i++) {
var arg = args_1[_i];
value = unwrap(arg);
if (breakOn(value)) {
break;
}
}
return value;
});
};
}
function struct(obj) {
if (isDerivable(obj)) {
return obj;
}
if (!Array.isArray(obj) && !utils.isPlainObject(obj)) {
throw new Error('"struct" only accepts Derivables, plain Objects and Arrays');
}
return derive(deepUnwrap, obj);
}
function deepUnwrap(obj) {
if (isDerivable(obj)) {
return obj.get();
}
if (Array.isArray(obj)) {
return obj.map(deepUnwrap);
}
if (utils.isPlainObject(obj)) {
var result = {};
for (var _i = 0, _a = Object.keys(obj); _i < _a.length; _i++) {
var key = _a[_i];
result[key] = deepUnwrap(obj[key]);
}
return result;
}
return obj;
}
/**
* A template literal tag to create a string derivation using a template literal.
*
* For example:
*
* ```
* const name$ = atom('Pete');
* const age$ = atom(24);
* const nameAndAge$ = template`${name$} is ${age$} years old`;
* nameAndAge$.get(); // -> Pete is 24 years old
* ```
*
* @param parts the string parts
* @param args the results of the expressions inside the template literal
*/
function template(parts) {
var args = [];
for (var _i = 1; _i < arguments.length; _i++) {
args[_i - 1] = arguments[_i];
}
return derive(function () {
var s = '';
for (var i = 0; i < parts.length; i++) {
s += parts[i];
if (i < args.length) {
s += unwrap(args[i]);
}
}
return s;
});
}
export { FunctionDataSource, and, copyState, dematerialize, derivableCache, firstNotNull, fromPromise, fromStateObject, getStateObject, lift, materialize, or, pairwise, peek, peekState, peekValue, scan, setStateObject, struct, syncState, template, toStateObject };
//# sourceMappingURL=sherlock-utils.esm.js.map