import { Context } from "./context.js"; import { event } from "./observable.js"; //#region src/state.d.ts /** Internal state assigned to states. */ declare const STORE: WeakMap>; /** States under construction - added on `new`, removed when activated or released. * Also carries work to run at the deadline, which `def` uses to bound its registry. */ declare const PENDING: Set void)>; declare namespace State { /** Any type of State, using own class constructor as its identifier. */ type Extends = (abstract new (...args: any[]) => T) & typeof State; /** A State constructor which may be instanciated. */ type Type = (new (...args: State.Args) => T) & Omit; /** State constructor arguments */ type Args = (Args | Init | Assign | void)[]; /** * Value of `static global`. A boolean opts a State in or out of the * process-global root; a resolver decides at activation, receiving the * instance and returning whether it registers - use it to make the choice * conditional (e.g. per environment). A bare literal (`= true` / `= false`) * seals the choice for subclasses; widen a subclass's `static global` type to * `State.Global` to permit a resolver or a later override. */ type Global = boolean | ((self: T) => boolean); /** * State constructor callback - runs during activation, in argument order, * before the `new()` lifecycle hook (so it may configure state `new()` * will observe). Returned function will run when state is destroyed. */ type Init = (this: T, thisArg: T) => Promise | (() => void) | Args | Assign | void; /** * Lifecycle handlers for `State.on`, keyed by when they run. A bare `Init` * function passed to `on` is sugar for `{ before }`. */ interface On { /** * Per-class setup, run once when the class is first bootstrapped - before * its members are classified and bound. Receives the class, so a handler * may inspect or reshape the prototype first. A handler registered on a * base class runs for each subclass too. */ type?(type: State.Extends): void; /** * Per-instance setup, run in the `prepare` phase before `observe` and the * `new()` hook. Equivalent to passing a bare function to `on`. May return a * cleanup, constructor args, or an assign overlay. */ before?(this: T, thisArg: T): void | (() => void) | Promise | Args | Assign; /** * Per-instance setup, run at the `new()` slot - after own values are * observed and constructor args applied. May return a cleanup function. */ after?(this: T, self: T): void | (() => void); } /** Object overlay to override values and methods on a state. */ type Assign = Record & { [K in Field]?: T[K] extends ((...args: infer A) => infer R) ? (this: T, ...args: A) => R : T[K] }; /** Subset of `keyof T` not defined by base State. **/ type Field = Exclude; /** Any valid key for state, including but not limited to Field. */ type Event = Field | number | symbol | (string & {}); /** Any event signal dispatched to listeners, including lifecycle meta events. */ type Signal = Event | true | false | null; /** Export/Import compatible value for a given property in a State. */ type Export = R extends State ? Values : R extends { get(): infer T; } ? T : R; /** * Values from current state of given state. * Differs from `Values` as values here will drill * into "real" values held by exotics like ref.Object. */ type Values = { [P in Field]: Export }; /** Object comperable to data found in T. */ type Partial = { [P in Field]?: Export }; /** Value for a property applied by a state. */ type Value> = K extends keyof T ? Export : unknown; type Setter = (value: T, previous: T) => T | void; /** Descriptor config for a managed property. */ type Apply = { value?: T; get?: ((source: State) => T) | boolean; set?: Setter | boolean; enumerable?: boolean; }; /** Descriptor config for set() overload - enforces type when key is a known field. */ type Define = K extends Field ? Apply> : Apply; type OnEvent = (this: T, key: Signal, source: T) => void | (() => void) | null; /** Exotic value, where actual value is contained within. */ type Ref = { (next: T): void; current: T | null; }; /** * A callback function which is subscribed to parent and updates when accessed properties change. * * @param current - Current state of this state. This is a proxy which detects properties which * where accessed, and thus depended upon to stay current. * * @param update - Set of properties which have changed, and events fired, since last update. * * @returns A callback function which will be called when this effect is stale. */ type Effect = (this: T, current: T, update: readonly State.Event[] | undefined) => EffectCallback | Promise | null | void; /** * A callback function returned by effect. Will be called when effect is stale. * * @param update - `true` if update is pending, `false` effect has been cancelled, `null` if state is destroyed. */ type EffectCallback = (update: boolean | null) => void; /** * A list of keys updated, or events fired, since last update. * This may be awaited to get full list (same array) when update is settled. */ type Updated = readonly Event[] & PromiseLike[]>; } declare abstract class State { /** * Whether an instance activated with no enclosing context registers itself * to the process-global root, where `get()` can resolve it from anywhere. * `false` (the default) keeps a context-less instance fully functional but * private - not injectable, though it can still read declared globals. Opt in * with `static readonly global = true`; a resolver (see {@link State.Global}) * makes the choice conditional. It must be declared per class - a subclass * that would inherit a global without its own declaration throws on * activation, so an accidental global (a forgotten `Provider`, an extended * global) cannot leak into the shared root. */ static readonly global: State.Global; /** * Loopback to instance of this state. This is useful when in a subscribed context, * to keep write access to `this` after a destructure. You can use it to read variables silently as well. **/ is: this; constructor(...args: State.Args); /** * Optional lifecycle hook called during State initialization. * Can return a cleanup function to run when state is destroyed. * * It is recommended you protect this method. */ protected new?(): void | (() => void); /** * Pull current values from state. Flattens all states and exotic values recursively. * * @returns Object with all values from this state. **/ get(): State.Values; /** * Run a function to run automatically when accessed values change. * * @param effect Function to run, and whenever accessed values change. * If effect returns a function, it will be called when a change occurs (syncronously), * effect is cancelled, or parent state is destroyed. * @returns Function to cancel listener. */ get(effect: State.Effect): () => void; /** * Get value of a property. Will fetch underlying value from exotic values like ref.Object. * * Any value which implements `get()` (with no arguments) will be treated as such. * * @param key - Property to get value of. * @param required - If true, will throw an error if property is not available. * @returns Value of property. */ get>(key: T, required?: boolean): State.Value; /** * Check if state is destroyed. * * @param status - `null` to check if state is destroyed. * @returns `true` if state is destroyed, `false` otherwise. */ get(status: null): boolean; /** * Callback when state is to be destroyed. * * @param callback - Function to call when state is destroyed. * @returns Function to cancel listener. */ get(status: null, callback: () => void): () => void; /** Fetch State of type from context. Throws if not found. */ get(type: State.Type, required?: true): T; /** Fetch a State from context. Undefined if not found. */ get(type: State.Type, required: boolean): T | undefined; /** * Subscribe to State becoming available in context. * * Will search both up and downstream by default. * Normally you can ignore this because State you expect is rarely both. * * Specify downstream if you only want to watch for State in children, which can be useful if you expect multiple of the same State in different branches of the tree. * * @param type - Type of State to watch for. * @param callback - Function to call when State is found. Will be called immediately if State is already available. * @param downstream - If true, will only watch for State in children, if false will only for parents. * @returns Function to cancel listener. */ get(type: State.Type, callback: Context.Expect, downstream?: boolean): () => void; /** * Get update in progress. * * @returns Promise which resolves object with updated values, `undefined` if there no update is pending. **/ set(): State.Updated; /** * Merge argument with current state, updating one or more properties at once. * Properties which are not managed by this state will be ignored. * * @param assign - Object with properties to update. * @param silent - If true, listeners will not be notified. If state is destroyed, will squash update without throwing. * @returns Array of keys updated, syncronously contains keys updated immediately and may be resolved (to itself) when all updates are settled. */ set(assign?: State.Assign, silent?: boolean): State.Updated; /** * Call a function when update occurs. * * Given function is called for every assignment (which changes value) or explicit `set`. * * To run logic on final value only, callback may return a function. The same * function for one or more events will be called only once when update is settled. * * Return `null` from callback to stop listening. * * @param callback - Function to call when update occurs. * @returns Function to remove listener. Will return `true` if removed, `false` if inactive already. */ set(callback: State.OnEvent): () => boolean; /** * Push an update. This will not change the value of associated property. * * Useful where a property value internally has changed, but the object is the same. * For example: An array has pushed a new value, or a nested property is updated. * * You can also use this to dispatch arbitrary events. * Symbols are recommended as non-property events, however you can use any string. * If doing so, be sure to avoid collisions using property names. An easy way to do this is * to prefix an event with "!" and/or use dash-case. e.g. `set("!event")` or `set("my-event")`. * * @param key - Property or event to dispatch. * @returns Promise resolves an array of keys updated. */ set(key: State.Event): State.Updated; /** * Declare an end to updates. This event is final and will freeze state. * This event can be watched for as well, to run cleanup logic and internally will remove all listeners. * * @param status - `null` to end updates. */ set(status: null): void; /** * Register a callback for a specific property or event. * * Callback receives the key, current value, and source instance. * * @param event - Property or event to watch. If `null`, will callback on destroy. * @param callback - Function to call when event occurs. * @returns Function to remove listener. */ set>(event: K | null, callback: State.OnEvent): () => boolean; /** * Define or update a managed property using a descriptor config. * If the property already is managed, config will only accept value. * If the property does not exist, it will be created and made reactive. * * @param key - Property to define or update. * @param config - Descriptor config with value, get, set, enumerable, and/or destroy. * @returns Promise resolves an array of keys updated. */ set>(key: K, config: State.Define): State.Updated; /** * Iterate over managed properties in this instance of State. * Yeilds the key and current value for each property. */ [Symbol.iterator](): Iterator<[string, unknown]>; /** * Create and activate a new instance of this state. * * **Important** - Unlike `new this(...)` - this method also activates state. * * @param args - arguments sent to constructor */ static new(this: State.Type, ...args: State.Args): T; /** * Static equivalent of `x instanceof this`. * Determines if provided class is a subtype of this one. * If so, language server will make available all static * methods and properties of this class. */ static is(this: T, maybe: unknown): maybe is T; /** * Register a lifecycle handler for this State and its subclasses. * * A bare function is per-instance setup run in the `prepare` phase (sugar for * `{ before }`); if it returns a function, that runs when the instance is * destroyed. Pass a {@link State.On} object to hook by cadence - `type` * (per-class, at bootstrap), `before` (per-instance, before `new()`), and * `after` (per-instance, at the `new()` slot). * * @returns Function to remove the handler. */ static on(this: State.Extends, handler: State.Init | State.On): () => boolean; } /** * Install a reactive computed property on a state instance, derived from a prototype * getter or an arity-bearing `set` factory. * * The getter is invoked with the tracking proxy as both `this` and its first argument; * reads of managed properties through it create subscriptions. Result is cached and * emits a keyed event when stale. */ declare function compute(this: State, getter: (self: any) => unknown, key: string): void; /** * Define or update a managed property using a descriptor config. * If the property already is managed, config will only accept value. * If the property does not exist, it will be created and made reactive. * * @param state - State to apply property to. * @param key - Property to define or update. * @param config - Descriptor config with value, get, set, enumerable, and/or destroy. * @param silent - If an update does occur, listeners will not be refreshed automatically. */ declare function apply(state: State, key: string, config: State.Apply, silent?: boolean): void; /** * Report States adopted by a parent - those already held, then each one * claimed later. Returns a callback to stop watching. */ declare function children(state: State, callback: (child: State) => void): () => boolean; declare function access(state: State, property: string, required?: boolean): any; /** * Update a property on a state instance and notify listeners. * * This is used internally to update properties, but can also be used to update properties which are not managed by state, or to update values without triggering setters. * * If `silent` is true, the update will not dispatch events and will return `false` instead of throwing if state is destroyed. */ declare function update(state: State, key: State.Event, value: T, silent?: boolean, own?: boolean): boolean; /** Random alphanumberic of length 6; always starts with a letter. */ declare function uid(): string; declare function unbind(fn: T): T; declare function unbind(fn?: T): T | undefined; /** * Read or claim a parent for a given child. In read mode (one argument) returns * the current parent, or `undefined` if never assigned. In write mode assigns * only if unclaimed and returns whether the assignment applied - `true` means * this call freshly claimed it, `false` means it was already parented. */ declare function parent(child: object): State | null | undefined; declare function parent(child: object, value: State | null): boolean; //#endregion export { PENDING, STORE, State, access, apply, children, compute, event, parent, uid, unbind, update }; //# sourceMappingURL=state.d.ts.map