import type { EventMap, Eventful, EventfulFactory as DefaultEventfulFactory } from 'asljs-eventful'; export interface EventfulFactory { >(object?: T): (T extends undefined ? {} : T) & Eventful; } /** * Options for observable(). */ export interface ObservableOptions { /** * Custom factory to augment the target with eventful API (defaults to * imported `asljs-eventful`). */ eventful?: EventfulFactory | DefaultEventfulFactory; /** * Optional trace hook: `(object, action, payload)` invoked on 'new', 'set', * 'delete', 'define'. */ trace?: ((object: object | Function, action: 'new' | 'set' | 'delete' | 'define', payload?: any) => void) | null; /** * Controls nested conversion for object/array inputs. * * - `false` (default): recursively converts nested objects and arrays. * - `true`: converts only the top-level value. */ shallow?: boolean; } /** Arrays: no 'define' events */ export type ArrayIndex = number; export type ArraySetPayload = { index: ArrayIndex; value: ArrayElement; previous: ArrayElement | undefined; } | { property: 'length'; value: number; previous: number; } | { property: string; value: unknown; previous: unknown; }; export type ArrayDeletePayload = { index: ArrayIndex; previous: ArrayElement | undefined; } | { property: string; previous: unknown; }; export type KeyedArraySetEvents = { [K in ArrayIndex as `set:${PropString}`]: [ { index: K; value: ArrayElement; previous: ArrayElement | undefined; } ]; } & { 'set:length': [ { property: 'length'; value: number; previous: number; } ]; }; export type KeyedArrayDeleteEvents = { [K in ArrayIndex as `delete:${PropString}`]: [ { index: K; previous: ArrayElement | undefined; } ]; }; export type PropString = K extends string ? K : K extends number ? `${K}` : never; export type KeyableObject = Extract; export type ArrayElement = T[number]; export type SetPayloadFor = { property: K; value: T[K]; previous: T[K] | undefined; }; export type DeletePayloadFor = { property: K; previous: T[K] | undefined; }; export type DefinePayloadFor = { property: K; descriptor: PropertyDescriptor; previous: PropertyDescriptor | null; }; export type SetPayload = { [K in keyof T]: SetPayloadFor; }[keyof T]; export type DeletePayload = { [K in keyof T]: DeletePayloadFor; }[keyof T]; export type DefinePayload = { [K in keyof T]: DefinePayloadFor; }[keyof T]; export type KeyedSetEvents = { [K in KeyableObject as `set:${PropString}`]: [ SetPayloadFor> ]; }; export type KeyedDeleteEvents = { [K in KeyableObject as `delete:${PropString}`]: [ DeletePayloadFor> ]; }; export type KeyedDefineEvents = { [K in KeyableObject as `define:${PropString}`]: [ DefinePayloadFor> ]; }; /** Event map for plain objects (include 'define') */ export type ObservableEventsObject = { 'set': [SetPayload]; } & { 'delete': [DeletePayload]; } & { 'define': [DefinePayload]; } & KeyedSetEvents & KeyedDeleteEvents & KeyedDefineEvents; /** Primitives: boxed as { value } and only 'set' events exist */ export type ObservableEventsPrimitive = { 'set': [{ property: 'value'; value: T; previous: T; }]; 'set:value': [{ property: 'value'; value: T; previous: T; }]; }; export type ObservableTraceFn = (object: object | Function, action: 'new' | 'set' | 'delete' | 'define', payload?: any) => void; export interface ObservableGlobalOptions { trace: ObservableTraceFn | null; } export type WatchedValues = { [I in keyof K]: K[I] extends keyof T ? T[K[I]] : never; }; export type ObservableWatchFn = { (target: T, property: string, callback: (value: any) => void): () => boolean; (target: T, properties: K, callback: (...values: any[]) => void): () => boolean; }; export type WatchMethod = { watch: { >(property: K, callback: (value: T[K]) => void): () => boolean; )[]>(properties: K, callback: (...values: WatchedValues) => void): () => boolean; }; }; export type ObservableArray = T & Eventful> & WatchMethod>>; export type ObservableObject = T & Eventful> & WatchMethod>>; export type ObservablePrimitive = { value: T; } & Eventful>; /** * Public observable composition type. * * - objects/arrays include Eventful API and a `watch()` helper. * - primitives are boxed into `{ value }` and include Eventful API. */ export type Observable = T extends readonly any[] ? ObservableArray : T extends object ? ObservableObject : ObservablePrimitive; export type ObservableEventsArray = { 'set': [ArraySetPayload]; } & { 'delete': [ArrayDeletePayload]; } & KeyedArraySetEvents & KeyedArrayDeleteEvents; export type ObservableFn = { /** Array overload */ (value: T, options?: ObservableOptions): ObservableArray; /** Object overload */ (value: T, options?: ObservableOptions): ObservableObject; /** Primitive overload (boxed as { value }) */ (value: T, options?: ObservableOptions): ObservablePrimitive; /** Primitive overload without initial value */ (): ObservablePrimitive; watch: ObservableWatchFn; options: ObservableGlobalOptions; };