UNPKG

@tioniq/eventiq

Version:

A library providing utilities for implementing the Event pattern, facilitating event handling in JavaScript and TypeScript applications. This library is a collection of common utilities for managing events and event handlers using the Event pattern. The i

1,929 lines (1,895 loc) 73.9 kB
"use strict"; var __defProp = Object.defineProperty; var __getOwnPropDesc = Object.getOwnPropertyDescriptor; var __getOwnPropNames = Object.getOwnPropertyNames; var __hasOwnProp = Object.prototype.hasOwnProperty; var __export = (target, all) => { for (var name in all) __defProp(target, name, { get: all[name], enumerable: true }); }; var __copyProps = (to, from, except, desc) => { if (from && typeof from === "object" || typeof from === "function") { for (let key of __getOwnPropNames(from)) if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); } return to; }; var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); // src/index.ts var index_exports = {}; __export(index_exports, { AndVariable: () => AndVariable, BaseLinkedChain: () => BaseLinkedChain, CombinedVariable: () => CombinedVariable, CompoundVariable: () => CompoundVariable, ConstVar: () => ConstantVariable, ConstVariable: () => ConstantVariable, ConstantVariable: () => ConstantVariable, DelegateVariable: () => DelegateVariable, DirectVariable: () => DirectVariable, EventDispatcher: () => EventDispatcher, EventObserver: () => EventObserver, EventObserverStub: () => EventObserverStub, EventSafeDispatcher: () => EventSafeDispatcher, FuncVar: () => FuncVariable, FuncVariable: () => FuncVariable, ImmutableVar: () => ConstantVariable, InvertVariable: () => InvertVariable, LazyEventDispatcher: () => LazyEventDispatcher, LazyVariable: () => FuncVariable, LinkedActionChain: () => LinkedActionChain, LinkedChain: () => LinkedChain, MapVariable: () => MapVariable, MaxVariable: () => MaxVariable, MinVariable: () => MinVariable, MutableVar: () => MutableVariable, MutableVariable: () => MutableVariable, ObservableList: () => ObservableList, OrVariable: () => OrVariable, ReadonlyVar: () => ConstantVariable, SealVariable: () => SealVariable, SumVariable: () => SumVariable, SwitchMapVariable: () => SwitchMapVariable, ThrottledVariable: () => ThrottledVariable, Var: () => Variable, Variable: () => Variable, Vary: () => MutableVariable, and: () => and, arrayEqualityComparer: () => arrayEqualityComparer, combine: () => combine, createConst: () => createConstVar, createConstVar: () => createConstVar, createDelayDispatcher: () => createDelayDispatcher, createDelegate: () => createDelegate, createDelegateVar: () => createDelegate, createDirect: () => createDirect, createDirectVar: () => createDirect, createFuncVar: () => createFuncVar, createLazyVar: () => createFuncVar, createVar: () => createVar, defaultEqualityComparer: () => defaultEqualityComparer, emptyString: () => emptyStringVariable, emptyStringVariable: () => emptyStringVariable, falseVar: () => falseVariable, falseVariable: () => falseVariable, functionEqualityComparer: () => functionEqualityComparer, generalEqualityComparer: () => generalEqualityComparer, isDelegateVariable: () => isDelegateVariable, isMutableVariable: () => isMutableVariable, isVariable: () => isVariable, isVariableOf: () => isVariableOf, max: () => max, merge: () => merge, min: () => min, nullVar: () => nullVariable, nullVariable: () => nullVariable, objectEqualityComparer: () => objectEqualityComparer, or: () => or, setDefaultEqualityComparer: () => setDefaultEqualityComparer, simpleEqualityComparer: () => simpleEqualityComparer, strictEqualityComparer: () => strictEqualityComparer, sum: () => sum, toVariable: () => toVariable, trueVar: () => trueVariable, trueVariable: () => trueVariable, undefinedVar: () => undefinedVariable, undefinedVariable: () => undefinedVariable, zeroVar: () => zeroVariable, zeroVariable: () => zeroVariable }); module.exports = __toCommonJS(index_exports); // src/functions.ts var import_disposiq20 = require("@tioniq/disposiq"); // src/linked-chain.ts var import_disposiq = require("@tioniq/disposiq"); // src/comparer.ts function strictEqualityComparer(a, b) { return a === b; } function simpleEqualityComparer(a, b) { return a == b; } var defaultEqualityComparer = strictEqualityComparer; function setDefaultEqualityComparer(comparer) { defaultEqualityComparer = comparer; } function functionEqualityComparer(a, b) { return a === b; } function generalEqualityComparer(a, b) { if (a === b) { return true; } const typeA = typeof a; const typeB = typeof b; if (typeA !== typeB) { return false; } if (typeA === "object") { return objectEqualityComparer(a, b); } if (typeA === "function") { return functionEqualityComparer(a, b); } return simpleEqualityComparer(a, b); } function objectEqualityComparer(a, b) { if (a === b) { return true; } if (!a || !b) { return false; } const arrayA = Array.isArray(a); const arrayB = Array.isArray(b); if (arrayA !== arrayB) { return false; } if (arrayA) { return arrayEqualityComparer(a, b); } const keysA = Object.keys(a); const keysB = Object.keys(b); if (keysA.length !== keysB.length) { return false; } for (const key of keysA) { if (!keysB.includes(key)) { return false; } const valueA = a[key]; const valueB = b[key]; if (!generalEqualityComparer(valueA, valueB)) { return false; } } return true; } function arrayEqualityComparer(a, b) { if (a === b) { return true; } if (!a || !b) { return false; } if (a.length !== b.length) { return false; } for (let i = 0; i < a.length; ++i) { if (!generalEqualityComparer(a[i], b[i])) { return false; } } return true; } // src/linked-chain.ts var BaseLinkedChain = class { constructor(equalityComparer) { /** * @internal */ this._head = null; /** * @internal */ this._tail = null; /** * @internal */ this._invoking = false; /** * @internal */ this._pendingHead = null; /** * @internal */ this._pendingTail = null; /** * Represents a callback action triggered when a specific condition * or state is determined to be empty. * */ this.onEmpty = null; this._equalityComparer = equalityComparer != null ? equalityComparer : defaultEqualityComparer; } /** * Checks if the chain has any elements */ get hasAny() { return this._head !== null || this._pendingHead !== null; } /** * Checks if the chain is empty */ get empty() { return this._head === null && this._pendingHead === null; } /** * Gets the number of elements in the chain * @remarks This getter should be used only for debugging purposes */ get count() { let count = 0; let node = this._head; if (node !== null) { do { ++count; node = node.next; } while (node !== null); } node = this._pendingHead; if (node !== null) { do { count++; node = node.next; } while (node !== null); } return count; } /** * Converts the chain to an array * @returns an array containing the elements of the chain * @remarks This method should be used only for debugging purposes */ toArray() { const count = this.count; if (count === 0) { return []; } const array = new Array(count); let node = this._head; let index = 0; if (node !== null) { do { array[index++] = node.value; node = node.next; } while (node !== null); } node = this._pendingHead; if (node !== null) { do { array[index++] = node.value; node = node.next; } while (node !== null); } return array; } /** * Adds an element to the chain. If the element is already in the chain, it will not be added again. * @param value the element to add * @returns an array containing the subscription and a boolean value indicating if the element was added */ addUnique(value) { const existing = this._findNode(value); if (existing !== null) { return [existing, false]; } return [this.add(value), true]; } /** * Adds an element to the end of the chain * @param value the element to add * @returns a subscription that can be used to remove the element from the chain */ add(value) { let node; if (this._invoking) { if (this._pendingHead === null) { node = new ChainNode(this, value); this._pendingHead = node; } else { node = new ChainNode(this, value, this._pendingTail, null); this._pendingTail.next = node; } this._pendingTail = node; return node; } if (this._head === null) { node = new ChainNode(this, value); this._head = node; } else { node = new ChainNode(this, value, this._tail, null); this._tail.next = node; } this._tail = node; return node; } /** * Adds an element to the beginning of the chain. If the element is already in the chain, it will not be added again. * @param value the element to add * @returns an array containing the subscription and a boolean value indicating if the element was added */ addToBeginUnique(value) { const existing = this._findNode(value); if (existing !== null) { return [existing, false]; } return [this.addToBegin(value), true]; } /** * Adds an element to the beginning of the chain * @param value the element to add * @returns a subscription that can be used to remove the element from the chain */ addToBegin(value) { let node; if (this._head === null) { node = new ChainNode(this, value); this._head = node; this._tail = node; } else { node = new ChainNode(this, value, null, this._head); this._head.previous = node; this._head = node; } return node; } /** * Adds a node and its children to the end of the chain * @param node * @remarks This method does not check if the node is already in a chain */ addToBeginNode(node) { let chainNode = _clearNode(node); if (chainNode === null) { return; } if (this._head === null) { this._head = chainNode; while (chainNode.next !== null) { chainNode = chainNode.next; } this._tail = chainNode; return; } let tail = chainNode; while (tail.next !== null) { tail = tail.next; } tail.next = this._head; this._head.previous = tail; this._head = chainNode; } /** * Removes an element from the chain * @param value the element to remove * @returns true if the element was removed, false otherwise */ remove(value) { let checkNode = this._head; while (checkNode !== null) { if (this._equalityComparer(checkNode.value, value)) { checkNode.dispose(); return true; } checkNode = checkNode.next; } checkNode = this._pendingHead; while (checkNode !== null) { if (this._equalityComparer(checkNode.value, value)) { checkNode.dispose(); return true; } checkNode = checkNode.next; } return false; } /** * Removes all elements from the chain */ clear() { var _a; let node = this._head; if (node === null && this._pendingHead === null) { return; } if (node !== null) { while (node !== null) { node.disposed = true; node = node.next; } this._head = null; this._tail = null; } node = this._pendingHead; if (node !== null) { while (node !== null) { node.disposed = true; node = node.next; } this._pendingHead = null; this._pendingTail = null; } (_a = this.onEmpty) == null ? void 0 : _a.call(this); } /** * Removes all elements from the chain and returns the head node * @returns the head node of the chain or null if the chain is empty */ removeAll() { var _a; const node = this._head; if (node === null) { return null; } this._head = null; this._tail = null; if (this._pendingHead === null) { (_a = this.onEmpty) == null ? void 0 : _a.call(this); } return node; } /** * @internal */ _findNode(value) { let checkNode = this._head; while (checkNode !== null) { if (this._equalityComparer(checkNode.value, value)) { return checkNode; } checkNode = checkNode.next; } if (this._invoking) { checkNode = this._pendingHead; while (checkNode !== null) { if (this._equalityComparer(checkNode.value, value)) { return checkNode; } checkNode = checkNode.next; } } return null; } }; var LinkedChain = class extends BaseLinkedChain { constructor() { super(...arguments); /** * @internal */ this._actionHead = null; } /** * Iterates over the elements of the chain and invokes the specified action for each element * @param valueHandler the action to invoke for each element */ forEach(valueHandler) { let handler = valueHandler; while (handler !== null) { if (this._head !== null) { if (this._invoking) { if (this._actionHead === null) { this._actionHead = [handler]; return; } this._actionHead.push(handler); return; } this._invoking = true; let node = this._head; while (node !== null) { if (!node.disposed) { handler(node.value); } node = node.next; } this._invoking = false; if (this._pendingHead != null) { if (this._head == null) { this._head = this._pendingHead; } else { this._pendingHead.previous = this._tail; this._tail.next = this._pendingHead; } this._tail = this._pendingTail; this._pendingHead = null; this._pendingTail = null; } } if (this._actionHead === null) { return; } handler = this._actionHead.shift(); if (this._actionHead.length === 0) { this._actionHead = null; } } } }; var LinkedActionChain = class extends BaseLinkedChain { constructor() { super(functionEqualityComparer); /** * @internal */ this._actionHead = null; } /** * Iterates over the elements of the chain and invokes each element * @param value the value to pass to each element */ forEach(value) { let theValue = value; while (true) { if (this._head !== null) { if (this._invoking) { if (this._actionHead === null) { this._actionHead = [theValue]; return; } this._actionHead.push(theValue); return; } this._invoking = true; let node = this._head; while (node !== null) { if (!node.disposed) { node.value(theValue); } node = node.next; } this._invoking = false; if (this._pendingHead != null) { if (this._head == null) { this._head = this._pendingHead; } else { this._pendingHead.previous = this._tail; this._tail.next = this._pendingHead; } this._tail = this._pendingTail; this._pendingHead = null; this._pendingTail = null; } } if (this._actionHead === null) { return; } theValue = this._actionHead.shift(); if (this._actionHead.length === 0) { this._actionHead = null; } } } }; var ChainNode = class extends import_disposiq.Disposiq { constructor(chain, value, previous, next) { super(); this.disposed = false; this.chain = chain; this.value = value; this.previous = previous != null ? previous : null; this.next = next != null ? next : null; } dispose() { var _a, _b; if (this.disposed) { return; } this.disposed = true; const chain = this.chain; if (this === chain._head) { if (this.next === null) { chain._head = null; chain._tail = null; if (chain._pendingHead === null) { (_a = chain.onEmpty) == null ? void 0 : _a.call(chain); } return; } chain._head = this.next; chain._head.previous = null; return; } if (this === chain._tail) { chain._tail = this.previous; chain._tail.next = null; return; } if (this === chain._pendingHead) { if (this.next === null) { chain._pendingHead = null; chain._pendingTail = null; if (chain._head === null) { (_b = chain.onEmpty) == null ? void 0 : _b.call(chain); } return; } chain._pendingHead = this.next; chain._pendingHead.previous = null; return; } if (this === chain._pendingTail) { chain._pendingTail = this.previous; chain._pendingTail.next = null; return; } if (this.previous !== null) { this.previous.next = this.next; } if (this.next !== null) { this.next.previous = this.previous; } } }; function _clearNode(chainNode) { let node = chainNode; let root = null; let tail = null; let next = node; while (next !== null) { node = next; next = node.next; if (node.disposed) { continue; } if (root === null) { root = node; tail = node; node.previous = null; continue; } tail.next = node; node.previous = tail; tail = node; } if (tail !== null) { tail.next = null; } return root; } // src/events/observer.ts var EventObserver = class { }; // src/events/dispatcher.ts var EventDispatcher = class extends EventObserver { constructor() { super(...arguments); /** * @internal */ this._nodes = new LinkedActionChain(); } subscribe(action) { return this._nodes.add(action); } /** * Dispatches the event to all subscribers * @param value the value of the event */ dispatch(value) { this._nodes.forEach(value); } /** * Checks if there are any subscriptions * @returns true if there are any subscriptions, false otherwise */ get hasSubscriptions() { return this._nodes.hasAny; } }; // src/events/functions.ts var import_disposiq3 = require("@tioniq/disposiq"); // src/events/lazy.ts var import_disposiq2 = require("@tioniq/disposiq"); var LazyEventDispatcher = class extends EventObserver { constructor(activator) { super(); /** * @internal */ this._nodes = new LinkedActionChain(); /** * @internal */ this._subscription = new import_disposiq2.DisposableContainer(); this._activator = activator; this._nodes.onEmpty = () => this._deactivate(); } /** * Checks if there are any subscriptions * @returns true if there are any subscriptions, false otherwise */ get hasSubscription() { return this._nodes.hasAny; } subscribe(callback) { let subscription; if (this._nodes.empty) { subscription = this._nodes.add(callback); this._activate(); } else { subscription = this._nodes.add(callback); } return subscription; } /** * Dispatches the event to all subscribers * @param value the value of the event */ dispatch(value) { this._nodes.forEach(value); } /** * @internal */ _activate() { this._subscription.disposeCurrent(); this._subscription.set((0, import_disposiq2.toDisposable)(this._activator(this))); } /** * @internal */ _deactivate() { this._subscription.disposeCurrent(); } }; // src/events/functions.ts function merge(...observers) { return new LazyEventDispatcher((dispatcher) => { const disposableStore = new import_disposiq3.DisposableStore(); for (const t of observers) { disposableStore.add(t.subscribe((v) => dispatcher.dispatch(v))); } return disposableStore; }); } // src/events/safe-dispatcher.ts var EventSafeDispatcher = class extends EventObserver { constructor() { super(...arguments); /** * @internal */ this._nodes = new LinkedChain(functionEqualityComparer); } subscribe(action) { return this._nodes.add(action); } /** * Dispatches the event to all subscribers safely * @param value the value of the event * @param onError error callback */ dispatch(value, onError) { this._nodes.forEach((a) => { try { a(value); } catch (e) { onError == null ? void 0 : onError(e); } }); } /** * Checks if there are any subscriptions * @returns true if there are any subscriptions, false otherwise */ get hasSubscriptions() { return this._nodes.hasAny; } }; // src/events/stub.ts var import_disposiq4 = require("@tioniq/disposiq"); var EventObserverStub = class extends EventObserver { subscribe() { return import_disposiq4.emptyDisposable; } }; // src/events/extensions.ts var import_disposiq5 = require("@tioniq/disposiq"); // src/noop.ts var noop = Object.freeze(() => { }); // src/events/extensions.ts EventObserver.prototype.subscribeOnce = function(callback) { const subscription = new import_disposiq5.DisposableContainer(); subscription.set( this.subscribe((value) => { subscription.dispose(); callback(value); }) ); return subscription; }; EventObserver.prototype.subscribeOnceWhere = function(callback, condition) { const subscription = new import_disposiq5.DisposableContainer(); subscription.set( this.subscribe((value) => { if (!condition(value)) { return; } subscription.dispose(); callback(value); }) ); return subscription; }; EventObserver.prototype.subscribeWhere = function(callback, condition) { return this.subscribe((value) => { if (condition(value)) { callback(value); } }); }; EventObserver.prototype.subscribeOn = function(callback, condition) { return condition.subscribeDisposable( (value) => value ? this.subscribe(callback) : import_disposiq5.emptyDisposable ); }; EventObserver.prototype.subscribeDisposable = function(callback) { const container = new import_disposiq5.DisposableContainer(); const subscription = this.subscribe((v) => { container.disposeCurrent(); container.set(callback(v)); }); return new import_disposiq5.DisposableAction(() => { subscription.dispose(); container.dispose(); }); }; EventObserver.prototype.map = function(mapper) { return new LazyEventDispatcher( (dispatcher) => this.subscribe((value) => dispatcher.dispatch(mapper(value))) ); }; EventObserver.prototype.where = function(condition) { return new LazyEventDispatcher( (dispatcher) => this.subscribe((value) => { if (condition(value)) { dispatcher.dispatch(value); } }) ); }; EventObserver.prototype.awaited = function(onRejection) { if (typeof onRejection === "function") { return new LazyEventDispatcher( (dispatcher) => this.subscribe((value) => { if (value instanceof Promise) { value.then( (v) => { dispatcher.dispatch(v); }, (e) => { onRejection(e, value); } ); } else { dispatcher.dispatch(value); } }) ); } return new LazyEventDispatcher( (dispatcher) => this.subscribe((value) => { if (value instanceof Promise) { value.then((v) => { dispatcher.dispatch(v); }, noop); } else { dispatcher.dispatch(value); } }) ); }; EventDispatcher.prototype.dispatchSafe = function(value) { try { this.dispatch(value); } catch (_e) { } }; // src/variable.ts var Variable = class { /** * Overload of the `toString` method. Returns the string representation of the value of the variable * @returns the string representation of the value of the variable */ toString() { const _value = this.value; if (_value === null || _value === void 0) { return `${_value}`; } return _value.toString(); } /** * Overload of the `valueOf` method. Converts the variable to a primitive value, in this case, the value of the variable * @returns the primitive value of the variable */ valueOf() { return this.value; } }; // src/vars/and.ts var import_disposiq6 = require("@tioniq/disposiq"); // src/vars/compound.ts var CompoundVariable = class extends Variable { constructor(initValue, equalityComparer) { super(); /** * @internal */ this._chain = new LinkedActionChain(); this._value = initValue; this._equalityComparer = equalityComparer != null ? equalityComparer : defaultEqualityComparer; this._chain.onEmpty = () => this.deactivate(); } /** * Checks if there are any subscriptions * @returns true if there are any subscriptions, false otherwise */ // TODO: should return true during `subscribe` get active() { return this._chain.hasAny; } get value() { if (this._chain.hasAny) { return this._value; } return this.getExactValue(); } /** * Sets the value of the variable. If the value is the same as the current value, the method will do nothing * @param value the new value of the variable * @protected internal use only */ set value(value) { if (this._equalityComparer(value, this._value)) { return; } this._value = value; this._chain.forEach(value); } subscribe(callback) { if (this._chain.empty) { this.activate(); } const [disposable, added] = this._chain.addUnique(callback); if (added) { callback(this._value); } return disposable; } subscribeSilent(callback) { if (this._chain.empty) { this.activate(); } return this._chain.addUnique(callback)[0]; } /** * A method for getting the exact value of the variable. It is called when there are no subscriptions * @protected internal use only * @returns the default behavior is to return the current (last) value of the variable * @remarks this method should be implemented in the derived class */ getExactValue() { return this._value; } /** * A method for setting the value of the variable without notifying subscribers * @protected internal use only * @param value the new value of the variable * @deprecated user `setSilent` instead */ setValueSilent(value) { this._value = value; } /** * A method for setting the value of the variable and notifying subscribers without checking the equality * @protected internal use only * @param value the new value of the variable * @deprecated user `setForce` instead */ setValueForce(value) { this._value = value; this._chain.forEach(value); } /** * A method for setting the value of the variable without notifying subscribers * @protected internal use only * @param value the new value of the variable */ setSilent(value) { this._value = value; } /** * A method for setting the value of the variable and notifying subscribers without checking the equality * @protected internal use only * @param value the new value of the variable */ setForce(value) { this._value = value; this._chain.forEach(value); } /** * A method for notifying subscribers about the value change * @protected internal use only */ notify() { const value = this._value; this._chain.forEach(value); } }; // src/vars/and.ts var AndVariable = class extends CompoundVariable { constructor(variables) { super(false); /** * @internal */ this._subscriptions = []; this._variables = variables; } activate() { this._listen(0); } deactivate() { (0, import_disposiq6.disposeAll)(this._subscriptions); } getExactValue() { const variables = this._variables; for (let i = 0; i < variables.length; ++i) { if (!variables[i].value) { return false; } } return true; } /** * @internal */ _listen(index) { if (index >= this._variables.length) { this.value = true; return; } if (this._subscriptions.length > index) { return; } const __listener = (value) => { if (value) { this._listen(index + 1); } else { this._unsubscribeFrom(index + 1); this.value = false; } }; const variable = this._variables[index]; this._subscriptions.push(variable.subscribeSilent(__listener)); __listener(variable.value); return; } /** * @internal */ _unsubscribeFrom(index) { var _a; while (index < this._subscriptions.length) { (_a = this._subscriptions.pop()) == null ? void 0 : _a.dispose(); } } }; // src/vars/combined.ts var import_disposiq7 = require("@tioniq/disposiq"); var CombinedVariable = class extends CompoundVariable { constructor(vars) { if (!(vars == null ? void 0 : vars.length)) { throw new Error("No variables provided"); } super(stubArray, arrayEqualityComparer); /** * @internal */ this._subscriptions = new import_disposiq7.DisposableStore(); this._vars = vars.slice(); } activate() { this._subscriptions.disposeCurrent(); const length = this._vars.length; const result = new Array(length); for (let i = 0; i < length; ++i) { const vary = this._vars[i]; this._subscriptions.add( vary.subscribeSilent((value) => { result[i] = value; this.setForce(result); }) ); result[i] = vary.value; } this.setForce(result); } deactivate() { this._subscriptions.disposeCurrent(); } getExactValue() { const length = this._vars.length; const result = new Array(length); for (let i = 0; i < length; ++i) { result[i] = this._vars[i].value; } return result; } }; var stubArray = Object.freeze([]); // src/vars/constant.ts var import_disposiq8 = require("@tioniq/disposiq"); var ConstantVariable = class extends Variable { constructor(value, equalityComparer) { super(); this._value = value; this._equalityComparer = equalityComparer != null ? equalityComparer : defaultEqualityComparer; } get value() { return this._value; } get equalityComparer() { return this._equalityComparer; } subscribe(callback) { callback(this._value); return import_disposiq8.emptyDisposable; } subscribeSilent(_) { return import_disposiq8.emptyDisposable; } }; // src/vars/delegate.ts var import_disposiq9 = require("@tioniq/disposiq"); var DelegateVariable = class extends CompoundVariable { constructor(sourceOrDefaultValue) { super( sourceOrDefaultValue instanceof Variable ? ( // biome-ignore lint/style/noNonNullAssertion: base value will not be used null ) : sourceOrDefaultValue != void 0 ? sourceOrDefaultValue : ( // biome-ignore lint/style/noNonNullAssertion: base value will not be used null ) ); /** * @internal */ this._sourceSubscription = new import_disposiq9.DisposableContainer(); if (sourceOrDefaultValue instanceof Variable) { this._source = sourceOrDefaultValue; } else { this._source = null; } } /** * Sets the source variable. The source variable will be used to get the value for the delegate variable * @param source the source variable or null to remove the source * @returns a disposable that will remove the source when disposed */ setSource(source) { if (!source) { if (this._source) { this.value = this._source.value; this._source = null; } this._sourceSubscription.disposeCurrent(); return import_disposiq9.emptyDisposable; } this._source = source; this._sourceSubscription.disposeCurrent(); if (this.active) { this._sourceSubscription.set( source.subscribeSilent((v) => this.setForce(v)) ); this.value = source.value; } return new import_disposiq9.DisposableAction(() => { if (this._source !== source) { return; } this.setSource(null); }); } activate() { if (this._source === null) { return; } this._sourceSubscription.disposeCurrent(); this._sourceSubscription.set( this._source.subscribeSilent((v) => this.setForce(v)) ); this.value = this._source.value; } deactivate() { if (this._source === null) { return; } this._sourceSubscription.disposeCurrent(); } getExactValue() { return this._source !== null ? this._source.value : super.getExactValue(); } }; // src/vars/direct.ts var DirectVariable = class extends Variable { constructor(initialValue, equalityComparer) { super(); /** * @internal */ this._chain = new LinkedActionChain(); this._value = initialValue; this._equalityComparer = equalityComparer != null ? equalityComparer : defaultEqualityComparer; } get value() { return this._value; } /** * Sets the value of the variable and notifies all subscribers without checking the equality * @param value the new value for the variable */ set value(value) { this._value = value; this._chain.forEach(value); } get equalityComparer() { return this._equalityComparer; } subscribe(callback) { const [disposable, added] = this._chain.addUnique(callback); if (added) { callback(this._value); } return disposable; } subscribeSilent(callback) { return this._chain.addUnique(callback)[0]; } /** * Sets the value of the variable without notifying the subscribers * @param value the new value for the variable * @remarks Use this method only if you are sure what you are doing. Combine this method with the `notify` method */ setSilent(value) { this._value = value; } /** * Notifies all subscribers about the change of the value forcibly * @remarks Use this method only if you are sure what you are doing. Combine this method with the `setSilent` method */ notify() { this._chain.forEach(this._value); } }; // src/vars/func.ts var import_disposiq10 = require("@tioniq/disposiq"); var FuncVariable = class extends CompoundVariable { constructor(activate, exactValue, equalityComparer) { super(null, equalityComparer); const disposable = new import_disposiq10.DisposableContainer(); this._activator = (self) => { disposable.disposeCurrent(); disposable.set((0, import_disposiq10.toDisposable)(activate(self))); }; this._deactivator = () => { disposable.disposeCurrent(); }; this._exactValue = exactValue; } get value() { return super.value; } /** * Sets the value of the variable. If the value is the same as the current value, the method will do nothing * @param value the new value of the variable */ set value(value) { super.value = value; } setValueForce(value) { super.setForce(value); } setValueSilent(value) { super.setSilent(value); } /** * A method for setting the value of the variable and notifying subscribers without checking the equality * @param value the new value of the variable */ setForce(value) { super.setForce(value); } /** * A method for setting the value of the variable without notifying subscribers * @param value the new value of the variable */ setSilent(value) { super.setSilent(value); } /** * A method for notifying subscribers about the value change */ notify() { super.notify(); } activate() { this._activator(this); } deactivate() { this._deactivator(this); } getExactValue() { return this._exactValue(); } }; // src/vars/invert.ts var import_disposiq11 = require("@tioniq/disposiq"); var InvertVariable = class extends Variable { constructor(variable) { super(); /** * @internal */ this._chain = new LinkedActionChain(); /** * @internal */ this._value = false; /** * @internal */ this._subscription = new import_disposiq11.DisposableContainer(); this._variable = variable; this._chain.onEmpty = () => this._deactivate(); } get value() { if (this._chain.hasAny) { return this._value; } return !this._variable.value; } subscribe(callback) { if (this._chain.empty) { this._activate(); } const [disposable, added] = this._chain.addUnique(callback); if (added) { callback(this._value); } return disposable; } subscribeSilent(callback) { return this._variable.subscribeSilent((value) => callback(!value)); } /** * @internal */ _activate() { this._subscription.disposeCurrent(); this._subscription.set( this._variable.subscribeSilent((v) => { const value = !v; this._value = value; this._chain.forEach(value); }) ); this._value = !this._variable.value; } /** * @internal */ _deactivate() { this._subscription.disposeCurrent(); } }; // src/vars/map.ts var import_disposiq12 = require("@tioniq/disposiq"); var MapVariable = class extends CompoundVariable { constructor(variable, mapper, equalityComparer) { super(null, equalityComparer); /** * @internal */ this._subscription = new import_disposiq12.DisposableContainer(); /** * @internal */ this._listener = (value) => { this.value = this._mapper(value); }; this._variable = variable; this._mapper = mapper; } activate() { this._subscription.disposeCurrent(); this._subscription.set(this._variable.subscribeSilent(this._listener)); this._listener(this._variable.value); } deactivate() { this._subscription.disposeCurrent(); } getExactValue() { return this._mapper(this._variable.value); } }; // src/vars/max.ts var import_disposiq13 = require("@tioniq/disposiq"); var MaxVariable = class extends CompoundVariable { constructor(vars) { super(0); /** * @internal */ this._subscriptions = new import_disposiq13.DisposableStore(); this._vars = vars.slice(); } activate() { const vars = this._vars; const length = vars.length; const subscriptions = this._subscriptions; subscriptions.disposeCurrent(); for (let i = 0; i < length; ++i) { subscriptions.add( vars[i].subscribeSilent(() => { this.postValue(); }) ); } this.postValue(); } deactivate() { this._subscriptions.dispose(); } getExactValue() { const vars = this._vars; const length = vars.length; let result = Number.NEGATIVE_INFINITY; for (let i = 0; i < length; ++i) { result = Math.max(result, vars[i].value); } return result; } postValue() { const vars = this._vars; const length = vars.length; let result = Number.NEGATIVE_INFINITY; for (let i = 0; i < length; ++i) { result = Math.max(result, vars[i].value); } this.value = result; } }; // src/vars/min.ts var import_disposiq14 = require("@tioniq/disposiq"); var MinVariable = class extends CompoundVariable { constructor(vars) { super(0); /** * @internal */ this._subscriptions = new import_disposiq14.DisposableStore(); this._vars = vars.slice(); } activate() { const vars = this._vars; const length = vars.length; const subscriptions = this._subscriptions; subscriptions.disposeCurrent(); for (let i = 0; i < length; ++i) { subscriptions.add( vars[i].subscribeSilent(() => { this.postValue(); }) ); } this.postValue(); } deactivate() { this._subscriptions.dispose(); } getExactValue() { const vars = this._vars; const length = vars.length; let result = Number.POSITIVE_INFINITY; for (let i = 0; i < length; ++i) { result = Math.min(result, vars[i].value); } return result; } postValue() { const vars = this._vars; const length = vars.length; let result = Number.POSITIVE_INFINITY; for (let i = 0; i < length; ++i) { result = Math.min(result, vars[i].value); } this.value = result; } }; // src/vars/mutable.ts var MutableVariable = class extends Variable { constructor(value, equalityComparer) { super(); /** * @internal */ this._chain = new LinkedActionChain(); this._value = value; this._equalityComparer = equalityComparer != null ? equalityComparer : defaultEqualityComparer; } get value() { return this._value; } /** * Sets the value of the variable. The value will be changed only if the new value is different from the old value * @param value the new value for the variable */ set value(value) { if (this._equalityComparer(value, this._value)) { return; } this._value = value; this._chain.forEach(value); } /** * Returns the equality comparer used to compare the old and new values of the variable */ get equalityComparer() { return this._equalityComparer; } subscribe(callback) { const [disposable, added] = this._chain.addUnique(callback); if (added) { callback(this._value); } return disposable; } subscribeSilent(callback) { return this._chain.addUnique(callback)[0]; } /** * Sets the value of the variable without notifying the subscribers * @param value the new value for the variable * @remarks Use this method only if you are sure what you are doing. Combine this method with the `notify` method */ setSilent(value) { this._value = value; } /** * Notifies all subscribers about the change of the value forcibly * @remarks Use this method only if you are sure what you are doing. Combine this method with the `setSilent` method */ notify() { this._chain.forEach(this._value); } }; // src/vars/or.ts var import_disposiq15 = require("@tioniq/disposiq"); var OrVariable = class extends CompoundVariable { constructor(variables) { super(false); /** * @internal */ this._subscriptions = []; this._variables = variables; } activate() { this._listen(0); } deactivate() { (0, import_disposiq15.disposeAll)(this._subscriptions); } getExactValue() { const variables = this._variables; for (let i = 0; i < variables.length; ++i) { if (variables[i].value) { return true; } } return false; } /** * @internal */ _listen(index) { if (index >= this._variables.length) { this.value = false; return; } if (this._subscriptions.length > index) { return; } const __listener = (value) => { if (value) { this._unsubscribeFrom(index + 1); this.value = true; } else { this._listen(index + 1); } }; const variable = this._variables[index]; this._subscriptions.push(variable.subscribeSilent(__listener)); __listener(variable.value); return; } /** * @internal */ _unsubscribeFrom(index) { var _a; while (index < this._subscriptions.length) { (_a = this._subscriptions.pop()) == null ? void 0 : _a.dispose(); } } }; // src/vars/seal.ts var import_disposiq16 = require("@tioniq/disposiq"); var SealVariable = class extends Variable { constructor(vary, equalityComparer) { super(); /** * @internal */ this._chain = new LinkedActionChain(); /** * @internal */ this._varSubscription = new import_disposiq16.DisposableContainer(); /** * @internal */ // biome-ignore lint/style/noNonNullAssertion: the field access is safe because it used only in the sealed state this._value = null; /** * @internal */ this._sealed = false; this._var = vary; this._equalityComparer = typeof equalityComparer === "function" ? equalityComparer : defaultEqualityComparer; this._chain.onEmpty = () => { if (!this._sealed) { this._deactivate(); } }; } get value() { if (this._sealed) { return this._value; } if (this._chain.empty) { return this._var.value; } return this._value; } get equalityComparer() { return this._equalityComparer; } subscribe(callback) { if (this._sealed) { callback(this._value); return import_disposiq16.emptyDisposable; } if (this._chain.empty) { this._activate(); } const [disposable, added] = this._chain.addUnique(callback); if (added) { callback(this._value); } return disposable; } subscribeSilent(callback) { if (this._sealed) { return import_disposiq16.emptyDisposable; } if (this._chain.empty) { this._activate(); } return this._chain.addUnique(callback)[0]; } /** * Seals the variable. If the variable is already sealed, the method will do nothing * @param valueToSeal the value to seal. If the value is not provided, the current value of the variable will be * sealed * @returns true if the variable was sealed, false if the variable was already sealed */ seal(valueToSeal) { if (this._sealed) { return false; } this._sealed = true; this._varSubscription.dispose(); if (arguments.length === 0) { const currentValue = this._chain.empty ? this._var.value : this._value; this._varSubscription.dispose(); this._sealValue(currentValue); return true; } this._varSubscription.dispose(); this._sealValue(valueToSeal); return true; } /** * @internal */ _activate() { this._varSubscription.disposeCurrent(); this._varSubscription.set( this._var.subscribeSilent((v) => { this._value = v; this._chain.forEach(v); }) ); this._value = this._var.value; } /** * @internal */ _deactivate() { this._varSubscription.disposeCurrent(); } /** * @internal */ _sealValue(value) { if (this._equalityComparer(value, this._value)) { this._chain.clear(); return; } this._value = value; this._chain.forEach(value); this._chain.clear(); } }; // src/vars/sum.ts var import_disposiq17 = require("@tioniq/disposiq"); var SumVariable = class extends CompoundVariable { constructor(vars) { super(0); /** * @internal */ this._subscriptions = new import_disposiq17.DisposableStore(); this._vars = vars.slice(); } activate() { const vars = this._vars; const length = vars.length; const subscriptions = this._subscriptions; subscriptions.disposeCurrent(); for (let i = 0; i < length; ++i) { const variable = vars[i]; subscriptions.add( variable.subscribeSilent(() => { this.postValue(); }) ); } this.postValue(); } deactivate() { this._subscriptions.dispose(); } getExactValue() { const vars = this._vars; const length = vars.length; let result = 0; for (let i = 0; i < length; ++i) { result += vars[i].value; } return result; } postValue() { const vars = this._vars; const length = vars.length; let result = 0; for (let i = 0; i < length; ++i) { result += vars[i].value; } this.value = result; } }; // src/vars/switch-map.ts var import_disposiq18 = require("@tioniq/disposiq"); var SwitchMapVariable = class extends CompoundVariable { constructor(vary, mapper, equalityComparer) { super(null, equalityComparer); /** * @internal */ this._switchSubscription = new import_disposiq18.DisposableContainer(); /** * @internal */ this._varSubscription = new import_disposiq18.DisposableContainer(); this._var = vary; this._mapper = mapper; } activate() { this._switchSubscription.disposeCurrent(); this._switchSubscription.set( this._var.subscribeSilent((i) => this._handleSwitch(i)) ); this._handleSwitch(this._var.value); } deactivate() { this._switchSubscription.disposeCurrent(); this._varSubscription.disposeCurrent(); } getExactValue() { return this._mapper(this._var.value).value; } /** * @internal */ _handleSwitch(input) { this._varSubscription.disposeCurrent(); const mappedVariable = this._mapper(input); this._varSubscription.set( mappedVariable.subscribeSilent((result) => { this.value = result; }) ); this.value = mappedVariable.value; } }; // src/vars/throttled.ts var import_disposiq19 = require("@tioniq/disposiq"); var noScheduledValue = Object.freeze({}); var ThrottledVariable = class extends CompoundVariable { constructor(vary, onUpdate, equalityComparer) { super(null, equalityComparer); /** * @internal */ this._subscription = new import_disposiq19.DisposableContainer(); /** * @internal */ this._updateSubscription = new import_disposiq19.DisposableContainer(); /** * @internal */ this._scheduledValue = noScheduledValue; this._var = vary; this._onUpdate = onUpdate; } activate() { this._subscription.disposeCurrent(); this._subscription.set( this._var.subscribeSilent((v) => { this._scheduleUpdate(v); }) ); this.value = this._var.value; } deactivate() { this._subscription.disposeCurrent(); this._updateSubscription.disposeCurrent(); } getExactValue() { return this._var.value; } /** * @internal */ _scheduleUpdate(value) { if (thi