@politie/sherlock-utils
Version:
Utility functions that are designed to work with Sherlock. His toolbelt.
306 lines (291 loc) • 10.1 kB
JavaScript
;
Object.defineProperty(exports, '__esModule', { value: true });
var sherlock = require('@politie/sherlock');
var tslib = require('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 = sherlock._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 = sherlock.isSettableDerivable(newDerivable)
? sherlock.lens({ get: function () { return newDerivable.get(); }, set: function (v) { return newDerivable.set(v); } }, sherlock.atom(0))
: sherlock.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 (!sherlock.isSettableDerivable(derivable)) {
throw sherlock._internal.augmentStack(new Error('Cached derivable is not settable'), derivable);
}
derivable.set(newValue);
},
};
return function (key) {
if (!sherlock.isDerivable(key)) {
var cacheItem = cache.get(key);
if (cacheItem) {
return cacheItem[CACHED_PROXY];
}
}
return sherlock.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) {
tslib.__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;
}(sherlock.PullDataSource));
function fromPromise(prom) {
var atom$ = sherlock.atom.unresolved();
prom.then(function (v) { return atom$.setFinal(v); }, function (e) { return atom$.setFinal(new sherlock.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 sherlock._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 sherlock._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 sherlock._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 sherlock._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 === sherlock.unresolved) {
return { errored: false, resolved: false };
}
if (state instanceof sherlock.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 sherlock.ErrorWrapper(state.error);
}
if (state.resolved) {
return state.value;
}
return sherlock.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 sherlock.derive(function () {
var value;
for (var _i = 0, args_1 = args; _i < args_1.length; _i++) {
var arg = args_1[_i];
value = sherlock.unwrap(arg);
if (breakOn(value)) {
break;
}
}
return value;
});
};
}
function struct(obj) {
if (sherlock.isDerivable(obj)) {
return obj;
}
if (!Array.isArray(obj) && !sherlock.utils.isPlainObject(obj)) {
throw new Error('"struct" only accepts Derivables, plain Objects and Arrays');
}
return sherlock.derive(deepUnwrap, obj);
}
function deepUnwrap(obj) {
if (sherlock.isDerivable(obj)) {
return obj.get();
}
if (Array.isArray(obj)) {
return obj.map(deepUnwrap);
}
if (sherlock.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 sherlock.derive(function () {
var s = '';
for (var i = 0; i < parts.length; i++) {
s += parts[i];
if (i < args.length) {
s += sherlock.unwrap(args[i]);
}
}
return s;
});
}
exports.FunctionDataSource = FunctionDataSource;
exports.and = and;
exports.copyState = copyState;
exports.dematerialize = dematerialize;
exports.derivableCache = derivableCache;
exports.firstNotNull = firstNotNull;
exports.fromPromise = fromPromise;
exports.fromStateObject = fromStateObject;
exports.getStateObject = getStateObject;
exports.lift = lift;
exports.materialize = materialize;
exports.or = or;
exports.pairwise = pairwise;
exports.peek = peek;
exports.peekState = peekState;
exports.peekValue = peekValue;
exports.scan = scan;
exports.setStateObject = setStateObject;
exports.struct = struct;
exports.syncState = syncState;
exports.template = template;
exports.toStateObject = toStateObject;
//# sourceMappingURL=sherlock-utils.cjs.js.map