import type { ValueReactor } from "../reactor-core/index.ts" import { effect } from "../reactor-core/index.ts" export type ValueInitializer = () => V /** * @description This initializer can be used to create a Signal with an undefined initial value which * is casted to the desired type T. So the real value getter function will not be called * until the Signal is actually used. * * Every signal should always have a valid value (or value getter which returns a valid value). * When using the castValueInitializer, the real value should be set as soon as possible. */ export const castValueInitializer = (): ValueInitializer => { return (): V => { return undefined as unknown as V } } export type Unsubscribe = () => void export interface SubscribeOptions { target: ValueReactor subscriber: (value: V) => void } /** * @description Subscribes to a ValueReactor and invokes the subscriber when the reactor's value * changes. * * The initial value is skipped; the subscriber is called only on subsequent updates. */ export const subscribe = (options: SubscribeOptions): Unsubscribe => { const { target, subscriber } = options const e = effect((context) => { const { isInitializingRun } = context const value = target.get() if (isInitializingRun === false) { subscriber(value) } }) const unsubscribe = (): void => { e.dispose() } return unsubscribe } // oxlint-disable-next-line no-explicit-any export type AnyValueReactorArray = Array> export type GetValuesInArray = { [K in keyof T]: T[K] extends ValueReactor ? U : never } export interface GetValuesInArrayOptions { target: T withoutTrack?: boolean } /** * @description Gets the current values from an array of ValueReactors. */ export const getValuesInArray = ( options: GetValuesInArrayOptions, ): GetValuesInArray => { const valueReactors = options.target const withoutTrack = options?.withoutTrack ?? false const values = valueReactors.map((vr) => { // oxlint-disable-next-line no-unsafe-return return withoutTrack ? vr.getWithoutTrack() : vr.get() }) as unknown as GetValuesInArray return values } // oxlint-disable-next-line no-explicit-any export type AnyValueReactorObject = Record> export type GetValuesInObject = { [K in keyof T]: T[K] extends ValueReactor ? U : never } export interface GetValueInObjectOptions { target: T withoutTrack?: boolean } /** * @description Gets the current values from an object of ValueReactors. */ export const getValuesInObject = ( options: GetValueInObjectOptions, ): GetValuesInObject => { const valueReactors = options.target const withoutTrack = options?.withoutTrack ?? false const values = {} as GetValuesInObject for (const key of Object.keys(valueReactors)) { const valueReactor = valueReactors[key]! const value = withoutTrack ? valueReactor.getWithoutTrack() : valueReactor.get() values[key as unknown as keyof GetValuesInObject] = value } return values }