import { EffectError, ToEffectError } from './effect-result'; import { Signals, SignalsFactory } from './signals-factory'; import { Effect, SafeEffectResult, UnhandledEffectError } from './store'; import { DerivedId, EffectId, EventId, NoValueType } from './store-utils'; /** * Value-type for the combined derived behavior produced by {@link EffectSignals}. * * @template Input - specifies the input type for the effect * @template Result - specifies the result type of the effect * @template Error - specifies the error type of the effect */ export type CombinedEffectResult = { /** * The current input (which might differ from the resultInput), * or `NO_VALUE`, if no input was received yet */ currentInput: Input | NoValueType; /** * The current effect-result, * or `NO_VALUE` if no result was received yet, * or the effect produced an error */ result: SafeEffectResult | NoValueType; /** * The input that produced the current effect-result, * or `NO_VALUE`, if initial result or no result received yet */ resultInput: Input | NoValueType; /** * Indicates whether the effect is currently running. * In case of a factory without trigger, this will be true whenever one or multiple * of the following conditions are met: * `currentInput !== resultInput`, * or an invalidation event has been sent, * or the effect has sent a result, but has not yet completed. * In case of a factory with result-trigger, in addition to the previous * criteria, a trigegr event must have been received. */ resultPending: boolean; }; /** * Type representing a {@link CombinedEffectResult} in it's error state (non-pending) * * @template Input - specifies the input type for the effect * @template Error - specifies the error type of the effect */ export type CombinedEffectResultInErrorState = { currentInput: Input; result: ToEffectError | EffectError; resultInput: Input; resultPending: false; }; /** * Typeguard to check if a {@link CombinedEffectResult} is a {@link CombinedEffectResultInErrorState} */ export declare const isCombinedEffectResultInErrorState: (cer: CombinedEffectResult) => cer is CombinedEffectResultInErrorState; /** * Type representing a {@link CombinedEffectResult} in it's success state (pending or non-pending) * * @template Input - specifies the input type for the effect * @template Result - specifies the result type of the effect */ export type CombinedEffectResultInSuccessState = { currentInput: Input | NoValueType; result: Result; resultInput: Input | NoValueType; resultPending: boolean; }; /** * Typeguard to check if a {@link CombinedEffectResult} is a {@link CombinedEffectResultInSuccessState} */ export declare const isCombinedEffectResultInSuccessState: (cer: CombinedEffectResult) => cer is CombinedEffectResultInSuccessState; /** * Type representing a {@link CombinedEffectResult} in it's success state (non-pending, hece completed effect) * * @template Input - specifies the input type for the effect * @template Result - specifies the result type of the effect */ export type CombinedEffectResultInCompletedSuccessState = { currentInput: Input | NoValueType; result: Result; resultInput: Input | NoValueType; resultPending: false; }; /** * Typeguard to check if a {@link CombinedEffectResult} is a {@link CombinedEffectResultInCompletedSuccessState} */ export declare const isCombinedEffectResultInCompletedSuccessState: (cer: CombinedEffectResult) => cer is CombinedEffectResultInCompletedSuccessState; /** * Value-type for result events produced by {@link EffectSignals}. * In case the effect completes after one result, two reult events will * be dispatched, one with completed false and one with completed true. * This is to handle cases where the effect might send multiple results * before completing. Thus, if an effect never completes, all result events * will have completed false. * * @template Input - specifies the input type for the effect * @template Result - specifies the result type of the effect * @template Error - specifies the error type of the effect */ export type EffectResultEvent = { /** the effect result */ result: SafeEffectResult; /** the effect input that lead to the result */ resultInput: Input; /** the input of the previous completed result, or `NO_VALUE` */ /** the previous completed result, or `NO_VALUE` */ /** has the effect for the given resultInput completed */ completed: boolean; }; /** * Value-type for completed result events produced by {@link EffectSignals}. * In contrast to {@link EffectResultEvent}, events with this type are only dispatched, * if the effect has completed, hence it will never be fired for effects that never complete. * * @template Input - specifies the input type for the effect * @template Result - specifies the result type of the effect * @template Error - specifies the error type of the effect */ export type EffectCompletedResultEvent = Omit, 'completed'> & { completed: true; }; /** * Typeguard to check if a {@link EffectResultEvent} is a {@link EffectCompletedResultEvent} */ export declare const isCompletedResultEvent: (value: EffectResultEvent | EffectCompletedResultEvent) => value is EffectCompletedResultEvent; export type EffectCompletedResultEventInSuccessState = { result: Result; resultInput: Input; completed: true; }; export type EffectCompletedResultEventInErrorState = { result: ToEffectError | EffectError; resultInput: Input; completed: true; }; export declare const isCompletedResultEventInSuccessState: (value: EffectCompletedResultEvent) => value is EffectCompletedResultEventInSuccessState; export declare const isCompletedResultEventInErrorState: (value: EffectCompletedResultEvent) => value is EffectCompletedResultEventInErrorState; /** * Type specifying the input {@link EffectSignals} (the corresponding signal-sources are NOT added to the store * by the EffectSignals-setup, but by whoever uses the signals, e.g. by extendSetup or fmap or just using dispatch). * The {@link EffectSignalsFactory} gives you the guarantee, that invalidate-events are NOT missed, even while * the combined-behavior is not subscribed. * * @template Input - specifies the input type for the effect */ export type EffectInputSignals = { /** Behavior being consumed by EffectSignals as input (see {@link EffectConfiguration} on how to configure your factory to subscribe the corresponding behavior eagerly) */ input: DerivedId; /** Event that can be dispatched to trigger re-evaluation of the current input under the given effect */ invalidate: EventId; /** * Event that can be dispatched to trigger the given effect. * This event has only meaning, if `withTrigger` is configured (see `EffectConfiguration`), * else dispatching it is a no-op. */ trigger: EventId; }; /** * Type specifying the output {@link EffectSignals} (signals produced by `EffectSignals`). * The {@link EffectSignalsFactory} takes care that subscribing the result-events keeps * the effect itself lazy (hence only subscribing the combined behavior will subscribe the effect itself)! * * @template Input - specifies the input type for the effect * @template Result - specifies the result type of the effect * @template Error - specifies the error type of the effect */ export type EffectOutputSignals = { /** Produced combined effect result behavior */ combined: DerivedId>; /** Produced success events */ results: EventId>; /** Produced success events */ completedResults: EventId>; }; /** * Type specifying the effect-type of {@link EffectSignals} (the corresponding effects are NOT added to the store * by the EffectSignals-setup, but by whoever uses the signals, e.g. by `useExistingEffect` or via extended configuration plus `extendSetup`). * * @template Input - specifies the input type for the effect * @template Result - specifies the result type of the effect * @template Error - specifies the error type of the effect */ export type EffectFactoryEffects = { id: EffectId; }; /** * This type specifies generic effect signals. `EffectSignals` generically handle side-effects (hence, are an abstraction over side-effects). * They fulfill the following requirements: * ```markdown * 1.) The produced CombinedEffectResult behavior must be lazy, hence, as long as it is not subscribed, * no effect will be triggered (so subscribing just results, or completedResults will not trigger the effect). * 2.) Unhandled effect errors are caught and will lead to an EffectError. * 3.) In addition to the combined-behavior, also event-streams for EffectResultEvent and EffectCompletedResultEvent are provided. This is important * in cases where e.g. an effect success should be used to trigger something else (e.g. close a popup), but you cannot use the result * behavior, because it would mean to always subscribe the result. In contrast, subscription of the an rsult-event-stream will NOT * subscribe the effect. * ``` * * See the documentation for {@link EffectConfiguration} for further configuration of `EffectSignals`. * * @template Input - specifies the input type for the effect * @template Result - specifies the result type of the effect * @template Error - specifies the error type of the effect */ export type EffectSignals = Signals, EffectOutputSignals, EffectFactoryEffects>; /** * This type specifies the type of the argument to {@link EffectSignalsBuild}, hence the configuration of {@link EffectSignals}. * * @template Input - specifies the input type for the effect * @template Result - specifies the result type of the effect * @template Error - specifies the error type of the effect */ export type EffectConfiguration = { /** Function used to determine whether a new input equals the previous one. Defaults to strict equals (`a === b`) */ effectInputEquals?: (a: Input, b: Input) => boolean; /** Defaults to false. If true, the effect will only be performed in case a trigger event is received (else, whenever the input changes) */ withTrigger?: boolean; /** If defined, this function will be used to determine an initial result for the result behavior */ initialResultGetter?: () => Result; /** If defined and `>0`, then it will be used as milliseconds to debounce new input to the effect (please DON'T debounce the input signal yourself, because that would debounce before trigger and/or input equals!) */ effectDebounceTime?: number; /** Function to wrap the effect defined by effectId with a custom `Effect` */ wrappedEffectGetter?: (effect: Effect) => Effect; /** Specifies whether the input behavior should be subscribed eagerly (defaults to false) */ eagerInputSubscription?: boolean; /** Optional string to be used as argument to calls of `getBehaviorId` and `getEventId` */ nameExtension?: string; }; /** * Type specifying the {@link SignalsBuild} function for {@link EffectSignals}, hence a function taking a {@link EffectConfiguration} and producing {@link EffectSignals}. * * @template Input - specifies the input type for the effect * @template Result - specifies the result type of the effect * @template Error - specifies the error type of the effect * @param {EffectConfiguration} config - the configuration for the `EffectSignals` */ export type EffectSignalsBuild = (config: EffectConfiguration) => EffectSignals; /** * This type specifies a {@link SignalsFactory} wrapping {@link EffectSignals}. * * @template Input - specifies the input type for the effect * @template Result - specifies the result type of the effect * @template Error - specifies the error type of the effect */ export type EffectSignalsFactory = SignalsFactory, EffectOutputSignals, EffectConfiguration, EffectFactoryEffects>; /** * This function creates an {@link EffectSignalsFactory}. * * @template Input - specifies the input type for the effect * @template Result - specifies the result type of the effect * @template Error - specifies the error type of the effect * @returns {EffectSignalsFactory} */ export declare const getEffectSignalsFactory: () => EffectSignalsFactory;