/**
* Lume.js TypeScript Definitions
*
* Provides type safety for reactive state management
*/
// ── Kernel types & functions ─────────────────────────────────────────────
// The DOM-free kernel (state, batch, withReadObserver and their types)
// lives in ./state.d.ts — the source of truth for the `lume-js/state`
// entry, which must type-check without lib.dom (Node/worker consumers).
// This entry re-exports the kernel and adds the DOM-flavored API.
export type {
Unsubscribe,
Subscriber,
Plugin,
TypedPlugin,
ReactiveState,
} from './state.js';
export { state, batch, withReadObserver } from './state.js';
import type { ReactiveState, Unsubscribe } from './state.js';
/**
* Handler interface for extending bindDom with custom reactive data-* attributes.
* Create plain objects matching this shape — no framework API needed.
*
* @example
* ```typescript
* const tooltip: Handler = {
* attr: 'data-tooltip',
* apply(el, val) { el.title = val ?? ''; }
* };
* bindDom(root, store, { handlers: [tooltip] });
* ```
*/
export interface Handler {
/** Data attribute name (e.g., 'data-show', 'data-class-active') */
readonly attr: string;
/** Apply the reactive value to the element */
apply(el: HTMLElement, val: any): void;
}
/**
* Options for bindDom function
*/
export interface BindDomOptions {
/**
* Skip auto-wait for DOM, bind immediately
* @default false
*/
immediate?: boolean;
/**
* Additional handlers for reactive data-* attributes.
* Handlers can be individual objects or arrays (auto-flattened).
* User handlers override built-in handlers with the same attr.
*
* Built-in handlers (always active): data-hidden, data-disabled,
* data-checked, data-required, data-aria-expanded, data-aria-hidden
*
* @example
* ```typescript
* import { show, classToggle } from 'lume-js/handlers';
* bindDom(root, store, { handlers: [show, classToggle('active')] });
* ```
*/
handlers?: (Handler | Handler[])[];
}
/**
* Bind reactive state to DOM elements
*
* Supported attributes:
* - `data-bind` - Two-way binding for inputs, textContent for others
* - `data-hidden` - Toggles hidden (truthy = hidden)
* - `data-disabled` - Toggles disabled (truthy = disabled)
* - `data-checked` - Toggles checked (for checkboxes/radios)
* - `data-required` - Toggles required (truthy = required)
* - `data-aria-expanded` - Sets aria-expanded to "true"/"false"
* - `data-aria-hidden` - Sets aria-hidden to "true"/"false"
*
* @param root - Root element to scan
* @param store - Reactive state object
* @param options - Optional configuration
* @returns Cleanup function to remove all bindings
*
* @example
* ```typescript
* const store = state({
* name: 'Alice',
* isHidden: false,
* isDisabled: false,
* menuOpen: false
* });
*
* const cleanup = bindDom(document.body, store);
* ```
*
* HTML:
* ```html
*
*
Content
*
*
* ```
*/
export function bindDom(
root: HTMLElement,
store: ReactiveState,
options?: BindDomOptions
): Unsubscribe;
/**
* Dependency tuple for explicit effect tracking
* Format: [store, key1, key2, ...] where store is a ReactiveState and keys are property names
*/
export type EffectDependency = [ReactiveState, ...string[]];
/**
* Type-safe dependency tuple that validates keys against the state type.
* Use this when you want compile-time validation of dependency keys.
*
* @example
* ```typescript
* const store = state({ count: 0, name: 'Alice' });
*
* // Type-safe: 'count' is validated as a key of store
* const deps: TypedEffectDependency[] = [[store, 'count']];
*
* // Error: 'invalid' is not a key of store
* const badDeps: TypedEffectDependency[] = [[store, 'invalid']];
* ```
*/
export type TypedEffectDependency> =
T extends ReactiveState
? [T, ...(keyof U & string)[]]
: never;
/**
* Create an effect with auto-tracking (default mode)
*
* The effect runs immediately and re-runs when any accessed state properties change.
* Only tracks properties that are actually accessed during execution.
*
* @param fn - Function to run reactively
* @returns Cleanup function to stop the effect
* @throws {Error} If fn is not a function
*
* @example
* ```typescript
* const store = state({ count: 0, name: 'Alice' });
*
* const cleanup = effect(() => {
* // Only tracks 'count' (name not accessed)
* console.log(`Count: ${store.count}`);
* });
*
* store.count = 5; // Effect re-runs
* store.name = 'Bob'; // Effect does NOT re-run
*
* cleanup(); // Stop the effect
* ```
*/
export function effect(fn: () => void): Unsubscribe;
/**
* Create an effect with explicit dependencies (no magic)
*
* The effect runs immediately and re-runs ONLY when specified dependencies change.
* Does not auto-track any state access. Ideal for side-effects like logging.
*
* @param fn - Function to run reactively
* @param deps - Array of [store, key] tuples specifying exact dependencies
* @returns Cleanup function to stop the effect
* @throws {Error} If fn is not a function
*
* @example
* ```typescript
* const store = state({ count: 0 });
*
* // Explicit: only re-runs when store.count changes
* const cleanup = effect(() => {
* analytics.track('count', store.count);
* }, [[store, 'count']]);
*
* cleanup(); // Stop the effect
* ```
*/
export function effect(fn: () => void, deps: EffectDependency[]): Unsubscribe;
// ============================================================================
// Utility Types
// ============================================================================
/**
* Extract the underlying state type from a ReactiveState
*
* @example
* ```typescript
* const store = state({ count: 0, name: 'Alice' });
* type Store = UnwrapReactive;
* // Store = { count: number; name: string }
* ```
*/
export type UnwrapReactive = T extends ReactiveState ? U : never;
/**
* Get all subscribable keys from a reactive state
*
* @example
* ```typescript
* const store = state({ count: 0, name: 'Alice' });
* type Keys = ReactiveKeys;
* // Keys = 'count' | 'name'
* ```
*/
export type ReactiveKeys = T extends ReactiveState ? keyof U : never;
/**
* Make all properties of T required and non-nullable.
* Useful for ensuring complete state initialization.
*/
export type RequiredState = {
[K in keyof T]-?: NonNullable;
};
/**
* Deep readonly type for immutable state snapshots
*/
export type DeepReadonly = T extends object
? { readonly [K in keyof T]: DeepReadonly }
: T;