/** * `arraySignal(initial)` — granular collection signal. * * A keyed-list-friendly variant of `signal()` that emits typed patch events * for every mutation (update / insert / remove / move / replace). When such * a signal is bound to `each(...)` inside a `mount()`, the keyed list * reconciler applies just the patches against the live DOM — no per-item * iteration, no `classifyItems` Map build, no LIS pass over unchanged rows. * * const rows = arraySignal([]); * * rows.update(42, (r) => ({ ...r, label: 'changed' })); // 1 update event * rows.insert(0, { id: 'x', ... }); // 1 insert event * rows.remove(7); // 1 remove event * rows.move(3, 0); // 1 move event * rows.replace([...]); // falls back to snapshot reconcile * * Read-side semantics match a regular signal: `arraySig.value` is a * snapshot, and reads inside `effect()` / `computed()` register as * dependencies, so derived values keep working. */ /** A single granular mutation event. */ type ArrayPatch = { type: 'update'; index: number; item: T; } | { type: 'insert'; index: number; item: T; } | { type: 'remove'; index: number; } | { type: 'move'; from: number; to: number; } | { type: 'replace'; items: readonly T[]; }; /** * Cross-bundle brand for `ArraySignal` instances. `each()` and the * granular reconciler check for this brand instead of `instanceof * ArraySignal`, so the main `kerfjs` barrel can detect arraySignal * inputs without importing the class at runtime — the class lives * only in the `kerfjs/array-signal` subpath, so apps that don't need * granular collections shed ~1 KB. * * Same `Symbol.for(...)`-based pattern as `SafeHtml` (KF-14): cross- * bundle-safe, zero-cost runtime check. */ declare const ARRAY_SIGNAL_BRAND: unique symbol; declare class ArraySignal { private _items; private _version; private _patches; readonly [ARRAY_SIGNAL_BRAND]: true; constructor(initial?: readonly T[]); /** Read-only snapshot. Reads inside an effect/computed register a dependency. */ get value(): readonly T[]; /** * Replace the item at `index` with `fn(currentItem)`. Emits one `update` * patch. Both styles work: returning a fresh object (idiomatic) invalidates * the row by identity, and mutating `item` in place and returning it works * too — a per-item content version (KF-418) makes the same-ref change visible * to every consumer's row memo. */ update(index: number, fn: (item: T) => T): void; /** Insert `item` at `index`. Existing items at index..N shift right. Emits one `insert` patch. */ insert(index: number, item: T): void; /** Append `item` at the end. Sugar for `insert(items.length, item)`. */ push(item: T): void; /** Remove and return the item at `index`. Emits one `remove` patch. */ remove(index: number): T; /** Move the item at `from` to position `to`. Emits one `move` patch (no-op when from === to). */ move(from: number, to: number): void; /** Replace every item. Emits one `replace` patch — the granular reconciler falls back to a full keyed diff for this case. */ replace(items: readonly T[]): void; /** * @internal Used by `each()` when binding this signal to a list. Returns * the queue of granular patches issued since the previous call, then * clears the queue. Best paired with a single binding — a second consumer * in the same render gets an empty array (which forces the snapshot * fall-back path, which is correct but slower). */ _consumePatches(): ArrayPatch[]; } /** Construct an array signal seeded with `initial`. */ declare function arraySignal(initial?: readonly T[]): ArraySignal; export { ARRAY_SIGNAL_BRAND, type ArrayPatch, ArraySignal, arraySignal };