tilia
Version:
🍃 State management library, domain-driven.
61 lines (48 loc) • 3.29 kB
Plain Text
# Tilia LLM Docs
This index is optimized for AI agents and coding assistants.
## Table of Contents
1. [ReScript guide](./llms-rescript.md)
2. [TypeScript guide](./llms-typescript.md)
## How AI Should Use These Docs
1. Read this file first.
2. Load the language-specific guide.
3. Follow the carve-first rules strictly.
4. Prefer simple, testable composition over inline reactive complexity.
## Non-Negotiable Rules
- Use `carve` as the default way to build feature modules.
- `carve` must bind parts into one reactive object, not contain business logic.
- Keep `carve(({ derived }) => ({ ... }))` glue-only.
- Move logic into standalone functions, then inject `self` via `derived`.
- Prefer `name: derived(name)` where `name` takes `self` as first parameter.
- In TypeScript examples, prefer arrow functions to keep dependency injection paths clean.
## API Anchors
- `carve: (deriver<'a> => 'a) => 'a`
- `deriver.derived: ('p => 'a) => 'a`
- `computed: (unit => 'a) => 'a`
- `source: ('a, ('a, 'a => unit) => 'ignored) => 'a`
- `store: (('a => unit) => 'a) => 'a`
- `tilia: 'a => 'a`
- `changing: (unit => dict<'a>, ~guard: (unit => bool)=?) => { changes: unit => { upsert: array<'a>, remove: array<string> }, mute: (unit => unit) => unit }`
## Coverage Requirement for AI
- Treat `llms-rescript.md` and `llms-typescript.md` as full API references.
- Cover: `tilia`, `carve`, `observe`, `watch`, `batch`, `signal`, `derived`, `lift`, `readonly`, `computed`, `source`, `store`, `changing`, `make`.
- For `source`, always explain:
- setup runs on first read
- setup re-runs when tracked dependencies change
- `set` emits the next value imperatively
- `previous` is the last emitted value
- recommended example: `source(empty(), derived(loader(service)))` where loader sets `stale(previous)` before async fetch
- when used with `carve` + `derived(loader)`, `previous` should be used to keep stale UI data visible during reload (no blink)
- the `derived(loader)` pattern enables conditional loader strategies based on sibling feature fields
- loading state sentinel pattern: use `tilia({})` as initial value; check with `===` identity comparison to distinguish "loading" from "loaded but empty"
- For `changing`, always explain:
- takes an accessor `() => dict` so the tracker can follow source swaps
- tracks key-level writes on a tilia-proxied dict
- returns `{ changes, mute }`: `changes` is a capture function for `watch` that drains accumulated changes as `{ upsert: T[], remove: string[] }`; `mute` runs a callback with tracking suppressed
- `upsert` contains objects captured at write time, `remove` contains keys of deleted entries
- last write wins for same-key overwrites within a batch
- each call creates an independent accumulator
- optional `guard` gates accumulation: when falsy, changes accumulate silently; when truthy, all accumulated changes drain
- `mute` is for inbound sync writes: writes inside `mute` are still reactive but not tracked, preventing feedback loops
- the accessor pattern enables use with `source`-backed data: when `source` replaces the dict, the tracker re-registers on the new object
- `source` handles inbound (loading into reactive data), `changing` + `watch` handles outbound (pushing writes to external)