File "compat.d.ts"

Full Path: /var/www/html/gitep_front/node_modules/effector/compat.d.ts
File size: 109.95 KB
MIME-type: text/plain
Charset: utf-8

/**
 * This tuple type is intended for use as a generic constraint to infer concrete
 * tuple type of ANY length.
 *
 * @see https://github.com/krzkaczor/ts-essentials/blob/a4c2485bc3f37843267820ec552aa662251767bc/lib/types.ts#L169
 */
type Tuple<T = unknown> = [T?, ...T[]]
type RoTuple<T = unknown> = readonly [T?, ...T[]]

/**
 * Non inferential type parameter usage. NoInfer in source and in return of fn helps with
 detecting loose objects against target type.
 *
 * @see https://github.com/microsoft/TypeScript/issues/14829#issuecomment-504042546
 */
type NoInfer<T> = [T][T extends any ? 0 : never]

/**
 * Generic Json type
 */
 type Json =
 | null
 | undefined
 | boolean
 | string
 | number
 | Json[]
 | {[k: string]: Json}

// Type for extention purpose. Represents combinable sample source.
export type Combinable = {[key: string]: Store<any>} | Tuple<Store<any>>
// Helper type, which unwraps combinable sample source value.
export type GetCombinedValue<T> = Show<{
  [K in keyof T]: T[K] extends Store<infer U> ? U : never
}>

export type StoreValue<T> = T extends Store<infer S> ? S : never
export type EventPayload<T> = T extends Event<infer P> ? P : never
export type UnitValue<T> = T extends Unit<infer V> ? V : never
export type EffectParams<FX extends Effect<any, any, any>> = FX extends Effect<
  infer P,
  any,
  any
>
  ? P
  : never
export type EffectResult<FX extends Effect<any, any, any>> = FX extends Effect<
  any,
  infer D,
  any
>
  ? D
  : never
export type EffectError<FX extends Effect<any, any, any>> = FX extends Effect<
  any,
  any,
  infer E
>
  ? E
  : never

// Taken from the source code of typescript 4.5. Remove when we separate types for different versions
/**
 * Recursively unwraps the "awaited type" of a type. Non-promise "thenables" should resolve to `never`. This emulates the behavior of `await`.
 */
type Awaited<T> = T extends null | undefined
  ? T // special case for `null | undefined` when not in `--strictNullChecks` mode
  : T extends object // `await` only unwraps object types with a callable then. Non-object types are not unwrapped.
  ? T extends {then(onfulfilled: infer F): any} // thenable, extracts the first argument to `then()`
    ? F extends (value: infer V) => any // if the argument to `then` is callable, extracts the argument
      ? Awaited<V> // recursively unwrap the value
      : never // the argument to `then` was not callable.
    : T // argument was not an object
  : T // non-thenable

type OptionalParams<Args extends any[]> =
  Args['length'] extends 0 // does handler accept 0 arguments?
    ? void // works since TS v3.3.3
    : 0 | 1 extends Args['length']  // is the first argument optional?
      /**
       * Applying `infer` to a variadic arguments here we'll get `Args` of
       * shape `[T]` or `[T?]`, where T(?) is a type of handler `params`.
       * In case T is optional we get `T | undefined` back from `Args[0]`.
       * We lose information about argument's optionality, but we can make it
       * optional again by appending `void` type, so the result type will be
       * `T | undefined | void`.
       *
       * The disadvantage of this method is that we can't restore optonality
       * in case of `params?: any` because in a union `any` type absorbs any
       * other type (`any | undefined | void` becomes just `any`). And we
       * have similar situation also with the `unknown` type.
       */
    ? Args[0] | void
    : Args[0]
type EffectByHandler<FN extends Function, Fail> = FN extends (...args: infer Args) => infer Done
  ? Effect<OptionalParams<Args>, Awaited<Done>, Fail>
  : never

export const version: string

export type kind = 'store' | 'event' | 'effect' | 'domain' | 'scope'

export type Observer<A> = {
  readonly next?: (value: A) => void
  //error(err: Error): void
  //complete(): void
}

export type Observable<T> = {
  subscribe: (observer: Observer<T>) => Subscription
}

export type Subscription = {
  (): void
  unsubscribe(): void
}

export interface Unit<T> {
  readonly kind: kind
  readonly __: T
}

export interface UnitTargetable<T> extends Unit<T> {
  readonly targetable: true
}

export type CompositeName = {
  shortName: string
  fullName: string
  path: Array<string>
}

/**
 * This is a workaround for https://github.com/microsoft/TypeScript/issues/35162
 *
 * The problem was that we couldn't use guard as sample's clock parameter because
 * sample's clock-related generic inferred as `unknown` in cases when guard returned
 * `Event<T>`. This happens because `Event` has a callable signature. With `Unit<T>`
 * as the return type we won't see any problems.
 */
type EventAsReturnType<Payload> = any extends Payload ? Event<Payload> : never
type EventCallableAsReturnType<Payload> = any extends Payload ? EventCallable<Payload> : never

/**
 * Event you can subscribe to.
 * It represents a user action, a step in the application process, a command to execute, or an intention to make modifications, among other things.
 */
export interface Event<Payload> extends Unit<Payload> {
  kind: "event"
  map<T>(fn: (payload: Payload) => T): EventAsReturnType<T>
  filter<T extends Payload>(config: {
    fn(payload: Payload): payload is T
  }): EventAsReturnType<T>
  filter(config: {fn(payload: Payload): boolean}): EventAsReturnType<Payload>
  filterMap<T>(fn: (payload: Payload) => T | undefined): EventAsReturnType<T>

  watch(watcher: (payload: Payload) => any): Subscription
  subscribe(observer: Observer<Payload>): Subscription
  /**
   * @deprecated use .compositeName.fullName instead
   */
  getType(): string

  compositeName: CompositeName
  sid: string | null
  shortName: string
}

/**
 * The function you can call to trigger an event.
 */
export interface EventCallable<Payload> extends Event<Payload>, UnitTargetable<Payload> {
  kind: "event"
  (payload: Payload): Payload
   (this: IfUnknown<Payload, void, Payload extends void ? void : `Error: Expected 1 argument, but got 0`>, payload?: Payload): void

  prepend<Before = void>(fn: (_: Before) => Payload): EventCallable<Before>
}

/**
 * Container for (possibly async) side effects
 */
export interface Effect<Params, Done, Fail = Error> extends UnitTargetable<Params> {
  kind: "effect"
  (params: Params): Promise<Done>
  readonly done: Event<{params: Params; result: Done}>
  readonly doneData: Event<Done>
  readonly fail: Event<{params: Params; error: Fail}>
  readonly failData: Event<Fail>
  readonly finally: Event<
    | {
        status: 'done'
        params: Params
        result: Done
      }
    | {
        status: 'fail'
        params: Params
        error: Fail
      }
  >
  readonly use: {
    (handler: (params: Params) => Promise<Done> | Done): Effect<
      Params,
      Done,
      Fail
    >
    getCurrent(): (params: Params) => Promise<Done>
  }
  pending: Store<boolean>
  inFlight: Store<number>
  watch(watcher: (payload: Params) => any): Subscription
  filter<T extends Params>(config: {
    fn(payload: Params): payload is T
  }): EventAsReturnType<T>
  filter(config: {fn(payload: Params): boolean}): EventAsReturnType<Params>
  filterMap<T>(fn: (payload: Params) => T | undefined): EventAsReturnType<T>
  map<T>(fn: (params: Params) => T): EventAsReturnType<T>
  prepend<Before>(fn: (_: Before) => Params): EventCallable<Before>
  subscribe(observer: Observer<Params>): Subscription
  getType(): string
  compositeName: CompositeName
  sid: string | null
  shortName: string
}
type InferValueFromTupleOfUnits<T extends Tuple<Unit<any>>> =
  T[number] extends Unit<infer R> ? R : never
type InferValueFromTupleOfUnitTargetables<T extends Tuple<UnitTargetable<any>>> =
  T[number] extends UnitTargetable<infer R>? R : never

export interface Store<State> extends Unit<State> {
  kind: "store"
  map<T>(fn: (state: State) => T, config?: {skipVoid?: boolean}): Store<T>

  updates: Event<State>

  getState(): State
  subscribe(listener: Observer<State> | ((state: State) => any)): Subscription
  watch<E>(watcher: (state: State, payload: undefined) => any): Subscription
  watch<E>(
    trigger: Unit<E>,
    watcher: (state: State, payload: E) => any,
  ): Subscription
  /**
   * @deprecated use js pipe instead
   */
  thru<U>(fn: (store: Store<State>) => U): U

  defaultState: State
  compositeName: CompositeName
  shortName: string
  sid: string | null
}

/**
 * Hacky way to force TS perform checks against unsafe widening
 */
interface StoreValueType<X> {
  _: X
  (type: X): void
}

export interface StoreWritable<State> extends Store<State>, UnitTargetable<State> {
  kind: "store"
  readonly ____: StoreValueType<State>

  on<E>(
    trigger: Unit<E>,
    reducer: (state: State, payload: E) => State | void,
  ): this
  on<E>(
    triggers: Unit<E>[],
    reducer: (state: State, payload: E) => State | void,
  ): this
  on<E extends Tuple<Unit<any>>>(
    triggers: E,
    reducer: (state: State, payload: InferValueFromTupleOfUnits<E>) => State | void,
  ): this
  off(trigger: Unit<any>): this
  reset(...triggers: Array<Unit<any>>): this
  reset(triggers: Array<Unit<any>>): this

  reinit: EventCallable<void>
}

interface InternalStore<State> extends StoreWritable<State> {
  setState(state: State): void
}

export const is: {
  unit(obj: unknown): obj is Unit<any> | UnitTargetable<any>

  store<O, T>(
    obj: O | Unit<T> | UnitTargetable<T>,
  ): obj is typeof obj extends Unit<T>
    ? Store<T> | StoreWritable<T>
    : Store<any> | StoreWritable<any>

  event<O, T>(
    obj: O | Unit<T> | UnitTargetable<T>
  ): obj is typeof obj extends Unit<T>
    ? Event<T> | EventCallable<T>
    : Event<any> | EventCallable<any>

  effect<O, T, P, F>(
    obj: O | Effect<T, P, F>
  ): obj is Effect<T, P, F>

  domain(obj: unknown): obj is Domain
  scope(obj: unknown): obj is Scope
  attached<E extends Effect<any, any, any>>(obj: unknown): obj is E

  targetable<T>(obj: Unit<T>): obj is UnitTargetable<T>
}

/**
 * A way to group and process events, stores and effects. Useful for logging and assigning a reset trigger to many effects.
 * Domain is notified via onCreateEvent, onCreateStore, onCreateEffect, onCreateDomain methods when events, stores, effects, or nested domains are created
 */
export class Domain implements Unit<any> {
  readonly kind: kind
  readonly __: any
  onCreateEvent(hook: (newEvent: EventCallable<unknown>) => any): Subscription
  onCreateEffect(
    hook: (newEffect: Effect<unknown, unknown, unknown>) => any,
  ): Subscription
  onCreateStore(
    hook: (newStore: InternalStore<unknown>) => any,
  ): Subscription
  onCreateDomain(hook: (newDomain: Domain) => any): Subscription
  event<Payload = void>(name?: string): EventCallable<Payload>
  event<Payload = void>(config: {name?: string; sid?: string}): EventCallable<Payload>
  createEvent<Payload = void>(name?: string): EventCallable<Payload>
  createEvent<Payload = void>(config: {
    name?: string
    sid?: string
  }): EventCallable<Payload>
  effect<FN extends Function>(handler: FN): EffectByHandler<FN, Error>
  effect<Params, Done, Fail = Error>(
    handler: (params: Params) => Done | Promise<Done>,
  ): Effect<Params, Done, Fail>
  effect<FN extends Function, Fail>(handler: FN): EffectByHandler<FN, Fail>
  effect<Params, Done, Fail = Error>(
    name?: string,
    config?: {
      handler?: (params: Params) => Promise<Done> | Done
      sid?: string
    },
  ): Effect<Params, Done, Fail>
  effect<Params, Done, Fail = Error>(config: {
    handler?: (params: Params) => Promise<Done> | Done
    sid?: string
    name?: string
  }): Effect<Params, Done, Fail>
  createEffect<FN extends Function>(handler: FN): EffectByHandler<FN, Error>
  createEffect<Params, Done, Fail = Error>(
    handler: (params: Params) => Done | Promise<Done>,
  ): Effect<Params, Done, Fail>
  createEffect<FN extends Function, Fail>(handler: FN): EffectByHandler<FN, Fail>
  createEffect<FN extends Function>(config: {
    name?: string
    handler: FN
    sid?: string
  }): EffectByHandler<FN, Error>
  createEffect<Params, Done, Fail = Error>(
    name?: string,
    config?: {
      handler?: (params: Params) => Promise<Done> | Done
      sid?: string
    },
  ): Effect<Params, Done, Fail>
  createEffect<Params, Done, Fail = Error>(config: {
    handler?: (params: Params) => Promise<Done> | Done
    sid?: string
    name?: string
  }): Effect<Params, Done, Fail>
  domain(name?: string): Domain
  createDomain(name?: string): Domain
  store<State, SerializedState extends Json = Json>(
    defaultState: State,
    config?: {
      name?: string
      sid?: string
      updateFilter?: (update: State, current: State) => boolean
      serialize?:
      | 'ignore'
      | {
          write: (state: State) => SerializedState
          read: (json: SerializedState) => State
        }
    },
  ): StoreWritable<State>
  createStore<State, SerializedState extends Json = Json>(
    defaultState: State,
    config?: {
      name?: string
      sid?: string
      updateFilter?: (update: State, current: State) => boolean
      serialize?:
        | 'ignore'
        | {
            write: (state: State) => SerializedState
            read: (json: SerializedState) => State
          }
    },
  ): StoreWritable<State>
  sid: string | null
  compositeName: CompositeName
  shortName: string
  getType(): string
  history: {
    domains: Set<Domain>
    stores: Set<StoreWritable<any>>
    effects: Set<Effect<any, any, any>>
    events: Set<EventCallable<any>>
  }
}

export type ID = string
export type StateRefOp =
  | {type: 'map'; from?: StateRef; fn?: (value: any) => any}
  | {type: 'field'; from: StateRef; field: string}
  | {type: 'closure'; of: StateRef}
export type StateRef = {
  id: ID
  current: any
  type?: 'list' | 'shape'
  before?: StateRefOp[]
  noInit?: boolean
  sid?: string
}

export type Stack = {
  value: any
  a: any
  b: any
  parent?: Stack
  node: Node
  page?: any
  scope?: Scope
  meta?: Record<string, any>
}

type BarrierPriorityTag = 'barrier' | 'sampler' | 'effect'

type FromValue = {
  from: 'value'
  store: any
}
type FromStore = {
  from: 'store'
  store: StateRef
}
type FromRegister = {
  from: 'a' | 'b' | 'stack'
}

type ToRegister = {
  to: 'a' | 'b' | 'stack'
}
type ToStore = {
  to: 'store'
  target: StateRef
}

type MoveCmd<Data> = {
  id: ID
  type: 'mov'
  data: Data
  order?: {
    priority: BarrierPriorityTag
    barrierID?: number
  }
}

export type Cmd =
  | Compute
  | Mov

type MovValReg = MoveCmd<FromValue & ToRegister>
type MovValStore = MoveCmd<FromValue & ToStore>
type MovStoreReg = MoveCmd<FromStore & ToRegister>
type MovStoreStore = MoveCmd<FromStore & ToStore>
type MovRegReg = MoveCmd<FromRegister & ToRegister>
type MovRegStore = MoveCmd<FromRegister & ToStore>

export type Mov =
  | MovValReg
  | MovValStore
  | MovStoreReg
  | MovStoreStore
  | MovRegReg
  | MovRegStore

export type Compute = {
  id: ID
  type: 'compute'
  data: {
    fn?: (data: any, scope: {[key: string]: any}, reg: Stack) => any
    safe: boolean
    filter: boolean
    pure: boolean
  }
  order?: {
    priority: BarrierPriorityTag
    barrierID?: number
  }
}
export type Node = {
  id: ID
  next: Array<Node>
  seq: Array<Cmd>
  scope: {[field: string]: any}
  meta: {[field: string]: any}
  family: {
    type: 'regular' | 'crosslink' | 'domain'
    links: Node[]
    owners: Node[]
  }
}

export const step: {
  compute(data: {
    fn?: (data: any, scope: {[key: string]: any}, stack: Stack) => any
    batch?: boolean
    priority?: BarrierPriorityTag | false
    safe?: boolean
    filter?: boolean
    pure?: boolean
  }): Compute
  filter(data: {
    fn: (data: any, scope: {[field: string]: any}, stack: Stack) => boolean
    pure?: boolean
  }): Compute
  run(data: {fn: (data: any, scope: {[field: string]: any}, stack: Stack) => any}): Compute
  mov(data: {
    from?: 'value' | 'store' | 'stack' | 'a' | 'b'
    to?: 'stack' | 'a' | 'b' | 'store'
    store?: StateRef
    target?: StateRef
    batch?: boolean
    priority?: BarrierPriorityTag | false
  }): Mov
}

/* `forward` types */
type ForwardTarget = UnitTargetable<unknown> | ReadonlyArray<UnitTargetable<unknown>>

type CleanSingleTarget<
  Target extends UnitTargetable<unknown>,
  Clock,
> = Target extends UnitTargetable<infer T>
  ? T extends void
    ? UnitTargetable<unknown>
    : T extends Clock
    ? UnitTargetable<T>
    // Needed to force typecheck
    : UnitTargetable<Clock>
  : never

  type CleanTarget<
    Target extends ForwardTarget,
    From,
  > = Target extends UnitTargetable<any>
    ? CleanSingleTarget<Target, From>
    : {
        [K in keyof Target]: Target[K] extends UnitTargetable<unknown>
          ? CleanSingleTarget<Target[K], From>
          : never
      }

/**
 * Method to create connection between units in a declarative way. Sends updates from one set of units to another
 * @deprecated use `sample({clock, target})` instead
 */
export function forward<From, T extends ForwardTarget>(opts: {
  /**
   * By default TS picks "best common type" `T` between `from` and `to` arguments.
   * This lets us forward from `string | number` to `string` for instance, and
   * this is wrong.
   *
   * Fortunately we have a way to disable such behavior. By adding `& {}` to some
   * generic type we tell TS "do not try to infer this generic type from
   * corresponding argument type".
   *
   * Generic `T` won't be inferred from `from` any more. Forwarding from "less
   * strict" to "more strict" will produce an error as expected.
   *
   * @see https://www.typescriptlang.org/docs/handbook/type-inference.html#best-common-type
   */
  from: Unit<From & {}>
  to: CleanTarget<T, From>
}): Subscription
/**
 * Method to create connection between units in a declarative way. Sends updates from one set of units to another
 * @deprecated use `sample({clock, target})` instead
 */
export function forward(opts: {
  from: Unit<any>
  to: ReadonlyArray<UnitTargetable<void>>
}): Subscription
/**
 * Method to create connection between units in a declarative way. Sends updates from one set of units to another
 * @deprecated use `sample({clock, target})` instead
 */
export function forward(opts: {
  from: ReadonlyArray<Unit<any>>
  to: ReadonlyArray<UnitTargetable<void>>
}): Subscription
/**
 * Method to create connection between units in a declarative way. Sends updates from one set of units to another
 * @deprecated use `sample({clock, target})` instead
 */
export function forward(opts: {
  from: ReadonlyArray<Unit<any>>
  to: UnitTargetable<void>
}): Subscription
/**
 * Method to create connection between units in a declarative way. Sends updates from one set of units to another
 * @deprecated use `sample({clock, target})` instead
 */
export function forward<To, From extends To>(opts: {
  from: ReadonlyArray<Unit<From>>
  to: UnitTargetable<To> | ReadonlyArray<UnitTargetable<To>>
}): Subscription
// Allow `* -> void` forwarding (e.g. `string -> void`).
/**
 * Method to create connection between units in a declarative way. Sends updates from one set of units to another
 * @deprecated use `sample({clock, target})` instead
 */
export function forward(opts: {from: Unit<any>; to: UnitTargetable<void>}): Subscription
// Do not remove the signature below to avoid breaking change!
/**
 * Method to create connection between units in a declarative way. Sends updates from one set of units to another
 * @deprecated use `sample({clock, target})` instead
 */
export function forward<To, From extends To>(opts: {
  from: Unit<From>
  to: UnitTargetable<To> | ReadonlyArray<UnitTargetable<To>>
}): Subscription

/**
 * Merges array of units (events, effects or stores), returns a new event, which fires upon trigger of any of given units
 * @param units array of units to be merged
 */
export function merge<T>(units: ReadonlyArray<Unit<T>>): EventAsReturnType<T>
/**
 * Merges array of units (events, effects or stores), returns a new event, which fires upon trigger of any of given units
 * @param units array of units to be merged
 */
export function merge<T extends ReadonlyArray<Unit<any>>>(
  units: T,
): T[number] extends Unit<infer R> ? Event<R> : never
/**
 * Method for destroying units and graph nodes. Low level tool, usually absent in common applications
 * @param unit unit to be erased
 * @param opts optional configuration object
 */
export function clearNode(unit: Unit<any> | Node, opts?: {deep?: boolean}): void
/**
 * Method to create a new graph node. Low level tool, usually absent in common applications
 */
export function createNode(opts?: {
  node?: Array<Cmd | false | void | null>
  parent?: Array<Unit<any> | Node>
  child?: Array<Unit<any> | Node>
  scope?: {[field: string]: any}
  meta?: {[field: string]: any}
  family?: {
    type?: 'regular' | 'crosslink' | 'domain'
    owners?: Unit<any> | Node | Array<Unit<any> | Node>
    links?: Unit<any> | Node | Array<Unit<any> | Node>
  }
  regional?: boolean
}): Node
/**
 * Allows to directly start computation from given unit or graph node. Low level tool, usually absent in common applications
 * @param unit unit or graph node to launch
 * @param payload data to pass to computation
 */
export function launch<T>(unit: Unit<T> | Node, payload: T): void
/**
 * Allows to directly start computation from given unit or graph node. Low level tool, usually absent in common applications
 * @param config configuration object
 */
export function launch<T>(config: {
  target: Unit<T> | Node
  params: T
  defer?: boolean
  page?: any
  scope?: Scope
  meta?: Record<string, any>
}): void
/**
 * Allows to directly start computation from given unit or graph node. Low level tool, usually absent in common applications
 * @param config configuration object
 */
export function launch(config: {
  target: Array<Unit<any> | Node>
  params: any[]
  defer?: boolean
  page?: any
  scope?: Scope
  meta?: Record<string, any>
}): void

/**
 * Method to create an event subscribed to given observable
 * @param observable object with `subscribe` method, e.g. rxjs stream or redux store
 */
export function fromObservable<T>(observable: unknown): Event<T>
/**
 * Creates an event
 */
export function createEvent<E = void>(eventName?: string): EventCallable<E>
/**
 * Creates an event
 */
export function createEvent<E = void>(config: {
  name?: string
  sid?: string
  domain?: Domain
}): EventCallable<E>

/**
 * Creates an effect
 * @param handler function to handle effect calls
 */
export function createEffect<FN extends Function>(handler: FN): EffectByHandler<FN, Error>
/**
 * Creates an effect
 * @param handler function to handle effect calls
 */
export function createEffect<Params, Done, Fail = Error>(
  handler: (params: Params) => Done | Promise<Done>,
): Effect<Params, Done, Fail>
/**
 * Creates an effect
 * @param handler function to handle effect calls
 */
export function createEffect<FN extends Function, Fail>(handler: FN): EffectByHandler<FN, Fail>
/**
 * Creates an effect
 */
export function createEffect<FN extends Function>(name: string, config: {
  handler: FN
  sid?: string
  domain?: Domain
}): EffectByHandler<FN, Error>
/**
 * Creates an effect
 */
export function createEffect<Params, Done, Fail = Error>(
  effectName?: string,
  config?: {
    handler?: (params: Params) => Promise<Done> | Done
    sid?: string
    domain?: Domain
  },
): Effect<Params, Done, Fail>
/**
 * Creates an effect
 */
export function createEffect<FN extends Function>(config: {
  name?: string
  handler: FN
  sid?: string
  domain?: Domain
}): EffectByHandler<FN, Error>
/**
 * Creates an effect
 */
export function createEffect<Params, Done, Fail = Error>(config: {
  name?: string
  handler?: (params: Params) => Promise<Done> | Done
  sid?: string
  domain?: Domain
}): Effect<Params, Done, Fail>

/**
 * Creates a store
 * @param defaultState default state
 * @param config optional configuration object
 */
export function createStore<State, SerializedState extends Json = Json>(
  defaultState: State,
  config?: {
    skipVoid?: boolean;
    name?: string;
    sid?: string
    updateFilter?: (update: State, current: State) => boolean
    serialize?:
    | 'ignore'
    | {
        write: (state: State) => SerializedState
        read: (json: SerializedState) => State
      }
    domain?: Domain;
  },
): StoreWritable<State>
export function setStoreName<State>(store: Store<State>, name: string): void

/**
 * Chooses one of the cases by given conditions. It "splits" source unit into several events, which fires when payload matches their conditions.
 * Works like pattern matching for payload values and external stores
 * @param source unit which will trigger computation in split
 * @param match object with matching functions which allows to trigger one of created events
 */
export function split<
  S,
  Match extends {[name: string]: (payload: S) => boolean}
>(
  source: Unit<S>,
  match: Match,
): Show<{
  [K in keyof Match]: Match[K] extends (p: any) => p is infer R
    ? Event<R>
    : Event<S>
} & {__: Event<S>}>

type SplitType<
  Cases extends CaseRecord,
  Match,
  Config,
  Source extends Unit<any>,
> =
  UnitValue<Source> extends CaseTypeReader<Cases, keyof Cases>
    ?
      Match extends Unit<any>
      ? Exclude<keyof Cases, '__'> extends UnitValue<Match>
        ? Config
        : {
          error: 'match unit should contain case names'
          need: Exclude<keyof Cases, '__'>
          got: UnitValue<Match>
        }

      : Match extends (p: UnitValue<Source>) => void
        ? Exclude<keyof Cases, '__'> extends ReturnType<Match>
          ? Config
          : {
            error: 'match function should return case names'
            need: Exclude<keyof Cases, '__'>
            got: ReturnType<Match>
          }

      : Match extends Record<string, ((p: UnitValue<Source>) => boolean) | Store<boolean>>
        ? Exclude<keyof Cases, '__'> extends keyof Match
          ? MatcherInferenceValidator<Cases, Match> extends Match
            ? Config
            : {
              error: 'case should extends type inferred by matcher function'
              incorrectCases: Show<MatcherInferenceIncorrectCases<Cases, Match>>
            }
          : {
            error: 'match object should contain case names'
            need: Exclude<keyof Cases, '__'>
            got: keyof Match
          }

      : {error: 'not implemented'}

  : {
    error: 'source type should extends cases'
    sourceType: UnitValue<Source>
    caseType: CaseTypeReader<Cases, keyof Cases>
  }

/**
 * Chooses one of cases by given conditions. It "splits" source unit into several targets, which fires when payload matches their conditions.
 * Works like pattern matching for payload values and external units
 */
export function split<
  Cases,
  Source,
  Match extends (
    | Unit<any>
    | ((p: UnitValue<Source>) => void)
    | Record<string, ((p: UnitValue<Source>) => boolean) | Store<boolean>>
  ),
  Clock,
>(
  config:
    {source: Source; match: Match; cases: Cases; clock: Clock} extends infer Config
    ?
      Config extends {cases: CaseRecord; match: any; source: Unit<any>; clock: Unit<any> | Array<Unit<any>>}
        ? Source extends Unit<any>
          ? Cases extends CaseRecord
            ? Clock extends Unit<any> | Array<Unit<any>>
              ? SplitType<Cases, Match, {source: Source; match: Match; cases: Cases; clock: Clock}, Source>
              : {error: 'clock should be a unit or array of units'; got: Clock}
            : {error: 'cases should be an object with units or arrays of units'; got: Cases}
          : {error: 'source should be a unit'; got: Source}

      : Config extends {cases: CaseRecord; match: any; source: Unit<any>}
        ? Source extends Unit<any>
          ? Cases extends CaseRecord
            ? SplitType<Cases, Match, {source: Source; match: Match; cases: Cases}, Source>
            : {error: 'cases should be an object with units or arrays of units'; got: Cases}
          : {error: 'source should be a unit'; got: Source}

      : {error: 'config should be object with fields "source", "match" and "cases"'; got: Config}

    : {error: 'cannot infer config object'}
): void

type CaseRecord = Record<string,  Unit<any> | Array<Unit<any>>>

type MatcherInferenceIncorrectCases<Cases, Match> = {
  [K in Exclude<keyof Match, keyof MatcherInferenceValidator<Cases, Match>>]: {
    caseType: CaseValue<Cases, Match, K>
    inferredType: Match[K] extends (p: any) => p is infer R ? R : never
  }
}

type MatcherInferenceValidator<Cases, Match> = {
  [
    K in keyof Match as
      Match[K] extends (p: any) => p is infer R
      ? R extends CaseValue<Cases, Match, K>
        ? K
        : never
      : K
  ]: Match[K]
}

type CaseTypeReader<Cases, K extends keyof Cases> =
  Cases[K] extends infer S
  ? WhichType<
    UnitValue<
      S extends Array<any>
      ? S[number]
      : S
    >
  > extends 'void'
    ? unknown
    : UnitValue<
      S extends Array<any>
      ? S[number]
      : S
    >
  : never

type CaseValue<Cases, Match, K extends keyof Match> =
  K extends keyof Cases
  ? CaseTypeReader<Cases, K>
  : never

/**
 * Shorthand for creating events attached to store by providing object with reducers for them
 * @param store target store
 * @param api object with reducers
 */
export function createApi<
  S,
  Api extends {[name: string]: ((store: S, e: any) => (S | void))}
>(
  store: StoreWritable<S>,
  api: Api,
): {
  [K in keyof Api]: ((store: S, e: void) => (S | void)) extends Api[K]
    ? EventCallable<void>
    : Api[K] extends ((store: S) => (S | void))
    ? EventCallable<void>
    : Api[K] extends ((store: S, e: infer E) => (S | void))
    ? EventCallable<E extends void ? Exclude<E, undefined> | void : E>
    : any
}

/**
 * Creates a Store out of successful results of Effect.
 * It works like a shortcut for `createStore(defaultState).on(effect.done, (_, {result}) => result)`
 * @param effect source effect
 * @param defaultState initial state of new store
 */
export function restore<Done>(
  effect: Effect<any, Done, any>,
  defaultState: Done,
): StoreWritable<Done>
/**
 * Creates a Store out of successful results of Effect.
 * It works like a shortcut for `createStore(defaultState).on(effect.done, (_, {result}) => result)`
 * @param effect source effect
 * @param defaultState initial state of new store
 */
export function restore<Done>(
  effect: Effect<any, Done, any>,
  defaultState: null,
): StoreWritable<Done | null>
/**
 * Creates a Store from Event.
 * It works like a shortcut for `createStore(defaultState).on(event, (_, payload) => payload)`
 * @param event source event
 * @param defaultState initial state of new store
 */
export function restore<E>(event: Event<E>, defaultState: E): StoreWritable<E>
/**
 * Creates a Store from Event.
 * It works like a shortcut for `createStore(defaultState).on(event, (_, payload) => payload)`
 * @param event source event
 * @param defaultState initial state of new store
 */
export function restore<E>(event: Event<E>, defaultState: null): StoreWritable<E | null>
export function restore<T extends Event<any>>(event: T): never
export function restore<T extends Effect<any, any, any>>(effect: T): never
export function restore<State extends {[key: string]: Store<any> | any}>(
  state: State,
): {
  [K in keyof State]: State[K] extends Store<infer S>
    ? StoreWritable<S>
    : StoreWritable<State[K]>
}

/**
 * Creates a domain
 */
export function createDomain(domainName?: string, config?: { domain?: Domain }): Domain
export function createDomain(config?: { name?: string; domain?: Domain }): Domain

type WhichTypeKind =
  | 'never'
  | 'any'
  | 'unknown'
  | 'void'
  | 'undefined'
  | 'value'
type NotType<T extends WhichTypeKind> = Exclude<WhichTypeKind, T>
type WhichType<T> = [T] extends [never]
  ? 'never'
  : [unknown] extends [T]
  ? [0] extends [1 & T]
    ? 'any'
    : 'unknown'
  : [T] extends [void]
  ? [void] extends [T]
    ? 'void'
    : 'undefined'
  : 'value'

type BuiltInObject =
    | Error
    | Date
    | RegExp
    | Int8Array
    | Uint8Array
    | Uint8ClampedArray
    | Int16Array
    | Uint16Array
    | Int32Array
    | Uint32Array
    | Float32Array
    | Float64Array
    | ReadonlyMap<unknown, unknown>
    | ReadonlySet<unknown>
    | WeakMap<object, unknown>
    | WeakSet<object>
    | ArrayBuffer
    | DataView
    | Function
    | Promise<unknown>
    | Generator

type UnitObject = Store<any> | Event<any> | Effect<any, any, any> | Unit<any>

/**
 * Force typescript to print real type instead of geneic types
 *
 * It's better to see {a: string; b: number}
 * instead of GetCombinedValue<{a: Store<string>; b: Store<number>}>
 * */
type Show<A extends any> =
    A extends BuiltInObject
    ? A
    : A extends UnitObject
    ? A
    : {
        [K in keyof A]: A[K]
      } // & {}

/* sample types */
type TupleObject<T extends Array<any>> = {
  [I in Exclude<keyof T, keyof any[]>]: T[I]
}

type IfAny<T, Y, N> = 0 extends (1 & T) ? Y : N;
type IfUnknown<T, Y, N> = 0 extends (1 & T) ? N : unknown extends T ? Y : N;
type IfAssignable<T, U, Y, N> =
  (<G>() => IfAny<T & U, 0, G extends T ? 1 : 2>) extends
    (<G>() => IfAny<T & U, 0, G extends U ? 1 : 2>)
    ? Y
    : (T extends U ? never : 1) extends never
      ? Y
      : T extends Array<any>
        ? number extends T['length']
          ? N
          : U extends Array<any>
            ? number extends U['length']
              ? N
              : TupleObject<T> extends TupleObject<U> ? Y : N
            : N
        : N

type Source<A> = Unit<A> | Combinable
type Clock<B> = Unit<B> | Tuple<Unit<any>>
type Target = UnitTargetable<any> | Tuple<any>

type GetTupleWithoutAny<T> = T extends Array<infer U>
  ? U extends Unit<infer Value>
    ? IfAny<Value, never, Value>
    : never
  : never

type GetMergedValue<T> = GetTupleWithoutAny<T> extends never ? any : GetTupleWithoutAny<T>

type GetSource<S> = S extends Unit<infer Value> ? Value : GetCombinedValue<S>
type GetClock<C> = C extends Unit<infer Value> ? Value : GetMergedValue<C>

/** Replaces incompatible unit type with string error message.
 *  There is no error message if target type is void.
 */
type ReplaceUnit<Target, Result, Value> = IfAssignable<Result, Value,
  Target,
  Value extends void
    ? Target
    : 'incompatible unit in target'
>

// [...T] is used to show sample result as a tuple (not array)
type TargetTuple<Target extends Array<unknown>, Result> = [...{
  [Index in keyof Target]: Target[Index] extends UnitTargetable<infer Value>
    ? ReplaceUnit<Target[Index], Result, Value>
    : 'non-unit item in target'
}]

type MultiTarget<Target, Result> = Target extends UnitTargetable<infer Value>
  ? ReplaceUnit<Target, Result, Value>
  : Target extends Tuple<unknown>
    ? TargetTuple<Target, Result>
    : 'non-unit item in target'

type SampleImpl<
  Target,
  Source,
  Clock,
  FLBool,
  FilterFun,
  FN,
  FNInf,
  FNInfSource extends (
    Source extends Unit<any> | SourceRecord
      ? TypeOfSource<Source>
      : never
    ),
  FNInfClock extends (
    Clock extends Units
      ? TypeOfClock<Clock>
      : never
    ),
  FNAltArg,
  FLUnit,
  SomeFN,
> =
  // no target
  unknown extends Target
    // no target, no source
  ? unknown extends Source
    ? unknown extends Clock
      ? [message: {error: 'either target, clock or source should exists'}]
        // no target, no source, has clock
      : Clock extends Units
        ? SampleFilterDef<
            ModeSelector<
              'clock |        | filter | fn |       ',
              'clock |        | filter |    |       ',
              'clock |        |        | fn |       ',
              'clock |        |        |    |       ',
              SomeFN
            >,
            Source, Clock, FLUnit, FLBool, FilterFun, FN, FNInf, FNInfSource, FNInfClock, FNAltArg, SomeFN
          >
        : [message: {error: 'clock should be unit or array of units'; got: Clock}]
      // no target, has source
    : Source extends Unit<any> | SourceRecord
        // no target, has source, no clock
      ? unknown extends Clock
        ? SampleFilterDef<
            ModeSelector<
              '      | source | filter | fn |       ',
              '      | source | filter |    |       ',
              '      | source |        | fn |       ',
              '      | source |        |    |       ',
              SomeFN
            >,
            Source, Clock, FLUnit, FLBool, FilterFun, FN, FNInf, FNInfSource, FNInfClock, FNAltArg, SomeFN
          >
          // no target, has source, has clock
        : Clock extends Units
          ? SampleFilterDef<
              ModeSelector<
                'clock | source | filter | fn |       ',
                'clock | source | filter |    |       ',
                'clock | source |        | fn |       ',
                'clock | source |        |    |       ',
                SomeFN
              >,
              Source, Clock, FLUnit, FLBool, FilterFun, FN, FNInf, FNInfSource, FNInfClock, FNAltArg, SomeFN
            >
          : [message: {error: 'clock should be unit or array of units'; got: Clock}]
      : [message: {error: 'source should be unit or object with stores'; got: Source}]
  // has target
  : Target extends UnitsTarget | ReadonlyArray<UnitTargetable<any>>
      // has target, no source
    ? unknown extends Source
      ? unknown extends Clock
        ? [message: {error: 'either target, clock or source should exists'}]
          // has target, no source, has clock
        : Clock extends Units
          ? SampleFilterTargetDef<
              ModeSelector<
                'clock |        | filter | fn | target',
                'clock |        | filter |    | target',
                'clock |        |        | fn | target',
                'clock |        |        |    | target',
                SomeFN
              >,
              Target, Source, Clock, FLUnit, FLBool, FilterFun, FN, FNInf, FNInfSource, FNInfClock, FNAltArg, SomeFN
            >
          : [message: {error: 'clock should be unit or array of units'; got: Clock}]
        // has target, has source
      : Source extends Unit<any> | SourceRecord
        // has target, has source, no clock
        ? unknown extends Clock
          ? SampleFilterTargetDef<
              ModeSelector<
                '      | source | filter | fn | target',
                '      | source | filter |    | target',
                '      | source |        | fn | target',
                '      | source |        |    | target',
                SomeFN
              >,
              Target, Source, Clock, FLUnit, FLBool, FilterFun, FN, FNInf, FNInfSource, FNInfClock, FNAltArg, SomeFN
            >
            // has target, has source, has clock
          : Clock extends Units
            ? SampleFilterTargetDef<
                ModeSelector<
                  'clock | source | filter | fn | target',
                  'clock | source | filter |    | target',
                  'clock | source |        | fn | target',
                  'clock | source |        |    | target',
                  SomeFN
                >,
                Target, Source, Clock, FLUnit, FLBool, FilterFun, FN, FNInf, FNInfSource, FNInfClock, FNAltArg, SomeFN
              >
            : [message: {error: 'clock should be unit or array of units'; got: Clock}]
        : [message: {error: 'source should be unit or object with stores'; got: Source}]
    : Target extends InvalidUnitsTarget ? [message: {error: 'derived units are not allowed in target'; got: Target}] : [message: {error: 'target should be unit or array of units'; got: Target}]

type ModeSelector<
  FilterAndFN,
  FilterOnly,
  FNOnly,
  None,
  SomeFN,
> = unknown extends SomeFN
  ? FilterAndFN
  : SomeFN extends {fn: any; filter: any}
    ? FilterAndFN
    : SomeFN extends {filter: any}
      ? FilterOnly
      : SomeFN extends {fn: any}
        ? FNOnly
        : None

type SampleRet<
  Target,
  Source,
  Clock,
  FLUnit,
  FLBool,
  FilterFun,
  FN,
  FNAltArg,
  FNInf,
  FNInfSource extends (
    Source extends Unit<any> | SourceRecord
      ? TypeOfSource<Source>
      : never
  ),
  FNInfClock extends (
    Clock extends Units
      ? TypeOfClock<Clock>
      : never
  ),
  SomeFN,
  ForceTargetInference
> = unknown extends Target
    ? unknown extends Clock
      ? unknown extends Source
        ? never
        : Source extends Unit<any> | SourceRecord
          // has filter, has fn
          ? unknown extends SomeFN
            ? FLUnit extends Unit<any>
              ? FN extends (src: TypeOfSource<Source>) => any
                ? EventAsReturnType<ReturnType<FN>>
                : never
              : FLBool extends BooleanConstructor
                ? FNAltArg extends (arg: NonFalsy<TypeOfSource<Source>>) => any
                  ? EventAsReturnType<ReturnType<FNAltArg>>
                  : never
                : FilterFun extends (src: TypeOfSource<Source>) => src is FNInfSource
                  ? FNInf extends (src: FNInfSource) => any
                    ? EventAsReturnType<ReturnType<FNInf>>
                    : never
                  : FN extends (src: TypeOfSource<Source>) => any
                    ? EventAsReturnType<ReturnType<FN>>
                    : never
            // has filter, has fn
            : SomeFN extends {filter: any; fn: any}
              ? FLUnit extends Unit<any>
                ? FN extends (src: TypeOfSource<Source>) => any
                  ? EventAsReturnType<ReturnType<FN>>
                  : never
                : FLBool extends BooleanConstructor
                  ? FNAltArg extends (arg: NonFalsy<TypeOfSource<Source>>) => any
                    ? EventAsReturnType<ReturnType<FNAltArg>>
                    : never
                  : FilterFun extends (src: TypeOfSource<Source>) => src is FNInfSource
                    ? FNInf extends (src: FNInfSource) => any
                      ? EventAsReturnType<ReturnType<FNInf>>
                      : never
                    : FN extends (src: TypeOfSource<Source>) => any
                      ? EventAsReturnType<ReturnType<FN>>
                      : never
              // no filter, has fn
              : SomeFN extends {fn: any}
                ? FN extends (src: TypeOfSource<Source>) => any
                  ? Source extends Store<any> | SourceRecord
                    ? Store<ReturnType<FN>>
                    : EventAsReturnType<ReturnType<FN>>
                  : never
                // has filter, no fn
                : SomeFN extends {filter: any}
                  ? FLUnit extends Unit<any>
                    ? EventAsReturnType<TypeOfSource<Source>>
                    : FLBool extends BooleanConstructor
                      ? EventAsReturnType<NonFalsy<TypeOfSource<Source>>>
                      : FilterFun extends (src: TypeOfSource<Source>) => src is FNInfSource
                        ? EventAsReturnType<FNInfSource>
                        : EventAsReturnType<TypeOfSource<Source>>
                  // no filter, no fn
                  : Source extends Store<any> | SourceRecord
                    ? Store<TypeOfSource<Source>>
                    : EventAsReturnType<TypeOfSource<Source>>
          : never
      : unknown extends Source
        ? Clock extends Units
          // has filter, has fn
          ? unknown extends SomeFN
            ? FLUnit extends Unit<any>
              ? FN extends (clk: TypeOfClock<Clock>) => any
                ? EventAsReturnType<ReturnType<FN>>
                : never
              : FLBool extends BooleanConstructor
                ? FNAltArg extends (arg: NonFalsy<TypeOfClock<Clock>>) => any
                  ? EventAsReturnType<ReturnType<FNAltArg>>
                  : never
                : FilterFun extends (clk: TypeOfClock<Clock>) => clk is FNInfClock
                  ? FNInf extends (clk: FNInfClock) => any
                    ? EventAsReturnType<ReturnType<FNInf>>
                    : never
                  : FN extends (clk: TypeOfClock<Clock>) => any
                    ? EventAsReturnType<ReturnType<FN>>
                    : never
            // has filter, has fn
            : SomeFN extends {filter: any; fn: any}
              ? FLUnit extends Unit<any>
                ? FN extends (clk: TypeOfClock<Clock>) => any
                  ? EventAsReturnType<ReturnType<FN>>
                  : never
                : FLBool extends BooleanConstructor
                  ? FNAltArg extends (arg: NonFalsy<TypeOfClock<Clock>>) => any
                    ? EventAsReturnType<ReturnType<FNAltArg>>
                    : never
                  : FilterFun extends (clk: TypeOfClock<Clock>) => clk is FNInfClock
                    ? FNInf extends (src: FNInfClock) => any
                      ? EventAsReturnType<ReturnType<FNInf>>
                      : never
                    : FN extends (clk: TypeOfClock<Clock>) => any
                      ? EventAsReturnType<ReturnType<FN>>
                      : never
              // no filter, has fn
              : SomeFN extends {fn: any}
                ? FN extends (clk: TypeOfClock<Clock>) => any
                  ? Clock extends Store<any>
                    ? Store<ReturnType<FN>>
                    : EventAsReturnType<ReturnType<FN>>
                  : never
                // has filter, no fn
                : SomeFN extends {filter: any}
                  ? FLUnit extends Unit<any>
                    ? EventAsReturnType<TypeOfClock<Clock>>
                    : FLBool extends BooleanConstructor
                      ? EventAsReturnType<NonFalsy<TypeOfClock<Clock>>>
                      : FilterFun extends (clk: TypeOfClock<Clock>) => clk is FNInfClock
                        ? EventAsReturnType<FNInfClock>
                        : EventAsReturnType<TypeOfClock<Clock>>
                  // no filter, no fn
                  : Clock extends Store<any>
                    ? Store<TypeOfClock<Clock>>
                    : EventAsReturnType<TypeOfClock<Clock>>
          : never
        : Clock extends Units
          ? Source extends Unit<any> | SourceRecord
            // has filter, has fn
            ? unknown extends SomeFN
              ? FLUnit extends Unit<any>
                ? FN extends (src: TypeOfSource<Source>, clk: TypeOfClock<Clock>) => any
                  ? EventAsReturnType<ReturnType<FN>>
                  : never
                : FLBool extends BooleanConstructor
                  ? FNAltArg extends (arg: NonFalsy<TypeOfSource<Source>>, clk: TypeOfClock<Clock>) => any
                    ? EventAsReturnType<ReturnType<FNAltArg>>
                    : never
                  : FilterFun extends (src: TypeOfSource<Source>, clk: TypeOfClock<Clock>) => src is FNInfSource
                    ? FNInf extends (src: FNInfSource, clk: TypeOfClock<Clock>) => any
                      ? EventAsReturnType<ReturnType<FNInf>>
                      : never
                    : FN extends (src: TypeOfSource<Source>, clk: TypeOfClock<Clock>) => any
                      ? EventAsReturnType<ReturnType<FN>>
                      : never
              // has filter, has fn
              : SomeFN extends {filter: any; fn: any}
                ? FLUnit extends Unit<any>
                  ? FN extends (src: TypeOfSource<Source>, clk: TypeOfClock<Clock>) => any
                    ? EventAsReturnType<ReturnType<FN>>
                    : never
                  : FLBool extends BooleanConstructor
                    ? FNAltArg extends (arg: NonFalsy<TypeOfSource<Source>>, clk: TypeOfClock<Clock>) => any
                      ? EventAsReturnType<ReturnType<FNAltArg>>
                      : never
                    : FilterFun extends (src: TypeOfSource<Source>, clk: TypeOfClock<Clock>) => src is FNInfSource
                      ? FNInf extends (src: FNInfSource, clk: TypeOfClock<Clock>) => any
                        ? EventAsReturnType<ReturnType<FNInf>>
                        : never
                      : FN extends (src: TypeOfSource<Source>, clk: TypeOfClock<Clock>) => any
                        ? EventAsReturnType<ReturnType<FN>>
                        : never
                // no filter, has fn
                : SomeFN extends {fn: any}
                  ? FN extends (src: TypeOfSource<Source>, clk: TypeOfClock<Clock>) => any
                    ? [Clock, Source] extends [Store<any>, Store<any> | SourceRecord]
                      ? Store<ReturnType<FN>>
                      : EventAsReturnType<ReturnType<FN>>
                    : never
                  // has filter, no fn
                  : SomeFN extends {filter: any}
                    ? FLUnit extends Unit<any>
                      ? EventAsReturnType<TypeOfSource<Source>>
                      : FLBool extends BooleanConstructor
                        ? EventAsReturnType<NonFalsy<TypeOfSource<Source>>>
                        : FilterFun extends (src: TypeOfSource<Source>, clk: TypeOfClock<Clock>) => src is FNInfSource
                          ? EventAsReturnType<FNInfSource>
                          : EventAsReturnType<TypeOfSource<Source>>
                    // no filter, no fn
                    : [Clock, Source] extends [Store<any>, Store<any> | SourceRecord]
                      ? Store<TypeOfSource<Source>>
                      : EventAsReturnType<TypeOfSource<Source>>
            : never
          : never
    : Target & ForceTargetInference

/**
 * Represents a step in a business logic workflow. It tells an application when it should act, which data it needs,
 * how it should be transformed and what should happens next
 *
 * ```js
 * sample({
 *   // when clickBuy event is triggered
 *   clock: clickBuy,
 *   // read state of $shoppingCart store
 *   source: $shoppingCart,
 *   // and if there at least one item in cart
 *   filter: (cart) => cart.items.length > 0,
 *   // then select items from cart
 *   fn: cart => cart.items,
 *   // and pass results to buyItemsFx effect and clearCart event
 *   target: [buyItemsFx, clearCart]
 * })
 * ```
 */
export function sample<
  Target,
  Source,
  Clock,
  FLBool,
  FNInfSource extends (
    Source extends Unit<any> | SourceRecord
      ? TypeOfSource<Source>
      : never
    ),
  FNInfClock extends (
    Clock extends Units
      ? TypeOfClock<Clock>
      : never
  ),
  FilterFun extends (
    Source extends Unit<any> | SourceRecord
    ? Clock extends Units
      ? (
        | never
        | ((src: TypeOfSource<Source>, clk: TypeOfClock<Clock>) => src is FNInfSource)
        | ((src: TypeOfSource<Source>, clk: TypeOfClock<Clock>) => boolean)
      )
      : (
        | never
        | ((src: TypeOfSource<Source>) => src is FNInfSource)
        | ((src: TypeOfSource<Source>) => boolean)
      )
    : Clock extends Units
      ? (
        | never
        | ((clk: TypeOfClock<Clock>) => clk is FNInfClock)
        | ((clk: TypeOfClock<Clock>) => boolean)
      )
      : never
  ),
  FN extends (
    Source extends Unit<any> | SourceRecord
    ? Clock extends Units
      ? (src: TypeOfSource<Source>, clk: TypeOfClock<Clock>) => any
      : (src: TypeOfSource<Source>) => any
    : Clock extends Units
      ? (clk: TypeOfClock<Clock>) => any
      : never
  ),
  FNInf extends (
    Source extends Unit<any> | SourceRecord
    ? Clock extends Units
      ? (src: FNInfSource, clk: TypeOfClock<Clock>) => any
      : (src: FNInfSource) => any
    : Clock extends Units
      ? (clk: FNInfClock) => any
      : never
  ),
  FNNonFalsy extends (
    Source extends Unit<any> | SourceRecord
    ? NonFalsy<TypeOfSource<Source>>
    : Clock extends Units
      ? NonFalsy<TypeOfClock<Clock>>
      : never
  ),
  FNAltArg extends (
    Source extends Unit<any> | SourceRecord
    ? Clock extends Units
      ? (src: FNNonFalsy, clk: TypeOfClock<Clock>) => any
      : (src: FNNonFalsy) => any
    : Clock extends Units
      ? (clk: FNNonFalsy) => any
      : never
  ),
  SomeFN,
  SourceNoConf,
  ClockNoConf,
  FNSrcNoConf extends (
    SourceNoConf extends Unit<any> | SourceRecord
      ? (src: TypeOfSource<SourceNoConf>) => any
      : never
  ),
  FNBothNoConf extends (
    SourceNoConf extends Unit<any> | SourceRecord
      ? ClockNoConf extends Units
        ? ((src: TypeOfSource<SourceNoConf>, clk: TypeOfClock<ClockNoConf>) => any)
        : never
      : never
  ),
  FLUnit,
  Args extends any[],
  InferTarget,
>(...args:
  SourceNoConf extends Unit<any> | SourceRecord
    ? ClockNoConf extends Units
      ? [any, any, any] extends Args
        ? [source: SourceNoConf, clock: ClockNoConf, fn: FNBothNoConf] & Args
        : [any, any] extends Args
          ? [source: SourceNoConf, clock: ClockNoConf] & Args
          : never
      : [any, any] extends Args
        ? [source: SourceNoConf, fn: FNSrcNoConf] & Args
        : Args extends [Unit<any>]
          ? [source: SourceNoConf] & Args
          : SampleImpl<Target, Source, Clock, FLBool, FilterFun, FN, FNInf, FNInfSource, FNInfClock, FNAltArg, FLUnit, SomeFN>
    : SampleImpl<Target, Source, Clock, FLBool, FilterFun, FN, FNInf, FNInfSource, FNInfClock, FNAltArg, FLUnit, SomeFN>
): SourceNoConf extends Unit<any> | SourceRecord
  ? ClockNoConf extends Units
    ? [any, any, any] extends Args
      ? SourceNoConf extends Store<any>
        ? ClockNoConf extends Store<any>
          ? Store<ReturnType<FNBothNoConf>>
          : EventAsReturnType<ReturnType<FNBothNoConf>>
        : EventAsReturnType<ReturnType<FNBothNoConf>>
      : [any, any] extends Args
        ? SourceNoConf extends Store<any>
          ? ClockNoConf extends Store<any>
            ? Store<TypeOfSource<SourceNoConf>>
            : EventAsReturnType<TypeOfSource<SourceNoConf>>
          : EventAsReturnType<TypeOfSource<SourceNoConf>>
        : never
    : [any, any] extends Args
      ? SourceNoConf extends Store<any>
        ? Store<ReturnType<FNSrcNoConf>>
        : EventAsReturnType<ReturnType<FNSrcNoConf>>
      : Args extends [Unit<any>]
        ? SourceNoConf extends Store<any>
          ? Store<TypeOfSource<SourceNoConf>>
          : EventAsReturnType<TypeOfSource<SourceNoConf>>
        : NoInfer<SampleRet<Target, Source, Clock, FLUnit, FLBool, FilterFun, FN, FNAltArg, FNInf, FNInfSource, FNInfClock, SomeFN, InferTarget>>
  : NoInfer<SampleRet<Target, Source, Clock, FLUnit, FLBool, FilterFun, FN, FNAltArg, FNInf, FNInfSource, FNInfClock, SomeFN, InferTarget>>

/*
  | 'clock | source | filter | fn | target'
  | 'clock | source |        | fn | target'
  | 'clock | source | filter | fn |       '
  | 'clock | source |        | fn |       '
  | 'clock | source | filter |    | target'
  | 'clock | source |        |    | target'
  | 'clock | source | filter |    |       '
  | 'clock | source |        |    |       '

  | '      | source | filter | fn | target'
  | '      | source |        | fn | target'
  | '      | source | filter | fn |       '
  | '      | source |        | fn |       '
  | '      | source | filter |    | target'
  | '      | source |        |    | target'
  | '      | source | filter |    |       '
  | '      | source |        |    |       '

  | 'clock |        | filter | fn | target'
  | 'clock |        |        | fn | target'
  | 'clock |        | filter | fn |       '
  | 'clock |        |        | fn |       '
  | 'clock |        | filter |    | target'
  | 'clock |        |        |    | target'
  | 'clock |        | filter |    |       '
  | 'clock |        |        |    |       '
*/
type Mode_Clk_Src = `clock | source | ${string}`;
type Mode_Clk_NoSrc  = `clock |        | ${string}`;
type Mode_NoClk_Src  = `      | source | ${string}`;
type Mode_Trg = `${string} | target`;
type Mode_Flt_Trg = `${string} | filter | ${string} | target`;
type Mode_NoFlt = `${string} | ${string} |        | ${string} | ${string}`;
type Mode_Fn_Trg = `${string} | fn | target`;
type Mode_Src = `${string} | source | ${string}`;
type Mode_Flt_Fn_Trg = `${string} | filter | fn | target`;
type Mode_Src_Flt_NoFn_Trg = `${string} | source | filter |    | target`;
type Mode_NoTrg = `${string} |       `;
type Mode_Flt = `${string} | filter | ${string}`;
type Mode_Flt_Fn = `${string} | filter | fn | ${string}`;

type TargetFilterFnConfig<
  Mode extends Mode_Flt_Trg,
  Target extends UnitsTarget | ReadonlyArray<UnitTargetable<any>>,
  Source,
  Clock,
  FilterFun,
  FN,
> = Mode extends 'clock | source | filter | fn | target'
  ? {clock: Clock; source: Source; filter?: FilterFun; fn?: FN; target: Target; greedy?: boolean; batch?: boolean}
  : Mode extends 'clock | source | filter |    | target'
  ? {clock: Clock; source: Source; filter: FilterFun; target: Target; greedy?: boolean; batch?: boolean}
  : Mode extends '      | source | filter | fn | target'
  ? {source: Source; clock?: never; filter?: FilterFun; fn?: FN; target: Target; greedy?: boolean; batch?: boolean}
  : Mode extends '      | source | filter |    | target'
  ? {source: Source; clock?: never; filter: FilterFun; target: Target; greedy?: boolean; batch?: boolean}
  : Mode extends 'clock |        | filter | fn | target'
  ? {clock: Clock; source?: never; filter?: FilterFun; fn?: FN; target: Target; greedy?: boolean; batch?: boolean}
  : Mode extends 'clock |        | filter |    | target'
  ? {clock: Clock; source?: never; filter: FilterFun; target: Target; greedy?: boolean; batch?: boolean}
  : never

type TargetConfigCheck<
  Mode extends Mode_Trg,
  Target extends UnitsTarget | ReadonlyArray<UnitTargetable<any>>,
  Source,
  Clock,
  FN,
  Config,
  SomeFN
> = // mode with fn
    Mode extends Mode_Fn_Trg
    // there should be an explicit conditional selection
    // of generic variable for function type
    // this could be a reason for a few unfixed implicit any
  ? FN extends DataSourceFunction<Source, Clock>
    ? TargetOrError<
        ReturnType<FN>,
        'fnRet',
        Target,
        Config & SomeFN
      >
    : [message: {error: 'function should accept data source types'; got: FN}]
    // mode with source only or with both clock and source
  : Mode extends Mode_Src
  ? Source extends Unit<any> | SourceRecord
    ? TargetOrError<
        TypeOfSource<Source>,
        'src',
        Target,
        Config & SomeFN
      >
    : [message: {error: 'source should be unit or object with stores'; got: Source}]
    // mode with clock only
  : Mode extends Mode_Clk_NoSrc
  ? Clock extends Units
    ? TargetOrError<
        TypeOfClock<Clock>,
        'clk',
        Target,
        Config & SomeFN
      >
    : [message: {error: 'clock should be unit or array of units'; got: Clock}]
  : never

type InferredType<Source, Clock, FilterFN> =
  Source extends Unit<any> | SourceRecord
  ? Clock extends Units
    ? FilterFN extends (src: any, clk: TypeOfClock<Clock>) => src is infer Ret
      ? Ret extends TypeOfSource<Source>
        ? Ret
        : never
      : never
    : FilterFN extends (src: any) => src is infer Ret
      ? Ret extends TypeOfSource<Source>
        ? Ret
        : never
      : never
  : Clock extends Units
    ? FilterFN extends (clk: any) => clk is infer Ret
      ? Ret extends TypeOfClock<Clock>
        ? Ret
        : never
      : never
    : never

type SampleFilterTargetDef<
  Mode extends Mode_Trg,
  Target extends UnitsTarget | ReadonlyArray<UnitTargetable<any>>,
  Source,
  Clock,
  FLUnit,
  FLBool,
  FilterFun,
  FN,
  FNInf,
  FNInfSource extends (
    Source extends Unit<any> | SourceRecord
      ? TypeOfSource<Source>
      : never
    ),
  FNInfClock extends (
    Clock extends Units
      ? TypeOfClock<Clock>
      : never
    ),
  FNAltArg,
  SomeFN
> = Mode extends Mode_Flt_Trg
  ? FLUnit extends Unit<any>
    ? boolean extends UnitValue<FLUnit>
      ? FN extends DataSourceFunction<Source, Clock>
        ? TargetConfigCheck<
            Mode, Target, Source, Clock, FN,
            Mode extends 'clock | source | filter | fn | target'
            ? {clock: Clock; source: Source; filter?: FLUnit; fn?: FN; target: Target; greedy?: boolean; batch?: boolean}
            : Mode extends 'clock | source | filter |    | target'
            ? {clock: Clock; source: Source; filter: FLUnit; target: Target; greedy?: boolean; batch?: boolean}
            : Mode extends '      | source | filter | fn | target'
            ? {source: Source; clock?: never; filter?: FLUnit; fn?: FN; target: Target; greedy?: boolean; batch?: boolean}
            : Mode extends '      | source | filter |    | target'
            ? {source: Source; clock?: never; filter: FLUnit; target: Target; greedy?: boolean; batch?: boolean}
            : Mode extends 'clock |        | filter | fn | target'
            ? {clock: Clock; source?: never; filter?: FLUnit; fn?: FN; target: Target; greedy?: boolean; batch?: boolean}
            : Mode extends 'clock |        | filter |    | target'
            ? {clock: Clock; source?: never; filter: FLUnit; target: Target; greedy?: boolean; batch?: boolean}
            : never,
            SomeFN
          >
          : [message: {error: 'filter unit should has boolean type'; got: UnitValue<FLUnit>}]
        : [message: {error: 'function should accept data source types'; got: FN}]
    : FLBool extends BooleanConstructor
      ? Mode extends Mode_Flt_Fn_Trg
        ? FNAltArg extends (arg?: any, clk?: any) => any
          ? TargetOrError<
              ReturnType<FNAltArg>,
              'fnRet',
              Target,
              TargetFilterFnConfig<Mode, Target, Source, Clock, FLBool, FNAltArg> & SomeFN
            >
          : never
          // mode with source only or with both clock and source
        : Mode extends Mode_Src_Flt_NoFn_Trg
        ? Source extends Unit<any> | SourceRecord
          ? TargetOrError<
              NonFalsy<TypeOfSource<Source>>,
              'src',
              Target,
              TargetFilterFnConfig<Mode, Target, Source, Clock, FLBool, never> & SomeFN
            >
          : [message: {error: 'source should be unit or object with stores'; got: Source}]
          // mode with clock only
        : Mode extends Mode_Clk_NoSrc
        ? Clock extends Units
          ? TargetOrError<
              NonFalsy<TypeOfClock<Clock>>,
              'clk',
              Target,
              TargetFilterFnConfig<Mode, Target, Source, Clock, FLBool, never> & SomeFN
            >
          : [message: {error: 'clock should be unit or array of units'; got: Clock}]
        : never
    : FilterFun extends (
        Source extends Unit<any> | SourceRecord
        ? Clock extends Units
          ? (src: TypeOfSource<Source>, clk: TypeOfClock<Clock>) => src is FNInfSource
          : (src: TypeOfSource<Source>) => src is FNInfSource
        : Clock extends Units
          ? (clk: TypeOfClock<Clock>) => clk is FNInfClock
          : never
      )
        // mode with fn
      ? Mode extends Mode_Flt_Fn_Trg
        ? FNInf extends (
            Source extends Unit<any> | SourceRecord
            ? Clock extends Units
              ? (src: FNInfSource, clk: TypeOfClock<Clock>) => any
              : (src: FNInfSource) => any
            : Clock extends Units
              ? (clk: FNInfClock) => any
              : any
          )
          ? TargetOrError<
              ReturnType<FNInf>,
              'fnRet',
              Target,
              TargetFilterFnConfig<Mode, Target, Source, Clock, FilterFun & (
                Source extends Unit<any> | SourceRecord
                ? Clock extends Units
                  ? (src: TypeOfSource<Source>, clk: TypeOfClock<Clock>) => src is FNInfSource
                  : (src: TypeOfSource<Source>) => src is FNInfSource
                : Clock extends Units
                  ? (clk: TypeOfClock<Clock>) => clk is FNInfClock
                  : never
              ), FNInf & (
                Source extends Unit<any> | SourceRecord
                ? Clock extends Units
                  ? (src: FNInfSource, clk: TypeOfClock<Clock>) => any
                  : (src: FNInfSource) => any
                : Clock extends Units
                  ? (clk: FNInfClock) => any
                  : any
              )> & SomeFN
            >
          : [message: {error: 'function should accept data source types'; got: FNInf}]
          // mode with source only or with both clock and source
        : Mode extends Mode_Src_Flt_NoFn_Trg
        ? Source extends Unit<any> | SourceRecord
          ? TargetOrError<
              InferredType<Source, Clock, FilterFun>,
              'src',
              Target,
              TargetFilterFnConfig<Mode, Target, Source, Clock, FilterFun, never> & SomeFN
            >
          : [message: {error: 'source should be unit or object with stores'; got: Source}]
          // mode with clock only
        : Mode extends Mode_Clk_NoSrc
        ? Clock extends Units
          ? TargetOrError<
              InferredType<Source, Clock, FilterFun>,
              'clk',
              Target,
              TargetFilterFnConfig<Mode, Target, Source, Clock, FilterFun, never> & SomeFN
            >
          : [message: {error: 'clock should be unit or array of units'; got: Clock}]
        : never
    : FilterFun extends (
          Mode extends Mode_Clk_Src
        ? Source extends Unit<any> | SourceRecord
          ? Clock extends Units
            ? ((src: TypeOfSource<Source>, clk: TypeOfClock<Clock>) => any)
            : never
          : never
        : Mode extends Mode_NoClk_Src
        ? Source extends Unit<any> | SourceRecord
          ? (src: TypeOfSource<Source>) => any
          : never
        : Clock extends Units
          ? (clk: TypeOfClock<Clock>) => any
          : never
      )
      ? ReturnType<FilterFun> extends boolean
        // mode with fn
        ? Mode extends Mode_Flt_Fn_Trg
          ? FN extends DataSourceFunction<Source, Clock>
            ? TargetOrError<
                ReturnType<FN>,
                'fnRet',
                Target,
                TargetFilterFnConfig<Mode, Target, Source, Clock, FilterFun, FN> & SomeFN
              >
            : [message: {error: 'function should accept data source types'; got: FN}]
            // mode with source only or with both clock and source
          : Mode extends Mode_Src_Flt_NoFn_Trg
          ? Source extends Unit<any> | SourceRecord
            ? TargetOrError<
                TypeOfSource<Source>,
                'src',
                Target,
                TargetFilterFnConfig<Mode, Target, Source, Clock, FilterFun, never> & SomeFN
              >
            : [message: {error: 'source should be unit or object with stores'; got: Source}]
            // mode with clock only
          : Mode extends Mode_Clk_NoSrc
          ? Clock extends Units
            ? TargetOrError<
                TypeOfClock<Clock>,
                'clk',
                Target,
                TargetFilterFnConfig<Mode, Target, Source, Clock, FilterFun, never> & SomeFN
              >
            : [message: {error: 'clock should be unit or array of units'; got: Clock}]
          : never
        : [message: {error: 'filter function should return boolean'; got: ReturnType<FilterFun>}]
      : [message: {error: 'filter should be function or unit'; got: FilterFun}]

  : Mode extends Mode_NoFlt
    ? FN extends DataSourceFunction<Source, Clock>
      ? TargetConfigCheck<
          Mode, Target, Source, Clock, FN,
          Mode extends 'clock | source |        | fn | target'
          ? {clock: Clock; source: Source; filter?: never; fn: FN; target: Target; greedy?: boolean; batch?: boolean}
          : Mode extends 'clock | source |        |    | target'
          ? {clock: Clock; source: Source; filter?: never; target: Target; greedy?: boolean; batch?: boolean}
          : Mode extends '      | source |        | fn | target'
          ? {source: Source; clock?: never; filter?: never; fn: FN; target: Target; greedy?: boolean; batch?: boolean}
          : Mode extends '      | source |        |    | target'
          ? {source: Source; clock?: never; filter?: never; target: Target; greedy?: boolean; batch?: boolean}
          : Mode extends 'clock |        |        | fn | target'
          ? {clock: Clock; source?: never; filter?: never; fn: FN; target: Target; greedy?: boolean; batch?: boolean}
          : Mode extends 'clock |        |        |    | target'
          ? {clock: Clock; source?: never; filter?: never; target: Target; greedy?: boolean; batch?: boolean}
          : never,
          SomeFN
        >
      : [message: {error: 'function should accept data source types'; got: FN}]
    : never

type TargetOrError<
  MatchingValue,
  Mode extends 'fnRet' | 'src' | 'clk',
  Target extends UnitsTarget | ReadonlyArray<UnitTargetable<any>>,
  ResultConfig
> = [TypeOfTarget<MatchingValue, Target, Mode>] extends [Target]
    ? [config: ResultConfig]
    : [Target] extends [TypeOfTargetSoft<MatchingValue, Target, Mode>]
      ? [config: ResultConfig]
      : [message: {
          error: Mode extends 'fnRet'
            ? 'fn result should extend target type'
            : Mode extends 'src'
              ? 'source should extend target type'
              : 'clock should extend target type'
          targets: Show<TypeOfTarget<MatchingValue, Target, Mode>>
        }]

type SampleFilterDef<
  Mode extends Mode_NoTrg,
  Source,
  Clock,
  FLUnit,
  FLBool,
  FilterFun,
  FN,
  FNInf,
  FNInfSource extends (
    Source extends Unit<any> | SourceRecord
      ? TypeOfSource<Source>
      : never
    ),
  FNInfClock extends (
    Clock extends Units
      ? TypeOfClock<Clock>
      : never
    ),
  FNAltArg,
  SomeFN
> =
  Mode extends Mode_Flt
  ? FLUnit extends Unit<any>
    ? boolean extends UnitValue<FLUnit>
      ? [config: (
            Mode extends 'clock | source | filter | fn |       '
          ? {clock: Clock; source: Source; filter?: FLUnit; fn?: FN; target?: never; greedy?: boolean; batch?: boolean; name?: string}
          : Mode extends 'clock | source | filter |    |       '
          ? {clock: Clock; source: Source; filter: FLUnit; target?: never; greedy?: boolean; batch?: boolean; name?: string}
          : Mode extends '      | source | filter | fn |       '
          ? {source: Source; clock?: never; filter?: FLUnit; fn?: FN; target?: never; greedy?: boolean; batch?: boolean; name?: string}
          : Mode extends '      | source | filter |    |       '
          ? {source: Source; clock?: never; filter: FLUnit; target?: never; greedy?: boolean; batch?: boolean; name?: string}
          : Mode extends 'clock |        | filter | fn |       '
          ? {clock: Clock; source?: never; filter?: FLUnit; fn?: FN; target?: never; greedy?: boolean; batch?: boolean; name?: string}
          : Mode extends 'clock |        | filter |    |       '
          ? {clock: Clock; source?: never; filter: FLUnit; target?: never; greedy?: boolean; batch?: boolean; name?: string}
          : never
        ) & SomeFN]
      : [message: {error: 'filter unit should has boolean type'; got: UnitValue<FLUnit>}]
    : FLBool extends BooleanConstructor
      ? Mode extends Mode_Flt_Fn
        ? [config: (
              Mode extends 'clock | source | filter | fn |       '
            ? {clock: Clock; source: Source; filter?: FLBool; fn?: FNAltArg; target?: never; greedy?: boolean; batch?: boolean; name?: string}
            : Mode extends '      | source | filter | fn |       '
            ? {source: Source; clock?: never; filter?: FLBool; fn?: FNAltArg; target?: never; greedy?: boolean; batch?: boolean; name?: string}
            : Mode extends 'clock |        | filter | fn |       '
            ? {clock: Clock; source?: never; filter?: FLBool; fn?: FNAltArg; target?: never; greedy?: boolean; batch?: boolean; name?: string}
            : never
          ) & SomeFN]
        : [config: (
              Mode extends 'clock | source | filter |    |       '
            ? {clock: Clock; source: Source; filter: FLBool; target?: never; greedy?: boolean; batch?: boolean; name?: string}
            : Mode extends '      | source | filter |    |       '
            ? {source: Source; clock?: never; filter: FLBool; target?: never; greedy?: boolean; batch?: boolean; name?: string}
            : Mode extends 'clock |        | filter |    |       '
            ? {clock: Clock; source?: never; filter: FLBool; target?: never; greedy?: boolean; batch?: boolean; name?: string}
            : never
          ) & SomeFN]
      : FilterFun extends (
          Source extends Unit<any> | SourceRecord
          ? Clock extends Units
            ? (src: TypeOfSource<Source>, clk: TypeOfClock<Clock>) => src is FNInfSource
            : (src: TypeOfSource<Source>) => src is FNInfSource
          : Clock extends Units
            ? (clk: TypeOfClock<Clock>) => clk is FNInfClock
            : never
        )
        ? Mode extends Mode_Flt_Fn
          ? FNInf extends (
              Source extends Unit<any> | SourceRecord
              ? Clock extends Units
                ? (src: FNInfSource, clk: TypeOfClock<Clock>) => any
                : (src: FNInfSource) => any
              : Clock extends Units
                ? (clk: FNInfClock) => any
                : any
            )
            ? [config: (
                  Mode extends 'clock | source | filter | fn |       '
                ? {clock: Clock; source: Source; filter?: FilterFun; fn?: FNInf; target?: never; greedy?: boolean; batch?: boolean; name?: string}
                : Mode extends '      | source | filter | fn |       '
                ? {source: Source; clock?: never; filter?: FilterFun; fn?: FNInf; target?: never; greedy?: boolean; batch?: boolean; name?: string}
                : Mode extends 'clock |        | filter | fn |       '
                ? {clock: Clock; source?: never; filter?: FilterFun; fn?: FNInf; target?: never; greedy?: boolean; batch?: boolean; name?: string}
                : never
              ) & SomeFN]
            : [message: {
                error: 'fn should match inferred type'
                inferred: (Source extends Unit<any> | SourceRecord ? FNInfSource : FNInfClock)
                fnArg: FNInf extends (arg: infer Arg) => any ? Arg : never
              }]
          : [config: (
              Mode extends 'clock | source | filter |    |       '
            ? {clock: Clock; source: Source; filter: FilterFun; target?: never; greedy?: boolean; batch?: boolean; name?: string}
            : Mode extends '      | source | filter |    |       '
            ? {source: Source; clock?: never; filter: FilterFun; target?: never; greedy?: boolean; batch?: boolean; name?: string}
            : Mode extends 'clock |        | filter |    |       '
            ? {clock: Clock; source?: never; filter: FilterFun; target?: never; greedy?: boolean; batch?: boolean; name?: string}
            : never
          ) & SomeFN]
      : FilterFun extends (
          Source extends Unit<any> | SourceRecord
          ? Clock extends Units
            ? (src: TypeOfSource<Source>, clk: TypeOfClock<Clock>) => any
            : (src: TypeOfSource<Source>) => any
          : Clock extends Units
            ? (clk: TypeOfClock<Clock>) => any
            : never
        )
        ? ReturnType<FilterFun> extends boolean
          ? [config: (
                Mode extends 'clock | source | filter | fn |       '
              ? {clock: Clock; source: Source; filter?: FilterFun; fn?: FN; target?: never; greedy?: boolean; batch?: boolean; name?: string}
              : Mode extends 'clock | source | filter |    |       '
              ? {clock: Clock; source: Source; filter: FilterFun; target?: never; greedy?: boolean; batch?: boolean; name?: string}
              : Mode extends '      | source | filter | fn |       '
              ? {source: Source; clock?: never; filter?: FilterFun; fn?: FN; target?: never; greedy?: boolean; batch?: boolean; name?: string}
              : Mode extends '      | source | filter |    |       '
              ? {source: Source; clock?: never; filter: FilterFun; target?: never; greedy?: boolean; batch?: boolean; name?: string}
              : Mode extends 'clock |        | filter | fn |       '
              ? {clock: Clock; source?: never; filter?: FilterFun; fn?: FN; target?: never; greedy?: boolean; batch?: boolean; name?: string}
              : Mode extends 'clock |        | filter |    |       '
              ? {clock: Clock; source?: never; filter: FilterFun; target?: never; greedy?: boolean; batch?: boolean; name?: string}
              : never
            ) & SomeFN]
          : [message: {error: 'filter function should return boolean'; got: ReturnType<FilterFun>}]
        : [message: {error: 'filter should be function or unit'; got: FilterFun}]
  : Mode extends Mode_NoFlt
  ? [config: (
        Mode extends 'clock | source |        | fn |       '
      ? {clock: Clock; source: Source; filter?: never; fn: FN; target?: never; greedy?: boolean; batch?: boolean; name?: string}
      : Mode extends 'clock | source |        |    |       '
      ? {clock: Clock; source: Source; filter?: never; target?: never; greedy?: boolean; batch?: boolean; name?: string}
      : Mode extends '      | source |        | fn |       '
      ? {source: Source; clock?: never; filter?: never; fn: FN; target?: never; greedy?: boolean; batch?: boolean; name?: string}
      : Mode extends '      | source |        |    |       '
      ? {source: Source; clock?: never; filter?: never; target?: never; greedy?: boolean; batch?: boolean; name?: string}
      : Mode extends 'clock |        |        | fn |       '
      ? {clock: Clock; source?: never; filter?: never; fn: FN; target?: never; greedy?: boolean; batch?: boolean; name?: string}
      : Mode extends 'clock |        |        |    |       '
      ? {clock: Clock; source?: never; filter?: never; target?: never; greedy?: boolean; batch?: boolean; name?: string}
      : never
    ) & SomeFN]
  : never

type DataSourceFunction<Source, Clock> =
  Source extends Unit<any> | SourceRecord
  ? Clock extends Units
    ? (src: TypeOfSource<Source>, clk: TypeOfClock<Clock>) => any
    : (src: TypeOfSource<Source>) => any
  : Clock extends Units
    ? (clk: TypeOfClock<Clock>) => any
    : never

type TypeOfTargetSoft<SourceType, Target extends Units | ReadonlyArray<Unit<any>>, Mode extends 'fnRet' | 'src' | 'clk'> =
  Target extends UnitTargetable<any>
    ? Target extends UnitTargetable<infer TargetType>
      ? [SourceType] extends [Readonly<TargetType>]
        ? Target
        : WhichType<TargetType> extends ('void' | 'any')
          ? Target
          : IfAssignable<SourceType, TargetType,
            Target,
            Mode extends 'fnRet'
              ? never & {fnResult: SourceType; targetType: TargetType}
              : Mode extends 'src'
                ? never & {sourceType: SourceType; targetType: TargetType}
                : {clockType: SourceType; targetType: TargetType}
            >
      : never
    : {
      [
        K in keyof Target
      ]: Target[K] extends UnitTargetable<infer TargetType>
        ? [SourceType] extends [Readonly<TargetType>]
          ? Target[K]
          : WhichType<TargetType> extends ('void' | 'any')
            ? Target[K]
            : IfAssignable<SourceType, TargetType,
              Target[K],
              Mode extends 'fnRet'
                ? never & {fnResult: SourceType; targetType: TargetType}
                : Mode extends 'src'
                  ? never & {sourceType: SourceType; targetType: TargetType}
                  : {clockType: SourceType; targetType: TargetType}
              >
        : never
    }

type TypeOfTarget<SourceType, Target extends UnitsTarget | ReadonlyArray<UnitTargetable<any>>, Mode extends 'fnRet' | 'src' | 'clk'> =
  Target extends UnitTargetable<any>
    ? Target extends UnitTargetable<infer TargetType>
      ? [SourceType] extends [Readonly<TargetType>]
        ? Target
        : WhichType<TargetType> extends ('void' | 'any')
          ? Target
          : Mode extends 'fnRet'
            ? {fnResult: SourceType; targetType: TargetType}
            : Mode extends 'src'
              ? {sourceType: SourceType; targetType: TargetType}
              : {clockType: SourceType; targetType: TargetType}
      : never
    : {
      [
        K in keyof Target
      ]: Target[K] extends UnitTargetable<infer TargetType>
        ? [SourceType] extends [Readonly<TargetType>]
          ? Target[K]
          : WhichType<TargetType> extends ('void' | 'any')
            ? Target[K]
            : Mode extends 'fnRet'
              ? {fnResult: SourceType; targetType: TargetType}
              : Mode extends 'src'
                ? {sourceType: SourceType; targetType: TargetType}
                : {clockType: SourceType; targetType: TargetType}
        : never
    }

type ClockValueOf<T> = T[keyof T]

type TypeOfSource<Source extends Unit<any> | SourceRecord> =
  Source extends Unit<any>
  ? UnitValue<Source>
  : Source extends SourceRecord
    ? {[K in keyof Source]: UnitValue<Source[K]>}
    : never

type TypeOfClock<Clock extends Units | ReadonlyArray<Unit<any>> | never[]> =
  Clock extends never[]
  ? unknown
  : Clock extends Unit<any>
  ? UnitValue<Clock>
  : Clock extends Tuple<Unit<any>>
    ? WhichType<UnitValue<ClockValueOf<{
        [
          K in keyof Clock as
            WhichType<UnitValue<Clock[K]>> extends 'any'
              ? never
              : K
        ]: Clock[K]
      }>>> extends 'never'
      ? any
      : UnitValue<ClockValueOf<{
        [
          K in keyof Clock as
            WhichType<UnitValue<Clock[K]>> extends 'any'
              ? never
              : K
        ]: Clock[K]
      }>>
    : Clock extends ReadonlyArray<Unit<any>>
      ? UnitValue<ClockValueOf<Clock>>
      : never

type SourceRecord = Record<string, Store<any>> | RoTuple<Store<any>>

type Units = Unit<any> | Tuple<Unit<any>>
type UnitsTarget = UnitTargetable<any> | Tuple<UnitTargetable<any>>
type InvalidUnitsTarget = Unit<any> | Tuple<Unit<any>> | ReadonlyArray<Unit<any>>

/* guard types */

type NonFalsy<T> = T extends null | undefined | false | 0 | 0n | "" ? never : T;

type GuardFilterSC<S, C> =
  | ((source: GetSource<S>, clock: GetClock<C>) => boolean)
  | Store<boolean>
type GuardFilterS<S> =
  | ((source: GetSource<S>) => boolean)
  | Store<boolean>
type GuardFilterC<C> =
  | ((clock: GetClock<C>) => boolean)
  | Store<boolean>

type GuardResult<Value> = EventAsReturnType<Value>

type GetGuardSource<S, F> = F extends BooleanConstructor
  ? NonFalsy<GetSource<S>>
  : GetSource<S>
type GetGuardClock<C, F> = F extends BooleanConstructor
  ? NonFalsy<GetClock<C>>
  : GetClock<C>

// ---------------------------------------
/* user-defined typeguard: with target */
// SСT
/**
 * Method for conditional event routing.
 * @deprecated use `sample({clock, source, filter, target})` instead
 */
export function guard<A, X extends GetSource<S>, B = any,
  S extends Source<A> = Source<A>,
  C extends Clock<B> = Clock<B>,
  T extends Target = Target
>(config: {
  source: S,
  clock: C,
  filter: (source: GetSource<S>, clock: GetClock<C>) => source is X,
  target: MultiTarget<T, X>,
  name?: string,
  greedy?: boolean
}): T
// ST
/**
 * Method for conditional event routing.
 * @deprecated use `sample({source, filter, target})` instead
 */
export function guard<A, X extends GetSource<S>,
  S extends Source<A> = Source<A>,
  T extends Target = Target
>(config: {
  source: S,
  filter: (source: GetSource<S>) => source is X,
  target: MultiTarget<T, X>,
  name?: string,
  greedy?: boolean
}): T
// СT
/**
 * Method for conditional event routing.
 * @deprecated use `sample({clock, filter, target})` instead
 */
export function guard<B, X extends GetClock<C>,
  C extends Clock<B> = Clock<B>,
  T extends Target = Target
>(config: {
  clock: C,
  filter: (clock: GetClock<C>) => clock is X,
  target: MultiTarget<T, X>,
  name?: string,
  greedy?: boolean
}): T

/* user-defined typeguard: without target */
// SC
/**
 * Method for conditional event routing.
 * @deprecated use `sample({clock, source, filter})` instead
 */
export function guard<A, X extends GetSource<S>, B = any,
  S extends Source<A> = Source<A>,
  C extends Clock<B> = Clock<B>
>(config: {
  source: S,
  clock: C,
  filter: (source: GetSource<S>, clock: GetClock<C>) => source is X,
  name?: string,
  greedy?: boolean
}): GuardResult<X>
// S
/**
 * Method for conditional event routing.
 * @deprecated use `sample({source, filter})` instead
 */
export function guard<A, X extends GetSource<S>,
  S extends Source<A> = Source<A>
>(config: {
  source: S,
  filter: (source: GetSource<S>) => source is X,
  name?: string,
  greedy?: boolean
}): GuardResult<X>
// C
/**
 * Method for conditional event routing.
 * @deprecated use `sample({clock, filter})` instead
 */
export function guard<B, X extends GetClock<C>,
  C extends Clock<B> = Clock<B>
>(config: {
  clock: C,
  filter: (clock: GetClock<C>) => clock is X,
  name?: string,
  greedy?: boolean
}): GuardResult<X>

// ---------------------------------------
/* boolean fn or store: with target */
// SСT
/**
 * Method for conditional event routing.
 * @deprecated use `sample({clock, source, filter, target})` instead
 */
export function guard<A = any, B = any,
  S extends Source<A> = Source<A>,
  C extends Clock<B> = Clock<B>,
  F extends GuardFilterSC<S, C> = GuardFilterSC<S, C>,
  T extends Target = Target
>(config: {
  source: S,
  clock: C,
  filter: F,
  target: MultiTarget<T, GetGuardSource<S, F>>,
  name?: string,
  greedy?: boolean
}): T
// ST
/**
 * Method for conditional event routing.
 * @deprecated use `sample({source, filter, target})` instead
 */
export function guard<A = any,
  S extends Source<A> = Source<A>,
  F extends GuardFilterS<S> = GuardFilterS<S>,
  T extends Target = Target
>(config: {
  source: S,
  filter: F,
  target: MultiTarget<T, GetGuardSource<S, F>>,
  name?: string,
  greedy?: boolean
}): T
// СT
/**
 * Method for conditional event routing.
 * @deprecated use `sample({clock, filter, target})` instead
 */
export function guard<B = any,
  C extends Clock<B> = Clock<B>,
  F extends GuardFilterC<C> = GuardFilterC<C>,
  T extends Target = Target
>(config: {
  clock: C,
  filter: F,
  target: MultiTarget<T, GetGuardClock<C, F>>,
  name?: string,
  greedy?: boolean
}): T

/* boolean fn or store: without target */
// SC (units: BooleanConstructor)
/**
 * Method for conditional event routing.
 * @deprecated use `sample({clock, source, filter})` instead
 */
export function guard<A = any, B = any>(config: {
  source: Unit<A>,
  clock: Unit<B>,
  filter: BooleanConstructor,
  name?: string,
  greedy?: boolean
}): GuardResult<NonFalsy<A>>
// SC (units: boolean fn or store)
/**
 * Method for conditional event routing.
 * @deprecated use `sample({clock, source, filter})` instead
 */
export function guard<A = any, B = any>(config: {
  source: Unit<A>,
  clock: Unit<B>,
  filter: ((source: A, clock: B) => boolean) | Store<boolean>,
  name?: string,
  greedy?: boolean
}): GuardResult<A>
// SC
/**
 * Method for conditional event routing.
 * @deprecated use `sample({clock, source, filter})` instead
 */
export function guard<A = any, B = any,
  S extends Source<A> = Source<A>,
  C extends Clock<B> = Clock<B>,
  F extends GuardFilterSC<S, C> = GuardFilterSC<S, C>,
>(config: {
  source: S,
  clock: C,
  filter: F,
  name?: string,
  greedy?: boolean
}): GuardResult<GetGuardSource<S, F>>
// S (unit: BooleanConstructor)
/**
 * Method for conditional event routing.
 * @deprecated use `sample({source, filter})` instead
 */
export function guard<A = any>(config: {
  source: Unit<A>,
  filter: BooleanConstructor,
  name?: string,
  greedy?: boolean
}): GuardResult<NonFalsy<A>>
// S (unit - boolean fn or store)
/**
 * Method for conditional event routing.
 * @deprecated use `sample({source, filter})` instead
 */
export function guard<A = any>(config: {
  source: Unit<A>,
  filter: ((source: A) => boolean) | Store<boolean>,
  name?: string,
  greedy?: boolean
}): GuardResult<A>
// S
/**
 * Method for conditional event routing.
 * @deprecated use `sample({source, filter})` instead
 */
export function guard<A = any,
  S extends Source<A> = Source<A>,
  F extends GuardFilterS<S> = GuardFilterS<S>,
>(config: {
  source: S,
  filter: F,
  name?: string,
  greedy?: boolean
}): GuardResult<GetGuardSource<S, F>>
// C (unit: boolean fn or store)
/**
 * Method for conditional event routing.
 * @deprecated use `sample({clock, filter})` instead
 */
export function guard<B = any>(config: {
  clock: Unit<B>,
  filter: BooleanConstructor,
  name?: string,
  greedy?: boolean
}): GuardResult<NonFalsy<B>>
// C (unit: boolean fn or store)
/**
 * Method for conditional event routing.
 * @deprecated use `sample({clock, filter})` instead
 */
export function guard<B = any>(config: {
  clock: Unit<B>,
  filter: ((clock: B) => boolean) | Store<boolean>,
  name?: string,
  greedy?: boolean
}): GuardResult<B>
// C
/**
 * Method for conditional event routing.
 * @deprecated use `sample({clock, filter})` instead
 */
export function guard<B = any,
  C extends Clock<B> = Clock<B>,
  F extends GuardFilterC<C> = GuardFilterC<C>,
>(config: {
  clock: C,
  filter: F,
  name?: string,
  greedy?: boolean
}): GuardResult<GetGuardClock<C, F>>

// ---------------------------------------
// guard with source param
// ---------------------------------------

/* user-defined typeguard: with target */
// SСT
/**
 * Method for conditional event routing.
 * @deprecated use `sample({clock, source, filter, target})` instead
 */
export function guard<A, X extends GetSource<S>, B = any,
  S extends Source<A> = Source<A>,
  C extends Clock<B> = Clock<B>,
  T extends Target = Target
>(source: S, config: {
  clock: C,
  filter: (source: GetSource<S>, clock: GetClock<C>) => source is X,
  target: MultiTarget<T, X>,
  name?: string,
  greedy?: boolean
}): T
// ST
/**
 * Method for conditional event routing.
 * @deprecated use `sample({source, filter, target})` instead
 */
export function guard<A, X extends GetSource<S>,
  S extends Source<A> = Source<A>,
  T extends Target = Target
>(source: S, config: {
  filter: (source: GetSource<S>) => source is X,
  target: MultiTarget<T, X>,
  name?: string,
  greedy?: boolean
}): T

/* user-defined typeguard: without target */
// SC
/**
 * Method for conditional event routing.
 * @deprecated use `sample({clock, source, filter})` instead
 */
export function guard<A, X extends GetSource<S>, B = any,
  S extends Source<A> = Source<A>,
  C extends Clock<B> = Clock<B>
>(source: S, config: {
  clock: C,
  filter: (source: GetSource<S>, clock: GetClock<C>) => source is X,
  name?: string,
  greedy?: boolean
}): GuardResult<X>
// S
/**
 * Method for conditional event routing.
 * @deprecated use `sample({source, filter})` instead
 */
export function guard<A, X extends GetSource<S>,
  S extends Source<A> = Source<A>
>(source: S, config: {
  filter: (source: GetSource<S>) => source is X,
  name?: string,
  greedy?: boolean
}): GuardResult<X>

// ---------------------------------------
/* boolean fn or store: with target */
// SСT
/**
 * Method for conditional event routing.
 * @deprecated use `sample({clock, source, filter, target})` instead
 */
export function guard<A = any, B = any,
  S extends Source<A> = Source<A>,
  C extends Clock<B> = Clock<B>,
  F extends GuardFilterSC<S, C> = GuardFilterSC<S, C>,
  T extends Target = Target
>(source: S, config: {
  clock: C,
  filter: F,
  target: MultiTarget<T, GetGuardSource<S, F>>,
  name?: string,
  greedy?: boolean
}): T
// ST
/**
 * Method for conditional event routing.
 * @deprecated use `sample({source, filter, target})` instead
 */
export function guard<A = any,
  S extends Source<A> = Source<A>,
  F extends GuardFilterS<S> = GuardFilterS<S>,
  T extends Target = Target
>(source: S, config: {
  filter: F,
  target: MultiTarget<T, GetGuardSource<S, F>>,
  name?: string,
  greedy?: boolean
}): T

/* boolean fn or store: without target */
// SC (units: BooleanConstructor)
/**
 * Method for conditional event routing.
 * @deprecated use `sample({clock, source, filter})` instead
 */
export function guard<A = any, B = any>(source: Unit<A>, config: {
  clock: Unit<B>,
  filter: BooleanConstructor,
  name?: string,
  greedy?: boolean
}): GuardResult<NonFalsy<A>>
// SC (units: boolean fn or store)
/**
 * Method for conditional event routing.
 * @deprecated use `sample({clock, source, filter})` instead
 */
export function guard<A = any, B = any>(source: Unit<A>, config: {
  clock: Unit<B>,
  filter: ((source: A, clock: B) => boolean) | Store<boolean>,
  name?: string,
  greedy?: boolean
}): GuardResult<A>
// SC
/**
 * Method for conditional event routing.
 * @deprecated use `sample({clock, source, filter})` instead
 */
export function guard<A = any, B = any,
  S extends Source<A> = Source<A>,
  C extends Clock<B> = Clock<B>,
  F extends GuardFilterSC<S, C> = GuardFilterSC<S, C>,
>(source: S, config: {
  clock: C,
  filter: F,
  name?: string,
  greedy?: boolean
}): GuardResult<GetGuardSource<S, F>>
// S (unit: BooleanConstructor)
/**
 * Method for conditional event routing.
 * @deprecated use `sample({source, filter})` instead
 */
export function guard<A = any>(source: Unit<A>, config: {
  filter: BooleanConstructor,
  name?: string,
  greedy?: boolean
}): GuardResult<NonFalsy<A>>
// S (unit: boolean fn or store)
/**
 * Method for conditional event routing.
 * @deprecated use `sample({source, filter})` instead
 */
export function guard<A = any>(source: Unit<A>, config: {
  filter: ((source: A) => boolean) | Store<boolean>,
  name?: string,
  greedy?: boolean
}): GuardResult<A>
// S
/**
 * Method for conditional event routing.
 * @deprecated use `sample({source, filter})` instead
 */
export function guard<A = any,
  S extends Source<A> = Source<A>,
  F extends GuardFilterS<S> = GuardFilterS<S>,
>(source: S, config: {
  filter: F,
  name?: string,
  greedy?: boolean
}): GuardResult<GetGuardSource<S, F>>

// guard's last overload for `guard(source, config)`
/**
 * Method for conditional event routing.
 * @deprecated use `sample({clock, source, filter, target})` instead
 */
export function guard<
  S extends Source<unknown>,
  C extends Clock<unknown>,
  F extends IfUnknown<UnitValue<S>,
    Store<boolean> | ((clock: GetClock<C>) => boolean),
    IfUnknown<UnitValue<C>,
      Store<boolean> | ((source: GetSource<S>) => boolean),
      Store<boolean> | ((source: GetSource<S>, clock: GetClock<C>) => boolean)
      >
    >,
  T extends Target
>(source: S, config: {
  clock?: C,
  filter: F,
  target: F extends (value: any, ...args: any) => value is infer X
    ? MultiTarget<T, X>
    : MultiTarget<T, GetGuardSource<S, F>>,
  name?: string,
  greedy?: boolean
}): T

// guard's last overload for `guard(config)`
/**
 * Method for conditional event routing.
 * @deprecated use `sample({clock, source, filter, target})` instead
 */
export function guard<
  S extends Source<unknown>,
  C extends Clock<unknown>,
  F extends IfUnknown<UnitValue<S>,
    Store<boolean> | ((clock: GetClock<C>) => boolean),
    IfUnknown<UnitValue<C>,
      Store<boolean> | ((source: GetSource<S>) => boolean),
      Store<boolean> | ((source: GetSource<S>, clock: GetClock<C>) => boolean)
      >
    >,
  T extends Target
>(config: {
  source?: S,
  clock?: C,
  filter: F,
  target: F extends (value: any, ...args: any) => value is infer X
    ? MultiTarget<T, X>
    : MultiTarget<T,
        IfUnknown<UnitValue<S>,
          GetGuardClock<C, F>,
          GetGuardSource<S, F>
        >
    >,
  name?: string,
  greedy?: boolean
}): T

/* attach types */

type StoreShape = Store<any> | Combinable
type GetShapeValue<T> = T extends Store<infer S> ? S : GetCombinedValue<T>

/**
 * Method for creating state-dependent effect and transforming effect payloads
 * @returns new effect
 */
export function attach<
  Params,
  States extends StoreShape,
  FX extends Effect<any, any, any>
>(config: {
  source: States
  effect: FX
  mapParams: (params: Params, states: GetShapeValue<States>) => NoInfer<EffectParams<FX>>
  name?: string
}): Effect<Params, EffectResult<FX>, EffectError<FX>>
/**
 * Method for transforming effect payloads
 * @returns new effect
 */
export function attach<FN extends ((params: any) => any), FX extends Effect<any, any, any>>(config: {
  effect: FX
  mapParams: FN extends (...args: any[]) => NoInfer<EffectParams<FX>>
    ? FN
    : never
  name?: string
}): FN extends (...args: infer Args) => NoInfer<EffectParams<FX>>
  ? Effect<
    Args['length'] extends 0
      ? void
      : 0 | 1 extends Args['length']
      ? Args[0] | void
      : Args[0],
    EffectResult<FX>,
    EffectError<FX>
  >
  : never
/**
 * Method for transforming effect payloads
 * @returns new effect
 */
export function attach<Params, FX extends Effect<any, any, any>>(config: {
  effect: FX
  mapParams: (params: Params) => NoInfer<EffectParams<FX>>
  name?: string
}): Effect<Params, EffectResult<FX>, EffectError<FX>>
/**
 * Method for passing state values to effects
 * @returns new effect
 */
export function attach<
  States extends StoreShape,
  FX extends Effect<GetShapeValue<States>, any, any>
>(config: {
  source: States
  effect: FX
  name?: string
}): Effect<void, EffectResult<FX>, EffectError<FX>>
/**
 * Creates state-dependent effect with provided function handler.
 * Allows the one to omit intermediate effects and declare effect handler right next to data source
 * @returns new effect
 */
export function attach<
  States extends StoreShape,
  FX extends (state: GetShapeValue<States>, params: any) => any
>(config: {
  source: States
  effect: FX
  domain?: Domain
  name?: string
}): FX extends (source: any, ...args: infer Args) => infer Done
  ? Effect<OptionalParams<Args>, Awaited<Done>>
  : never
/**
 * Creates independent instance of given effect. Used to add subscribers to effect call in a particular business case
 * @returns new effect linked to given one
 */
export function attach<
  FX extends Effect<any, any, any>,
>(config: {
  effect: FX
  name?: string
}): Effect<EffectParams<FX>, EffectResult<FX>, EffectError<FX>>
/**
 * Method for creating state-dependent effect and transforming effect payload
 * @returns new effect
 */
export function attach<
  States extends StoreShape,
  FX extends Effect<any, any, any>,
  FN extends ((params: any, source: GetShapeValue<States>) => NoInfer<EffectParams<FX>>)
>(config: {
  source: States
  effect: FX
  mapParams: FN
  name?: string
}): Effect<Parameters<FN>[0] , EffectResult<FX>, EffectError<FX>>

type CombineState<State> = State[keyof State] extends Store<any>
  // ensure that CombineState will be used only with explicit generics
  // without Store type in them
  ? never
  : {
    [K in keyof State]:
      | State[K]
      | (undefined extends State[K]
        ? Store<Exclude<State[K], undefined>>
        : Store<State[K]>)
}

/**
 * Bind units and links between them created inside `cb` callback to unit or graph node to erase during `clearNode` call
 * Low level tool, usually absent in common applications
 * @param unit parent unit or graph node
 * @param cb callback assumed to create some units
 */
export function withRegion<T = void>(unit: Unit<any> | Node, cb: () => T): T

/**
 * Helper type for combine
 */
type CombVal<T> = T extends Store<any> ? StoreValue<T> : T

/**
 * Convert given stores to store with array which values updated upon changes in given ones
 * @returns derived store
 */
export function combine<T extends Store<any>>(
  store: T,
): T extends Store<infer R> ? Store<[R]> : never
/**
 * Convert array of stores to store with array which values updated upon changes in given ones
 * @param tuple array of stores
 * @returns derived store updated upon changes in given ones
 */
export function combine<State extends Tuple>(
  tuple: State,
): Store<{[K in keyof State]: State[K] extends Store<infer U> ? U : State[K]}>
/**
 * Convert object with stores to object store which fields updated upon changes in given ones
 * @param shape object with stores
 * @returns derived store updated upon changes in given ones
 */
export function combine<State>(shape: CombineState<State>): Store<State>
/**
 * Convert object with stores to object store which fields updated upon changes in given ones
 * @param shape object with stores
 * @returns derived store updated upon changes in given ones
 */
export function combine<State>(
  shape: State,
): Store<{[K in keyof State]: State[K] extends Store<infer U> ? U : State[K]}>
/**
 * Creates derived store from given one, transforming value using the function
 * @param source source store
 * @param fn transformer function, accepts a store value
 * @returns derived store updated upon changes in given one
 */
export function combine<A, R>(source: Store<A>, fn: (source: A) => R, config?: {skipVoid: boolean}): Store<R>
/**
 * Convert array of stores into derived store, transforming values using the function
 * @param tuple array of stores
 * @param fn transformer function, accepts an array of values
 * @returns derived store updated upon changes in given ones
 */
export function combine<State extends Tuple, R>(
  tuple: State,
  fn: (
    tuple: {[K in keyof State]: State[K] extends Store<infer U> ? U : State[K]},
  ) => R,
  config?: {skipVoid: boolean}
): Store<R>
/**
 * Convert object with stores into derived store, transforming values using the function
 * @param shape object with stores
 * @param fn transformer function, accepts object with values
 * @returns derived store updated upon changes in given ones
 */
export function combine<State, R>(
  shape: State,
  fn: (
    shape: {[K in keyof State]: State[K] extends Store<infer U> ? U : State[K]},
  ) => R,
  config?: {skipVoid: boolean}
): Store<R>
/**
 * Convert given stores into derived store, transforming values using the function
 * @param fn transformer function, accepts store values in separate arguments
 * @returns derived store updated upon changes in given ones
 */
export function combine<A, B, R>(
  a: A,
  b: B,
  fn: (a: CombVal<A>, b: CombVal<B>) => R,
  config?: {skipVoid: boolean}
): Store<R>
/**
 * Convert given stores into derived store, transforming values using the function
 * @param fn transformer function, accepts store values in separate arguments
 * @returns derived store updated upon changes in given ones
 */
export function combine<A, B, C, R>(
  a: A,
  b: B,
  c: C,
  fn: (a: CombVal<A>, b: CombVal<B>, c: CombVal<C>) => R,
  config?: {skipVoid: boolean}
): Store<R>
/**
 * Convert given stores into derived store, transforming values using the function
 * @param fn transformer function, accepts store values in separate arguments
 * @returns derived store updated upon changes in given ones
 */
export function combine<A, B, C, D, R>(
  a: A,
  b: B,
  c: C,
  d: D,
  fn: (a: CombVal<A>, b: CombVal<B>, c: CombVal<C>, d: CombVal<D>) => R,
  config?: {skipVoid: boolean}
): Store<R>
/**
 * Convert given stores into derived store, transforming values using the function
 * @param fn transformer function, accepts store values in separate arguments
 * @returns derived store updated upon changes in given ones
 */
export function combine<A, B, C, D, E, R>(
  a: A,
  b: B,
  c: C,
  d: D,
  e: E,
  fn: (a: CombVal<A>, b: CombVal<B>, c: CombVal<C>, d: CombVal<D>, e: CombVal<E>) => R,
  config?: {skipVoid: boolean}
): Store<R>
/**
 * Convert given stores into derived store, transforming values using the function
 * @param fn transformer function, accepts store values in separate arguments
 * @returns derived store updated upon changes in given ones
 */
export function combine<A, B, C, D, E, F, R>(
  a: A,
  b: B,
  c: C,
  d: D,
  e: E,
  f: F,
  fn: (a: CombVal<A>, b: CombVal<B>, c: CombVal<C>, d: CombVal<D>, e: CombVal<E>, f: CombVal<F>) => R,
  config?: {skipVoid: boolean}
): Store<R>
/**
 * Convert given stores into derived store, transforming values using the function
 * @param fn transformer function, accepts store values in separate arguments
 * @returns derived store updated upon changes in given ones
 */
export function combine<A, B, C, D, E, F, G, R>(
  a: A,
  b: B,
  c: C,
  d: D,
  e: E,
  f: F,
  g: G,
  fn: (a: CombVal<A>, b: CombVal<B>, c: CombVal<C>, d: CombVal<D>, e: CombVal<E>, f: CombVal<F>, g: CombVal<G>) => R,
  config?: {skipVoid: boolean}
): Store<R>
/**
 * Convert given stores into derived store, transforming values using the function
 *
 * > Consider using `combine(arrayOfStores, arrayOfValues => ...)` instead
 *
 * @param fn transformer function, accepts store values in separate arguments
 * @returns derived store updated upon changes in given ones
 */
export function combine<A, B, C, D, E, F, G, H, R>(
  a: A,
  b: B,
  c: C,
  d: D,
  e: E,
  f: F,
  g: G,
  h: H,
  fn: (a: CombVal<A>, b: CombVal<B>, c: CombVal<C>, d: CombVal<D>, e: CombVal<E>, f: CombVal<F>, g: CombVal<G>, h: CombVal<H>) => R,
  config?: {skipVoid: boolean}
): Store<R>
/**
 * Convert given stores into derived store, transforming values using the function
 *
 * > Consider using `combine(arrayOfStores, arrayOfValues => ...)` instead
 *
 * @param fn transformer function, accepts store values in separate arguments
 * @returns derived store updated upon changes in given ones
 */
export function combine<A, B, C, D, E, F, G, H, I, R>(
  a: A,
  b: B,
  c: C,
  d: D,
  e: E,
  f: F,
  g: G,
  h: H,
  i: I,
  fn: (a: CombVal<A>, b: CombVal<B>, c: CombVal<C>, d: CombVal<D>, e: CombVal<E>, f: CombVal<F>, g: CombVal<G>, h: CombVal<H>, i: CombVal<I>) => R,
  config?: {skipVoid: boolean}
): Store<R>
/**
 * Convert given stores into derived store, transforming values using the function
 *
 * > Consider using `combine(arrayOfStores, arrayOfValues => ...)` instead
 *
 * @param fn transformer function, accepts store values in separate arguments
 * @returns derived store updated upon changes in given ones
 */
export function combine<A, B, C, D, E, F, G, H, I, J, R>(
  a: A,
  b: B,
  c: C,
  d: D,
  e: E,
  f: F,
  g: G,
  h: H,
  i: I,
  j: J,
  fn: (a: CombVal<A>, b: CombVal<B>, c: CombVal<C>, d: CombVal<D>, e: CombVal<E>, f: CombVal<F>, g: CombVal<G>, h: CombVal<H>, i: CombVal<I>, j: CombVal<J>) => R,
  config?: {skipVoid: boolean}
): Store<R>
/**
 * Convert given stores into derived store, transforming values using the function
 *
 * > Consider using `combine(arrayOfStores, arrayOfValues => ...)` instead
 *
 * @param fn transformer function, accepts store values in separate arguments
 * @returns derived store updated upon changes in given ones
 */
export function combine<A, B, C, D, E, F, G, H, I, J, K, R>(
  a: A,
  b: B,
  c: C,
  d: D,
  e: E,
  f: F,
  g: G,
  h: H,
  i: I,
  j: J,
  k: K,
  fn: (a: CombVal<A>, b: CombVal<B>, c: CombVal<C>, d: CombVal<D>, e: CombVal<E>, f: CombVal<F>, g: CombVal<G>, h: CombVal<H>, i: CombVal<I>, j: CombVal<J>, k: CombVal<K>) => R,
  config?: {skipVoid: boolean}
): Store<R>
/**
 * Convert given stores to store with array which values updated upon changes in given ones
 * @returns derived store
 */
export function combine<T extends Tuple<Store<any> | any>>(
  ...stores: T
): Store<{[K in keyof T]: T[K] extends Store<infer U> ? U : T[K]}>

/**
 * Fully isolated instance of application. The primary purpose of scope includes SSR and testing
 */
export interface Scope extends Unit<any> {
  getState<T>(store: Store<T>): T
}

export type ValueMap = Map<StoreWritable<any>, any> | Array<[StoreWritable<any>, any]> | {[sid: string]: any}

/**
 * Fill stores with given values in provided scope or domain
 * 
 * @deprecated use `fork({values})` instead
 */
export function hydrate(domainOrScope: Domain | Scope, config: {values: ValueMap}): void

/**
 * Serialize store values from given scope
 * @returns object with saved values
 */
export function serialize(
  scope: Scope,
  options?: {ignore?: Array<Store<any>>; onlyChanges?: boolean},
): {[sid: string]: any}

/**
 * Bind event to a scope to be called later.
 *
 * When `scope` is not provided this method retrieve scope implicitly from scope of the handler (effect handler or watch function) inside which it's being called
 * @param unit event to bind
 * @returns function which will trigger an event in a given scope
 */
export function scopeBind<T>(unit: EventCallable<T>, opts?: {scope?: Scope; safe?: boolean}): (payload: T) => void
/**
 * Bind effect to a scope to be called later.
 *
 * When `scope` is not provided this method retrieve scope implicitly from scope of the handler (effect handler or watch function) inside which it's being called
 * @param unit effect to bind
 * @returns function which will trigger an effect in a given scope and returns a promise with a result
 */
export function scopeBind<P, D, F = Error>(unit: Effect<P, D, F>, opts?: {scope?: Scope; safe?: boolean}): (params: P) => Promise<D>

/**
 * Bind arbitary callback to a scope to be called later.
 * When `scope` is not provided this method retrieve scope implicitly from scope of the handler (effect handler or watch function) inside which it's being called
 * @param unit effect to bind
 * @returns function which will trigger an effect in a given scope and returns a promise with a result
 */
export function scopeBind<T extends Function>(fn: T, opts?: {scope?: Scope; safe?: boolean}): T

// fork types
type Handlers = Map<Effect<any, any, any>, Function> | Array<[Effect<any, any, any>, Function]> | {[sid: string]: Function};
type SerializedState = Record<string, unknown>;
type LegacyMap = Map<StoreWritable<any>, any>

type StorePair<T extends unknown = unknown> = [StoreWritable<T>, T];

/**
 * Creates isolated instance of application. Primary purposes of this method are SSR and testing.
 * @param config optional configuration object with initial store values and effect handlers
 * @returns new scope
 */
export function fork(
  config?: {
    values?: StorePair<any>[] | SerializedState | LegacyMap,
    handlers?: Handlers
  },
): Scope


// legacy overloads
/**
 * Creates isolated instance of application. Primary purposes of this method are SSR and testing.
 * 
 * @deprecated use `fork({values, handlers})` instead
 * 
 * @param domain optional root domain
 * @param config optional configuration object with initial store values and effect handlers
 * @returns new scope
 */
export function fork(
  domain: Domain,
  config?: {
    values?: SerializedState | LegacyMap | Array<[StoreWritable<any>, any]>,
    handlers?: Handlers;
  },
): Scope

/**
 * Run effect in scope and wait for all triggered effects to settle. This method never throw an error
 * @param unit effect to run
 * @returns promise with status object for given effect, will resolve when there will be no pending effects in given scope
 */
export function allSettled<FX extends Effect<any, any, any>>(
  unit: FX,
  config: {scope: Scope; params: EffectParams<FX>},
): Promise<{status: 'done', value: EffectResult<FX>} | {status: 'fail'; value: EffectError<FX>}>
/**
 * Run effect withot arguments in scope and wait for all triggered effects to settle. This method never throw an error
 * @param unit effect to run
 * @returns promise with status object for given effect, will resolve when there will be no pending effects in given scope
 */
export function allSettled<FX extends Effect<void, any, any>>(
  unit: FX,
  config: {scope: Scope},
): Promise<{status: 'done', value: EffectResult<FX>} | {status: 'fail'; value: EffectError<FX>}>
/**
 * Run unit in scope and wait for all triggered effects to settle. This method never throw an error
 * @param unit event or store to run
 * @returns void promise, will resolve when there will be no pending effects in given scope
 */
export function allSettled<T>(
  unit: UnitTargetable<T>,
  config: {scope: Scope; params: T},
): Promise<void>
/**
 * Run unit without arguments in scope and wait for all triggered effects to settle. This method never throw an error
 * @param unit event or store to run
 * @returns void promise, will resolve when there will be no pending effects in given scope
 */
export function allSettled(
  unit: UnitTargetable<void>,
  config: {scope: Scope},
): Promise<void>
/**
 * Check for any ongoing computations in provided scope and wait for them to settle.
 * @param scope {Scope}
 * @returns void promise, will resolve when there will be no pending effects in given scope
 */
export function allSettled(scope: Scope): Promise<void>

export function createWatch<T>({
  unit,
  fn,
  scope,
  batch,
}: {
  unit: Unit<T> | Unit<T>[]
  fn: (value: T) => any
  scope?: Scope
  batch?: boolean
}): Subscription