@tldraw/state
Version:
tldraw infinite canvas SDK (state).
995 lines (957 loc) • 32.8 kB
TypeScript
/* Excluded from this release type: ArraySet */
/**
* An Atom is a signal that can be updated directly by calling {@link Atom.set} or {@link Atom.update}.
*
* Atoms are created using the {@link atom} function.
*
* @example
* ```ts
* const name = atom('name', 'John')
*
* print(name.get()) // 'John'
* ```
*
* @public
*/
export declare interface Atom<Value, Diff = unknown> extends Signal<Value, Diff> {
/**
* Sets the value of this atom to the given value. If the value is the same as the current value, this is a no-op.
*
* @param value - The new value to set.
* @param diff - The diff to use for the update. If not provided, the diff will be computed using {@link AtomOptions.computeDiff}.
*/
set(value: Value, diff?: Diff): Value;
/**
* Updates the value of this atom using the given updater function. If the returned value is the same as the current value, this is a no-op.
*
* @param updater - A function that takes the current value and returns the new value.
*/
update(updater: (value: Value) => Value): Value;
}
/**
* Creates a new {@link Atom}.
*
* An Atom is a signal that can be updated directly by calling {@link Atom.set} or {@link Atom.update}.
*
* @example
* ```ts
* const name = atom('name', 'John')
*
* name.get() // 'John'
*
* name.set('Jane')
*
* name.get() // 'Jane'
* ```
*
* @public
*/
export declare function atom<Value, Diff = unknown>(
/**
* A name for the signal. This is used for debugging and profiling purposes, it does not need to be unique.
*/
name: string,
/**
* The initial value of the signal.
*/
initialValue: Value,
/**
* The options to configure the atom. See {@link AtomOptions}.
*/
options?: AtomOptions<Value, Diff>): Atom<Value, Diff>;
/**
* The options to configure an atom, passed into the {@link atom} function.
* @public
*/
export declare interface AtomOptions<Value, Diff> {
/**
* The maximum number of diffs to keep in the history buffer.
*
* If you don't need to compute diffs, or if you will supply diffs manually via {@link Atom.set}, you can leave this as `undefined` and no history buffer will be created.
*
* If you expect the value to be part of an active effect subscription all the time, and to not change multiple times inside of a single transaction, you can set this to a relatively low number (e.g. 10).
*
* Otherwise, set this to a higher number based on your usage pattern and memory constraints.
*
*/
historyLength?: number;
/**
* A method used to compute a diff between the atom's old and new values. If provided, it will not be used unless you also specify {@link AtomOptions.historyLength}.
*/
computeDiff?: ComputeDiff<Value, Diff>;
/**
* If provided, this will be used to compare the old and new values of the atom to determine if the value has changed.
* By default, values are compared using first using strict equality (`===`), then `Object.is`, and finally any `.equals` method present in the object's prototype chain.
* @param a - The old value
* @param b - The new value
* @returns True if the values are equal, false otherwise.
*/
isEqual?(a: any, b: any): boolean;
}
/* Excluded from this release type: Child */
/**
* A computed signal created via the `computed` function or `@computed` decorator.
* Computed signals derive their values from other signals and automatically update when their dependencies change.
* They use lazy evaluation, only recalculating when accessed and dependencies have changed.
*
* @example
* ```ts
* const firstName = atom('firstName', 'John')
* const lastName = atom('lastName', 'Doe')
* const fullName = computed('fullName', () => `${firstName.get()} ${lastName.get()}`)
*
* console.log(fullName.get()) // "John Doe"
* firstName.set('Jane')
* console.log(fullName.get()) // "Jane Doe"
* ```
*
* @public
*/
export declare interface Computed<Value, Diff = unknown> extends Signal<Value, Diff> {
/**
* Whether this computed signal is involved in an actively-running effect graph.
* Returns true if there are any reactions or other computed signals depending on this one.
* @public
*/
readonly isActivelyListening: boolean;
/* Excluded from this release type: parentSet */
/* Excluded from this release type: parents */
/* Excluded from this release type: parentEpochs */
}
/**
* Creates a computed signal that derives its value from other signals.
* Computed signals automatically update when their dependencies change and use lazy evaluation
* for optimal performance.
*
* @example
* ```ts
* const name = atom('name', 'John')
* const greeting = computed('greeting', () => `Hello ${name.get()}!`)
* console.log(greeting.get()) // 'Hello John!'
* ```
*
* `computed` may also be used as a decorator for creating computed getter methods.
*
* @example
* ```ts
* class Counter {
* max = 100
* count = atom<number>(0)
*
* @computed getRemaining() {
* return this.max - this.count.get()
* }
* }
* ```
*
* You may optionally pass in a {@link ComputedOptions} when used as a decorator:
*
* @example
* ```ts
* class Counter {
* max = 100
* count = atom<number>(0)
*
* @computed({isEqual: (a, b) => a === b})
* getRemaining() {
* return this.max - this.count.get()
* }
* }
* ```
*
* @param name - The name of the signal for debugging purposes
* @param compute - The function that computes the value of the signal. Receives the previous value and last computed epoch
* @param options - Optional configuration for the computed signal
* @returns A new computed signal
* @public
*/
export declare function computed<Value, Diff = unknown>(name: string, compute: (previousValue: typeof UNINITIALIZED | Value, lastComputedEpoch: number) => Value | WithDiff<Value, Diff>, options?: ComputedOptions<Value, Diff>): Computed<Value, Diff>;
/**
* TC39 decorator for creating computed methods in classes.
*
* @example
* ```ts
* class MyClass {
* value = atom('value', 10)
*
* @computed
* doubled() {
* return this.value.get() * 2
* }
* }
* ```
*
* @param compute - The method to be decorated
* @param context - The decorator context provided by TypeScript
* @returns The decorated method
* @public
*/
export declare function computed<This extends object, Value>(compute: () => Value, context: ClassMethodDecoratorContext<This, () => Value>): () => Value;
/**
* Legacy TypeScript decorator for creating computed methods in classes.
*
* @example
* ```ts
* class MyClass {
* value = atom('value', 10)
*
* @computed
* doubled() {
* return this.value.get() * 2
* }
* }
* ```
*
* @param target - The class prototype
* @param key - The property key
* @param descriptor - The property descriptor
* @returns The modified property descriptor
* @public
*/
export declare function computed(target: any, key: string, descriptor: PropertyDescriptor): PropertyDescriptor;
/**
* Decorator factory for creating computed methods with options.
*
* @example
* ```ts
* class MyClass {
* items = atom('items', [1, 2, 3])
*
* @computed({ historyLength: 10 })
* sum() {
* return this.items.get().reduce((a, b) => a + b, 0)
* }
* }
* ```
*
* @param options - Configuration options for the computed signal
* @returns A decorator function that can be applied to methods
* @public
*/
export declare function computed<Value, Diff = unknown>(options?: ComputedOptions<Value, Diff>): ((target: any, key: string, descriptor: PropertyDescriptor) => PropertyDescriptor) & (<This>(compute: () => Value, context: ClassMethodDecoratorContext<This, () => Value>) => () => Value);
/**
* A function type that computes the difference between two values of a signal.
*
* This function is used to generate incremental diffs that can be applied to
* reconstruct state changes over time. It's particularly useful for features
* like undo/redo, synchronization, and change tracking.
*
* The function should analyze the previous and current values and return a
* diff object that represents the change. If the diff cannot be computed
* (e.g., the values are too different or incompatible), it should return
* the unique symbol RESET_VALUE to indicate that a full state reset is required.
*
* @param previousValue - The previous value of the signal
* @param currentValue - The current value of the signal
* @param lastComputedEpoch - The epoch when the previous value was set
* @param currentEpoch - The epoch when the current value was set
* @returns A diff object representing the change, or the unique symbol RESET_VALUE if no diff can be computed
*
* @example
* ```ts
* import { atom, RESET_VALUE } from '@tldraw/state'
*
* // Simple numeric diff
* const numberDiff: ComputeDiff<number, number> = (prev, curr) => curr - prev
*
* // Array diff with reset fallback
* const arrayDiff: ComputeDiff<string[], { added: string[], removed: string[] }> = (prev, curr) => {
* if (prev.length > 1000 || curr.length > 1000) {
* return RESET_VALUE // Too complex, force reset
* }
* return {
* added: curr.filter(item => !prev.includes(item)),
* removed: prev.filter(item => !curr.includes(item))
* }
* }
*
* const count = atom('count', 0, { computeDiff: numberDiff })
* ```
*
* @public
*/
export declare type ComputeDiff<Value, Diff> = (previousValue: Value, currentValue: Value, lastComputedEpoch: number, currentEpoch: number) => Diff | RESET_VALUE;
/**
* Options for configuring computed signals. Used when calling `computed` or using the `@computed` decorator.
*
* @example
* ```ts
* const greeting = computed('greeting', () => `Hello ${name.get()}!`, {
* historyLength: 10,
* isEqual: (a, b) => a === b,
* computeDiff: (oldVal, newVal) => ({ type: 'change', from: oldVal, to: newVal })
* })
* ```
*
* @public
*/
export declare interface ComputedOptions<Value, Diff> {
/**
* The maximum number of diffs to keep in the history buffer.
*
* If you don't need to compute diffs, or if you will supply diffs manually via {@link Atom.set}, you can leave this as `undefined` and no history buffer will be created.
*
* If you expect the value to be part of an active effect subscription all the time, and to not change multiple times inside of a single transaction, you can set this to a relatively low number (e.g. 10).
*
* Otherwise, set this to a higher number based on your usage pattern and memory constraints.
*
*/
historyLength?: number;
/**
* A method used to compute a diff between the computed's old and new values. If provided, it will not be used unless you also specify {@link ComputedOptions.historyLength}.
*/
computeDiff?: ComputeDiff<Value, Diff>;
/**
* If provided, this will be used to compare the old and new values of the computed to determine if the value has changed.
* By default, values are compared using first using strict equality (`===`), then `Object.is`, and finally any `.equals` method present in the object's prototype chain.
* @param a - The old value
* @param b - The new value
* @returns True if the values are equal, false otherwise.
*/
isEqual?(a: any, b: any): boolean;
}
/* Excluded from this release type: deferAsyncEffects */
/**
* An EffectScheduler is responsible for executing side effects in response to changes in state.
*
* You probably don't need to use this directly unless you're integrating this library with a framework of some kind.
*
* Instead, use the {@link react} and {@link reactor} functions.
*
* @example
* ```ts
* const render = new EffectScheduler('render', drawToCanvas)
*
* render.attach()
* render.execute()
* ```
*
* @public
*/
export declare const EffectScheduler: new <Result>(name: string, runEffect: (lastReactedEpoch: number) => Result, options?: EffectSchedulerOptions) => EffectScheduler<Result>;
/** @public */
export declare interface EffectScheduler<Result> {
/**
* Whether this scheduler is attached and actively listening to its parents.
* @public
*/
readonly isActivelyListening: boolean;
/* Excluded from this release type: lastTraversedEpoch */
/** @public */
readonly name: string;
/* Excluded from this release type: __debug_ancestor_epochs__ */
/**
* The number of times this effect has been scheduled.
* @public
*/
readonly scheduleCount: number;
/* Excluded from this release type: parentSet */
/* Excluded from this release type: parentEpochs */
/* Excluded from this release type: parents */
/* Excluded from this release type: maybeScheduleEffect */
/* Excluded from this release type: scheduleEffect */
/* Excluded from this release type: maybeExecute */
/**
* Makes this scheduler become 'actively listening' to its parents.
* If it has been executed before it will immediately become eligible to receive 'maybeScheduleEffect' calls.
* If it has not executed before it will need to be manually executed once to become eligible for scheduling, i.e. by calling `EffectScheduler.execute`.
* @public
*/
attach(): void;
/**
* Makes this scheduler stop 'actively listening' to its parents.
* It will no longer be eligible to receive 'maybeScheduleEffect' calls until `EffectScheduler.attach` is called again.
* @public
*/
detach(): void;
/**
* Executes the effect immediately and returns the result.
* @returns The result of the effect.
* @public
*/
execute(): Result;
}
/** @public */
export declare interface EffectSchedulerOptions {
/**
* scheduleEffect is a function that will be called when the effect is scheduled.
*
* It can be used to defer running effects until a later time, for example to batch them together with requestAnimationFrame.
*
*
* @example
* ```ts
* let isRafScheduled = false
* const scheduledEffects: Array<() => void> = []
* const scheduleEffect = (runEffect: () => void) => {
* scheduledEffects.push(runEffect)
* if (!isRafScheduled) {
* isRafScheduled = true
* requestAnimationFrame(() => {
* isRafScheduled = false
* scheduledEffects.forEach((runEffect) => runEffect())
* scheduledEffects.length = 0
* })
* }
* }
* const stop = react('set page title', () => {
* document.title = doc.title,
* }, scheduleEffect)
* ```
*
* @param execute - A function that will execute the effect.
* @returns void
*/
scheduleEffect?: (execute: () => void) => void;
}
/**
* @public
*/
export declare const EMPTY_ARRAY: [];
/**
* Retrieves the underlying computed instance for a given property created with the `computed`
* decorator.
*
* @example
* ```ts
* class Counter {
* max = 100
* count = atom(0)
*
* @computed getRemaining() {
* return this.max - this.count.get()
* }
* }
*
* const c = new Counter()
* const remaining = getComputedInstance(c, 'getRemaining')
* remaining.get() === 100 // true
* c.count.set(13)
* remaining.get() === 87 // true
* ```
*
* @param obj - The object
* @param propertyName - The property name
* @public
*/
export declare function getComputedInstance<Obj extends object, Prop extends keyof Obj>(obj: Obj, propertyName: Prop): Computed<Obj[Prop]>;
/**
* Returns true if the given value is an {@link Atom}.
*
* @param value - The value to check
* @returns True if the value is an Atom, false otherwise
* @example
* ```ts
* const myAtom = atom('test', 42)
* const notAtom = 'hello'
*
* console.log(isAtom(myAtom)) // true
* console.log(isAtom(notAtom)) // false
* ```
* @public
*/
export declare function isAtom(value: unknown): value is Atom<unknown>;
/**
* Type guard function that determines whether a value is a signal (either an Atom or Computed).
*
* This utility function is helpful when working with mixed data types and you need to
* differentiate between regular values and reactive signal containers. It returns `true`
* if the provided value is either an atomic signal created with `atom()` or a computed
* signal created with `computed()`.
*
* @param value - The value to check, can be of any type
* @returns `true` if the value is a Signal (Atom or Computed), `false` otherwise
*
* @example
* ```ts
* import { atom, computed, isSignal } from '@tldraw/state'
*
* const count = atom('count', 5)
* const doubled = computed('doubled', () => count.get() * 2)
* const regularValue = 'hello'
*
* console.log(isSignal(count)) // true
* console.log(isSignal(doubled)) // true
* console.log(isSignal(regularValue)) // false
* console.log(isSignal(null)) // false
* ```
*
* @public
*/
export declare function isSignal(value: any): value is Signal<any>;
/**
* Call this inside a computed signal function to determine whether it is the first time the function is being called.
*
* Mainly useful for incremental signal computation.
*
* @example
* ```ts
* const count = atom('count', 0)
* const double = computed('double', (prevValue) => {
* if (isUninitialized(prevValue)) {
* print('First time!')
* }
* return count.get() * 2
* })
* ```
*
* @param value - The value to check.
* @public
*/
export declare function isUninitialized(value: any): value is UNINITIALIZED;
/**
* Creates a new {@link Atom} that persists its value to localStorage.
*
* The atom is automatically synced with localStorage - changes to the atom are saved to localStorage,
* and the initial value is read from localStorage if it exists. Returns both the atom and a cleanup
* function that should be called to stop syncing when the atom is no longer needed. If you need to delete
* the atom, you should do it manually after all cleanup functions have been called.
*
* @example
* ```ts
* const [theme, cleanup] = localStorageAtom('theme', 'light')
*
* theme.get() // 'light' or value from localStorage if it exists
*
* theme.set('dark') // updates atom and saves to localStorage
*
* // When done:
* cleanup() // stops syncing to localStorage
* ```
*
* @param name - The localStorage key and atom name. This is used for both localStorage persistence
* and debugging/profiling purposes.
* @param initialValue - The initial value of the atom, used if no value exists in localStorage.
* @param options - Optional atom configuration. See {@link AtomOptions}.
* @returns A tuple containing the atom and a cleanup function to stop localStorage syncing.
* @public
*/
export declare function localStorageAtom<Value, Diff = unknown>(name: string, initialValue: Value, options?: AtomOptions<Value, Diff>): [Atom<Value, Diff>, () => void];
/**
* Starts a new effect scheduler, scheduling the effect immediately.
*
* Returns a function that can be called to stop the scheduler.
*
* @example
* ```ts
* const color = atom('color', 'red')
* const stop = react('set style', () => {
* divElem.style.color = color.get()
* })
* color.set('blue')
* // divElem.style.color === 'blue'
* stop()
* color.set('green')
* // divElem.style.color === 'blue'
* ```
*
*
* Also useful in React applications for running effects outside of the render cycle.
*
* @example
* ```ts
* useEffect(() => react('set style', () => {
* divRef.current.style.color = color.get()
* }), [])
* ```
*
* @public
*/
export declare function react(name: string, fn: (lastReactedEpoch: number) => any, options?: EffectSchedulerOptions): () => void;
/**
* The reactor is a user-friendly interface for starting and stopping an `EffectScheduler`.
*
* Calling `.start()` will attach the scheduler and execute the effect immediately the first time it is called.
*
* If the reactor is stopped, calling `.start()` will re-attach the scheduler but will only execute the effect if any of its parents have changed since it was stopped.
*
* You can create a reactor with {@link reactor}.
* @public
*/
export declare interface Reactor<T = unknown> {
/**
* The underlying effect scheduler.
* @public
*/
scheduler: EffectScheduler<T>;
/**
* Start the scheduler. The first time this is called the effect will be scheduled immediately.
*
* If the reactor is stopped, calling this will start the scheduler again but will only execute the effect if any of its parents have changed since it was stopped.
*
* If you need to force re-execution of the effect, pass `{ force: true }`.
* @public
*/
start(options?: {
force?: boolean;
}): void;
/**
* Stop the scheduler.
* @public
*/
stop(): void;
}
/**
* Creates a {@link Reactor}, which is a thin wrapper around an `EffectScheduler`.
*
* @public
*/
export declare function reactor<Result>(name: string, fn: (lastReactedEpoch: number) => Result, options?: EffectSchedulerOptions): Reactor<Result>;
/**
* A unique symbol used to indicate that a signal's value should be reset or that
* there is insufficient history to compute diffs between epochs.
*
* This value is returned by {@link Signal.getDiffSince} when the requested epoch
* is too far in the past and the diff sequence cannot be reconstructed.
*
* @example
* ```ts
* import { atom, getGlobalEpoch, RESET_VALUE } from '@tldraw/state'
*
* const count = atom('count', 0, { historyLength: 3 })
* const oldEpoch = getGlobalEpoch()
*
* // Make many changes that exceed history length
* count.set(1)
* count.set(2)
* count.set(3)
* count.set(4)
*
* const diffs = count.getDiffSince(oldEpoch)
* if (diffs === RESET_VALUE) {
* console.log('Too many changes, need to reset state')
* }
* ```
*
* @public
*/
export declare const RESET_VALUE: unique symbol;
/**
* Type representing the the unique symbol RESET_VALUE symbol, used in type annotations
* to indicate when a signal value should be reset or when diff computation
* cannot proceed due to insufficient history.
*
* @public
*/
export declare type RESET_VALUE = typeof RESET_VALUE;
/**
* A reactive value container that can change over time and track diffs between sequential values.
*
* Signals are the foundation of the \@tldraw/state reactive system. They automatically manage
* dependencies and trigger updates when their values change. Any computed signal or effect
* that reads from this signal will be automatically recomputed when the signal's value changes.
*
* There are two types of signal:
* - **Atomic signals** - Created using `atom()`. These are mutable containers that can be
* directly updated using `set()` or `update()` methods.
* - **Computed signals** - Created using `computed()`. These derive their values from other
* signals and are automatically recomputed when dependencies change.
*
* @example
* ```ts
* import { atom, computed } from '@tldraw/state'
*
* // Create an atomic signal
* const count = atom('count', 0)
*
* // Create a computed signal that derives from the atom
* const doubled = computed('doubled', () => count.get() * 2)
*
* console.log(doubled.get()) // 0
* count.set(5)
* console.log(doubled.get()) // 10
* ```
*
* @public
*/
export declare interface Signal<Value, Diff = unknown> {
/**
* A human-readable identifier for this signal, used primarily for debugging and performance profiling.
*
* The name is displayed in debug output from {@link whyAmIRunning} and other diagnostic tools.
* It does not need to be globally unique within your application.
*/
name: string;
/**
* Gets the current value of the signal and establishes a dependency relationship.
*
* When called from within a computed signal or effect, this signal will be automatically
* tracked as a dependency. If this signal's value changes, any dependent computations
* or effects will be marked for re-execution.
*
* @returns The current value stored in the signal
*/
get(): Value;
/**
* The global epoch number when this signal's value last changed.
*
* Note that this represents when the value actually changed, not when it was last computed.
* A computed signal may recalculate and produce the same value without changing its epoch.
* This is used internally for dependency tracking and history management.
*/
lastChangedEpoch: number;
/**
* Gets the sequence of diffs that occurred between a specific epoch and the current state.
*
* This method enables incremental synchronization by providing a list of changes that
* have occurred since a specific point in time. If the requested epoch is too far in
* the past or the signal doesn't have enough history, it returns the unique symbol RESET_VALUE
* to indicate that a full state reset is required.
*
* @param epoch - The epoch timestamp to get diffs since
* @returns An array of diff objects representing changes since the epoch, or the unique symbol RESET_VALUE if insufficient history is available
*/
getDiffSince(epoch: number): Diff[] | RESET_VALUE;
/**
* Gets the current value of the signal without establishing a dependency relationship.
*
* This method bypasses the automatic dependency tracking system, making it useful for
* performance-critical code paths where the overhead of dependency capture would be
* problematic. Use with caution as it breaks the reactive guarantees of the system.
*
* **Warning**: This method should only be used when you're certain that you don't need
* the calling context to react to changes in this signal.
*
* @param ignoreErrors - Whether to suppress errors during value retrieval (optional)
* @returns The current value without establishing dependencies
*/
__unsafe__getWithoutCapture(ignoreErrors?: boolean): Value;
/* Excluded from this release type: children */
}
/**
* Like {@link transaction}, but does not create a new transaction if there is already one in progress.
* This is the preferred way to batch state updates when you don't need the rollback functionality.
*
* @example
* ```ts
* const count = atom('count', 0)
* const doubled = atom('doubled', 0)
*
* react('update doubled', () => {
* console.log(`Count: ${count.get()}, Doubled: ${doubled.get()}`)
* })
*
* // This batches both updates into a single reaction
* transact(() => {
* count.set(5)
* doubled.set(count.get() * 2)
* })
* // Logs: "Count: 5, Doubled: 10"
* ```
*
* @param fn - The function to run in a transaction
* @returns The return value of the function
* @public
*/
export declare function transact<T>(fn: () => T): T;
/**
* Batches state updates, deferring side effects until after the transaction completes.
* Unlike {@link transact}, this function always creates a new transaction, allowing for nested transactions.
*
* @example
* ```ts
* const firstName = atom('firstName', 'John')
* const lastName = atom('lastName', 'Doe')
*
* react('greet', () => {
* console.log(`Hello, ${firstName.get()} ${lastName.get()}!`)
* })
*
* // Logs "Hello, John Doe!"
*
* transaction(() => {
* firstName.set('Jane')
* lastName.set('Smith')
* })
*
* // Logs "Hello, Jane Smith!"
* ```
*
* If the function throws, the transaction is aborted and any signals that were updated during the transaction revert to their state before the transaction began.
*
* @example
* ```ts
* const firstName = atom('firstName', 'John')
* const lastName = atom('lastName', 'Doe')
*
* react('greet', () => {
* console.log(`Hello, ${firstName.get()} ${lastName.get()}!`)
* })
*
* // Logs "Hello, John Doe!"
*
* transaction(() => {
* firstName.set('Jane')
* throw new Error('oops')
* })
*
* // Does not log
* // firstName.get() === 'John'
* ```
*
* A `rollback` callback is passed into the function.
* Calling this will prevent the transaction from committing and will revert any signals that were updated during the transaction to their state before the transaction began.
*
* @example
* ```ts
* const firstName = atom('firstName', 'John')
* const lastName = atom('lastName', 'Doe')
*
* react('greet', () => {
* console.log(`Hello, ${firstName.get()} ${lastName.get()}!`)
* })
*
* // Logs "Hello, John Doe!"
*
* transaction((rollback) => {
* firstName.set('Jane')
* lastName.set('Smith')
* rollback()
* })
*
* // Does not log
* // firstName.get() === 'John'
* // lastName.get() === 'Doe'
* ```
*
* @param fn - The function to run in a transaction, called with a function to roll back the change.
* @returns The return value of the function
* @public
*/
export declare function transaction<T>(fn: (rollback: () => void) => T): T;
/**
* A special symbol used to indicate that a computed signal has not been initialized yet.
* This is passed as the `previousValue` parameter to a computed signal function on its first run.
*
* @example
* ```ts
* const count = atom('count', 0)
* const double = computed('double', (prevValue) => {
* if (isUninitialized(prevValue)) {
* console.log('First computation!')
* }
* return count.get() * 2
* })
* ```
*
* @public
*/
export declare const UNINITIALIZED: unique symbol;
/**
* The type of the first value passed to a computed signal function as the 'prevValue' parameter.
* This type represents the uninitialized state of a computed signal before its first calculation.
*
* @see {@link isUninitialized}
* @public
*/
export declare type UNINITIALIZED = typeof UNINITIALIZED;
/**
* Executes the given function without capturing any parents in the current capture context.
*
* This is mainly useful if you want to run an effect only when certain signals change while also
* dereferencing other signals which should not cause the effect to rerun on their own.
*
* @example
* ```ts
* const name = atom('name', 'Sam')
* const time = atom('time', () => new Date().getTime())
*
* setInterval(() => {
* time.set(new Date().getTime())
* })
*
* react('log name changes', () => {
* print(name.get(), 'was changed at', unsafe__withoutCapture(() => time.get()))
* })
*
* ```
*
* @public
*/
export declare function unsafe__withoutCapture<T>(fn: () => T): T;
/**
* A debugging tool that tells you why a computed signal or effect is running.
* Call in the body of a computed signal or effect function.
*
* @example
* ```ts
* const name = atom('name', 'Bob')
* react('greeting', () => {
* whyAmIRunning()
* print('Hello', name.get())
* })
*
* name.set('Alice')
*
* // 'greeting' is running because:
* // 'name' changed => 'Alice'
* ```
*
* @public
*/
export declare function whyAmIRunning(): void;
/**
* A singleton class used to wrap computed signal values along with their diffs.
* This class is used internally by the {@link withDiff} function to provide both
* the computed value and its diff to the signal system.
*
* @example
* ```ts
* const count = atom('count', 0)
* const double = computed('double', (prevValue) => {
* const nextValue = count.get() * 2
* if (isUninitialized(prevValue)) {
* return nextValue
* }
* return withDiff(nextValue, nextValue - prevValue)
* })
* ```
*
* @public
*/
export declare const WithDiff: {
new <Value, Diff>(value: Value, diff: Diff): {
diff: Diff;
value: Value;
};
};
/**
* Interface representing a value wrapped with its corresponding diff.
* Used in incremental computation to provide both the new value and the diff from the previous value.
*
* @public
*/
export declare interface WithDiff<Value, Diff> {
/**
* The computed value.
*/
value: Value;
/**
* The diff between the previous and current value.
*/
diff: Diff;
}
/**
* When writing incrementally-computed signals it is convenient (and usually more performant) to incrementally compute the diff too.
*
* You can use this function to wrap the return value of a computed signal function to indicate that the diff should be used instead of calculating a new one with {@link AtomOptions.computeDiff}.
*
* @example
* ```ts
* const count = atom('count', 0)
* const double = computed('double', (prevValue) => {
* const nextValue = count.get() * 2
* if (isUninitialized(prevValue)) {
* return nextValue
* }
* return withDiff(nextValue, nextValue - prevValue)
* }, { historyLength: 10 })
* ```
*
*
* @param value - The value.
* @param diff - The diff.
* @public
*/
export declare function withDiff<Value, Diff>(value: Value, diff: Diff): WithDiff<Value, Diff>;
export { }