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.
154 lines
4.8 KiB
Markdown
154 lines
4.8 KiB
Markdown
## API Report File for "@tldraw/state"
|
|
|
|
> Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/).
|
|
|
|
```ts
|
|
|
|
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)
|
|
|
|
```
|