@politie/sherlock-utils
Version:
Utility functions that are designed to work with Sherlock. His toolbelt.
345 lines (324 loc) • 13.4 kB
JavaScript
(function (global, factory) {
typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports, require('@politie/sherlock')) :
typeof define === 'function' && define.amd ? define(['exports', '@politie/sherlock'], factory) :
(global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory(global.SherlockUtils = {}, global.Sherlock));
})(this, (function (exports, sherlock) { 'use strict';
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);
};
}
/******************************************************************************
Copyright (c) Microsoft Corporation.
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
***************************************************************************** */
/* global Reflect, Promise, SuppressedError, Symbol */
var extendStatics = function(d, b) {
extendStatics = Object.setPrototypeOf ||
({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
function (d, b) { for (var p in b) if (Object.prototype.hasOwnProperty.call(b, p)) d[p] = b[p]; };
return extendStatics(d, b);
};
function __extends(d, b) {
if (typeof b !== "function" && b !== null)
throw new TypeError("Class extends value " + String(b) + " is not a constructor or null");
extendStatics(d, b);
function __() { this.constructor = d; }
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
}
typeof SuppressedError === "function" ? SuppressedError : function (error, suppressed, message) {
var e = new Error(message);
return e.name = "SuppressedError", e.error = error, e.suppressed = suppressed, e;
};
/**
* 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;
}(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;
Object.defineProperty(exports, '__esModule', { value: true });
}));
//# sourceMappingURL=sherlock-utils.umd.js.map