mono/packages/core/dist/observableInternal/base.d.ts
2025-01-28 13:42:22 +01:00

258 lines
11 KiB
TypeScript

import { DebugNameData, DebugOwner } from './debugName.js';
import { DisposableStore, EqualityComparer, IDisposable } from './commonFacade/deps.js';
import { keepObserved, recomputeInitiallyAndOnChange } from './utils.js';
/**
* Represents an observable value.
*
* @template T The type of the values the observable can hold.
*/
export interface IObservable<T> extends IObservableWithChange<T, unknown> {
}
/**
* Represents an observable value.
*
* @template T The type of the values the observable can hold.
* @template TChange The type used to describe value changes
* (usually `void` and only used in advanced scenarios).
* While observers can miss temporary values of an observable,
* they will receive all change values (as long as they are subscribed)!
*/
export interface IObservableWithChange<T, TChange = unknown> {
/**
* Returns the current value.
*
* Calls {@link IObserver.handleChange} if the observable notices that the value changed.
* Must not be called from {@link IObserver.handleChange}!
*/
get(): T;
/**
* Forces the observable to check for changes and report them.
*
* Has the same effect as calling {@link IObservable.get}, but does not force the observable
* to actually construct the value, e.g. if change deltas are used.
* Calls {@link IObserver.handleChange} if the observable notices that the value changed.
* Must not be called from {@link IObserver.handleChange}!
*/
reportChanges(): void;
/**
* Adds the observer to the set of subscribed observers.
* This method is idempotent.
*/
addObserver(observer: IObserver): void;
/**
* Removes the observer from the set of subscribed observers.
* This method is idempotent.
*/
removeObserver(observer: IObserver): void;
/**
* Reads the current value and subscribes the reader to this observable.
*
* Calls {@link IReader.readObservable} if a reader is given, otherwise {@link IObservable.get}
* (see {@link ConvenientObservable.read} for the implementation).
*/
read(reader: IReader | undefined): T;
/**
* Creates a derived observable that depends on this observable.
* Use the reader to read other observables
* (see {@link ConvenientObservable.map} for the implementation).
*/
map<TNew>(fn: (value: T, reader: IReader) => TNew): IObservable<TNew>;
map<TNew>(owner: object, fn: (value: T, reader: IReader) => TNew): IObservable<TNew>;
flatten<TNew>(this: IObservable<IObservable<TNew>>): IObservable<TNew>;
/**
* ONLY FOR DEBUGGING!
* Logs computations of this derived.
*/
log(): IObservableWithChange<T, TChange>;
/**
* Makes sure this value is computed eagerly.
*/
recomputeInitiallyAndOnChange(store: DisposableStore, handleValue?: (value: T) => void): IObservable<T>;
/**
* Makes sure this value is cached.
*/
keepObserved(store: DisposableStore): IObservable<T>;
/**
* A human-readable name for debugging purposes.
*/
readonly debugName: string;
/**
* This property captures the type of the change object. Do not use it at runtime!
*/
readonly TChange: TChange;
}
export interface IReader {
/**
* Reads the value of an observable and subscribes to it.
*/
readObservable<T>(observable: IObservableWithChange<T, any>): T;
}
/**
* Represents an observer that can be subscribed to an observable.
*
* If an observer is subscribed to an observable and that observable didn't signal
* a change through one of the observer methods, the observer can assume that the
* observable didn't change.
* If an observable reported a possible change, {@link IObservable.reportChanges} forces
* the observable to report an actual change if there was one.
*/
export interface IObserver {
/**
* Signals that the given observable might have changed and a transaction potentially modifying that observable started.
* Before the given observable can call this method again, is must call {@link IObserver.endUpdate}.
*
* Implementations must not get/read the value of other observables, as they might not have received this event yet!
* The method {@link IObservable.reportChanges} can be used to force the observable to report the changes.
*/
beginUpdate<T>(observable: IObservable<T>): void;
/**
* Signals that the transaction that potentially modified the given observable ended.
* This is a good place to react to (potential) changes.
*/
endUpdate<T>(observable: IObservable<T>): void;
/**
* Signals that the given observable might have changed.
* The method {@link IObservable.reportChanges} can be used to force the observable to report the changes.
*
* Implementations must not get/read the value of other observables, as they might not have received this event yet!
* The change should be processed lazily or in {@link IObserver.endUpdate}.
*/
handlePossibleChange<T>(observable: IObservable<T>): void;
/**
* Signals that the given {@link observable} changed.
*
* Implementations must not get/read the value of other observables, as they might not have received this event yet!
* The change should be processed lazily or in {@link IObserver.endUpdate}.
*
* @param change Indicates how or why the value changed.
*/
handleChange<T, TChange>(observable: IObservableWithChange<T, TChange>, change: TChange): void;
}
export interface ISettable<T, TChange = void> {
/**
* Sets the value of the observable.
* Use a transaction to batch multiple changes (with a transaction, observers only react at the end of the transaction).
*
* @param transaction When given, value changes are handled on demand or when the transaction ends.
* @param change Describes how or why the value changed.
*/
set(value: T, transaction: ITransaction | undefined, change: TChange): void;
}
export interface ITransaction {
/**
* Calls {@link Observer.beginUpdate} immediately
* and {@link Observer.endUpdate} when the transaction ends.
*/
updateObserver(observer: IObserver, observable: IObservableWithChange<any, any>): void;
}
declare let _recomputeInitiallyAndOnChange: typeof recomputeInitiallyAndOnChange;
export declare function _setRecomputeInitiallyAndOnChange(recomputeInitiallyAndOnChange: typeof _recomputeInitiallyAndOnChange): void;
declare let _keepObserved: typeof keepObserved;
export declare function _setKeepObserved(keepObserved: typeof _keepObserved): void;
export declare abstract class ConvenientObservable<T, TChange> implements IObservableWithChange<T, TChange> {
get TChange(): TChange;
abstract get(): T;
reportChanges(): void;
abstract addObserver(observer: IObserver): void;
abstract removeObserver(observer: IObserver): void;
/** @sealed */
read(reader: IReader | undefined): T;
/** @sealed */
map<TNew>(fn: (value: T, reader: IReader) => TNew): IObservable<TNew>;
map<TNew>(owner: DebugOwner, fn: (value: T, reader: IReader) => TNew): IObservable<TNew>;
log(): IObservableWithChange<T, TChange>;
/**
* @sealed
* Converts an observable of an observable value into a direct observable of the value.
*/
flatten<TNew>(this: IObservable<IObservableWithChange<TNew, any>>): IObservable<TNew>;
recomputeInitiallyAndOnChange(store: DisposableStore, handleValue?: (value: T) => void): IObservable<T>;
/**
* Ensures that this observable is observed. This keeps the cache alive.
* However, in case of deriveds, it does not force eager evaluation (only when the value is read/get).
* Use `recomputeInitiallyAndOnChange` for eager evaluation.
*/
keepObserved(store: DisposableStore): IObservable<T>;
abstract get debugName(): string;
protected get debugValue(): T;
}
export declare abstract class BaseObservable<T, TChange = void> extends ConvenientObservable<T, TChange> {
protected readonly observers: Set<IObserver>;
addObserver(observer: IObserver): void;
removeObserver(observer: IObserver): void;
protected onFirstObserverAdded(): void;
protected onLastObserverRemoved(): void;
}
/**
* Starts a transaction in which many observables can be changed at once.
* {@link fn} should start with a JS Doc using `@description` to give the transaction a debug name.
* Reaction run on demand or when the transaction ends.
*/
export declare function transaction(fn: (tx: ITransaction) => void, getDebugName?: () => string): void;
export declare function globalTransaction(fn: (tx: ITransaction) => void): void;
export declare function asyncTransaction(fn: (tx: ITransaction) => Promise<void>, getDebugName?: () => string): Promise<void>;
/**
* Allows to chain transactions.
*/
export declare function subtransaction(tx: ITransaction | undefined, fn: (tx: ITransaction) => void, getDebugName?: () => string): void;
export declare class TransactionImpl implements ITransaction {
readonly _fn: Function;
private readonly _getDebugName?;
private updatingObservers;
constructor(_fn: Function, _getDebugName?: () => string);
getDebugName(): string | undefined;
updateObserver(observer: IObserver, observable: IObservable<any>): void;
finish(): void;
}
/**
* A settable observable.
*/
export interface ISettableObservable<T, TChange = void> extends IObservableWithChange<T, TChange>, ISettable<T, TChange> {
}
/**
* Creates an observable value.
* Observers get informed when the value changes.
* @template TChange An arbitrary type to describe how or why the value changed. Defaults to `void`.
* Observers will receive every single change value.
*/
export declare function observableValue<T, TChange = void>(name: string, initialValue: T): ISettableObservable<T, TChange>;
export declare function observableValue<T, TChange = void>(owner: object, initialValue: T): ISettableObservable<T, TChange>;
export declare class ObservableValue<T, TChange = void> extends BaseObservable<T, TChange> implements ISettableObservable<T, TChange> {
private readonly _debugNameData;
private readonly _equalityComparator;
protected _value: T;
get debugName(): string;
constructor(_debugNameData: DebugNameData, initialValue: T, _equalityComparator: EqualityComparer<T>);
get(): T;
set(value: T, tx: ITransaction | undefined, change: TChange): void;
toString(): string;
protected _setValue(newValue: T): void;
}
/**
* A disposable observable. When disposed, its value is also disposed.
* When a new value is set, the previous value is disposed.
*/
export declare function disposableObservableValue<T extends IDisposable | undefined, TChange = void>(nameOrOwner: string | object, initialValue: T): ISettableObservable<T, TChange> & IDisposable;
export declare class DisposableObservableValue<T extends IDisposable | undefined, TChange = void> extends ObservableValue<T, TChange> implements IDisposable {
protected _setValue(newValue: T): void;
dispose(): void;
}
export interface IChangeTracker {
/**
* Returns if this change should cause an invalidation.
* Implementations can record changes.
*/
handleChange(context: IChangeContext): boolean;
}
export interface IChangeContext {
readonly changedObservable: IObservableWithChange<any, any>;
readonly change: unknown;
/**
* Returns if the given observable caused the change.
*/
didChange<T, TChange>(observable: IObservableWithChange<T, TChange>): this is {
change: TChange;
};
}
export {};