/** a equal-calorie clone of the popular reactivity library [SolidJS](https://github.com/solidjs/solid). <br>
 * @module
*/
import { Context } from "./context.js";
import { Accessor, EqualityCheck, EqualityFn, ID, Setter, SignalUpdateStatus, TO_ID, UNTRACKED_ID, Updater } from "./typedefs.js";
/** the configuration options used by most signal constructors, and especially the basic/primitive ones. */
export interface SimpleSignalConfig<T> {
    /** give a name to the signal for debugging purposes */
    name?: string;
    /** when a signal's value is updated (either through a {@link Setter}, or a change in the value of a dependency signal in the case of a memo),
     * then the dependants/observers of THIS signal will only be notified if the equality check function evaluates to a `false`. <br>
     * see {@link EqualityCheck} to see its function signature and default behavior when left `undefined`
    */
    equals?: EqualityCheck<T>;
    /** when `false`, the computaion/effect function will be be evaluated/run immediately after it is declared. <br>
     * however, if left `undefined`, or `true`, the function's execution will be put off until the reactive signal returned by the createXYZ is called/accessed. <br>
     * by default, `defer` is `true`, and reactivity is not immediately executed during initialization. <br>
     * the reason why you might want to defer a reactive function is because the body of the reactive function may contain symbols/variables
     * that have not been defined yet, in which case an error will be raised, unless you choose to defer the first execution. <br>
    */
    defer?: boolean;
}
/** the configuration options used by most primitive derived/computed signal constructors. */
export interface MemoSignalConfig<T> extends SimpleSignalConfig<T> {
    /** initial value declaration for reactive signals. <br>
     * its purpose is only to be used as a previous value (`prev_value`) for the optional `equals` equality function,
     * so that you don't get an `undefined` as the `prev_value` on the very first comparison.
    */
    value?: T;
}
/** an arbitrary instance of a simple/primitive signal. */
export type SimpleSignalInstance = InstanceType<ReturnType<typeof SimpleSignal_Factory>>;
/** the base signal class inherited by most other signal classes. <br>
 * its only function is to:
 * - when {@link Signal.get | read}, it return its `this.value`, and register any new observers (those with a nonzero runtime-id {@link Signal.rid | `Signal.rid`})
 * - if {@link Signal.set | set} to a new value, compare it to its previous value through its `this.equals` function,
 *   and return a boolean specifying whether or not the old and new values are the same.
 * - when {@link Signal.run | ran}, it will always return `0` (unchanged), unless it is forced, in which case it will return a `1`.
*/
export declare const SimpleSignal_Factory: (ctx: Context) => {
    new <T>(value?: T | undefined, { name, equals, }?: SimpleSignalConfig<T>): {
        id: ID;
        rid: ID | UNTRACKED_ID;
        name?: string | undefined;
        value?: T | undefined;
        equals: EqualityFn<T>;
        fn?: ((observer_id: TO_ID | UNTRACKED_ID) => any) | undefined;
        prerun?(): any;
        postrun?(): any;
        get(observer_id?: TO_ID | UNTRACKED_ID): T;
        set(new_value: T | Updater<T>): boolean;
        run(forced?: boolean): SignalUpdateStatus;
        bindMethod<M extends keyof any>(method_name: M): any[M];
    };
    create<T_1>(...args: any[]): [id: ID, ...any[]];
};
/** creates state signals, which when {@link Signal.set | set} to a changed value, it will fire an update to all of its dependent/observer signals. */
export declare const StateSignal_Factory: (ctx: Context) => {
    new <T>(value: T, config?: SimpleSignalConfig<T> | undefined): {
        value: T;
        fn: never;
        prerun: never;
        postrun: never;
        set(new_value: T | Updater<T>): boolean;
        id: ID;
        rid: ID | UNTRACKED_ID;
        name?: string | undefined;
        equals: EqualityFn<T>;
        get(observer_id?: TO_ID | UNTRACKED_ID): T;
        run(forced?: boolean): SignalUpdateStatus;
        bindMethod<M extends keyof any>(method_name: M): any[M];
    };
    create<T_1>(value: T_1, config?: SimpleSignalConfig<T_1> | undefined): [idState: number, getState: Accessor<T_1>, setState: Setter<T_1>];
};
/** type definition for a memorizable function. to be used as a call parameter for {@link createMemo} */
export type MemoFn<T> = (observer_id: TO_ID | UNTRACKED_ID) => T | Updater<T>;
/** creates a computational/derived signal that only fires again if at least one of its dependencies has fired,
 * and after the {@link SimpleSignalInstance.fn | recomputation} (`this.fn`), the new computed value is different from the old one (according to `this.equals`).
*/
export declare const MemoSignal_Factory: (ctx: Context) => {
    new <T>(fn: MemoFn<T>, config?: MemoSignalConfig<T> | undefined): {
        fn: MemoFn<T>;
        prerun: never;
        postrun: never;
        get(observer_id?: TO_ID | UNTRACKED_ID): T;
        run(forced?: boolean): SignalUpdateStatus;
        id: ID;
        rid: ID | UNTRACKED_ID;
        name?: string | undefined;
        value?: T | undefined;
        equals: EqualityFn<T>;
        set(new_value: T | Updater<T>): boolean;
        bindMethod<M extends keyof any>(method_name: M): any[M];
    };
    create<T_1>(fn: MemoFn<T_1>, config?: MemoSignalConfig<T_1> | undefined): [idMemo: number, getMemo: Accessor<T_1>];
};
/** similar to {@link MemoSignal_Factory | `MemoSignal`}, creates a computed/derived signal, but it only recomputes if:
 * - it is dirty (`this.dirty = 1`)
 * - AND some signal/observer/caller calls this signal to {@link Signal.get | get} its value.
 *
 * this signal becomes dirty when at least one of its dependencies has fired an update. <br>
 * after which, it will remain dirty unless some caller requests its value, after which it will become not-dirty again.
 *
 * this signal also always fires an update when at least one of its dependencies has fired an update.
 * and it abandons checking for equality all together, since it only recomputes after a get request,
 * by which it is too late to signal no update in the value (because its observer is already running).
 *
 * this signal becomes pointless (in terms of efficiency) once a {@link MemoSignal_Factory | `MemoSignal`} depends on it.
 * but it is increadibly useful (i.e. lazy) when other {@link LazySignal_Factory | `LazySignal`s} depend on one another.
*/
export declare const LazySignal_Factory: (ctx: Context) => {
    new <T>(fn: MemoFn<T>, config?: MemoSignalConfig<T> | undefined): {
        fn: MemoFn<T>;
        dirty: 0 | 1;
        prerun: never;
        postrun: never;
        run(forced?: boolean): SignalUpdateStatus.UPDATED;
        get(observer_id?: TO_ID | UNTRACKED_ID): T;
        id: ID;
        rid: ID | UNTRACKED_ID;
        name?: string | undefined;
        value?: T | undefined;
        equals: EqualityFn<T>;
        set(new_value: T | Updater<T>): boolean;
        bindMethod<M extends keyof any>(method_name: M): any[M];
    };
    create<T_1>(fn: MemoFn<T_1>, config?: MemoSignalConfig<T_1> | undefined): [idLazy: number, getLazy: Accessor<T_1>];
};
/** type definition for an effect function. to be used as a call parameter for {@link createEffect} <br>
 * the return value of the function describes whether or not the signal should propagate. <br>
 * if `undefined` or `true` (or truethy), then the effect signal will propagate onto its observer signals,
 * otherwise if it is explicitly `false`, then it won't propagate.
*/
export type EffectFn = (observer_id: TO_ID | UNTRACKED_ID) => void | undefined | boolean;
/** a function that forcefully runs the {@link EffectFn} of an effect signal, and then propagates towards the observers of that effect signal. <br>
 * the return value is `true` if the effect is ran and propagated immediately,
 * or `false` if it did not fire immediately because of some form of batching stopped it from doing so.
*/
export type EffectEmitter = () => boolean;
/** extremely similar to {@link MemoSignal_Factory | `MemoSignal`}, but without a value to output, and also has the ability to fire on its own.
 * TODO-DOC: explain more
*/
export declare const EffectSignal_Factory: (ctx: Context) => {
    new (fn: EffectFn, config?: SimpleSignalConfig<void>): {
        fn: EffectFn;
        prerun: never;
        postrun: never;
        /** a non-untracked observer (which is what all new observers are) depending on an effect signal will result in the triggering of effect function.
         * this is an intentional design choice so that effects can be scaffolded on top of other effects.
         * TODO: reconsider, because you can also check for `this.rid !== 0` to determine that `this.fn` effect function has never run before, thus it must run at least once if the observer is not untracked_id
         * is it really necessary for us to rerun `this.fn` effect function for every new observer? it seems to create chaos rather than reducing it.
         * UPDATE: decided NOT to re-run on every new observer
         * TODO: cleanup this messy doc and redeclare how createEffect works
        */
        get(observer_id?: TO_ID | UNTRACKED_ID): void;
        set(): boolean;
        run(forced?: boolean): SignalUpdateStatus;
        id: ID;
        rid: ID | UNTRACKED_ID;
        name?: string | undefined;
        value?: void | undefined;
        equals: EqualityFn<void>;
        bindMethod<M extends keyof any>(method_name: M): any[M];
    };
    create(fn: EffectFn, config?: SimpleSignalConfig<void>): [idEffect: ID, dependOnEffect: Accessor<void>, fireEffect: EffectEmitter];
};
