f9f5c6afcb
This PR revamps how errors in signia are handled. This was brought about by a situation that @MitjaBezensek encountered where he added a reactor to a shape util class. During fuzz tests, that reactor was being executed at times when the Editor was not in a usable state (we had a minor hole in our sync rebase logic that allowed this, fixed elsewhere) and the reactor was throwing errors because it dereferenced a parent signal that relied on the page state (getShapesInCurrentPage or whatever) when there were no page records in the store. The strange part was that even if we wrapped the body of the reactor function in a try/catch, ignoring the error, we'd still see the error bubble up somehow. That was because the error was being thrown in a Computed derive function, and those are evaluated independently (i.e. outside of the reactor function) by signia as it traverses the dependency graph from leaves to roots in the `haveParentsChanged()` internal function. So the immediate fix was to make it so that `haveParentsChanged` ignores errors somehow. But the better fix involved completely revamping how signia handles errors, and they work very much like how signia handles values now. i.e. - signia still assumes that deriver functions are pure, and that if a deriver function throws once it will throw again unless its parent signals change value, so **it caches thrown errors for computed values** and throws them again if .get() is called again before the parents change - it clears the history buffer if an error is thrown - it does not allow errors to bubble during dirty checking i.e. inside `haveParentsChanged` or while calculating diffs. ### Change Type - [x] `patch` — Bug fix - [ ] `minor` — New feature - [ ] `major` — Breaking change - [ ] `dependencies` — Changes to package dependencies[^1] - [ ] `documentation` — Changes to the documentation only[^2] - [ ] `tests` — Changes to any test code only[^2] - [ ] `internal` — Any other changes that don't affect the published package[^2] - [ ] I don't know [^1]: publishes a `patch` release, for devDependencies use `internal` [^2]: will not publish a new version ### Test Plan 1. Add a step-by-step description of how to test your PR here. 2. - [x] Unit Tests - [ ] End to end tests ### Release Notes - Add a brief release note for your PR here.
4.8 KiB
4.8 KiB
API Report File for "@tldraw/state"
Do not edit this file. It is a report generated by API Extractor.
import { FunctionComponent } from 'react';
import { default as React_2 } from 'react';
// @public
export interface Atom<Value, Diff = unknown> extends Signal<Value, Diff> {
set(value: Value, diff?: Diff): Value;
update(updater: (value: Value) => Value): Value;
}
// @public
export function atom<Value, Diff = unknown>(
name: string,
initialValue: Value,
options?: AtomOptions<Value, Diff>): Atom<Value, Diff>;
// @public
export interface AtomOptions<Value, Diff> {
computeDiff?: ComputeDiff<Value, Diff>;
historyLength?: number;
isEqual?: (a: any, b: any) => boolean;
}
// @public
export interface Computed<Value, Diff = unknown> extends Signal<Value, Diff> {
readonly isActivelyListening: boolean;
// @internal (undocumented)
readonly parentEpochs: number[];
// @internal (undocumented)
readonly parents: Signal<any, any>[];
}
// @public
export 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>;
// @public (undocumented)
export function computed(target: any, key: string, descriptor: PropertyDescriptor): PropertyDescriptor;
// @public (undocumented)
export function computed<Value, Diff = unknown>(options?: ComputedOptions<Value, Diff>): (target: any, key: string, descriptor: PropertyDescriptor) => PropertyDescriptor;
// @public
export interface ComputedOptions<Value, Diff> {
computeDiff?: ComputeDiff<Value, Diff>;
historyLength?: number;
isEqual?: (a: any, b: any) => boolean;
}
// @public
export const EffectScheduler: typeof __EffectScheduler__;
// @public (undocumented)
export type EffectScheduler<Result> = __EffectScheduler__<Result>;
// @public (undocumented)
export const EMPTY_ARRAY: [];
// @public
export function getComputedInstance<Obj extends object, Prop extends keyof Obj>(obj: Obj, propertyName: Prop): Computed<Obj[Prop]>;
// @public
export function isAtom(value: unknown): value is Atom<unknown>;
// @public (undocumented)
export function isSignal(value: any): value is Signal<any>;
// @public
export const isUninitialized: (value: any) => value is typeof UNINITIALIZED;
// @public
export function react(name: string, fn: (lastReactedEpoch: number) => any, options?: EffectSchedulerOptions): () => void;
// @public
export interface Reactor<T = unknown> {
scheduler: EffectScheduler<T>;
start(options?: {
force?: boolean;
}): void;
stop(): void;
}
// @public
export function reactor<Result>(name: string, fn: (lastReactedEpoch: number) => Result, options?: EffectSchedulerOptions): Reactor<Result>;
// @public (undocumented)
export const RESET_VALUE: unique symbol;
// @public (undocumented)
export type RESET_VALUE = typeof RESET_VALUE;
// @public
export interface Signal<Value, Diff = unknown> {
__unsafe__getWithoutCapture(ignoreErrors?: boolean): Value;
// @internal (undocumented)
children: ArraySet<Child>;
get(): Value;
getDiffSince(epoch: number): Diff[] | RESET_VALUE;
lastChangedEpoch: number;
name: string;
}
// @public
export function track<T extends FunctionComponent<any>>(baseComponent: T): T extends React_2.MemoExoticComponent<any> ? T : React_2.MemoExoticComponent<T>;
// @public
export function transact<T>(fn: () => T): T;
// @public
export function transaction<T>(fn: (rollback: () => void) => T): T;
// @public
export function unsafe__withoutCapture<T>(fn: () => T): T;
// @public
export function useAtom<Value, Diff = unknown>(
name: string,
valueOrInitialiser: (() => Value) | Value,
options?: AtomOptions<Value, Diff>): Atom<Value, Diff>;
// @public
export function useComputed<Value>(name: string, compute: () => Value, deps: any[]): Computed<Value>;
// @public (undocumented)
export function useComputed<Value, Diff = unknown>(name: string, compute: () => Value, opts: ComputedOptions<Value, Diff>, deps: any[]): Computed<Value>;
// @public (undocumented)
export function useQuickReactor(name: string, reactFn: () => void, deps?: any[]): void;
// @public (undocumented)
export function useReactor(name: string, reactFn: () => void, deps?: any[] | undefined): void;
// @internal (undocumented)
export function useStateTracking<T>(name: string, render: () => T): T;
// @public
export function useValue<Value>(value: Signal<Value>): Value;
// @public (undocumented)
export function useValue<Value>(name: string, fn: () => Value, deps: unknown[]): Value;
// @public
export function whyAmIRunning(): void;
// @public
export function withDiff<Value, Diff>(value: Value, diff: Diff): WithDiff<Value, Diff>;
// (No @packageDocumentation comment for this package)