Publishing a precompiled LLui library
When an app derives a reactive value through one of your package's helpers —
text((s) => itemFill(s, index)) // itemFill imported from your package
— the consumer's compiler normally can't see your helper's body (it ships as
compiled dist/*.d.ts), so it conservatively assumes the helper may read the
whole slice and re-evaluates the binding on every change. A dependency
manifest closes that gap: it records what each helper reads, so the consumer
narrows the binding to the exact paths — across the npm boundary.
The manifest
A package ships dist/__llui_deps.json (schema v2). You don't hand-write it —
the analyzer generates it:
node scripts/emit-deps.mjs packages/<your-package>
This runs the @llui/compiler producer (buildManifest) over your src/ and
writes the manifest. It's wired into scripts/publish.sh, so a normal publish
emits a fresh manifest automatically. Requirements:
@llui/compilermust be built first (it is, in the publish/build order).- The manifest ships because
package.jsonalready has"files": ["dist"]. - The consumer resolves it by reading
<pkg>/dist/__llui_deps.jsondirectly.
What gets narrowed (and what doesn't)
The producer emits an entry for every exported helper called as
helper(stateValue, …) — the state value passed directly, the
state.map(s => helper(s)) shape (state-value params). Each state-value
param records the dotted sub-paths the helper reads.
Helpers composed via runtime Signal handles — connect(state.at('x'), send)
/ overlay({...}) — are not narrowed through the manifest: .at('x') already
scopes them at the call site, so their state param is correctly emitted as
opaque. That's expected, not a gap.
Soundness
The manifest only ever makes narrowing more precise; it can never cause a missed update:
- No manifest, version-incompatible manifest, malformed JSON, or a helper the manifest doesn't describe → the consumer coarsens (re-evaluates on any change), exactly as before manifests existed.
- A parameter the producer can't fully characterize (passed whole, element
access
s[k], delegated to an opaque call) is emittedopaque→ coarsen.
So a partial or stale manifest is always safe — it degrades to the pre-manifest behavior, never to a wrong one.
Versioning
The manifest carries version (schema, currently 2) and compilerVersion. A
consumer ignores a manifest whose schema version differs or whose compiler major
doesn't match — again coarsening rather than risking a mismatched read.