declare const o: unique symbol;
declare const r: unique symbol;
export type Observer = { readonly [o]: true };
export type Signal<T> = { value: T };
export type Readonly<T> = { readonly data: T };
export type Setter<T> = (v: T) => void;
export type Deriver<U> = { derived: <T>(fn: (p: U) => T) => T };
export type Tilia = {
  /** Transform a regular object or array into a reactive tilia proxy. */
  tilia: <T>(branch: T) => T;
  /** Transform a regular object or array into a reactive tilia proxy, with the possibility to derive state from the object itself. */
  carve: <T>(fn: (deriver: Deriver<T>) => T) => T;
  /** Register a callback to re-run whenever any observed value changes (push reactivity). */
  observe: (fn: () => void) => void;
  /** React to changes: the first function captures values, the second runs when they change. */
  watch: <T>(fn: () => T, effect: (v: T) => void) => void;
  /** Run operations in a batch, blocking notifications until the batch completes. */
  batch: (fn: () => void) => void;
  /** Wrap a primitive in a reactive signal with a `value` field and a setter. */
  signal: <T>(value: T) => Signal<T>;
  /** Derive a signal from other reactive values. */
  derived: <T>(fn: () => T) => Signal<T>;
  /**
   * Return a reactive source value. The setup runs on first read and re-runs
   * when tracked dependencies change. The setter updates the value imperatively.
   */
  source: <T>(initialValue: T, fn: (previous: T, set: Setter<T>) => unknown) => T;
  /**
   * Return a managed value. The setup runs on first access and re-runs when
   * dependencies change. It receives a setter and returns the initial value.
   */
  store: <T>(fn: (set: Setter<T>) => T) => T;
  /** Track key-level writes on a tilia-proxied dict. Returns `{ changes, mute }`. */
  changing: <T>(accessor: () => Record<string, T>, guard?: () => boolean) => Changing<T>;
  /** @internal */
  _observe(callback: () => void): Observer;
};

/**
 * Create a new tilia context with its own `tilia`, `observe`, `batch`, etc.
 * @param gc Maximum cleared watchers before garbage collection (default: 50).
 */
export function make(flush?: (fn: () => void) => void, gc?: number): Tilia;

/** Transform a regular object or array into a reactive tilia proxy. */
export function tilia<T>(branch: T): T;
/** Transform a regular object or array into a reactive tilia proxy, with the possibility to derive state from the object itself. */
export function carve<T>(fn: (deriver: Deriver<T>) => T): T;
/** Register a callback to re-run whenever any observed value changes (push reactivity). */
export function observe(fn: () => void): void;
/**
 * React to changes of captured values. The first function captures values to
 * observe; the second runs with the captured result whenever they change.
 */
export function watch<T>(fn: () => T, effect: (v: T) => void): void;
/** Run operations in a batch, blocking notifications until the batch completes. */
export function batch(fn: () => void): void;

/** Return a computed value. Cached on read, invalidated when observed values change (pull reactivity). */
export function computed<T>(fn: () => T): T;
/**
 * Return a reactive source value. The setup runs on first read and re-runs
 * when tracked dependencies change. The setter updates the value imperatively.
 */
export function source<T>(
  initialValue: T,
  fn: (previous: T, set: Setter<T>) => unknown
): T;
/**
 * Return a managed value. The setup runs on first access and re-runs when
 * dependencies change. It receives a setter and returns the initial value.
 */
export function store<T>(fn: (set: Setter<T>) => T): T;
/** Wrap a value in a readonly holder to avoid tracking. */
export function readonly<T>(data: T): Readonly<T>;
/** Wrap a primitive in a reactive signal with a `value` field and a setter. */
export function signal<T>(value: T): [Signal<T>, Setter<T>];
/** Derive a signal from other reactive values. */
export function derived<T>(fn: () => T): Signal<T>;
/** Lift a signal into a computed value that tracks its inner `value` field. */
export function lift<T>(s: Signal<T>): T;
export interface Changes<T> {
  upsert: T[];
  remove: string[];
}
export interface Changing<T> {
  /** Capture function for `watch`. Drains accumulated changes into `{ upsert, remove }`. */
  changes: () => Changes<T>;
  /** Run a callback with tracking temporarily removed. Includes `batch`. */
  mute: (fn: () => void) => void;
}

/**
 * Track key-level writes on a tilia-proxied dict. Takes an accessor so the
 * tracker follows source swaps. Returns `{ changes, mute }`.
 * `changes` drains accumulated changes: `upsert` contains objects captured at
 * write time, `remove` contains keys of deleted entries. Last write wins per key.
 * `mute` runs code without tracking (for inbound sync writes).
 * Each call creates an independent accumulator. When `guard` returns false,
 * changes accumulate silently; when it becomes true, all accumulated changes drain.
 */
export function changing<T>(accessor: () => Record<string, T>, guard?: () => boolean): Changing<T>;

/** @internal */
export function _observe(callback: () => void): Observer;
/** @internal */
export function _done(observer: Observer): void;
/** @internal */
export function _ready(observer: Observer, notifyIfChanged?: boolean): void;
/** @internal */
export function _clear(observer: Observer): void;
/** @internal */
export function _meta<T>(tree: T): unknown;
/** @internal */
export const _ctx: Tilia;
