@politie/sherlock
Advanced tools
Comparing version 1.0.0-beta.4 to 1.0.0
@@ -7,3 +7,3 @@ import { Derivable } from '../derivable'; | ||
*/ | ||
export declare const and: <V>(...args: (V | Derivable<V>)[]) => Derivable<V>; | ||
export declare const and: <V>(...args: (V | Derivable<V>)[]) => Derivable<NonNullable<V>>; | ||
/** | ||
@@ -14,3 +14,3 @@ * Performs JavaScript `||` operation on the provided arguments after unpacking. | ||
*/ | ||
export declare const or: <V>(...args: (V | Derivable<V>)[]) => Derivable<V>; | ||
export declare const or: <V>(...args: (V | Derivable<V>)[]) => Derivable<NonNullable<V>>; | ||
/** | ||
@@ -21,2 +21,2 @@ * Returns the first operand that is not `null` or `undefined` after unpacking. | ||
*/ | ||
export declare const firstNotNull: <V>(...args: (V | Derivable<V>)[]) => Derivable<V>; | ||
export declare const firstNotNull: <V>(...args: (V | Derivable<V>)[]) => Derivable<NonNullable<V>>; |
{ | ||
"name": "@politie/sherlock", | ||
"version": "1.0.0-beta.4", | ||
"version": "1.0.0", | ||
"private": false, | ||
@@ -59,9 +59,9 @@ "description": "A reactive programming library for JavaScript applications, built with TypeScript.", | ||
"dependencies": { | ||
"tslib": "^1.8.0" | ||
"tslib": "^1.9.0" | ||
}, | ||
"devDependencies": { | ||
"@types/chai": "^4.0.4", | ||
"@types/mocha": "^2.2.44", | ||
"@types/node": "^9.3.0", | ||
"@types/sinon": "^4.1.0", | ||
"@types/chai": "^4.1.3", | ||
"@types/mocha": "^5.2.0", | ||
"@types/node": "^10.0.0", | ||
"@types/sinon": "^4.3.1", | ||
"@types/sinon-chai": "^2.7.29", | ||
@@ -72,21 +72,21 @@ "chai": "^4.1.2", | ||
"immutable": "^3.8.2", | ||
"mocha": "^5.0.0", | ||
"nodemon": "^1.12.1", | ||
"npm-run-all": "^4.1.1", | ||
"nyc": "^11.2.1", | ||
"mocha": "^5.1.1", | ||
"nodemon": "^1.17.3", | ||
"npm-run-all": "^4.1.2", | ||
"nyc": "^11.7.1", | ||
"rimraf": "^2.6.2", | ||
"rollup": "^0.56.2", | ||
"rollup-plugin-commonjs": "^8.2.6", | ||
"rollup-plugin-node-resolve": "^3.0.0", | ||
"rollup": "^0.58.2", | ||
"rollup-plugin-commonjs": "^9.1.2", | ||
"rollup-plugin-node-resolve": "^3.3.0", | ||
"rollup-plugin-sourcemaps": "^0.4.2", | ||
"rollup-plugin-visualizer": "^0.3.1", | ||
"rxjs": "^5.5.2", | ||
"shelljs": "^0.8.0", | ||
"sinon": "^4.0.2", | ||
"sinon-chai": "^2.14.0", | ||
"ts-node": "^5.0.0", | ||
"tsconfig-paths": "^3.1.1", | ||
"tslint": "^5.8.0", | ||
"typescript": "~2.7.1" | ||
"rollup-plugin-visualizer": "^0.6.0", | ||
"rxjs": "^5.4.0 || ^6.0.0", | ||
"shelljs": "^0.8.1", | ||
"sinon": "^5.0.1", | ||
"sinon-chai": "^3.0.0", | ||
"ts-node": "^6.0.2", | ||
"tsconfig-paths": "^3.3.1", | ||
"tslint": "^5.9.1", | ||
"typescript": "^2.8.3" | ||
} | ||
} |
2228
sherlock.umd.js
(function (global, factory) { | ||
typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) : | ||
typeof define === 'function' && define.amd ? define(['exports'], factory) : | ||
(factory((global.sherlock = {}))); | ||
typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) : | ||
typeof define === 'function' && define.amd ? define(['exports'], factory) : | ||
(factory((global.sherlock = {}))); | ||
}(this, (function (exports) { 'use strict'; | ||
/*! ***************************************************************************** | ||
Copyright (c) Microsoft Corporation. All rights reserved. | ||
Licensed under the Apache License, Version 2.0 (the "License"); you may not use | ||
this file except in compliance with the License. You may obtain a copy of the | ||
License at http://www.apache.org/licenses/LICENSE-2.0 | ||
/*! ***************************************************************************** | ||
Copyright (c) Microsoft Corporation. All rights reserved. | ||
Licensed under the Apache License, Version 2.0 (the "License"); you may not use | ||
this file except in compliance with the License. You may obtain a copy of the | ||
License at http://www.apache.org/licenses/LICENSE-2.0 | ||
THIS CODE IS PROVIDED ON AN *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY | ||
KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION ANY IMPLIED | ||
WARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR PURPOSE, | ||
MERCHANTABLITY OR NON-INFRINGEMENT. | ||
THIS CODE IS PROVIDED ON AN *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY | ||
KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION ANY IMPLIED | ||
WARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR PURPOSE, | ||
MERCHANTABLITY OR NON-INFRINGEMENT. | ||
See the Apache Version 2.0 License for specific language governing permissions | ||
and limitations under the License. | ||
***************************************************************************** */ | ||
/* global Reflect, Promise */ | ||
See the Apache Version 2.0 License for specific language governing permissions | ||
and limitations under the License. | ||
***************************************************************************** */ | ||
/* global Reflect, Promise */ | ||
var extendStatics = Object.setPrototypeOf || | ||
({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || | ||
function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; | ||
var extendStatics = Object.setPrototypeOf || | ||
({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || | ||
function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; | ||
function __extends(d, b) { | ||
extendStatics(d, b); | ||
function __() { this.constructor = d; } | ||
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); | ||
} | ||
function __extends(d, b) { | ||
extendStatics(d, b); | ||
function __() { this.constructor = d; } | ||
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); | ||
} | ||
var __assign = Object.assign || function __assign(t) { | ||
for (var s, i = 1, n = arguments.length; i < n; i++) { | ||
s = arguments[i]; | ||
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) t[p] = s[p]; | ||
var __assign = Object.assign || function __assign(t) { | ||
for (var s, i = 1, n = arguments.length; i < n; i++) { | ||
s = arguments[i]; | ||
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) t[p] = s[p]; | ||
} | ||
return t; | ||
}; | ||
function __decorate(decorators, target, key, desc) { | ||
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; | ||
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); | ||
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; | ||
return c > 3 && r && Object.defineProperty(target, key, r), r; | ||
} | ||
return t; | ||
}; | ||
function __decorate(decorators, target, key, desc) { | ||
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; | ||
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); | ||
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; | ||
return c > 3 && r && Object.defineProperty(target, key, r), r; | ||
} | ||
var currentRecording; | ||
/** | ||
* Will record all dependencies that were touched until {@link stopRecordingObservations} is called. Changes | ||
* {@link TrackedObserver#dependencies} during the recording, so both the number of element and the order of elements | ||
* cannot be relied upon. | ||
* | ||
* Recordings can be nested, this recording will replace the current recording if applicable. After stopping this recording | ||
* the previous recording will become active again. | ||
* | ||
* @param observer the observer that is interested in its dependencies | ||
*/ | ||
function startRecordingObservations(observer) { | ||
// Check for cyclic observer dependencies (which is only possible when using impure functions in Derivables) | ||
// we do not support that. | ||
var r = currentRecording; | ||
while (r) { | ||
if (r.observer === observer) { | ||
throw new Error('cyclic dependency between derivables detected'); | ||
var currentRecording; | ||
/** | ||
* Will record all dependencies that were touched until {@link stopRecordingObservations} is called. Changes | ||
* {@link TrackedObserver#dependencies} during the recording, so both the number of element and the order of elements | ||
* cannot be relied upon. | ||
* | ||
* Recordings can be nested, this recording will replace the current recording if applicable. After stopping this recording | ||
* the previous recording will become active again. | ||
* | ||
* @param observer the observer that is interested in its dependencies | ||
*/ | ||
function startRecordingObservations(observer) { | ||
// Check for cyclic observer dependencies (which is only possible when using impure functions in Derivables) | ||
// we do not support that. | ||
var r = currentRecording; | ||
while (r) { | ||
if (r.observer === observer) { | ||
throw new Error('cyclic dependency between derivables detected'); | ||
} | ||
r = r.previousRecording; | ||
} | ||
r = r.previousRecording; | ||
currentRecording = { observer: observer, confirmed: 0, previousRecording: currentRecording }; | ||
} | ||
currentRecording = { observer: observer, confirmed: 0, previousRecording: currentRecording }; | ||
} | ||
/** | ||
* Stops the current recording and returns to the previous recording if applicable. Will remove any dependency from the | ||
* {@link TrackedObserver#dependencies} array that was not touched since the call to {@link startRecordingObservations}. | ||
* It will also update {@link TrackedObserver#dependencyVersions} and call disconnect on any dependency that can be disconnected | ||
* if it is not observed anymore. | ||
*/ | ||
function stopRecordingObservations() { | ||
var recording = currentRecording; | ||
if (!recording) { | ||
throw new Error('No active recording!'); | ||
/** | ||
* Stops the current recording and returns to the previous recording if applicable. Will remove any dependency from the | ||
* {@link TrackedObserver#dependencies} array that was not touched since the call to {@link startRecordingObservations}. | ||
* It will also update {@link TrackedObserver#dependencyVersions} and call disconnect on any dependency that can be disconnected | ||
* if it is not observed anymore. | ||
*/ | ||
function stopRecordingObservations() { | ||
var recording = currentRecording; | ||
if (!recording) { | ||
throw new Error('No active recording!'); | ||
} | ||
currentRecording = recording.previousRecording; | ||
var confirmed = recording.confirmed, observer = recording.observer; | ||
var dependencies = observer.dependencies, dependencyVersions = observer.dependencyVersions; | ||
// Any previous dependency that was not confirmed during the recording can be removed now. | ||
for (var i = confirmed, n = dependencies.length; i < n; i++) { | ||
removeObserver(dependencies[i], observer); | ||
} | ||
dependencies.length = confirmed; | ||
for (var _i = 0, dependencies_1 = dependencies; _i < dependencies_1.length; _i++) { | ||
var dep = dependencies_1[_i]; | ||
dependencyVersions[dep.id] = dep.version; | ||
} | ||
} | ||
currentRecording = recording.previousRecording; | ||
var confirmed = recording.confirmed, observer = recording.observer; | ||
var dependencies = observer.dependencies, dependencyVersions = observer.dependencyVersions; | ||
// Any previous dependency that was not confirmed during the recording can be removed now. | ||
for (var i = confirmed, n = dependencies.length; i < n; i++) { | ||
removeObserver(dependencies[i], observer); | ||
/** | ||
* Returns true iff we are currently recording dependencies. | ||
*/ | ||
function isRecordingObservations() { | ||
return !!currentRecording; | ||
} | ||
dependencies.length = confirmed; | ||
for (var _i = 0, dependencies_1 = dependencies; _i < dependencies_1.length; _i++) { | ||
var dep = dependencies_1[_i]; | ||
dependencyVersions[dep.id] = dep.version; | ||
} | ||
} | ||
/** | ||
* Returns true iff we are currently recording dependencies. | ||
*/ | ||
function isRecordingObservations() { | ||
return !!currentRecording; | ||
} | ||
/** | ||
* Records in the current recording (if applicable) that the given `dependency` was observed. | ||
* | ||
* @param dependency the observable that is being observed | ||
*/ | ||
function recordObservation(dependency) { | ||
if (!currentRecording) { | ||
// Not currently recording observations, nevermind... | ||
return; | ||
} | ||
var observer = currentRecording.observer; | ||
var dependencies = observer.dependencies; | ||
// Invariants: | ||
// - dependencies[0..currentRecording.confirmed) have been recorded (confirmed) as dependencies | ||
// - dependencies[currentRecording.confirmed..n) have not yet been recorded (confirmed) | ||
if (dependencies[currentRecording.confirmed] === dependency) { | ||
// This is the expected branch almost everytime we rerecord a derivation. The dependencies are often encountered in the | ||
// same order as before. So we found our dependency at dependencies[currentRecording.confirmed]. We can keep our invariant and | ||
// include our latest observation by incrementing the confirmed counter. Our observer is already registered at this | ||
// dependency because of last time. | ||
currentRecording.confirmed++; | ||
} | ||
else { | ||
// This branch means this is either the first recording, this dependency is new, or the dependencies are out of order | ||
// compared to last time. | ||
var index = dependencies.indexOf(dependency); | ||
if (index < 0) { | ||
// dependency not yet present in dependencies array. This means we have to register the observer at | ||
// the dependency and add the dependency to the observer (both ways). | ||
dependency.observers.push(observer); | ||
if (currentRecording.confirmed === dependencies.length) { | ||
// We don't have to reorder dependencies, because it is empty to the right of currentRecording.confirmed. | ||
dependencies.push(dependency); | ||
/** | ||
* Records in the current recording (if applicable) that the given `dependency` was observed. | ||
* | ||
* @param dependency the observable that is being observed | ||
*/ | ||
function recordObservation(dependency) { | ||
if (!currentRecording) { | ||
// Not currently recording observations, nevermind... | ||
return; | ||
} | ||
var observer = currentRecording.observer; | ||
var dependencies = observer.dependencies; | ||
// Invariants: | ||
// - dependencies[0..currentRecording.confirmed) have been recorded (confirmed) as dependencies | ||
// - dependencies[currentRecording.confirmed..n) have not yet been recorded (confirmed) | ||
if (dependencies[currentRecording.confirmed] === dependency) { | ||
// This is the expected branch almost everytime we rerecord a derivation. The dependencies are often encountered in the | ||
// same order as before. So we found our dependency at dependencies[currentRecording.confirmed]. We can keep our invariant and | ||
// include our latest observation by incrementing the confirmed counter. Our observer is already registered at this | ||
// dependency because of last time. | ||
currentRecording.confirmed++; | ||
} | ||
else { | ||
// This branch means this is either the first recording, this dependency is new, or the dependencies are out of order | ||
// compared to last time. | ||
var index = dependencies.indexOf(dependency); | ||
if (index < 0) { | ||
// dependency not yet present in dependencies array. This means we have to register the observer at | ||
// the dependency and add the dependency to the observer (both ways). | ||
dependency.observers.push(observer); | ||
if (currentRecording.confirmed === dependencies.length) { | ||
// We don't have to reorder dependencies, because it is empty to the right of currentRecording.confirmed. | ||
dependencies.push(dependency); | ||
} | ||
else { | ||
// Simple way to keep the invariant, move the current item to the end and | ||
// insert the new dependency in the current position. | ||
dependencies.push(dependencies[currentRecording.confirmed]); | ||
dependencies[currentRecording.confirmed] = dependency; | ||
} | ||
// dependencies[0..currentRecording.confirmed) are dependencies and dependencies[currentRecording.confirmed] is a dependency | ||
currentRecording.confirmed++; | ||
// dependencies[0..currentRecording.confirmed) are dependencies | ||
} | ||
else { | ||
// Simple way to keep the invariant, move the current item to the end and | ||
// insert the new dependency in the current position. | ||
dependencies.push(dependencies[currentRecording.confirmed]); | ||
else if (index > currentRecording.confirmed) { | ||
// dependency is present in dependencies, but we were not expecting it yet, swap places | ||
dependencies[index] = dependencies[currentRecording.confirmed]; | ||
dependencies[currentRecording.confirmed] = dependency; | ||
currentRecording.confirmed++; | ||
} | ||
// dependencies[0..currentRecording.confirmed) are dependencies and dependencies[currentRecording.confirmed] is a dependency | ||
currentRecording.confirmed++; | ||
// dependencies[0..currentRecording.confirmed) are dependencies | ||
// else: index >= 0 && index < currentRecording.confirmed, i.e. already seen before and already confirmed. Do nothing. | ||
} | ||
else if (index > currentRecording.confirmed) { | ||
// dependency is present in dependencies, but we were not expecting it yet, swap places | ||
dependencies[index] = dependencies[currentRecording.confirmed]; | ||
dependencies[currentRecording.confirmed] = dependency; | ||
currentRecording.confirmed++; | ||
// Postcondition: | ||
// - dependency in dependencies[0..currentRecording.confirmed) | ||
} | ||
/** | ||
* Removes a single observer from the registered observers of the observable. Will error if the observer is not known. | ||
* If the observable has no observers left and can be disconnected it will be disconnected. | ||
* | ||
* @param observable the observable from which to remove the registered observer | ||
* @param observer the observer that has to be removed | ||
*/ | ||
function removeObserver(observable, observer) { | ||
var observers = observable.observers; | ||
var i = observers.indexOf(observer); | ||
// istanbul ignore if: should never happen! | ||
if (i < 0) { | ||
throw new Error('Inconsistent state!'); | ||
} | ||
// else: index >= 0 && index < currentRecording.confirmed, i.e. already seen before and already confirmed. Do nothing. | ||
observers.splice(i, 1); | ||
// If the dependency is itself another observer and is not observed anymore, we should disconnect it. | ||
if (observers.length === 0 && canBeDisconnected(observable)) { | ||
observable.disconnect(); | ||
} | ||
} | ||
// Postcondition: | ||
// - dependency in dependencies[0..currentRecording.confirmed) | ||
} | ||
/** | ||
* Removes a single observer from the registered observers of the observable. Will error if the observer is not known. | ||
* If the observable has no observers left and can be disconnected it will be disconnected. | ||
* | ||
* @param observable the observable from which to remove the registered observer | ||
* @param observer the observer that has to be removed | ||
*/ | ||
function removeObserver(observable, observer) { | ||
var observers = observable.observers; | ||
var i = observers.indexOf(observer); | ||
// istanbul ignore if: should never happen! | ||
if (i < 0) { | ||
throw new Error('Inconsistent state!'); | ||
function canBeDisconnected(obj) { | ||
return obj && typeof obj.disconnect === 'function'; | ||
} | ||
observers.splice(i, 1); | ||
// If the dependency is itself another observer and is not observed anymore, we should disconnect it. | ||
if (observers.length === 0 && canBeDisconnected(observable)) { | ||
observable.disconnect(); | ||
} | ||
} | ||
function canBeDisconnected(obj) { | ||
return obj && typeof obj.disconnect === 'function'; | ||
} | ||
var currentTransaction; | ||
/** | ||
* Returns true iff we are currently in a transaction. | ||
*/ | ||
function inTransaction() { | ||
return !!currentTransaction; | ||
} | ||
/** | ||
* Processes a changed atom. The observertree starting at this atom will be marked stale. When not inside a transaction all | ||
* reactors in this tree will also get a chance to fire (otherwise they will be remembered and fired later). | ||
* | ||
* @param atom the atom that was changed | ||
* @param oldValue the previous value of the atom | ||
* @param oldVersion the previous version number of the atom | ||
*/ | ||
function processChangedAtom(atom, oldValue, oldVersion) { | ||
if (currentTransaction) { | ||
processChangedAtomInTransaction(currentTransaction, atom, oldValue, oldVersion); | ||
var currentTransaction; | ||
/** | ||
* Returns true iff we are currently in a transaction. | ||
*/ | ||
function inTransaction() { | ||
return !!currentTransaction; | ||
} | ||
else { | ||
var reactors = []; | ||
markObservers(atom, reactors); | ||
reactIfNeeded(reactors); | ||
/** | ||
* Processes a changed atom. The observertree starting at this atom will be marked stale. When not inside a transaction all | ||
* reactors in this tree will also get a chance to fire (otherwise they will be remembered and fired later). | ||
* | ||
* @param atom the atom that was changed | ||
* @param oldValue the previous value of the atom | ||
* @param oldVersion the previous version number of the atom | ||
*/ | ||
function processChangedAtom(atom, oldValue, oldVersion) { | ||
if (currentTransaction) { | ||
processChangedAtomInTransaction(currentTransaction, atom, oldValue, oldVersion); | ||
} | ||
else { | ||
var reactors = []; | ||
markObservers(atom, reactors); | ||
reactIfNeeded(reactors); | ||
} | ||
} | ||
} | ||
function markObservers(changedAtom, reactorSink) { | ||
for (var _i = 0, _a = changedAtom.observers; _i < _a.length; _i++) { | ||
var observer = _a[_i]; | ||
observer.mark(reactorSink); | ||
function markObservers(changedAtom, reactorSink) { | ||
for (var _i = 0, _a = changedAtom.observers; _i < _a.length; _i++) { | ||
var observer = _a[_i]; | ||
observer.mark(reactorSink); | ||
} | ||
} | ||
} | ||
function reactIfNeeded(reactors) { | ||
for (var _i = 0, reactors_1 = reactors; _i < reactors_1.length; _i++) { | ||
var reactor = reactors_1[_i]; | ||
reactor.reactIfNeeded(); | ||
function reactIfNeeded(reactors) { | ||
for (var _i = 0, reactors_1 = reactors; _i < reactors_1.length; _i++) { | ||
var reactor = reactors_1[_i]; | ||
reactor.reactIfNeeded(); | ||
} | ||
} | ||
} | ||
/** | ||
* Runs the given function inside a new transaction. Transactions can be nested. If a transaction is already running, this will nest | ||
* the new transaction inside the other. If the function completes normally it will commit the transaction, on a thrown Error it will | ||
* rollback. Returns the return value or rethrows the Error. Only when the outermost transaction completes normally all pending | ||
* reactions will fire, otherwise all atoms will be restored to their state before the transaction started. | ||
* | ||
* @param f the function to run in a transaction | ||
*/ | ||
function transact(f) { | ||
beginTransaction(); | ||
var result; | ||
try { | ||
result = f(); | ||
/** | ||
* Runs the given function inside a new transaction. Transactions can be nested. If a transaction is already running, this will nest | ||
* the new transaction inside the other. If the function completes normally it will commit the transaction, on a thrown Error it will | ||
* rollback. Returns the return value or rethrows the Error. Only when the outermost transaction completes normally all pending | ||
* reactions will fire, otherwise all atoms will be restored to their state before the transaction started. | ||
* | ||
* @param f the function to run in a transaction | ||
*/ | ||
function transact(f) { | ||
beginTransaction(); | ||
var result; | ||
try { | ||
result = f(); | ||
} | ||
catch (e) { | ||
rollbackTransaction(); | ||
throw e; | ||
} | ||
commitTransaction(); | ||
return result; | ||
} | ||
catch (e) { | ||
rollbackTransaction(); | ||
throw e; | ||
/** | ||
* Guarantees that f is called inside transaction, but will not create a new transaction when one is already running. | ||
* | ||
* @param f the function to run in a transaction | ||
*/ | ||
function atomically(f) { | ||
if (inTransaction()) { | ||
return f(); | ||
} | ||
else { | ||
return transact(f); | ||
} | ||
} | ||
commitTransaction(); | ||
return result; | ||
} | ||
/** | ||
* Guarantees that f is called inside transaction, but will not create a new transaction when one is already running. | ||
* | ||
* @param f the function to run in a transaction | ||
*/ | ||
function atomically(f) { | ||
if (inTransaction()) { | ||
return f(); | ||
function atomic(f) { | ||
if (f) { | ||
return function atomicFunction() { | ||
var args = []; | ||
for (var _i = 0; _i < arguments.length; _i++) { | ||
args[_i] = arguments[_i]; | ||
} | ||
var ctx = this; | ||
return atomically(function () { return f.apply(ctx, args); }); | ||
}; | ||
} | ||
else { | ||
return function (_target, _propertyKey, descriptor) { | ||
descriptor.value = descriptor.value && atomic(descriptor.value); | ||
}; | ||
} | ||
} | ||
else { | ||
return transact(f); | ||
function transaction(f) { | ||
if (f) { | ||
return function transactionFunction() { | ||
var args = []; | ||
for (var _i = 0; _i < arguments.length; _i++) { | ||
args[_i] = arguments[_i]; | ||
} | ||
var ctx = this; | ||
return transact(function () { return f.apply(ctx, args); }); | ||
}; | ||
} | ||
else { | ||
return function (_target, _propertyKey, descriptor) { | ||
descriptor.value = descriptor.value && transaction(descriptor.value); | ||
}; | ||
} | ||
} | ||
} | ||
function atomic(f) { | ||
if (f) { | ||
return function atomicFunction() { | ||
var args = []; | ||
for (var _i = 0; _i < arguments.length; _i++) { | ||
args[_i] = arguments[_i]; | ||
} | ||
var ctx = this; | ||
return atomically(function () { return f.apply(ctx, args); }); | ||
function beginTransaction() { | ||
currentTransaction = { | ||
touchedAtoms: [], | ||
oldValues: {}, | ||
oldVersions: {}, | ||
touchedReactors: [], | ||
parentTransaction: currentTransaction, | ||
}; | ||
} | ||
else { | ||
return function (_target, _propertyKey, descriptor) { | ||
descriptor.value = descriptor.value && atomic(descriptor.value); | ||
}; | ||
function commitTransaction() { | ||
var ctx = currentTransaction; | ||
// istanbul ignore if: should never happen! | ||
if (!ctx) { | ||
throw new Error('No active transaction!'); | ||
} | ||
currentTransaction = ctx.parentTransaction; | ||
if (currentTransaction) { | ||
// Hand over all info to the parent transaction | ||
ctx.touchedAtoms.forEach(function (atom) { return processChangedAtomInTransaction(currentTransaction, atom, ctx.oldValues[atom.id], ctx.oldVersions[atom.id]); }); | ||
// Not all reactors might be reached through notifyObservers. When | ||
// all paths between ctx.touchedAtoms and ctx.touchedReactors have | ||
// derivations with in !upToDate state in them, the reactors are not | ||
// added to currentTransaction.touchedReactors, therefore we have to | ||
// do it explicitly here. | ||
(_a = currentTransaction.touchedReactors).push.apply(_a, ctx.touchedReactors); | ||
} | ||
else { | ||
// No parent transaction, so we just committed the outermost transaction, let's react. | ||
reactIfNeeded(ctx.touchedReactors); | ||
} | ||
var _a; | ||
} | ||
} | ||
function transaction(f) { | ||
if (f) { | ||
return function transactionFunction() { | ||
var args = []; | ||
for (var _i = 0; _i < arguments.length; _i++) { | ||
args[_i] = arguments[_i]; | ||
} | ||
var ctx = this; | ||
return transact(function () { return f.apply(ctx, args); }); | ||
}; | ||
function rollbackTransaction() { | ||
var ctx = currentTransaction; | ||
// istanbul ignore if: should never happen! | ||
if (!ctx) { | ||
throw new Error('No active transaction!'); | ||
} | ||
currentTransaction = ctx.parentTransaction; | ||
// Restore the state of all touched atoms. | ||
ctx.touchedAtoms.forEach(function (atom) { | ||
atom.value = ctx.oldValues[atom.id]; | ||
atom.version = ctx.oldVersions[atom.id]; | ||
markObservers(atom, []); | ||
}); | ||
} | ||
else { | ||
return function (_target, _propertyKey, descriptor) { | ||
descriptor.value = descriptor.value && transaction(descriptor.value); | ||
}; | ||
function processChangedAtomInTransaction(txn, atom, oldValue, oldVersion) { | ||
markObservers(atom, txn.touchedReactors); | ||
var id = atom.id; | ||
if (!(id in txn.oldValues)) { | ||
txn.touchedAtoms.push(atom); | ||
txn.oldValues[id] = oldValue; | ||
txn.oldVersions[id] = oldVersion; | ||
} | ||
} | ||
} | ||
function beginTransaction() { | ||
currentTransaction = { | ||
touchedAtoms: [], | ||
oldValues: {}, | ||
oldVersions: {}, | ||
touchedReactors: [], | ||
parentTransaction: currentTransaction, | ||
}; | ||
} | ||
function commitTransaction() { | ||
var ctx = currentTransaction; | ||
// istanbul ignore if: should never happen! | ||
if (!ctx) { | ||
throw new Error('No active transaction!'); | ||
/** | ||
* Creates a shallow clone of the object with the same prototype chain and working getters and setters. | ||
* | ||
* @param obj the object to clone | ||
*/ | ||
function clone(obj) { | ||
if (Array.isArray(obj)) { | ||
// Special support for Array.isArray is needed because Array.isArray checks for array exotic object, which can only be made by | ||
// the Array constructor. | ||
return cloneArray(obj); | ||
} | ||
return Object.create(Object.getPrototypeOf(obj), getOwnPropertyDescriptors(obj)); | ||
} | ||
currentTransaction = ctx.parentTransaction; | ||
if (currentTransaction) { | ||
// Hand over all info to the parent transaction | ||
ctx.touchedAtoms.forEach(function (atom) { return processChangedAtomInTransaction(currentTransaction, atom, ctx.oldValues[atom.id], ctx.oldVersions[atom.id]); }); | ||
// Not all reactors might be reached through notifyObservers. When | ||
// all paths between ctx.touchedAtoms and ctx.touchedReactors have | ||
// derivations with in !upToDate state in them, the reactors are not | ||
// added to currentTransaction.touchedReactors, therefore we have to | ||
// do it explicitly here. | ||
(_a = currentTransaction.touchedReactors).push.apply(_a, ctx.touchedReactors); | ||
function cloneArray(obj) { | ||
try { | ||
return Object.defineProperties(new obj.constructor(), getOwnPropertyDescriptors(obj)); | ||
} | ||
catch (e) { | ||
// istanbul ignore next: for debug purposes | ||
throw Object.assign(new Error('could not construct a clone of the provided Array: ' + (e && e.message)), { jse_cause: e }); | ||
} | ||
} | ||
else { | ||
// No parent transaction, so we just committed the outermost transaction, let's react. | ||
reactIfNeeded(ctx.touchedReactors); | ||
// istanbul ignore next: cannot be tested in a modern Node environment | ||
var getOwnPropertyDescriptors = Object.getOwnPropertyDescriptors || getOwnPropertyDescriptorsShim; | ||
function getOwnPropertyDescriptorsShim(obj) { | ||
var properties = {}; | ||
for (var _i = 0, _a = ownKeys(obj); _i < _a.length; _i++) { | ||
var prop = _a[_i]; | ||
properties[prop] = Object.getOwnPropertyDescriptor(obj, prop); | ||
} | ||
return properties; | ||
} | ||
var _a; | ||
} | ||
function rollbackTransaction() { | ||
var ctx = currentTransaction; | ||
// istanbul ignore if: should never happen! | ||
if (!ctx) { | ||
throw new Error('No active transaction!'); | ||
// istanbul ignore next: cannot be tested in a modern Node environment | ||
var ownKeys = typeof Reflect !== 'undefined' ? Reflect.ownKeys : ownKeysShim; | ||
function ownKeysShim(obj) { | ||
return Object.getOwnPropertyNames(obj).concat(Object.getOwnPropertySymbols(obj)); | ||
} | ||
currentTransaction = ctx.parentTransaction; | ||
// Restore the state of all touched atoms. | ||
ctx.touchedAtoms.forEach(function (atom) { | ||
atom.value = ctx.oldValues[atom.id]; | ||
atom.version = ctx.oldVersions[atom.id]; | ||
markObservers(atom, []); | ||
}); | ||
} | ||
function processChangedAtomInTransaction(txn, atom, oldValue, oldVersion) { | ||
markObservers(atom, txn.touchedReactors); | ||
var id = atom.id; | ||
if (!(id in txn.oldValues)) { | ||
txn.touchedAtoms.push(atom); | ||
txn.oldValues[id] = oldValue; | ||
txn.oldVersions[id] = oldVersion; | ||
} | ||
} | ||
/** | ||
* Creates a shallow clone of the object with the same prototype chain and working getters and setters. | ||
* | ||
* @param obj the object to clone | ||
*/ | ||
function clone(obj) { | ||
if (Array.isArray(obj)) { | ||
// Special support for Array.isArray is needed because Array.isArray checks for array exotic object, which can only be made by | ||
// the Array constructor. | ||
return cloneArray(obj); | ||
/** | ||
* Returns whether the two arguments are either: | ||
* - equal according to JavaScript rules | ||
* - equal immutable objects | ||
* - equal derivables | ||
* - equal derivable proxies | ||
*/ | ||
function equals(a, b) { | ||
return Object.is(a, b) | ||
|| hasEqualsMethod(a) && !!a.equals(b); | ||
} | ||
return Object.create(Object.getPrototypeOf(obj), getOwnPropertyDescriptors(obj)); | ||
} | ||
function cloneArray(obj) { | ||
try { | ||
return Object.defineProperties(new obj.constructor(), getOwnPropertyDescriptors(obj)); | ||
function hasEqualsMethod(obj) { | ||
return obj && typeof obj.equals === 'function'; | ||
} | ||
catch (e) { | ||
// istanbul ignore next: for debug purposes | ||
throw Object.assign(new Error('could not construct a clone of the provided Array: ' + (e && e.message)), { jse_cause: e }); | ||
/** | ||
* Function to test if an object is a plain object, i.e. is constructed | ||
* by the built-in Object constructor or inherits directly from Object.prototype | ||
* or null. Some built-in objects pass the test, e.g. Math which is a plain object | ||
* and some host or exotic objects may pass also. | ||
* | ||
* @param obj the object to test | ||
*/ | ||
function isPlainObject(obj) { | ||
// Basic check for type 'object' that's not null | ||
if (typeof obj !== 'object' || obj == null) { | ||
return false; | ||
} | ||
var proto = Object.getPrototypeOf(obj); | ||
return proto === Object.prototype || proto === null || obj.constructor === Object; | ||
} | ||
} | ||
// istanbul ignore next: cannot be tested in a modern Node environment | ||
var getOwnPropertyDescriptors = Object.getOwnPropertyDescriptors || getOwnPropertyDescriptorsShim; | ||
function getOwnPropertyDescriptorsShim(obj) { | ||
var properties = {}; | ||
for (var _i = 0, _a = ownKeys(obj); _i < _a.length; _i++) { | ||
var prop = _a[_i]; | ||
properties[prop] = Object.getOwnPropertyDescriptor(obj, prop); | ||
var nextId = 0; | ||
function uniqueId() { | ||
return nextId++; | ||
} | ||
return properties; | ||
} | ||
// istanbul ignore next: cannot be tested in a modern Node environment | ||
var ownKeys = typeof Reflect !== 'undefined' ? Reflect.ownKeys : ownKeysShim; | ||
function ownKeysShim(obj) { | ||
return Object.getOwnPropertyNames(obj).concat(Object.getOwnPropertySymbols(obj)); | ||
} | ||
/** | ||
* Returns whether the two arguments are either: | ||
* - equal according to JavaScript rules | ||
* - equal immutable objects | ||
* - equal derivables | ||
* - equal derivable proxies | ||
*/ | ||
function equals(a, b) { | ||
return Object.is(a, b) | ||
|| hasEqualsMethod(a) && !!a.equals(b); | ||
} | ||
function hasEqualsMethod(obj) { | ||
return obj && typeof obj.equals === 'function'; | ||
} | ||
/** | ||
* Function to test if an object is a plain object, i.e. is constructed | ||
* by the built-in Object constructor or inherits directly from Object.prototype | ||
* or null. Some built-in objects pass the test, e.g. Math which is a plain object | ||
* and some host or exotic objects may pass also. | ||
* | ||
* @param obj the object to test | ||
*/ | ||
function isPlainObject(obj) { | ||
// Basic check for type 'object' that's not null | ||
if (typeof obj !== 'object' || obj == null) { | ||
return false; | ||
// Debug mode is implemented in the barrel file, because barrel-files don't support mutable variables. | ||
// The syntax: `export { ... } from '...'` creates a new copy of the variable instead of linking. | ||
var debugMode = false; | ||
function setDebugMode(val) { | ||
debugMode = val; | ||
} | ||
var proto = Object.getPrototypeOf(obj); | ||
return proto === Object.prototype || proto === null || obj.constructor === Object; | ||
} | ||
var nextId = 0; | ||
function uniqueId() { | ||
return nextId++; | ||
} | ||
// Debug mode is implemented in the barrel file, because barrel-files don't support mutable variables. | ||
// The syntax: `export { ... } from '...'` creates a new copy of the variable instead of linking. | ||
var debugMode = false; | ||
function setDebugMode(val) { | ||
debugMode = val; | ||
} | ||
/** | ||
* Derivable is the base class of all derivable state constructs: Atom, Constant, Derivation and Lens. | ||
*/ | ||
var Derivable = /** @class */ (function () { | ||
function Derivable() { | ||
/** | ||
* Derivable is the base class of all derivable state constructs: Atom, Constant, Derivation and Lens. | ||
*/ | ||
var Derivable = /** @class */ (function () { | ||
function Derivable() { | ||
/** | ||
* The unique ID of this Derivable. Can be used to uniquely identify this Derivable. | ||
*/ | ||
this.id = uniqueId(); | ||
/** | ||
* @internal | ||
* The observers of this Derivable, do not use this in application code. | ||
*/ | ||
this.observers = []; | ||
} | ||
/** | ||
* The unique ID of this Derivable. Can be used to uniquely identify this Derivable. | ||
* Sets this Derivable to autoCache mode. This will cache the value of this Derivable the first time {@link #get} is called every tick | ||
* and release this cache some time after this tick. The value is still guaranteed to be up-to-date with respect to changes in any of | ||
* its dependencies, by using the same mechanism that is used by a reactor. It has a setup cost comparable to starting a reactor every | ||
* first time #get is called per tick. Starting a reactor on a Derivable with an active and up-to-date cache is cheap though. | ||
*/ | ||
this.id = uniqueId(); | ||
Derivable.prototype.autoCache = function () { return this; }; | ||
return Derivable; | ||
}()); | ||
/** | ||
* Atom is the basic state holder in a Derivable world. It contains the actual mutable state. In contrast | ||
* with other kinds of derivables that only store immutable (constant) or derived state. Should be constructed | ||
* with the initial state. | ||
*/ | ||
var Atom = /** @class */ (function (_super) { | ||
__extends(Atom, _super); | ||
/** | ||
* @internal | ||
* The observers of this Derivable, do not use this in application code. | ||
* Construct a new atom with the provided initial value. | ||
* | ||
* @param value the initial value | ||
*/ | ||
this.observers = []; | ||
} | ||
function Atom(value) { | ||
var _this = _super.call(this) || this; | ||
/** | ||
* @internal | ||
* The current version of the state. This number gets incremented every time the state changes. Setting the state to | ||
* an immutable object that is structurally equal to the previous immutable object is not considered a state change. | ||
*/ | ||
_this.version = 0; | ||
_this.value = value; | ||
return _this; | ||
} | ||
/** | ||
* Returns the current value of this derivable. Automatically records the use of this derivable when inside a derivation. | ||
*/ | ||
Atom.prototype.get = function () { | ||
recordObservation(this); | ||
return this.value; | ||
}; | ||
/** | ||
* Sets the value of this atom, fires reactors when expected. | ||
* | ||
* @param newValue the new state | ||
*/ | ||
Atom.prototype.set = function (newValue) { | ||
var oldValue = this.value; | ||
if (!equals(newValue, oldValue)) { | ||
this.value = newValue; | ||
processChangedAtom(this, oldValue, this.version++); | ||
} | ||
}; | ||
Atom.prototype.swap = function (f) { | ||
var args = []; | ||
for (var _i = 1; _i < arguments.length; _i++) { | ||
args[_i - 1] = arguments[_i]; | ||
} | ||
this.set(f.apply(void 0, [this.value].concat(args))); | ||
}; | ||
return Atom; | ||
}(Derivable)); | ||
/** | ||
* Sets this Derivable to autoCache mode. This will cache the value of this Derivable the first time {@link #get} is called every tick | ||
* and release this cache some time after this tick. The value is still guaranteed to be up-to-date with respect to changes in any of | ||
* its dependencies, by using the same mechanism that is used by a reactor. It has a setup cost comparable to starting a reactor every | ||
* first time #get is called per tick. Starting a reactor on a Derivable with an active and up-to-date cache is cheap though. | ||
*/ | ||
Derivable.prototype.autoCache = function () { return this; }; | ||
return Derivable; | ||
}()); | ||
/** | ||
* Atom is the basic state holder in a Derivable world. It contains the actual mutable state. In contrast | ||
* with other kinds of derivables that only store immutable (constant) or derived state. Should be constructed | ||
* with the initial state. | ||
*/ | ||
var Atom = /** @class */ (function (_super) { | ||
__extends(Atom, _super); | ||
/** | ||
* @internal | ||
* Construct a new atom with the provided initial value. | ||
@@ -461,74 +505,55 @@ * | ||
*/ | ||
function Atom(value) { | ||
var _this = _super.call(this) || this; | ||
/** | ||
* @internal | ||
* The current version of the state. This number gets incremented every time the state changes. Setting the state to | ||
* an immutable object that is structurally equal to the previous immutable object is not considered a state change. | ||
*/ | ||
_this.version = 0; | ||
_this.value = value; | ||
return _this; | ||
function atom(value) { | ||
return new Atom(value); | ||
} | ||
/** | ||
* Returns the current value of this derivable. Automatically records the use of this derivable when inside a derivation. | ||
*/ | ||
Atom.prototype.get = function () { | ||
recordObservation(this); | ||
return this.value; | ||
function unpack(v) { | ||
return v instanceof Derivable ? v.get() : v; | ||
} | ||
// Implementations: | ||
Derivable.prototype.and = function and(other) { | ||
return this.derive(function (v) { return v && unpack(other); }); | ||
}; | ||
/** | ||
* Sets the value of this atom, fires reactors when expected. | ||
* | ||
* @param newValue the new state | ||
*/ | ||
Atom.prototype.set = function (newValue) { | ||
var oldValue = this.value; | ||
if (!equals(newValue, oldValue)) { | ||
this.value = newValue; | ||
processChangedAtom(this, oldValue, this.version++); | ||
} | ||
Derivable.prototype.or = function or(other) { | ||
return this.derive(function (v) { return v || unpack(other); }); | ||
}; | ||
Atom.prototype.swap = function (f) { | ||
var args = []; | ||
for (var _i = 1; _i < arguments.length; _i++) { | ||
args[_i - 1] = arguments[_i]; | ||
} | ||
this.set(f.apply(void 0, [this.value].concat(args))); | ||
Derivable.prototype.not = function not() { | ||
return this.derive(function (v) { return !v; }); | ||
}; | ||
return Atom; | ||
}(Derivable)); | ||
/** | ||
* Construct a new atom with the provided initial value. | ||
* | ||
* @param value the initial value | ||
*/ | ||
function atom(value) { | ||
return new Atom(value); | ||
} | ||
Derivable.prototype.is = function is(other) { | ||
return this.derive(equals, other); | ||
}; | ||
function unpack(v) { | ||
return v instanceof Derivable ? v.get() : v; | ||
} | ||
// Implementations: | ||
Derivable.prototype.and = function and(other) { | ||
return this.derive(function (v) { return v && unpack(other); }); | ||
}; | ||
Derivable.prototype.or = function or(other) { | ||
return this.derive(function (v) { return v || unpack(other); }); | ||
}; | ||
Derivable.prototype.not = function not() { | ||
return this.derive(function (v) { return !v; }); | ||
}; | ||
Derivable.prototype.is = function is(other) { | ||
return this.derive(equals, other); | ||
}; | ||
/** | ||
* Constant represents a basic immutable building block of derivations. | ||
*/ | ||
var Constant = /** @class */ (function (_super) { | ||
__extends(Constant, _super); | ||
/** | ||
* Constant represents a basic immutable building block of derivations. | ||
*/ | ||
var Constant = /** @class */ (function (_super) { | ||
__extends(Constant, _super); | ||
/** | ||
* Creates a new Constant with the give value. | ||
* | ||
* @param value the immutable value of this Constant | ||
*/ | ||
function Constant( | ||
/** | ||
* The readonly value of this Constant. | ||
*/ | ||
value) { | ||
var _this = _super.call(this) || this; | ||
_this.value = value; | ||
/** | ||
* @internal | ||
* The version of this Constant, should always stay at 0, because Constants never change. | ||
*/ | ||
_this.version = 0; | ||
return _this; | ||
} | ||
/** | ||
* Returns the value of this Constant. | ||
*/ | ||
Constant.prototype.get = function () { return this.value; }; | ||
return Constant; | ||
}(Derivable)); | ||
/** | ||
* Creates a new Constant with the give value. | ||
@@ -538,744 +563,719 @@ * | ||
*/ | ||
function Constant( | ||
/** | ||
* The readonly value of this Constant. | ||
*/ | ||
value) { | ||
var _this = _super.call(this) || this; | ||
_this.value = value; | ||
/** | ||
* @internal | ||
* The version of this Constant, should always stay at 0, because Constants never change. | ||
*/ | ||
_this.version = 0; | ||
return _this; | ||
function constant(value) { | ||
return new Constant(value); | ||
} | ||
/** | ||
* Returns the value of this Constant. | ||
*/ | ||
Constant.prototype.get = function () { return this.value; }; | ||
return Constant; | ||
}(Derivable)); | ||
/** | ||
* Creates a new Constant with the give value. | ||
* | ||
* @param value the immutable value of this Constant | ||
*/ | ||
function constant(value) { | ||
return new Constant(value); | ||
} | ||
/** | ||
* Derivation is the implementation of derived state. Automatically tracks other Derivables that are used in the deriver function | ||
* and updates when needed. | ||
*/ | ||
var Derivation = /** @class */ (function (_super) { | ||
__extends(Derivation, _super); | ||
/** | ||
* Create a new Derivation using the deriver function. | ||
* | ||
* @param deriver the deriver function | ||
* Derivation is the implementation of derived state. Automatically tracks other Derivables that are used in the deriver function | ||
* and updates when needed. | ||
*/ | ||
function Derivation( | ||
/** | ||
* The deriver function that is used to calculate the value of this derivation. | ||
*/ | ||
deriver, | ||
/** | ||
* Arguments that will be passed unpacked to the deriver function. | ||
*/ | ||
args) { | ||
var _this = _super.call(this) || this; | ||
_this.deriver = deriver; | ||
_this.args = args; | ||
var Derivation = /** @class */ (function (_super) { | ||
__extends(Derivation, _super); | ||
/** | ||
* @internal | ||
* The recorded dependencies of this derivation. Is only used when the derivation is connected (i.e. it is actively used to | ||
* power a reactor, either directly or indirectly with other derivations in between). | ||
* Create a new Derivation using the deriver function. | ||
* | ||
* @param deriver the deriver function | ||
*/ | ||
_this.dependencies = []; | ||
function Derivation( | ||
/** | ||
* @internal | ||
* The versions of all dependencies that were used to calculate the currently known value. Is used to determine whether | ||
* the deriver function needs to be called. | ||
* The deriver function that is used to calculate the value of this derivation. | ||
*/ | ||
_this.dependencyVersions = {}; | ||
deriver, | ||
/** | ||
* Indicates whether the derivation is actively used to power a reactor, either directly or indirectly with other derivations in | ||
* between. Should always be kept up to date in order to prevent memory leaks. | ||
* Arguments that will be passed unpacked to the deriver function. | ||
*/ | ||
_this.connected = false; | ||
args) { | ||
var _this = _super.call(this) || this; | ||
_this.deriver = deriver; | ||
_this.args = args; | ||
/** | ||
* @internal | ||
* The recorded dependencies of this derivation. Is only used when the derivation is connected (i.e. it is actively used to | ||
* power a reactor, either directly or indirectly with other derivations in between). | ||
*/ | ||
_this.dependencies = []; | ||
/** | ||
* @internal | ||
* The versions of all dependencies that were used to calculate the currently known value. Is used to determine whether | ||
* the deriver function needs to be called. | ||
*/ | ||
_this.dependencyVersions = {}; | ||
/** | ||
* Indicates whether the derivation is actively used to power a reactor, either directly or indirectly with other derivations in | ||
* between. Should always be kept up to date in order to prevent memory leaks. | ||
*/ | ||
_this.connected = false; | ||
/** | ||
* Indicates whether the current cachedValue of this derivation is known to be up to date, or might need an update. Is set to false | ||
* by our dependencies when needed. We should be able to trust `true`. | ||
*/ | ||
_this.isUpToDate = false; | ||
/** | ||
* Indicates whether the derivation is in autoCache mode. | ||
*/ | ||
_this.autoCacheMode = false; | ||
/** | ||
* Used for debugging. A stack that shows the location where this derivation was created. | ||
*/ | ||
_this.stack = debugMode ? Error().stack : undefined; | ||
_this._version = 0; | ||
return _this; | ||
} | ||
Object.defineProperty(Derivation.prototype, "version", { | ||
/** | ||
* @internal | ||
* The current version of the state. This number gets incremented every time the state changes when connected. The version | ||
* is only guaranteed to increase on each change when connected. | ||
*/ | ||
get: function () { | ||
if (this.connected && this.shouldUpdate()) { | ||
this.update(); | ||
} | ||
return this._version; | ||
}, | ||
enumerable: true, | ||
configurable: true | ||
}); | ||
/** | ||
* Indicates whether the current cachedValue of this derivation is known to be up to date, or might need an update. Is set to false | ||
* by our dependencies when needed. We should be able to trust `true`. | ||
* Returns the current value of this derivable. Automatically records the use of this derivable when inside a derivation. | ||
*/ | ||
_this.isUpToDate = false; | ||
Derivation.prototype.get = function () { | ||
if (this.autoCacheMode && !this.connected) { | ||
// We will connect because of autoCacheMode in next block, after a tick we maybe need to disconnect (if no reactor was started | ||
// in this tick). | ||
this.maybeDisconnectInNextTick(); | ||
} | ||
// Are we currently connected or should we connect now? We know we need to connect if an observer is registered or | ||
// isRecordingObservations() returns true (in which case our observer is connecting and therefore recording its dependencies). | ||
if (this.autoCacheMode || this.observers.length || isRecordingObservations()) { | ||
recordObservation(this); | ||
// If we are not connected we should connect (by calling update). Otherwise ask shouldUpdate(). | ||
if (!this.connected || this.shouldUpdate()) { | ||
this.update(); | ||
} | ||
if (this.cachedError) { | ||
throw this.cachedError; | ||
} | ||
return this.cachedValue; | ||
} | ||
return this.callDeriver(); | ||
}; | ||
/** | ||
* Indicates whether the derivation is in autoCache mode. | ||
* Ensure the derivable is connected and update the currently cached value of this derivation. | ||
*/ | ||
_this.autoCacheMode = false; | ||
Derivation.prototype.update = function () { | ||
startRecordingObservations(this); | ||
var oldValue = this.cachedValue; | ||
try { | ||
var newValue = this.cachedValue = this.callDeriver(); | ||
this.cachedError = undefined; | ||
this.isUpToDate = true; | ||
this.connected = true; | ||
if (!equals(newValue, oldValue)) { | ||
this._version++; | ||
} | ||
} | ||
catch (error) { | ||
this.cachedError = error; | ||
} | ||
finally { | ||
stopRecordingObservations(); | ||
} | ||
}; | ||
/** | ||
* Used for debugging. A stack that shows the location where this derivation was created. | ||
* Call the deriver function without `this` context and log debug stack traces when applicable. | ||
*/ | ||
_this.stack = debugMode ? Error().stack : undefined; | ||
_this._version = 0; | ||
return _this; | ||
} | ||
Object.defineProperty(Derivation.prototype, "version", { | ||
Derivation.prototype.callDeriver = function () { | ||
try { | ||
var _a = this, deriver = _a.deriver, args = _a.args; | ||
return args ? deriver.apply(void 0, args.map(unpack)) : deriver(); | ||
} | ||
catch (e) { | ||
// tslint:disable-next-line:no-console - console.error is only called when debugMode is set to true | ||
this.stack && console.error(e.message, this.stack); | ||
throw e; | ||
} | ||
}; | ||
/** | ||
* Determine if this derivation needs an update (when connected). Compares the recorded dependencyVersions with the | ||
* current actual versions of the dependencies. If there is any mismatch between versions we need to update. Simple. | ||
*/ | ||
Derivation.prototype.shouldUpdate = function () { | ||
var _this = this; | ||
// Update the isUpToDate boolean only when it is false. | ||
if (!this.isUpToDate) { | ||
this.isUpToDate = this.dependencies.every(function (obs) { return _this.dependencyVersions[obs.id] === obs.version; }); | ||
} | ||
return !this.isUpToDate; | ||
}; | ||
/** | ||
* @internal | ||
* The current version of the state. This number gets incremented every time the state changes when connected. The version | ||
* is only guaranteed to increase on each change when connected. | ||
* Mark this derivation and all observers of this derivation as "possible outdated" or "state unknown". If this derivation is already | ||
* in that state, all observers of this derivation are also expected to already be in that state. This invariant should never | ||
* be invalidated. Any reactors we encounter are pushed into the reactorSink. | ||
*/ | ||
get: function () { | ||
if (this.connected && this.shouldUpdate()) { | ||
this.update(); | ||
Derivation.prototype.mark = function (reactorSink) { | ||
// If we think we're up-to-date our observers might think the same, otherwise, we're good, cause our observers can never | ||
// believe the're up-to-date when any of their dependencies is not up-to-date. | ||
if (this.isUpToDate) { | ||
this.isUpToDate = false; | ||
for (var _i = 0, _a = this.observers; _i < _a.length; _i++) { | ||
var observer = _a[_i]; | ||
observer.mark(reactorSink); | ||
} | ||
} | ||
return this._version; | ||
}, | ||
enumerable: true, | ||
configurable: true | ||
}); | ||
/** | ||
* Returns the current value of this derivable. Automatically records the use of this derivable when inside a derivation. | ||
*/ | ||
Derivation.prototype.get = function () { | ||
if (this.autoCacheMode && !this.connected) { | ||
// We will connect because of autoCacheMode in next block, after a tick we maybe need to disconnect (if no reactor was started | ||
// in this tick). | ||
this.maybeDisconnectInNextTick(); | ||
} | ||
// Are we currently connected or should we connect now? We know we need to connect if an observer is registered or | ||
// isRecordingObservations() returns true (in which case our observer is connecting and therefore recording its dependencies). | ||
if (this.autoCacheMode || this.observers.length || isRecordingObservations()) { | ||
recordObservation(this); | ||
// If we are not connected we should connect (by calling update). Otherwise ask shouldUpdate(). | ||
if (!this.connected || this.shouldUpdate()) { | ||
this.update(); | ||
}; | ||
/** | ||
* @internal | ||
* Disconnect this derivation. It will disconnect all remaining observers (downstream), stop all reactors that depend on this | ||
* derivation and disconnect all dependencies (upstream) that have no other observers. | ||
*/ | ||
Derivation.prototype.disconnect = function () { | ||
if (this.autoCacheMode) { | ||
this.maybeDisconnectInNextTick(); | ||
} | ||
if (this.cachedError) { | ||
throw this.cachedError; | ||
else { | ||
this.disconnectNow(); | ||
} | ||
return this.cachedValue; | ||
} | ||
return this.callDeriver(); | ||
}; | ||
/** | ||
* Ensure the derivable is connected and update the currently cached value of this derivation. | ||
*/ | ||
Derivation.prototype.update = function () { | ||
startRecordingObservations(this); | ||
var oldValue = this.cachedValue; | ||
try { | ||
var newValue = this.cachedValue = this.callDeriver(); | ||
this.cachedError = undefined; | ||
this.isUpToDate = true; | ||
this.connected = true; | ||
if (!equals(newValue, oldValue)) { | ||
this._version++; | ||
}; | ||
Derivation.prototype.maybeDisconnectInNextTick = function () { | ||
var _this = this; | ||
setTimeout(function () { return _this.observers.length || _this.disconnectNow(); }, 0); | ||
}; | ||
Derivation.prototype.disconnectNow = function () { | ||
this.isUpToDate = false; | ||
this.connected = false; | ||
// Disconnect all observers. When an observer disconnects it removes itself from this array. | ||
for (var i = this.observers.length - 1; i >= 0; i--) { | ||
this.observers[i].disconnect(); | ||
} | ||
for (var _i = 0, _a = this.dependencies; _i < _a.length; _i++) { | ||
var dep = _a[_i]; | ||
removeObserver(dep, this); | ||
} | ||
this.dependencies.length = 0; | ||
}; | ||
Derivation.prototype.autoCache = function () { | ||
this.autoCacheMode = true; | ||
return this; | ||
}; | ||
return Derivation; | ||
}(Derivable)); | ||
function derivation(f) { | ||
var ps = []; | ||
for (var _i = 1; _i < arguments.length; _i++) { | ||
ps[_i - 1] = arguments[_i]; | ||
} | ||
catch (error) { | ||
this.cachedError = error; | ||
return new Derivation(f, ps.length ? ps : undefined); | ||
} | ||
Derivable.prototype.derive = function derive(f) { | ||
var ps = []; | ||
for (var _i = 1; _i < arguments.length; _i++) { | ||
ps[_i - 1] = arguments[_i]; | ||
} | ||
finally { | ||
stopRecordingObservations(); | ||
} | ||
return new Derivation(f, [this].concat(ps)); | ||
}; | ||
/** | ||
* Call the deriver function without `this` context and log debug stack traces when applicable. | ||
*/ | ||
Derivation.prototype.callDeriver = function () { | ||
try { | ||
var _a = this, deriver = _a.deriver, args = _a.args; | ||
return args ? deriver.apply(void 0, args.map(unpack)) : deriver(); | ||
} | ||
catch (e) { | ||
// tslint:disable-next-line:no-console - console.error is only called when debugMode is set to true | ||
this.stack && console.error(e.message, this.stack); | ||
throw e; | ||
} | ||
Derivable.prototype.pluck = function pluck(key) { | ||
return this.derive(plucker, key); | ||
}; | ||
function plucker(obj, key) { | ||
return hasGetter(obj) | ||
? obj.get(key) | ||
: obj[key]; | ||
} | ||
function hasGetter(obj) { | ||
return obj && typeof obj.get === 'function'; | ||
} | ||
/** | ||
* Determine if this derivation needs an update (when connected). Compares the recorded dependencyVersions with the | ||
* current actual versions of the dependencies. If there is any mismatch between versions we need to update. Simple. | ||
* A Lens is a Derivation that is also settable. It satisfies the Atom interface and can be created using an | ||
* arbitrary get and set function or as a derivation from another Atom or Lens using a deriver (get) and | ||
* transform (set) function. The set function is always called inside a transaction (but will not create a new | ||
* transaction if one is already active) to prevent inconsistent state when an error occurs. | ||
*/ | ||
Derivation.prototype.shouldUpdate = function () { | ||
var _this = this; | ||
// Update the isUpToDate boolean only when it is false. | ||
if (!this.isUpToDate) { | ||
this.isUpToDate = this.dependencies.every(function (obs) { return _this.dependencyVersions[obs.id] === obs.version; }); | ||
var Lens = /** @class */ (function (_super) { | ||
__extends(Lens, _super); | ||
/** | ||
* Create a new Lens using a get and a set function. The get is used as a normal deriver function | ||
* including the automatic recording of dependencies, the set is used as a sink for new values. | ||
* | ||
* @param param0 the get and set functions | ||
*/ | ||
function Lens(_a, args) { | ||
var get = _a.get, set = _a.set; | ||
var _this = _super.call(this, get, args) || this; | ||
_this.setter = set; | ||
return _this; | ||
} | ||
return !this.isUpToDate; | ||
}; | ||
/** | ||
* @internal | ||
* Mark this derivation and all observers of this derivation as "possible outdated" or "state unknown". If this derivation is already | ||
* in that state, all observers of this derivation are also expected to already be in that state. This invariant should never | ||
* be invalidated. Any reactors we encounter are pushed into the reactorSink. | ||
*/ | ||
Derivation.prototype.mark = function (reactorSink) { | ||
// If we think we're up-to-date our observers might think the same, otherwise, we're good, cause our observers can never | ||
// believe the're up-to-date when any of their dependencies is not up-to-date. | ||
if (this.isUpToDate) { | ||
this.isUpToDate = false; | ||
for (var _i = 0, _a = this.observers; _i < _a.length; _i++) { | ||
var observer = _a[_i]; | ||
observer.mark(reactorSink); | ||
/** | ||
* Sets the value of this Lens, the set function provided in the constructor is responsible for maintaining | ||
* the state and should make sure that the next call to `get()` returns the `newValue`. | ||
* | ||
* @param newValue the new state | ||
*/ | ||
Lens.prototype.set = function (newValue) { | ||
var _a = this, setter = _a.setter, args = _a.args; | ||
if (args) { | ||
setter.apply(void 0, [newValue].concat(args.map(unpack))); | ||
} | ||
else { | ||
setter(newValue); | ||
} | ||
}; | ||
/** | ||
* Swaps the current value of this atom using the provided swap function. Any additional arguments to this function are | ||
* fed to the swap function. | ||
* | ||
* @param f the swap function | ||
*/ | ||
Lens.prototype.swap = function (f) { | ||
var args = []; | ||
for (var _i = 1; _i < arguments.length; _i++) { | ||
args[_i - 1] = arguments[_i]; | ||
} | ||
this.set(f.apply(void 0, [this.get()].concat(args))); | ||
}; | ||
/** | ||
* Create a new Lens using the provided deriver (get) and transform (set) functions. | ||
* | ||
* @param param0 the deriver (get) and transform (set) functions | ||
*/ | ||
Lens.prototype.lens = function (_a) { | ||
var get = _a.get, set = _a.set; | ||
var ps = []; | ||
for (var _i = 1; _i < arguments.length; _i++) { | ||
ps[_i - 1] = arguments[_i]; | ||
} | ||
var atom$$1 = this; | ||
return new Lens({ | ||
get: get, | ||
set: function () { atom$$1.set(set.apply(undefined, arguments)); }, | ||
}, [atom$$1].concat(ps)); | ||
}; | ||
// Locally overridden to return an Atom instead of an ordinary Derivable. | ||
Lens.prototype.pluck = function (key) { | ||
return this.lens({ | ||
get: plucker, | ||
set: pluckSetter, | ||
}, key); | ||
}; | ||
__decorate([ | ||
atomic() | ||
], Lens.prototype, "set", null); | ||
return Lens; | ||
}(Derivation)); | ||
function pluckSetter(newValue, object, key) { | ||
if (hasGetter(object)) { | ||
if (hasSetter(object)) { | ||
return object.set(key, newValue); | ||
} | ||
throw new Error('object is readonly'); | ||
} | ||
}; | ||
/** | ||
* @internal | ||
* Disconnect this derivation. It will disconnect all remaining observers (downstream), stop all reactors that depend on this | ||
* derivation and disconnect all dependencies (upstream) that have no other observers. | ||
*/ | ||
Derivation.prototype.disconnect = function () { | ||
if (this.autoCacheMode) { | ||
this.maybeDisconnectInNextTick(); | ||
var result = clone(object); | ||
result[key] = newValue; | ||
return result; | ||
} | ||
function hasSetter(obj) { | ||
return typeof obj.set === 'function'; | ||
} | ||
function lens(descriptor) { | ||
var ps = []; | ||
for (var _i = 1; _i < arguments.length; _i++) { | ||
ps[_i - 1] = arguments[_i]; | ||
} | ||
else { | ||
this.disconnectNow(); | ||
} | ||
}; | ||
Derivation.prototype.maybeDisconnectInNextTick = function () { | ||
var _this = this; | ||
setTimeout(function () { return _this.observers.length || _this.disconnectNow(); }, 0); | ||
}; | ||
Derivation.prototype.disconnectNow = function () { | ||
this.isUpToDate = false; | ||
this.connected = false; | ||
// Disconnect all observers. When an observer disconnects it removes itself from this array. | ||
for (var i = this.observers.length - 1; i >= 0; i--) { | ||
this.observers[i].disconnect(); | ||
} | ||
for (var _i = 0, _a = this.dependencies; _i < _a.length; _i++) { | ||
var dep = _a[_i]; | ||
removeObserver(dep, this); | ||
} | ||
this.dependencies.length = 0; | ||
}; | ||
Derivation.prototype.autoCache = function () { | ||
this.autoCacheMode = true; | ||
return this; | ||
}; | ||
return Derivation; | ||
}(Derivable)); | ||
function derivation(f) { | ||
var ps = []; | ||
for (var _i = 1; _i < arguments.length; _i++) { | ||
ps[_i - 1] = arguments[_i]; | ||
return new Lens(descriptor, ps.length ? ps : undefined); | ||
} | ||
return new Derivation(f, ps.length ? ps : undefined); | ||
} | ||
Derivable.prototype.derive = function derive(f) { | ||
var ps = []; | ||
for (var _i = 1; _i < arguments.length; _i++) { | ||
ps[_i - 1] = arguments[_i]; | ||
Atom.prototype.lens = Lens.prototype.lens; | ||
Atom.prototype.pluck = Lens.prototype.pluck; | ||
function lift(f) { | ||
return function () { | ||
var ps = []; | ||
for (var _i = 0; _i < arguments.length; _i++) { | ||
ps[_i] = arguments[_i]; | ||
} | ||
return new Derivation(f, ps); | ||
}; | ||
} | ||
return new Derivation(f, [this].concat(ps)); | ||
}; | ||
Derivable.prototype.pluck = function pluck(key) { | ||
return this.derive(plucker, key); | ||
}; | ||
function plucker(obj, key) { | ||
return hasGetter(obj) | ||
? obj.get(key) | ||
: obj[key]; | ||
} | ||
function hasGetter(obj) { | ||
return obj && typeof obj.get === 'function'; | ||
} | ||
/** | ||
* A Lens is a Derivation that is also settable. It satisfies the Atom interface and can be created using an | ||
* arbitrary get and set function or as a derivation from another Atom or Lens using a deriver (get) and | ||
* transform (set) function. The set function is always called inside a transaction (but will not create a new | ||
* transaction if one is already active) to prevent inconsistent state when an error occurs. | ||
*/ | ||
var Lens = /** @class */ (function (_super) { | ||
__extends(Lens, _super); | ||
/** | ||
* Create a new Lens using a get and a set function. The get is used as a normal deriver function | ||
* including the automatic recording of dependencies, the set is used as a sink for new values. | ||
* | ||
* @param param0 the get and set functions | ||
*/ | ||
function Lens(_a, args) { | ||
var get = _a.get, set = _a.set; | ||
var _this = _super.call(this, get, args) || this; | ||
_this.setter = set; | ||
return _this; | ||
function scan(f, seed) { | ||
var acc = seed; | ||
return function wrapped(value) { | ||
return acc = f.call(this, acc, value); | ||
}; | ||
} | ||
/** | ||
* Sets the value of this Lens, the set function provided in the constructor is responsible for maintaining | ||
* the state and should make sure that the next call to `get()` returns the `newValue`. | ||
* Performs JavaScript `&&` operation on the provided arguments after unpacking. | ||
* | ||
* @param newValue the new state | ||
* @method | ||
*/ | ||
Lens.prototype.set = function (newValue) { | ||
var _a = this, setter = _a.setter, args = _a.args; | ||
if (args) { | ||
setter.apply(void 0, [newValue].concat(args.map(unpack))); | ||
} | ||
else { | ||
setter(newValue); | ||
} | ||
}; | ||
var and = andOrImpl(function (v) { return !v; }); | ||
/** | ||
* Swaps the current value of this atom using the provided swap function. Any additional arguments to this function are | ||
* fed to the swap function. | ||
* Performs JavaScript `||` operation on the provided arguments after unpacking. | ||
* | ||
* @param f the swap function | ||
* @method | ||
*/ | ||
Lens.prototype.swap = function (f) { | ||
var args = []; | ||
for (var _i = 1; _i < arguments.length; _i++) { | ||
args[_i - 1] = arguments[_i]; | ||
} | ||
this.set(f.apply(void 0, [this.get()].concat(args))); | ||
}; | ||
var or = andOrImpl(function (v) { return !!v; }); | ||
/** | ||
* Create a new Lens using the provided deriver (get) and transform (set) functions. | ||
* Returns the first operand that is not `null` or `undefined` after unpacking. | ||
* | ||
* @param param0 the deriver (get) and transform (set) functions | ||
* @method | ||
*/ | ||
Lens.prototype.lens = function (_a) { | ||
var get = _a.get, set = _a.set; | ||
var ps = []; | ||
for (var _i = 1; _i < arguments.length; _i++) { | ||
ps[_i - 1] = arguments[_i]; | ||
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 derivation(function () { | ||
var value; | ||
for (var _i = 0, args_1 = args; _i < args_1.length; _i++) { | ||
var arg = args_1[_i]; | ||
value = unpack(arg); | ||
if (breakOn(value)) { | ||
break; | ||
} | ||
} | ||
return value; | ||
}); | ||
}; | ||
} | ||
function struct(obj) { | ||
if (obj instanceof Derivable) { | ||
return obj; | ||
} | ||
var atom$$1 = this; | ||
return new Lens({ | ||
get: get, | ||
set: function () { atom$$1.set(set.apply(undefined, arguments)); }, | ||
}, [atom$$1].concat(ps)); | ||
}; | ||
// Locally overridden to return an Atom instead of an ordinary Derivable. | ||
Lens.prototype.pluck = function (key) { | ||
return this.lens({ | ||
get: plucker, | ||
set: pluckSetter, | ||
}, key); | ||
}; | ||
__decorate([ | ||
atomic() | ||
], Lens.prototype, "set", null); | ||
return Lens; | ||
}(Derivation)); | ||
function pluckSetter(newValue, object, key) { | ||
if (hasGetter(object)) { | ||
if (hasSetter(object)) { | ||
return object.set(key, newValue); | ||
if (!Array.isArray(obj) && !isPlainObject(obj)) { | ||
throw new Error('"struct" only accepts Derivables, plain Objects and Arrays'); | ||
} | ||
throw new Error('object is readonly'); | ||
return derivation(deepUnpack, obj); | ||
} | ||
var result = clone(object); | ||
result[key] = newValue; | ||
return result; | ||
} | ||
function hasSetter(obj) { | ||
return typeof obj.set === 'function'; | ||
} | ||
function lens(descriptor) { | ||
var ps = []; | ||
for (var _i = 1; _i < arguments.length; _i++) { | ||
ps[_i - 1] = arguments[_i]; | ||
function deepUnpack(obj) { | ||
if (obj instanceof Derivable) { | ||
return obj.get(); | ||
} | ||
if (Array.isArray(obj)) { | ||
return obj.map(deepUnpack); | ||
} | ||
if (isPlainObject(obj)) { | ||
var result = {}; | ||
for (var _i = 0, _a = Object.keys(obj); _i < _a.length; _i++) { | ||
var key = _a[_i]; | ||
result[key] = deepUnpack(obj[key]); | ||
} | ||
return result; | ||
} | ||
return obj; | ||
} | ||
return new Lens(descriptor, ps.length ? ps : undefined); | ||
} | ||
Atom.prototype.lens = Lens.prototype.lens; | ||
Atom.prototype.pluck = Lens.prototype.pluck; | ||
function lift(f) { | ||
return function () { | ||
var ps = []; | ||
for (var _i = 0; _i < arguments.length; _i++) { | ||
ps[_i] = arguments[_i]; | ||
} | ||
return new Derivation(f, ps); | ||
}; | ||
} | ||
function scan(f, seed) { | ||
var acc = seed; | ||
return function wrapped(value) { | ||
return acc = f.call(this, acc, value); | ||
}; | ||
} | ||
/** | ||
* Performs JavaScript `&&` operation on the provided arguments after unpacking. | ||
* | ||
* @method | ||
*/ | ||
var and = andOrImpl(function (v) { return !v; }); | ||
/** | ||
* Performs JavaScript `||` operation on the provided arguments after unpacking. | ||
* | ||
* @method | ||
*/ | ||
var or = andOrImpl(function (v) { return !!v; }); | ||
/** | ||
* Returns the first operand that is not `null` or `undefined` after unpacking. | ||
* | ||
* @method | ||
*/ | ||
var firstNotNull = andOrImpl(function (v) { return v != null; }); | ||
function andOrImpl(breakOn) { | ||
return function () { | ||
/** | ||
* 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 = 0; _i < arguments.length; _i++) { | ||
args[_i] = arguments[_i]; | ||
for (var _i = 1; _i < arguments.length; _i++) { | ||
args[_i - 1] = arguments[_i]; | ||
} | ||
return derivation(function () { | ||
var value; | ||
for (var _i = 0, args_1 = args; _i < args_1.length; _i++) { | ||
var arg = args_1[_i]; | ||
value = unpack(arg); | ||
if (breakOn(value)) { | ||
break; | ||
var s = ''; | ||
for (var i = 0; i < parts.length; i++) { | ||
s += parts[i]; | ||
if (i < args.length) { | ||
s += unpack(args[i]); | ||
} | ||
} | ||
return value; | ||
return s; | ||
}); | ||
}; | ||
} | ||
} | ||
function struct(obj) { | ||
if (obj instanceof Derivable) { | ||
return obj; | ||
function isAtom(derivable) { | ||
return derivable instanceof Atom || derivable instanceof Lens; | ||
} | ||
if (!Array.isArray(obj) && !isPlainObject(obj)) { | ||
throw new Error('"struct" only accepts Derivables, plain Objects and Arrays'); | ||
function isConstant(derivable) { | ||
return derivable instanceof Constant; | ||
} | ||
return derivation(deepUnpack, obj); | ||
} | ||
function deepUnpack(obj) { | ||
if (obj instanceof Derivable) { | ||
return obj.get(); | ||
function isDerivable(derivable) { | ||
return derivable instanceof Derivable; | ||
} | ||
if (Array.isArray(obj)) { | ||
return obj.map(deepUnpack); | ||
function isDerivation(derivable) { | ||
return derivable instanceof Derivation; | ||
} | ||
if (isPlainObject(obj)) { | ||
var result = {}; | ||
for (var _i = 0, _a = Object.keys(obj); _i < _a.length; _i++) { | ||
var key = _a[_i]; | ||
result[key] = deepUnpack(obj[key]); | ||
} | ||
return result; | ||
function isLens(derivable) { | ||
return derivable instanceof Lens; | ||
} | ||
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]; | ||
function wrapPreviousState(f, init) { | ||
var oldValue = init; | ||
return function wrapped(newValue) { | ||
var result = f.call(this, newValue, oldValue); | ||
oldValue = newValue; | ||
return result; | ||
}; | ||
} | ||
return derivation(function () { | ||
var s = ''; | ||
for (var i = 0; i < parts.length; i++) { | ||
s += parts[i]; | ||
if (i < args.length) { | ||
s += unpack(args[i]); | ||
} | ||
} | ||
return s; | ||
}); | ||
} | ||
function isAtom(derivable) { | ||
return derivable instanceof Atom || derivable instanceof Lens; | ||
} | ||
function isConstant(derivable) { | ||
return derivable instanceof Constant; | ||
} | ||
function isDerivable(derivable) { | ||
return derivable instanceof Derivable; | ||
} | ||
function isDerivation(derivable) { | ||
return derivable instanceof Derivation; | ||
} | ||
function isLens(derivable) { | ||
return derivable instanceof Lens; | ||
} | ||
function wrapPreviousState(f, init) { | ||
var oldValue = init; | ||
return function wrapped(newValue) { | ||
var result = f.call(this, newValue, oldValue); | ||
oldValue = newValue; | ||
return result; | ||
var true$ = constant(true); | ||
var false$ = constant(false); | ||
Derivable.prototype.react = function react(reaction, options) { | ||
return Reactor.create(this, reaction, options); | ||
}; | ||
} | ||
var true$ = constant(true); | ||
var false$ = constant(false); | ||
Derivable.prototype.react = function react(reaction, options) { | ||
return Reactor.create(this, reaction, options); | ||
}; | ||
/** | ||
* The maximum recursion depth for a single Reactor. Is used to fail faster than JavaScripts "Maximum call stack size | ||
* exceeded" and provide better error messages. | ||
*/ | ||
var MAX_REACTION_DEPTH = 100; | ||
var defaultOptions = { from: true$, until: false$, when: true$, once: false, skipFirst: false }; | ||
/** | ||
* A Reactor is an observer of a derivable that automatically performs some reaction whenever the derivable changes. Will not | ||
* react to changes when a transaction is active. When all transactions are committed all pending reactors will fire. Note that a | ||
* reactor that is created inside a transaction will still fire the first time (unless skipFirst = true), but will not react | ||
* to changes until the transaction ends. | ||
*/ | ||
var Reactor = /** @class */ (function () { | ||
/** | ||
* Create a new instance of Reactor, do not use this directly, use {@link Reactor.create} instead. | ||
* | ||
* @param parent the derivable that should be observed | ||
* @param reaction the reaction that should fire | ||
* The maximum recursion depth for a single Reactor. Is used to fail faster than JavaScripts "Maximum call stack size | ||
* exceeded" and provide better error messages. | ||
*/ | ||
function Reactor( | ||
var MAX_REACTION_DEPTH = 100; | ||
var defaultOptions = { from: true$, until: false$, when: true$, once: false, skipFirst: false }; | ||
/** | ||
* The derivable that is observed to determine changes. | ||
* A Reactor is an observer of a derivable that automatically performs some reaction whenever the derivable changes. Will not | ||
* react to changes when a transaction is active. When all transactions are committed all pending reactors will fire. Note that a | ||
* reactor that is created inside a transaction will still fire the first time (unless skipFirst = true), but will not react | ||
* to changes until the transaction ends. | ||
*/ | ||
parent, | ||
/** | ||
* The reaction that should fire when the derivable changes. | ||
*/ | ||
reaction) { | ||
this.parent = parent; | ||
this.reaction = reaction; | ||
var Reactor = /** @class */ (function () { | ||
/** | ||
* When a reactor is active it observes its derivable (parent) and reacts to changes. | ||
* Create a new instance of Reactor, do not use this directly, use {@link Reactor.create} instead. | ||
* | ||
* @param parent the derivable that should be observed | ||
* @param reaction the reaction that should fire | ||
*/ | ||
this.active = false; | ||
function Reactor( | ||
/** | ||
* Unique ID for debugging purposes. | ||
* The derivable that is observed to determine changes. | ||
*/ | ||
this.id = uniqueId(); | ||
parent, | ||
/** | ||
* The current recursion depth of reactions. Can reach a maximum at which point an Error will be thrown. | ||
* The reaction that should fire when the derivable changes. | ||
*/ | ||
this.reactionDepth = 0; | ||
reaction) { | ||
this.parent = parent; | ||
this.reaction = reaction; | ||
/** | ||
* When a reactor is active it observes its derivable (parent) and reacts to changes. | ||
*/ | ||
this.active = false; | ||
/** | ||
* Unique ID for debugging purposes. | ||
*/ | ||
this.id = uniqueId(); | ||
/** | ||
* The current recursion depth of reactions. Can reach a maximum at which point an Error will be thrown. | ||
*/ | ||
this.reactionDepth = 0; | ||
/** | ||
* Used for debugging. A stack that shows the location where this derivation was created. | ||
*/ | ||
this.stack = debugMode ? new Error().stack : undefined; | ||
/** | ||
* The value of the parent when this reactor last reacted. Is used to determine whether it should react again or not. | ||
*/ | ||
this.lastValue = {}; | ||
} | ||
/** | ||
* Used for debugging. A stack that shows the location where this derivation was created. | ||
* Start this reactor if not already started. Will always run the reaction once with the current value of parent on start. | ||
*/ | ||
this.stack = debugMode ? new Error().stack : undefined; | ||
Reactor.prototype.start = function () { | ||
if (this.active) { | ||
return; | ||
} | ||
this.parent.observers.push(this); | ||
this.active = true; | ||
this.reactIfNeeded(); | ||
}; | ||
/** | ||
* The value of the parent when this reactor last reacted. Is used to determine whether it should react again or not. | ||
* React when active and needed. Does nothing when a reaction is not appropriate. | ||
*/ | ||
this.lastValue = {}; | ||
} | ||
/** | ||
* Start this reactor if not already started. Will always run the reaction once with the current value of parent on start. | ||
*/ | ||
Reactor.prototype.start = function () { | ||
if (this.active) { | ||
return; | ||
} | ||
this.parent.observers.push(this); | ||
this.active = true; | ||
this.reactIfNeeded(); | ||
}; | ||
/** | ||
* React when active and needed. Does nothing when a reaction is not appropriate. | ||
*/ | ||
Reactor.prototype.reactIfNeeded = function () { | ||
if (!this.active) { | ||
return; | ||
} | ||
// Our controller always has first right to react. | ||
if (this.controller) { | ||
this.controller.reactIfNeeded(); | ||
} | ||
// Check active again, could have been stopped by controller now. | ||
if (!this.active) { | ||
return; | ||
} | ||
var lastValue = this.lastValue; | ||
var nextValue = this.lastValue = this.parent.get(); | ||
if (!equals(lastValue, nextValue)) { | ||
this.react(nextValue); | ||
} | ||
}; | ||
/** | ||
* React once. Will call the reaction with the current value of parent and remember the current version of the parent to | ||
* be able to determine when to react next. | ||
*/ | ||
Reactor.prototype.react = function (value) { | ||
this.reactionDepth++; | ||
try { | ||
if (this.reactionDepth > MAX_REACTION_DEPTH) { | ||
throw new Error('Too deeply nested synchronous cyclical reactions disallowed. Use setImmediate.'); | ||
Reactor.prototype.reactIfNeeded = function () { | ||
if (!this.active) { | ||
return; | ||
} | ||
this.reaction(value); | ||
} | ||
catch (e) { | ||
// tslint:disable-next-line:no-console - console.error is only called when debugMode is set to true | ||
this.stack && console.error(e.message, this.stack); | ||
throw e; | ||
} | ||
finally { | ||
this.reactionDepth--; | ||
} | ||
}; | ||
/** | ||
* Stop reacting on parent changes, will remove this reactor as an observer from the parent which might disconnect the parent. | ||
*/ | ||
Reactor.prototype.stop = function () { | ||
if (!this.active) { | ||
return this; | ||
} | ||
this.active = false; | ||
removeObserver(this.parent, this); | ||
return this; | ||
}; | ||
/** | ||
* If for some reason any upstream derivable is ordered to disconnect, we have to disconnect as well, which means: stop the reactor. | ||
*/ | ||
Reactor.prototype.disconnect = function () { | ||
this.stop(); | ||
}; | ||
/** | ||
* @internal | ||
* During the mark phase add this reactor to the reactorSink. This way, the transaction knows we exist and we get to `reactIfNeeded` | ||
* later on. | ||
*/ | ||
Reactor.prototype.mark = function (reactorSink) { | ||
if (reactorSink.indexOf(this) < 0) { | ||
reactorSink.push(this); | ||
} | ||
}; | ||
/** | ||
* Create a new Reactor with lifecycle options. This is the only way to create a new Reactor. Returns a callback function that can | ||
* be used to stop the reactor indefinitely. | ||
* | ||
* @param parent the derivable to react on | ||
* @param reaction the function to call on each reaction | ||
* @param options lifecycle options | ||
* @param ended an optional callback that fires when the reactor is stopped indefinitely (by once or until option) | ||
*/ | ||
Reactor.create = function (parent, reaction, options, ended) { | ||
var resolvedOptions = __assign({}, defaultOptions, options); | ||
var from = resolvedOptions.from, until = resolvedOptions.until, when = resolvedOptions.when, once = resolvedOptions.once; | ||
var skipFirst = resolvedOptions.skipFirst; | ||
// Wrap the reaction to enforce skipFirst and once. | ||
var reactor = new Reactor(parent, function (value) { | ||
if (skipFirst) { | ||
skipFirst = false; | ||
// Our controller always has first right to react. | ||
if (this.controller) { | ||
this.controller.reactIfNeeded(); | ||
} | ||
else { | ||
reaction(value); | ||
if (once) { | ||
done(); | ||
// Check active again, could have been stopped by controller now. | ||
if (!this.active) { | ||
return; | ||
} | ||
var lastValue = this.lastValue; | ||
var nextValue = this.lastValue = this.parent.get(); | ||
if (!equals(lastValue, nextValue)) { | ||
this.react(nextValue); | ||
} | ||
}; | ||
/** | ||
* React once. Will call the reaction with the current value of parent and remember the current version of the parent to | ||
* be able to determine when to react next. | ||
*/ | ||
Reactor.prototype.react = function (value) { | ||
this.reactionDepth++; | ||
try { | ||
if (this.reactionDepth > MAX_REACTION_DEPTH) { | ||
throw new Error('Too deeply nested synchronous cyclical reactions disallowed. Use setImmediate.'); | ||
} | ||
this.reaction(value); | ||
} | ||
}); | ||
// Listen to when and until conditions, starting and stopping the reactor when | ||
// needed, and stopping the reaction and controller when until becomes true. | ||
var controller = new Reactor(combineWhenUntil(parent, when, until), function (conds) { | ||
if (conds.until) { | ||
done(); | ||
catch (e) { | ||
// tslint:disable-next-line:no-console - console.error is only called when debugMode is set to true | ||
this.stack && console.error(e.message, this.stack); | ||
throw e; | ||
} | ||
else if (conds.when) { | ||
reactor.start(); | ||
finally { | ||
this.reactionDepth--; | ||
} | ||
else if (reactor.active) { | ||
reactor.stop(); | ||
}; | ||
/** | ||
* Stop reacting on parent changes, will remove this reactor as an observer from the parent which might disconnect the parent. | ||
*/ | ||
Reactor.prototype.stop = function () { | ||
if (!this.active) { | ||
return this; | ||
} | ||
}); | ||
// The controller needs to act before the reactor in order to ensure deterministic until and when behavior. | ||
reactor.controller = controller; | ||
// The starter waits until `from` to start the controller. | ||
var starter = new Reactor(toDerivable(from, parent), function (value) { | ||
if (value) { | ||
controller.start(); | ||
this.active = false; | ||
removeObserver(this.parent, this); | ||
return this; | ||
}; | ||
/** | ||
* If for some reason any upstream derivable is ordered to disconnect, we have to disconnect as well, which means: stop the reactor. | ||
*/ | ||
Reactor.prototype.disconnect = function () { | ||
this.stop(); | ||
}; | ||
/** | ||
* @internal | ||
* During the mark phase add this reactor to the reactorSink. This way, the transaction knows we exist and we get to `reactIfNeeded` | ||
* later on. | ||
*/ | ||
Reactor.prototype.mark = function (reactorSink) { | ||
if (reactorSink.indexOf(this) < 0) { | ||
reactorSink.push(this); | ||
} | ||
}; | ||
/** | ||
* Create a new Reactor with lifecycle options. This is the only way to create a new Reactor. Returns a callback function that can | ||
* be used to stop the reactor indefinitely. | ||
* | ||
* @param parent the derivable to react on | ||
* @param reaction the function to call on each reaction | ||
* @param options lifecycle options | ||
* @param ended an optional callback that fires when the reactor is stopped indefinitely (by once or until option) | ||
*/ | ||
Reactor.create = function (parent, reaction, options, ended) { | ||
var resolvedOptions = __assign({}, defaultOptions, options); | ||
var from = resolvedOptions.from, until = resolvedOptions.until, when = resolvedOptions.when, once = resolvedOptions.once; | ||
var skipFirst = resolvedOptions.skipFirst; | ||
// Wrap the reaction to enforce skipFirst and once. | ||
var reactor = new Reactor(parent, function (value) { | ||
if (skipFirst) { | ||
skipFirst = false; | ||
} | ||
else { | ||
reaction(value); | ||
if (once) { | ||
done(); | ||
} | ||
} | ||
}); | ||
// Listen to when and until conditions, starting and stopping the reactor when | ||
// needed, and stopping the reaction and controller when until becomes true. | ||
var controller = new Reactor(combineWhenUntil(parent, when, until), function (conds) { | ||
if (conds.until) { | ||
done(); | ||
} | ||
else if (conds.when) { | ||
reactor.start(); | ||
} | ||
else if (reactor.active) { | ||
reactor.stop(); | ||
} | ||
}); | ||
// The controller needs to act before the reactor in order to ensure deterministic until and when behavior. | ||
reactor.controller = controller; | ||
// The starter waits until `from` to start the controller. | ||
var starter = new Reactor(toDerivable(from, parent), function (value) { | ||
if (value) { | ||
controller.start(); | ||
starter.stop(); | ||
} | ||
}); | ||
function done() { | ||
starter.stop(); | ||
controller.stop(); | ||
reactor.stop(); | ||
ended && ended(); | ||
} | ||
}); | ||
function done() { | ||
starter.stop(); | ||
controller.stop(); | ||
reactor.stop(); | ||
ended && ended(); | ||
// Go!!! | ||
starter.start(); | ||
return done; | ||
}; | ||
return Reactor; | ||
}()); | ||
function toDerivable(option, derivable) { | ||
if (isDerivable(option)) { | ||
return option; | ||
} | ||
// Go!!! | ||
starter.start(); | ||
return done; | ||
}; | ||
return Reactor; | ||
}()); | ||
function toDerivable(option, derivable) { | ||
if (isDerivable(option)) { | ||
return option; | ||
if (typeof option === 'function') { | ||
return derivation(function () { return unpack(option(derivable)); }); | ||
} | ||
return option ? true$ : false$; | ||
} | ||
if (typeof option === 'function') { | ||
return derivation(function () { return unpack(option(derivable)); }); | ||
function combineWhenUntil(parent, whenOption, untilOption) { | ||
var when = toDerivable(whenOption, parent); | ||
var until = toDerivable(untilOption, parent); | ||
if (isConstant(when) && isConstant(until)) { | ||
return constant({ when: when.value, until: until.value }); | ||
} | ||
return derivation(whenUntil, when, until); | ||
} | ||
return option ? true$ : false$; | ||
} | ||
function combineWhenUntil(parent, whenOption, untilOption) { | ||
var when = toDerivable(whenOption, parent); | ||
var until = toDerivable(untilOption, parent); | ||
if (isConstant(when) && isConstant(until)) { | ||
return constant({ when: when.value, until: until.value }); | ||
} | ||
return derivation(whenUntil, when, until); | ||
} | ||
function whenUntil(when, until) { return { when: when, until: until }; } | ||
function whenUntil(when, until) { return { when: when, until: until }; } | ||
exports.atom = atom; | ||
exports.Atom = Atom; | ||
exports.constant = constant; | ||
exports.Constant = Constant; | ||
exports.Derivable = Derivable; | ||
exports.derivation = derivation; | ||
exports.Derivation = Derivation; | ||
exports.lens = lens; | ||
exports.Lens = Lens; | ||
exports.unpack = unpack; | ||
exports.and = and; | ||
exports.firstNotNull = firstNotNull; | ||
exports.isAtom = isAtom; | ||
exports.isConstant = isConstant; | ||
exports.isDerivable = isDerivable; | ||
exports.isDerivation = isDerivation; | ||
exports.isLens = isLens; | ||
exports.lift = lift; | ||
exports.or = or; | ||
exports.scan = scan; | ||
exports.struct = struct; | ||
exports.template = template; | ||
exports.wrapPreviousState = wrapPreviousState; | ||
exports.Reactor = Reactor; | ||
exports.atomic = atomic; | ||
exports.atomically = atomically; | ||
exports.inTransaction = inTransaction; | ||
exports.transact = transact; | ||
exports.transaction = transaction; | ||
exports.clone = clone; | ||
exports.equals = equals; | ||
exports.isPlainObject = isPlainObject; | ||
exports.setDebugMode = setDebugMode; | ||
exports.atom = atom; | ||
exports.Atom = Atom; | ||
exports.constant = constant; | ||
exports.Constant = Constant; | ||
exports.Derivable = Derivable; | ||
exports.derivation = derivation; | ||
exports.Derivation = Derivation; | ||
exports.lens = lens; | ||
exports.Lens = Lens; | ||
exports.unpack = unpack; | ||
exports.and = and; | ||
exports.firstNotNull = firstNotNull; | ||
exports.isAtom = isAtom; | ||
exports.isConstant = isConstant; | ||
exports.isDerivable = isDerivable; | ||
exports.isDerivation = isDerivation; | ||
exports.isLens = isLens; | ||
exports.lift = lift; | ||
exports.or = or; | ||
exports.scan = scan; | ||
exports.struct = struct; | ||
exports.template = template; | ||
exports.wrapPreviousState = wrapPreviousState; | ||
exports.Reactor = Reactor; | ||
exports.atomic = atomic; | ||
exports.atomically = atomically; | ||
exports.inTransaction = inTransaction; | ||
exports.transact = transact; | ||
exports.transaction = transaction; | ||
exports.clone = clone; | ||
exports.equals = equals; | ||
exports.isPlainObject = isPlainObject; | ||
exports.setDebugMode = setDebugMode; | ||
Object.defineProperty(exports, '__esModule', { value: true }); | ||
Object.defineProperty(exports, '__esModule', { value: true }); | ||
}))); | ||
//# sourceMappingURL=sherlock.umd.js.map |
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
No v1
QualityPackage is not semver >=1. This means it is not stable and does not support ^ ranges.
Found 1 instance in 1 package
460525
0
Updatedtslib@^1.9.0